a few days ago I stumbled upon a nice playlist of PureData tutorials by SoundSimulator and, in particular, I was impressed by the video regarding the glitch and wanted to bring the PureData code that is shown in the video, into SuperCollider.
The part of the PD code that I tried to translate is as follows:
and, at the moment, my current translation in SC language is following:
(
SynthDef(\bplay, {
|out=0, amp=1.0, buf, fb=1.0|
var glitch = MouseButton.kr(0, 1);
var localBuf = LocalBuf.new( SampleRate.ir(), 1);
var original = PlayBuf.ar(1, buf, BufRateScale.kr(buf), 1, loop:1.0) * (1-glitch);
var tapPhase = LocalIn.ar(1);
var delay = DelTapRd.ar(localBuf, tapPhase, MouseY.kr(0.01, 0.7), 2, mul:fb) * glitch;
var sig = original + delay;
LocalOut.ar( DelTapWr.ar(localBuf, sig) );
sig = sig * amp;
Out.ar(out, sig!2);
}).add;
)
In my synth it is the mouse button that activates or deactivates the ârepeaterâ effect while the drag allows you to shorten or lengthen the ârepeatedâ portion of the audio.
You can try yourself following the steps below:
// 1. boot the server
s.boot;
// 2. load your audiofile
~sample = Buffer.readChannel(s, "/path/to/my/sound/file.wav", channels:0);
// 3. instantiate the synth
x = Synth(\bplay, [\buf, ~sample]);
// 4. play with the mouse :)
// 5. eventually free the synth
x.free;
While the general sound behavior of the synthesizer in SC seems similar to that in Puredata, I noticed one important difference: when I change the delay duration - dragging with the mouse - the sound undergoes drifts in pitch. The pitch increases when the delay time is shortened and decreases instead when you allug the delay time. Just as if it were a tape delay.
While I have researched this effect in some cases in the past, in this particular context I would like it not to be there instead.
How do I modify my implementation to achieve the desired effect?
Thank you so much for your support
One thing is that the Pd patch isnât ramping the delay time â itâs just instantaneously jumping. (delread~ canât smooth out delay time changes because itâs a control input, not a signal input. delread4~ can.)
But MouseY has a built in lag.
So at minimum, you might try disabling the lag by setting it to 0, and see if the sound is closer to the original.
Thank you guys for your support, @jamshark70 thank you for pointing out the delread~ object behaviour in PureData.
I was eventually able to get quite near to the same acoustic effect setting the lag to 0.0 for the MouseY object.
n
Hi, Iâm coming back to this topic almost a year later to ask your opinion on this beatRepeater/glitcher Iâm trying to implement.
Following @jamshark70 suggestion I had actually managed to make a synth that didnât incur the unwanted pitch-shift when the delay time changed (by setting the lag value to 0 for MouseY).
(
SynthDef(\glitcher, {
|out=0, amp=1.0, buf, fb=1.0|
var glitch = MouseButton.kr(0, 1);
var localBuf = LocalBuf.new( SampleRate.ir(), 1);
var original = PlayBuf.ar(1, buf, BufRateScale.kr(buf), 1, loop:1.0) * (1-glitch);
var tapPhase = LocalIn.ar(1);
var delay = DelTapRd.ar(localBuf, tapPhase, MouseY.kr(0.01, 0.7, lag:0.0), 2, mul:fb) * glitch;
var sig = original + delay;
LocalOut.ar( DelTapWr.ar(localBuf, sig) );
sig = sig * amp;
Out.ar(out, sig!2);
}).add;
)
However, as I continue to test the synth, it is evident how essentially different the sound quality of the glitch it produces is compared to the sound that can be obtained from the PureData patch.
In order to test this difference, I prepared a setup to play and record the two implementations of the âglitcherâ simultaneously.
I connected an audio stream to the input of both Synths and used a MIDI controller to send the two implementations:
the signal to activate/deactivate the âglitchingâ effect;
s.boot;
(
SynthDef(\glitcher, {
|out=1, amp=0.75, dly=0.25, fb=1.0, glitch=0|
var localBuf = LocalBuf.new( SampleRate.ir(), 1); // 1ch, 1 second of buffer
var original = In.ar(2,1) * (1-glitch);
var tapPhase = LocalIn.ar(1);
var delay = DelTapRd.ar(localBuf, tapPhase, dly, mul:fb) * glitch;
var sig = original + delay;
LocalOut.ar( DelTapWr.ar(localBuf, sig) );
sig = sig * amp;
Out.ar(out, sig);
}).add;
)
x = Synth(\glitcher);
// MIDI Controller to drive the synth
// (the same controls will be used to control thje PureData implementation)
MIDIClient.init;
MIDIIn.connectAll;
~glitch_status = 0;
~glitch_dly = 0.25;
(
MIDIdef.noteOn(\activate_effect, {
arg ...args;
if( args[1] == 9, {
~glitch_status = (~glitch_status + 1) % 2;
("glitch activate: " + ~glitch_status).postln;
// Emit a pulse to be recorded in order to visually
// see where the effect was activated/deactivated
{Out.ar(0, EnvGen.ar(Env.perc(0.0, 1), doneAction:2) * Impulse.ar(0)!2)}.play;
x.set(\glitch, ~glitch_status);
});
});
MIDIdef.cc(\change_delay_time, {
arg ...args;
if(args[1] == 21, {
var value = args[0] / 127;
value = (value * 490) + 10;
value = value / 1000.0; // expressed in seconds
~glitch_dly = value;
("glitch delay time: " + ~glitch_dly).postln;
x.set(\dly, ~glitch_dly);
});
});
)
I immediately started sending a sine wave to the two synths and recording the output within Reaper. You can see, and hear very well, the auditory differences between the two audio outputs.
In the images below, obtained after recording the outputs, you can see how the signal output from SuperCollider (bottom track) is essentially different from that obtained from PureData (top track).
The audio result can also be heard in this audio file:
in the left channel the output of PureData;
in the right channel the output from SuperCollider;
It is evident that the artefacts/glitches in PureDataâs output are âcleanerâ while SuperColliderâs output is muddier, greasier, more imprecise and overloaded with harmonics.
Why this difference?
I suppose it could be some difference in the implementation I have done in SuperCollider.
Perhaps something to do with order-of-execution of the various elements in the audio chain?
Or maybe something to do with not taking into account the inherent block-size delay between writing and reading the local buffer by DelTapWr and DelTapRd?
TL;DR Make sure that the delay time input to DelTapRd is audio rate, not control rate.
I had a feeling yesterday this was probably the case, but wasnât sure enough to post speculation that might not have panned out. But now I can prove it
SC pluginsâ convention when using a control rate input to an audio rate UGen is to perform a linear ramp from the old value to the new value within each control block.
Following @jamshark70âs suggestion I had actually managed to make a synth that didnât incur the unwanted pitch-shift when the delay time changed (by setting the lag value to 0 for MouseY).
From this, I take it that your intention is to avoid any sort of ramp in the delay time. But, DelTapRdâs internal up-sampling of the control-rate delay time adds its own ramp.
s.boot;
b = Buffer.alloc(s, s.sampleRate * 0.02, 1);
(
{
var sig = SinOsc.ar(440);
var phase = DelTapWr.ar(b, sig);
var tfreq = SampleRate.ir * 0.01;
var trig = Impulse.kr(tfreq);
// kr delay time, analogous to your current synth
var deltimeK = TRand.kr(0, b.duration - 0.001, trig);
// a trick to upsample K-->A without any ramp
// you can't use K2A for this because it ramps
var deltimeA = Demand.ar(T2A.ar(trig), 0, deltimeK);
var delayK = DelTapRd.ar(b, phase, deltimeK, 4);
var delayA = DelTapRd.ar(b, phase, deltimeA, 4);
[sig, deltimeA * 40, delayK, delayA]
}.plot(duration: 0.05);
)
⌠and delayK looks like your SC result, while delayA looks like your Pd result.
That was only to produce a sensible plot, no other reason⌠meaning, youâre not required to use that exact frequency.
First, a trigger is defined as a value > 0 coming immediately after a value <= 0. So the fastest possible triggering is 1, 0, 1, 0 â half of the sample rate or control rate.
Second, the pd patch provides a new delay time only when a message goes through the send/receive pair, which isnât the same as sampling every control block in SC. Youâre looking for a delay time signal that looks like a sample/hold.
I think the changed UGen can do the job in a more proper way:
(
SynthDef(\glitcher, {
|out=0, amp=0.5, deltime=0.25, fb=1.0, glitch=0|
var localBuf = LocalBuf.new( SampleRate.ir()*1.0, 1);
var trig = Changed.kr( deltime );
var original = In.ar(2,1); // In.ar(2,1)
var phase = LocalIn.ar(1);
var deltimeA = Demand.ar(T2A.ar(trig), 0, deltime);
var delay = DelTapRd.ar(localBuf, phase, deltimeA, mul:fb);
// in the mean time I added also a little cross-fade (to better simulate the PD patch)
var sig = SelectX.ar(glitch.lag(0.125), [original, delay]);
LocalOut.ar( DelTapWr.ar(localBuf, sig) );
sig = sig * amp;
Out.ar(out, sig!2);
}).add;
)