Smooth Clipping and Folding a suite of pseudo ugens for smooth clipping and folding
Part of: miSCellaneous
See also: SmoothClipS, SmoothClipQ, SmoothFoldS, SmoothFoldQ, SmoothFoldS2, SmoothFoldQ2
Wave folding is a synthesis technique from analog days, going back to Donald Buchla and the tradition of west coast synthesis. Smooth clipping and folding pseudo ugens from miSCellaneous lib come in variants which include quadratic and sinusoidal waveshaping and allow clipping and folding without aliasing. This can also be used for buffer scratching – a synthesis technique which I have been experimenting with recently with great fun.
Ex. 1: Different types of folding
// A typical usage is preamplifying a signal, here we start with a sine wave, compare plots
{
[
// just smooth clipping
SmoothClipS.ar(SinOsc.ar(50) * 10),
// folding with main lib's Fold ugen
Fold.ar(SinOsc.ar(50) * 10, -1, 1),
// folding with rather low smoothing
// wave shaper is partiallly a sine wave
SmoothFoldS.ar(SinOsc.ar(50) * 10, smoothAmount: 0.3),
// folding with maximum smoothing
// wave shaper is full sine wave
SmoothFoldS.ar(SinOsc.ar(50) * 10, smoothAmount: 1),
// wave is folded back only to border ranges
SmoothFoldS.ar(SinOsc.ar(50) * 10, foldRange: 0.3),
// folding with different sizes of border ranges
SmoothFoldS2.ar(SinOsc.ar(50) * 10, foldRangeLo: 0.5, foldRangeHi: 0.2)
]
}.plot(1/50)
Ex. 2: Generating rich spectra by folding sine waves
// Folding ugens do multichannel expansion, let two anticyclic sines control the fold range,
// control smoothing amount with MouseX
(
x = {
var source = SinOsc.ar(50);
SmoothFoldS.ar(source, -0.1, 0.1, SinOsc.kr(0.05, [0, pi]).range(0.1, 1), MouseX.kr(0, 1))
}.scope
)
x.release
// Compare with the parabolic smoothing variant, the difference isn't great in this case
(
x = {
var source = SinOsc.ar(50);
SmoothFoldQ.ar(source, -0.1, 0.1, SinOsc.kr(0.05, [0, pi]).range(0.1, 1), MouseX.kr(0, 1))
}.scope
)
x.release
// slow modulations of source frequency with independant LFOs
(
x = {
var source = SinOsc.ar(50 * { LFDNoise3.kr(0.1).range(0.98, 1.02) } ! 2);
SmoothFoldS.ar(source, -0.1, 0.1, SinOsc.kr(0.05, [0, pi]).range(0.1, 1))
}.scope
)
x.release
// Adding more complexity by applying preamplification (causes more folding) and adding an offset,
// these operations are also L/R-independant
(
x = {
var source = SinOsc.ar(
50 * { LFDNoise3.kr(0.1).range(0.98, 1.02) } ! 2,
0,
{ LFDNoise3.kr(0.15).range(0.5, 3) } ! 2,
{ LFDNoise3.kr(0.2).range(-2, 2) } ! 2
);
SmoothFoldS.ar(source, -0.1, 0.1, SinOsc.kr(0.05, [0, pi]).range(0.1, 1))
}.scope
)
x.release
Ex. 3: Applying modulated folding to LFO sources
// the other way round, take a lfo source and modulate folding parameters, here the relative folding range
(
x = {
var source = LFDNoise3.ar(0.3!2).range(0.5, 1);
SmoothFoldS.ar(source, -0.1, 0.1, SinOsc.ar([50, 50.1]).range(0.1, 1) )
}.scope
)
x.release
// modulating fold bounds
(
x = {
var source = LFDNoise3.ar(0.3!2).range(0.5, 1);
var bounds = SinOsc.ar([50, 50.1]).range(0.02, 0.1);
SmoothFoldS.ar(source, bounds.neg, bounds)
}.scope
)
x.release
// modulating bounds and range
(
x = {
var source = LFDNoise3.ar(0.3!2).range(0.5, 1);
var range = SinOsc.ar([50, 50.1]).range(0.02, 0.1);
SmoothFoldS.ar(source, range.neg, range, SinOsc.ar([200, 200.1]).range(0.5, 1))
}.scope
)
x.release
Ex. 4: Buffer scratching with folded signal as position control
// Interesting micro textures can be generated that way.
// Technically this is waveshaping with an audio buffer as transfer function and the folded signal as source.
// compare with granulation, sound file from buffer granulation tutorial
b = Buffer.read(s, Platform.miSCellaneousDirs[0] +/+ "Sounds" +/+ "kitchen_sounds_1.wav");
// This searches the most likely extension places for the miSCellaneous folder.
// In case of an extraordinary install situation or a removed sound file, pass the concerned path.
(
SynthDef(\bufScratchFold, { |bufnum = 0, globalFreq = 0.7, localOscSize = 0.01, foldRange = 0.28,
localFreq = 0.87, preAmp = 1.4, smoothAmount = 0.36|
var sig = BufRd.ar(
1,
bufnum,
(
// define global and local movement
LFDNoise3.ar(globalFreq).range(0.2, 0.7) +
SmoothFoldS.ar(
// adding space by decorrelating the local scratching / oscillation
LFTri.ar(localFreq * ({ LFDNoise3.ar(0.2).range(0.999, 1.001) } ! 2)) * preAmp,
foldRange: foldRange,
smoothAmount: smoothAmount
) * localOscSize
) * BufFrames.ir(bufnum)
);
// as local oscillation can stick with positive or negative values, a dc leaker is recommended
Out.ar(0, LeakDC.ar(sig) * EnvGate.new)
}).add
)
x = Synth(\bufScratchFold, [bufnum: b])
x.set(\preAmp, 5.4)
x.set(\foldRange, 0.08)
x.set(\localFreq, 0.5)
x.set(\localOscSize, 0.05)
x.set(\foldRange, 0.02)
x.set(\localFreq, 0.1)
x.release