2LiftStudios VCVRack Modules
First of all I want to say thank you to VCVRack and the VCV community. I love the platform and how everyone can experiment and share. When I first discovered VCV, December of 2021, I mostly tried to figure out what it was, what it did, and how to replicate what other people had done. Eventually I was having ideas of my own and learning how to make (what I think are) musically interesting ambient patches. I found that my patches while interesting at any point in time, tended to sound the same across longer periods of time and I was looking for ways to randomly vary the patch without deliberately sequencing sections (e.g. part A, B, A, B, C, D, A, B). I started thinking about how I could link logic modules together to turn things on and off, and things would get complex and I couldn't really wrap my head around all the logic and cabling.
I decided to see if writing some modules that combined some of the function I was looking for would help. And so I started down a different journey. The first few modules I wrote were really just to see if I could build a working module at all. I started simple - with Split and Merge - which of course already exists. But one of the things I always wanted was to pull the low or high note out of a series of notes. So my Merge and Split have a sorting capability - just enough to call it different. My sample and hold has mutliple tracking modes (always, low and high). Not special but different enough that I haven't deleted it. I then started playing arpeggiators thinking I could do something really cool there. Alberti bass anyone? I still haven't gotten that working they way I want, but I made a couple stops along the way to my goal of randomly turning things on and off. I created two modules to help me with that (e.g. Comps, Steps - see below)
The catalyst for finally going public was one of @OmriCohen's patreon videos (March 2023 at 49:18). Someone asked for a probabilistic sequence of notes taken from a set distribution, where each randomly chosen value affected the outcome of subsequent values - similar to pulling items from a basket, and not putting them back in the basket until the basket is empty. As it turns out I had mostly written that (only the other way around - I was putting every item back in the basket right away). So now I have a better version that has two modes - one with dependent probability and one without. And so I have now released it - because I know there is at least one customer (@CryptoTecky).
If you find any of these interesting or useful, then please let me know. If you find bugs or performance issues then please let me know. If you find the function difficult and have ideas on how to improve, then please ... let me know. If you have other ideas, especially in the random probability space, and want to see a module with some new behavior, then consider sending me a note. And with that said, here is a list of the current modules in the plug-in.
Modules for VCV Rack, an open-source Eurorack-style virtual modular synthesizer:
Mac, Linux and Windows builds of the latest version are available through the VCV Rack Library. Find release notes on the releases page.
You'll need to be set up to build VCV Rack itself. The main branch of this module currently builds against Rack 2.1.2. Under the Rack build directory, switch to plugins/
, and then:
git clone https://github.com/zachwieja/2LiftStudios.git
cd 2LiftStudios
make dist
Then copy the ./dist/2LiftStudios directory to your Rack plugins directory
You need to install Inkscape to build the .svg files found in the ./src/res
directories. All of the .svg files are pre-built and in the ./res
subdirectories. However, after cloning the repository, they may appear older than the source files and make might try to rebuild them. Then, if you do not have Inkscape installed, and/or do not have the $(INKSCAPE) variable defined, then the build will fail. An easy workaround is to simply touch
all the .svg files in the ./res
subdirectories.
If you make updates to any of the .svg
files, then there is no avoiding the installation of Inkscape.
COMPS is comprised of seven voltage parameter knobs and seven corresponding GATE ports. A GATE port is high when the IN voltage is strictly greater than (not equal) to the corresponding THRESH (threshold) voltage parameter / knob. Gates are inverted if the invert button for the corresponding gate is depressed. Note that inversion is intentionally imperfect. Negating a > comparision typically results in a <= comparison. In this case, inversion is simply a < comparison. Gates for thresholds that are equal to the input value are never high. A low gate is always 0.0V. High gates default to 10.0V but can be configured via a popup menu to be 1.0V or 5.0V.
The LOGIC gate goes high when the logic condition is met. The condition is configured by (repeatedly) pressing the button next to the LOGIC gate. The three logic conditions are None, Any and _All.
None - the LOGIC gate is high when the number of connected gates is greater than 0 and none of those gates are high.
Any - the LOGIC gate is high when at least one of the connected gates is high.
All - the LOGIC gate is high when all of the connected gates are high
Takes up to 8 monophonic inputs and produces a single polyphonic output. A context menu allows setting the polyphony of the output. Setting the polyphony to a specific number will take the signals from the first N inputs (top to bottom) regardless of connectivity. Unconnected inputs yield 0.0V outputs. There are two automatic/dynamic polyphony modes. Setting the polyphony to "Highest #" sets the polyphony to the highest (bottommost) connected input. Setting the polyphony to "# Connected" sets the polyphony to the number of connected inputs and outputs a polyphonic signal with no gaps (no 0.0V signals for unconnected inputs).
Sorting, if enabled, is done as a function of the polyphony. All included polyphonic channels, as described above, are sorted before sending them to the output. The default order is None and no sorting occurs. The other two sort orders are Ascending and Descending. Channels are sorted and assigned channels starting from zero. Pressing the sort button multiple times toggles the sort order.
Sorting is useful for finding the highest or lowest V/OCT and as input to arpeggiators. Sorting polyphonic audio rate signals is possible - though perhaps less useful.
Is comprised of two polyphonic quantizers. Each quantizer takes a polyphonic input chord, and a second polyphonic input representing values that are quantized to the values in the input chord. The quantized values are emitted on the polyphonic output.
The module supports four modes: CLOSEST_DOWN, DOWN, CLOSEST_UP, and UP. The CLOSEST modes quantizes to the closest pitch from the input chord. In the case where the input value lands precisely between two values, then the its takes the lower or higher value depending on the mode, CLOSEST_DOWN or CLOSEST_UP respectively. For instance, if the input chord contains the notes C4 and E4, and the input to be quantized is D4, then the _DOWN or _UP designation determines the result. The DOWN and UP modes, quantize to the next lowest or next highest value respectively. For instance, if the polyphonic chord contains the values C4 and G4, and the mode is DOWN, then the values C4 through F#4 all quantize to C4, and the values G4 through B4 all quantize to G4. The default mode is CLOSEST_DOWN.
Each quantizer in the module has its own OCTAVE parameter. The value can be in the range -5 to +5, and is added to the quantized value.
ProbS produces a random sequence of notes based on a weighted distribution. ProbS allows setting of up to six WEIGHT values that define a probability distribution. For instance, setting four WEIGHT values to 1, 2, 3, and 4 results in a total weight of 10 and a probability of 1/10th, 2/10ths, 3/10ths and 4/10ths respectively. At each CLOCK, a random number is generated, and a corresponding OFFSET value is chosen based on the probabilty distribution. ProbS has two modes which can be set using the MODE parameter.
In Stochastic mode, the generated OFFSET values will approach the specified weighted distribution. This is much like flipping a coin. The outcome of previous coin flips do not affect subsequent coin flips. You can receive the same result over and over again, but in the long run you expect the outcome to approach the distrubution - 50/50 for coin flips.
In Frequency mode, previous outcomes do affect subsequent outcomes. This is similar to drawing items from a basket and setting them aside until the entire basket is empty - then putting everything back in the basket.
OFFSET values are generated whenever the combined CLOCK input and MANUAL clock button generate a leading edge - when the previous state was off/low for both, and at least one of them is now high.
OFFSET values can be set in the range [-10V .. 10V]. They are typically set to voltages representing pitch, but can be set to any voltage and used to probabalistically drive scenes and other module parameters. For any given CLOCK, the light next to the chosen OFFSET value is illuminated.
Changing the OFFSET value while it is currently selected (light is illuminated) will have immediate affect (before the next CLOCK)
WEIGHT values are integers in the range [0, 100]. If a given WEIGHT value is set to zero, then the corresponding OFFSET value will never be chosen. The probability of any single OFFSET value being chosen is equal to the WEIGHT of the corresponding OFFSET divided by the sum of the weights.
The RESET input has no effect in Stochastic mode. In Frequency mode, it restores all the WEIGHTS values to the currently set distribution (i.e. puts all the items "back in the basket").
When in Stochastic mode, changing the WEIGHT of any OFFSET has immediate affect - changes the probability on the next CLOCK. When in Frequency mode, adding WEIGHT is always added to the set of already generated values. Added WEIGHT affects the probability once all the values have been generated - or on the next RESET. When subtracting weight in Frequency mode, the module will first attempt to remove the weight from already generated values (so as not to affect the current cycle). If an insufficient number of values have been generated in the current cycle, then values are removed from the remaining ungenerated values.
SandH is comprised of two polyphonic sample and hold sub-modules. The sub-modules are completely independent and can each operate in one of four different modes.
Track - in this mode, the GATE is ignored, and SandH is simply a pass through. Channel values on the IN are continuously copied to the corresponding OUT channels. If no IN is connected, then a noise value is generated for each GATE channel.
TrackHigh - for any given channel, if the GATE is high, the corresponding OUT channel tracks (samples and copies) the IN. When the GATE goes low, the channel stops tracking and outputs the last sample (just before the gate went low) until the GATE goes high again - at which point it starts tracking again. If no IN is connected, then a noise value is generated for each GATE channel.
TrackLow - for any given channel, if the GATE is low, the corresponding OUT channel tracks (samples and copies) the IN. When the GATE goes high, the channel stops tracking and outputs the last sample (just before the gate went high) until the GATE goes low again - at which point it starts tracking again. If no IN is connected, then a noise value is generated for each GATE channel.
SampleAndHold - for any given channel, when the GATE goes high (leading edge), the IN value for the corresponding channel is sampled, and copied to the matching OUT channel, until the next GATE for that channel. If no IN is connected, then a noise value is generated for each GATE channel.
A GATE is high when either the channel input is high or the MANUAL gate button is held down. The GATE is low when the channel input is low and the MANUAL button is not held down.
A leading edge (needed for SampleAndHold) occurs when either the GATE is high or the MANUAL button is held down, but neither were high/held down before. For instance, assume a GATE is low, and the MANUAL button is not held down. If the MANUAL button is now pressed and held down, then that generates a leading edge. While the button is held down, if the one or more channels for the GATE go from low to high, then no leading edge is detected (since the MANUAL button is still being held down).
There are two different types of noise Random and Gaussian. Random noise is simply a random value in the specified voltage range. There are no guarantees as to constant power, etc. Gaussian is implemented as white noise (with power guarantees). The default is set to Random, because it is consumes less CPU. Running 16 gates into both sub-modules with no input would generate 32 separate noise values (which consumes alot of CPU).
If there are more IN channels than GATE channels, then the last gate is used for the IN channels without a corresponding GATE. If there are more GATE channels than IN channels, then a noise value is generated for the GATE channels that have no corresponding IN channel. This effectively means, if no input is connected, then a noise value is generated for the input.
If there is no IN or GATE connected, then a single noise value is emitted. This value is still goverened by the tracking mode. That is, with no GATE connected, the GATE input is always low. If the MODE is set to Track, or TrackLow, then a single continuously changing noise value will be sent to the OUT. If the MODE is set to TrackHigh, then a continously changing value will be sent to the OUT when the MANUAL gate button is depressed. If the MODE is set to SampleAndHold, then the last sampled value (from the last gate (either from the GATE input or the MANUAL button)) is sent to the OUT.
The MANUAL gate button affects all channels.
Takes a single polyphonic input and spreads the first 8 channels across the first N outputs. Outputs with a signal have a small green light next to them.
Sorting, if enabled, is done as a function of the polyphony. All included polyphonic channels, as described above, are sorted before sending them to the output. The default order is None and no sorting occurs. The other two sort orders are Ascending and Descending. Channels are sorted and assigned channels starting from zero. Pressing the sort button multiple times toggles the sort order.
Produces a set of stepped voltages starting from a root voltage and then changing the voltage, based on the MODE, each time the module receives a CLOCK (or a RESET). There are five modes: Increment, Decrement, Exclusive, Inclusive and Random.
Increment, the output voltage starts at the ROOT and moves by the STEP voltage at each _CLOCK. After LENGTH iterations, the value wraps and the process repeats starting at the the ROOT.
Decrement, the output voltage starts at the ROOT voltage + (STEP voltage * LENGTH) and moves by the STEP voltage at each CLOCK - back toward the ROOT. After LENGTH iterations, the value wraps back to the original starting value and the process repeats.
Exclusive, the output voltage starts at the ROOT voltage and moves by the STEP voltage at each _CLOCK. After LENGTH iterations, the steps reverse and the OUT voltage starts moving back toward the ROOT - at which point it reverses again and the process repeats. In Exclusive mode the OUT voltage at the beginning and end of the sequence are not repated. For instance, with a ROOT of 0V, a STEP of 1V and a LENGTH of 4, the output voltages will be 0, 1, 2, 3, 2, 1, 0, 1, 2, ...
Inclusive, the output voltage starts at the ROOT voltage and moves by the STEP voltage at each _CLOCK. After LENGTH iterations, the steps reverse and the OUT voltage starts moving back toward the ROOT - at which point it reverses again and the process repeats. In Inclusive mode the OUT voltages at beginning and end of the sequence are repeated once. For instance, with a ROOT of 0V, a STEP of 1V and a LENGTH of 4, the output voltages will be 0, 1, 2, 3, 3, 2, 1, 0, 0, 1, 2, ...
Random, the output voltage is computed using a random integer value between 0 and LENGTH - 1 which is multiplied by the STEP voltage and added to the ROOT voltage.
The leading edge of a clock signal is defined as any non-positive voltage going positive. Any positive value, no matter how small, will trigger the CLOCK and step the OUT voltage toward its next value.
The LENGTH can be anything between 2 and 100 inclusive. A LENGTH of 1 would play the same note over and over again which would be better served by a different / simpler module.
For all modes this resets the sequence and moves the output voltage back to the starting voltage - For Increment, Exclusive and Inclusive this is the ROOT voltage. For Decrement this is the ROOT voltage + STEP voltage * LENGTH.
This is the voltage change at each step. The value can be positive or negative. Both initially move away from the ROOT voltage for Increment, Inclusive, and Exclusive modes (before snapping or moving back toward the ROOT voltage). Decrement starts the furthest from the ROOT voltage and then moves toward the ROOT. Random mode returns values that are multiples of the STEP voltage offset from the ROOT voltage.
Sorting, if enabled, is done across all incoming channels. If the polyphony on the cable is N and the actual number of signals is < N, then the zeroes coming in on those unused channels will be included in the sort. The default sorting order is None and channels are routed to the outputs as you would expect. The other two orders are Ascending an Descending.
When the LENGTH value is reduced between successive CLOCK signals and the current step is beyond the new LENGTH, the next step is adjusted - depending on the mode. For Increment mode the current step is set to 0 (the first step). For Decrement, Inclusive, and Exclusive, the value is set to LENGTH - 1.
DO NOT USE THIS - It is a work in progress and buggy (memory corruption and will crash VCV Rack)
Takes a single polyphonic input, quantizes each channel to the closest note within an evenly tempered scale, and copies the quantized values to the output. This module does not support non-symmetric scales (different values on they way down). There are three separate sets of controls for the the scale, root and octave parameters.
The scale can be set to any of 44 preconfigued scales. The names of the scales will appear in the hover text when spinning the knob. If the CV input is connected, it completely overrides the knob. That is, it is not additive. Valid CV values start at 0.0V and each 0.1V will change to the next scale. This allows for up to 100 scales and allows for backward compatibility when adding scales. Values below or above the range of scales result in selection of the first or last scale respectively. The supported scales are documented here here
The root knob raises or lowers the voltage of the root note by 0 to N-1 steps. For instance, in a typical 12 step scale, each step represents 1/12 volts, yielding an overall range of [-11/12, +11/12] volts. A quartertone scale yields a range of [-23/24, +23/24] volts. Connecting the CV input is additive. The combined values are clamped at [-(N-1)/N, (N-1)/N] volts, where N is the number of steps in the scale (12 for most scales). The CV value is treated as monophonic. The value of the first channel is used across all channels of the IN input.
The octave note adds from -5V to +5V in 1V increments to the output signal to raise or lower the pitch by an equivalent number of octaves. If the CV input is connected, the voltage is added to the value from the knob and then the floor of the resultant value is clamped to the range [-5V, +5V]. For instance, a knob value of +1V added to a CV input of 1.333V results in an overall +2V (or 2 octaves). Subtracting 1.333V from 1V yields -0.333V which is then floored to -1V. The CV value is treated as monophonic. The value of the first channel is used across all channels of the IN input.
The input can be polyphonic. Each channel is clamped to [-5V, +5V]. All channels are affected equally by the scale, root and octave parameters.
The output has the same number of channels as the input. Each channel is clamped to [-5V, +5V].
If the root knob (and or combined root CV) is non-zero, and the scale is changed to one with a different number of steps (for instance between any 12 step scale to the 24 step quartertone scale), the the root value is adjusted to snap to the closest root within that scale. For instance, root offsets for a 12 step scale are in the range [1/12V, 11/12V]. If the root is set to 6/12V, and the scale is changed to a 53 step evenly tempered scale, then the root is recalibrated to the closest step in that scale - which is 26/53V.
All notes are quantized to the closest numerical/mathematical note in the scale. For instance, the C major scale is modeled as 7 notes over an evenly tempered 12 step scale with intervals of 2, 2, 1, 2, 2, 2 and 1 steps. The values for those 7 notes are equivalent to 0, 2/12, 4/12, 5/12, 7/12, 9/12 and 11/12 volts. The root and octave offsets are added to the input value and then the decimal portion of the voltage is snapped to one of those voltages. Any decimal portion in the range [-23/24V, 1/12V) snaps to 0V. Anything in the range [1/12V, 3/12V) snaps to 2/12V. Anything in the range [3/12V, 9/24V) snaps to 4/12V and so forth. Anything in the range [23/24V, 26/24V) snaps to 1V. Note the use of range notation. Open square brackets indicate inclusive values, and closing parenthesis indicate exclusive values.
The VCASR module is a combined polyphonic VCA and Attack, Sustain, Release envelope generator. It takes a polyphonic IN and varies the signal(s) sent to the OUT based on the corresponding GATE signal(s) and the currently defined envelope parameters.
The IN input is polyphonic. The number of channels on the IN will be copied to the OUT, EOC and ENV outputs.
The GATE input is polyphonic. If the number of GATE channels is less than the number of IN channels, then the last gate is used for the higher IN channels. In Gated mode, a new ASR cycle starts each time a GATE goes high (leading edge). In Triggered mode a new ASR cycle starts every other time the GATE goes high. The MANUAL gate button affects all channels.
The ATTACK, and RELEASE parameters are expressed in seconds. The maximum is 60 seconds. The default is 10 seconds. The SUSTAIN parameter is expressed as a percentage of the input signal. This module currently only supports a linear attack and release.
THe OUT, EOC (end-of-cycle), and ENV outputs are all polyphonic based on IN channels. The EOC emits a trigger at the end of the release cycle. If a new attack cycle starts before the end of the release, then no EOC trigger is generated for that cycle.
There are two modes which can be set using the context menu.
Gated - Each channel starts a new cycle on the leading edge of the GATE. It attacks for the configured number of seconds, and then sustains until the trailing edge of the corresponding GATE, and then releases for the configured number of seconds.
Triggered - Each channel starts a new cycle on the leading edge of the GATE. It attacks for the configured number of seconds, and then sustains until the next leading edge of the corresponding GATE, and then releases for the configured number of seconds.
All of the modules are themed. There are two official themes: Light and _Dark. You can switch the theme of any module using the context menu for that module. You can also switch the default theme - which affects any newly instantiated modules.
If you are source code savvy and are willing to build the modules from scratch, then you will note in the ./scripts directory that there are several sed scripts that generate additional themes to match some of my favorite module collections. Each script does a global substitution of the 8 different colors used in the source .svg files.
s/fill:#e6e6e6/fill:#rrggbb/g
s/fill:#fafafa/fill:#rrggbb/g
s/fill:#bbbbbb/fill:#rrggbb/g
s/fill:#010101/fill:#rrggbb/g
s/fill:#020202/fill:#rrggbb/g
s/fill:#030303/fill:#rrggbb/g
s/stroke:#fafafa/stroke:#rrggbb/g
s/stroke:#bbbbbb/stroke:#rrggbb/g
You can add themes to the file ./src/common/Themes.cpp
, simply cut and paste an existing theme, and then change the theme name. Then go to the ./Makefile
and add the rules for the theme - instructions are in the Makefile.