On a recent Kata, a question was asked about different modulation approaches for replicated sounds with a focus on reducing the overall number of controls to improve playability.

Here are some notes and examples from that session. Contributions from a number of people including Rio, Alan, Pete, and Vlad.


If you want to build along with these notes, I’ll list these in one order from “right to left” but that doesn’t necessarily mean that would be how I’d build in practice. That often depends on the flow of working through an idea. Also included at the bottom is the kym file with all of this built out with several variations.


Build notes
Open a new sound file or add a replicator to your existing sound and set that up for 16 voices. For this example, that is enough voices to see some of the more complex behavior in action.

The nice thing about using the replicator rather than a script here is that it will be easy to modify the number of replicators to use your use case. I personally default to enabling RenameSpecialEvents and then add which event values you want to generate multiples of. I find that usually there are only a handful which I want replicated and especially in the case where we are trying to reduce the number of real time controls.

Put a SoundtoGlobalController (STGC) into the Replicator. In this example, we are using it mostly for visualization. Name the Generated event to something like !ModOut or something generic. Add that name to your RenameSpecialEvents in the Replicator.

Into the STGC, add AddWrap. An AddWrap is a mixer where if the sum of the inputs is greater than -1 or 1, instead of clipping, the values wrap around. This is particularly useful when doing index or phase manipulation.

Into the AddWrap, add a TimeIndex and a STGC. Copy that STGC into the AddWrap so it is in there twice. A constant is typically used but in this case I am using another SoundToGlobalController so we can monitor how the phase offset is being manipulated. Name the STGC generated !Phase and add that to the Replicated sound.

We put the Spread STGC twice because we want to create a range of 2 so that when the index offset value is -1, we can spread across the full range. Keep this in mind if you replace the Ramp with another source or remove it altogether. You may need to scale the source or if using fixed values, add an offset, or remove one of the Spread STGC depending on your use case.

A scaled, equal weight distribution Spread would have the value: (?VoiceNumber-1)/(?NumberVoices-1) * !Spread

But we may not want equal weight distribution so let’s add distribution curves to the spread. Somewhat new to Kyma is the Capytalk function warpBy that provides an adjustable nonlinear warping function that can vary from logarithmic, to linear, to exponential. Adding that gives us: ((?VoiceNumber-1)/(?NumberVoices-1) warpBy: !Distribution) * !Spread

Note, that the !Distribution control will go from -1 to 1 in the VCS.

Other distribution strategies are also possible. One method is creating arrays and then using the ?VoiceNumber to index into that array. This could be in addition to or in place of the above method.

If we want to add repeating ‘horizontal’ patterns, we can use the GainWrap feature. I recommend using 1 + (!Wrap * (?NumberVoices-1)) here as it will automatically scale based on the number of voices but it is just as valid to hard use 1 + !Wrap or even just !Wrap and scale the fader from 1 to N max wraps in the VCS. There are pros and cons to each approach.

For the TimeIndex, create a hot control for the cycle time at minimum. I also recommend adding a reset button and a freeze function. The freeze function is by adding a toggle that sets the frequency to zero. Within a sound you may want to map reset to !KeyDown and the !Freeze function could be triggered. This is perhaps more interesting when using the modulator in sample playback or time index (granulator, spectral, sample, etc).

So now we have a fairly full featured basic spread. I recommend spending some time with this basic version to get a feel for how the controls interact. We only have a few at this point: Frequency, Spread, Distribution, and Wrap plus the optional freeze and rest to drive modulation across how many replications we desire (example 1).

I’d also spend a bit of time tidying up the VCS so that it is easy to monitor and visualize how the replicas are behaving. I prefer making the replicated controls into an array to make it easier to clean-up and move things around.

As mentioned at the start, we used an index instead of a traditional LFO because it provides a bit more flexibility to drive more complex shapes.

The next step is to add a wavetable reader in the form of the Morph1DWaveshaper after the GainWrap (example 2). Add in a set of wavetables. In these example, we are using bipolar wavetables but that isn’t required.

We can also add a form of PulseWidth or Tilt control. We do this by using the InputOutputCharacteristic prototype to remap input and output values (example 3) to change the zero crossing center point. By using positive only waves like Gaussian or TrianglePos, we can change the length of the rise and fall portions of the wave. An example of this technique is found in Chase Bliss Warped Vinyl and Gravitas pedals.

Next let’s add some random variation. There are a few approaches which can be loosely grouped into correlated and uncorrelated. If you want each voice to be completely uncorrelated then take a noise source, each having unique seed values based on ?VoiceNumber, and run it through a sample and hold.

An example of a correlated approach would be to use a shift register so that each voice is a step along the register and a value is passed to the next voice once it receives a new value. Depending on how this is set up, all the voices will start at the same place and then drift as the ASR fills up or we can seed all the voices with a starting point.

There are additional variations and additions here, for example, using an LFSR, concepts from the Buchla 266, random walk, smoothing, etc.

These examples focus on direct randomization of modulation output but I do also recommend experimenting with randomization elsewhere within the modulation chain.

Spread Examples

SpreadExamples.kym (294.8 KB)

Additional comments
A quicker method than the set up above would to just use an oscillator or the Morph1D oscillator and insert the spread offset into the phase modulation section. Especially, if you don’t need tilt or want to do other processing of the ramp before it hits the wave shaper.

8 Likes