Skip to content

Commit

Permalink
Time stretching (#229)
Browse files Browse the repository at this point in the history
First implementation for time stretching in tidal.
bgold-cosmos, Cc-authored-by: Julian Rohrhuber
  • Loading branch information
bgold-cosmos authored Aug 20, 2021
1 parent f7bccb9 commit 6878cde
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 4 deletions.
1 change: 1 addition & 0 deletions classes/DirtEvent.sc
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ DirtEvent {

~fadeTime = min(~fadeTime.value, sustain * 0.19098);
~fadeInTime = if(~begin != 0) { ~fadeTime } { 0.0 };
if (~timescale.notNil) {sustain = sustain * ~timescale };
~sustain = sustain - (~fadeTime + ~fadeInTime);
~speed = speed;
~endSpeed = endSpeed;
Expand Down
8 changes: 7 additions & 1 deletion classes/DirtSoundLibrary.sc
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,7 @@ DirtSoundLibrary {
bufferObject: buffer,
notYetRead: doNotReadYet,
instrument: this.instrumentForBuffer(buffer),
stretchInstrument: this.stretchInstrumentForBuffer(buffer),
bufNumFrames: buffer.numFrames,
bufNumChannels: buffer.numChannels,
unitDuration: { buffer.duration * baseFreq / ~freq.value },
Expand All @@ -294,6 +295,10 @@ DirtSoundLibrary {
^format(synthName, buffer.numChannels, this.numChannels).asSymbol
}

stretchInstrumentForBuffer { |buffer|
^format("dirt_stretchsample_%_%", buffer.numChannels, this.numChannels).asSymbol
}

openFolder { |name, index = 0|
var buf, list;
list = buffers.at(name);
Expand All @@ -313,7 +318,8 @@ DirtSoundLibrary {
numChannels = n;
bufferEvents = bufferEvents.collect { |list|
list.do { |event|
event[\instrument] = this.instrumentForBuffer(event[\buffer])
event[\instrument] = this.instrumentForBuffer(event[\buffer]);
event[\stretchInstrument] = this.stretchInstrumentForBuffer(event[\buffer]);
}
}
}
Expand Down
17 changes: 14 additions & 3 deletions synths/core-modules.scd
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,9 @@ this may be refacored later.
{ |dirtEvent|
if(~diversion.value.isNil) {
if(~buffer.notNil) {
// argumets could be omitted using getMsgFunc, but for making it easier to understand, we write them out
dirtEvent.sendSynth(~instrument, [
// arguments could be omitted using getMsgFunc, but for making it easier to understand, we write them out
var instrument;
var args = [
bufnum: ~buffer,
sustain: ~sustain,
speed: ~speed,
Expand All @@ -48,7 +49,17 @@ this may be refacored later.
end: ~end,
pan: ~pan,
out: ~out
])
];

if(~timescale.notNil) {
instrument = ~stretchInstrument;
args = args ++ [\timescale, ~timescale, \timescalewin, ~timescalewin ? 1];
} {
instrument = ~instrument;
};

dirtEvent.sendSynth(instrument, args);

} {
if(~instrument.isNil) {
"module 'sound': instrument not found: %".format(~s).postln
Expand Down
66 changes: 66 additions & 0 deletions synths/core-synths.scd
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,72 @@ live coding them requires that you have your SuperDirt instance in an environmen
}, [\ir, \ir, \ir, \ir, \ir, \ir, \ir, \ir, \kr]).add; // pan can be modulated
};

// this is the time-stretching sample player, based on an overlap-add method
// the method is designed for timescale > 1 (i.e. stretching),
// but works reasonably well for values between 0.1 and 3, depending on the sample
(1..SuperDirt.maxSampleNumChannels).do { |sampleNumChannels|

var name = format("dirt_stretchsample_%_%", sampleNumChannels, numChannels);

SynthDef(name, { |out, bufnum, sustain = 1, begin = 0, end = 1, speed = 1, endSpeed = 1, freq = 440, pan = 0, timescale = 1, timescalewin = 1|

var sound, rate, phase, sawrate, numFrames, index, windowIndex, window, timescaleStep;
var sound0, sound1, windowSize, nSteps;

// playback speed
rate = Line.kr(speed, endSpeed, sustain) * (freq / 60.midicps);

// sample phase
// BufSampleRate adjusts the rate if the sound file doesn't have the same rate as the soundcard
//phase = Sweep.ar(1, rate * BufSampleRate.ir(bufnum)) + (BufFrames.ir(bufnum) * begin);

numFrames = BufFrames.ir(bufnum);
// Picking the right window size is a tricky thing, something around 2000 samples is usually
// OK for sounds of moderate length (around 0.5 to several seconds long).
// But this is also scaled below to be a bit smaller/larger based on desired playback rate.
// If it's still not good, you can use the "timescalewin" parameter to multiply the window size
windowSize = timescale.clip(0.1,2) * 0.05 * BufSampleRate.ir(bufnum) * timescalewin;
// Next is the (pre-scaled) number of samples between indices. Note that while windowSize
// is clipped, timescaleStep cannot be without the duration of the resulting sound becoming
// different from timescale * sample_duration.
// For very small values of timescale, this means it'll "skip" through the sound, only playing
// a few samples at regular intervals
// For very large values of timescale, the overlap will be very large, possibly leading to
// some audible repetition
timescaleStep = windowSize / timescale;
sawrate = rate * BufSampleRate.ir(bufnum) / (absdif(begin, end) * timescale * numFrames);
phase = (speed.sign * LFSaw.ar(sawrate, 1)).range(begin,end) * numFrames * timescale;
// do the overlap-add by running through a pair of indices, shifting weights with the window function
nSteps = phase.div(timescaleStep) - [1.0, 0.0];
index = nSteps * (timescaleStep / timescale - timescaleStep) + phase;
windowIndex = phase - (nSteps * timescaleStep);
// Gaussian window, the "50" means it's about 3.5 sigma to the edge of the window
window = exp(-50 * squared(windowIndex/windowSize - 0.5));

sound0 = window[0] * BufRd.ar(
numChannels: sampleNumChannels,
bufnum: bufnum,
phase: index[0],
loop: 0,
interpolation: 4 // cubic interpolation
);
sound1 = window[1] * BufRd.ar(
numChannels: sampleNumChannels,
bufnum: bufnum,
phase: index[1],
loop: 0,
interpolation: 4 // cubic interpolation
);
sound = (sound0 + sound1) / window.sum;

sound = DirtPan.ar(sound, numChannels, pan);

Out.ar(out, sound)
}, [\ir, \ir, \ir, \ir, \ir, \ir, \ir, \ir, \kr, \ir, \ir]).add;
};



/*
Bus Routing Monitor
Expand Down

0 comments on commit 6878cde

Please sign in to comment.