pitched delay

This commit is contained in:
Jade (Rose) Rowland 2025-06-10 12:31:21 +02:00
parent 3963b807eb
commit 7e8206dcbc
2 changed files with 46 additions and 18 deletions

View file

@ -964,14 +964,27 @@ export const { delay } = registerControl(['delay', 'delaytime', 'delayfeedback']
* *
*/ */
export const { delayfeedback, delayfb, dfb } = registerControl('delayfeedback', 'delayfb', 'dfb'); export const { delayfeedback, delayfb, dfb } = registerControl('delayfeedback', 'delayfb', 'dfb');
/**
* Sets the level of the signal that is fed back into the delay.
* Caution: Values >= 1 will result in a signal that gets louder and louder! Don't do it
*
* @name delayfeedback
* @param {number | Pattern} feedback between 0 and 1
* @synonyms delayfb, dfb
* @example
* s("bd").delay(.25).delayfeedback("<.25 .5 .75 1>")
*
*/
export const { delayspeed } = registerControl('delayspeed');
/** /**
* Sets the time of the delay effect. * Sets the time of the delay effect.
* *
* @name delaytime * @name delayspeed
* @param {number | Pattern} seconds between 0 and Infinity * @param {number | Pattern} delayspeed controls the pitch of the delay feedback
* @synonyms delayt, dt * @synonyms delayt, dt
* @example * @example
* s("bd bd").delay(.25).delaytime("<.125 .25 .5 1>") * note("d d a# a".fast(2)).s("sawtooth").delay(.8).delaytime(1/2).delayspeed("<2 .5 -1 -2>")
* *
*/ */
export const { delaytime, delayt, dt } = registerControl('delaytime', 'delayt', 'dt'); export const { delaytime, delayt, dt } = registerControl('delaytime', 'delayt', 'dt');

View file

@ -44,6 +44,7 @@ function polyBlep(t, dt) {
// 0 otherwise // 0 otherwise
return 0; return 0;
} }
export class SawOsc { export class SawOsc {
constructor(props = {}) { constructor(props = {}) {
this.phase = props.phase ?? 0; this.phase = props.phase ?? 0;
@ -349,21 +350,31 @@ export class ADSR {
.out()*/ .out()*/
const MAX_DELAY_TIME = 10; const MAX_DELAY_TIME = 10;
export class Delay { export class Delay {
writeIdx = 0; constructor(_props = {}) {
readIdx = 0; this.buffer = new Float32Array(MAX_DELAY_TIME * SAMPLE_RATE);
buffer = new Float32Array(MAX_DELAY_TIME * SAMPLE_RATE); //.fill(0) this.writeIdx = 0;
write(s, delayTime) { this.readIdx = 0;
this.writeIdx = (this.writeIdx + 1) % this.buffer.length; this.numSamples = 0;
this.buffer[this.writeIdx] = s;
// Calculate how far in the past to read
let numSamples = Math.min(Math.floor(SAMPLE_RATE * delayTime), this.buffer.length - 1);
this.readIdx = this.writeIdx - numSamples;
// If past the start of the buffer, wrap around
if (this.readIdx < 0) this.readIdx += this.buffer.length;
} }
update(input, delayTime) { write(s, delayTime) {
// Calculate how far in the past to read
this.numSamples = Math.min(Math.floor(SAMPLE_RATE * delayTime), this.buffer.length - 1);
this.writeIdx = (this.writeIdx + 1) % this.numSamples;
this.buffer[this.writeIdx] = s;
this.readIdx = this.writeIdx - this.numSamples + 1;
// If past the start of the buffer, wrap around (Q: is this possible?)
if (this.readIdx < 0) this.readIdx += this.numSamples;
}
update(input, delayTime, speed = 1) {
this.write(input, delayTime); this.write(input, delayTime);
return this.buffer[this.readIdx]; let index = this.readIdx;
if (speed < 0) {
index = this.numSamples - Math.floor(Math.abs(this.readIdx * speed) % this.numSamples);
} else {
index = Math.floor(this.readIdx * speed) % this.numSamples;
}
return this.buffer[index];
} }
} }
@ -550,6 +561,7 @@ const defaultDefaultValues = {
delay: 0, delay: 0,
byteBeatExpression: '0', byteBeatExpression: '0',
delayfeedback: 0.5, delayfeedback: 0.5,
delayspeed: 1,
delaytime: 0.25, delaytime: 0.25,
orbit: 1, orbit: 1,
i: 1, i: 1,
@ -662,6 +674,7 @@ export class DoughVoice {
// delay // delay
$.delay = applyGainCurve($.delay ?? getDefaultValue('delay')); $.delay = applyGainCurve($.delay ?? getDefaultValue('delay'));
$.delayfeedback = $.delayfeedback ?? getDefaultValue('delayfeedback'); $.delayfeedback = $.delayfeedback ?? getDefaultValue('delayfeedback');
$.delayspeed = $.delayspeed ?? getDefaultValue('delayspeed');
$.delaytime = $.delaytime ?? getDefaultValue('delaytime'); $.delaytime = $.delaytime ?? getDefaultValue('delaytime');
// precalculated values // precalculated values
@ -822,6 +835,7 @@ export class Dough {
delaysend = [0, 0]; delaysend = [0, 0];
delaytime = getDefaultValue('delaytime'); delaytime = getDefaultValue('delaytime');
delayfeedback = getDefaultValue('delayfeedback'); delayfeedback = getDefaultValue('delayfeedback');
delayspeed = getDefaultValue('delayspeed');
t = 0; t = 0;
// sampleRate: number, currentTime: number (seconds) // sampleRate: number, currentTime: number (seconds)
constructor(sampleRate = 48000, currentTime = 0) { constructor(sampleRate = 48000, currentTime = 0) {
@ -897,12 +911,13 @@ export class Dough {
this.delaysend[0] += this.voices[v].out[0] * this.voices[v].delay; this.delaysend[0] += this.voices[v].out[0] * this.voices[v].delay;
this.delaysend[1] += this.voices[v].out[1] * this.voices[v].delay; this.delaysend[1] += this.voices[v].out[1] * this.voices[v].delay;
this.delaytime = this.voices[v].delaytime; // we trust that these are initialized in the voice this.delaytime = this.voices[v].delaytime; // we trust that these are initialized in the voice
this.delayspeed = this.voices[v].delayspeed; // we trust that these are initialized in the voice
this.delayfeedback = this.voices[v].delayfeedback; this.delayfeedback = this.voices[v].delayfeedback;
} }
} }
// todo: how to change delaytime / delayfeedback from a voice? // todo: how to change delaytime / delayfeedback from a voice?
const delayL = this._delayL.update(this.delaysend[0], this.delaytime); const delayL = this._delayL.update(this.delaysend[0], this.delaytime, this.delayspeed);
const delayR = this._delayR.update(this.delaysend[1], this.delaytime); const delayR = this._delayR.update(this.delaysend[1], this.delaytime, this.delayspeed);
this.delaysend[0] = delayL * this.delayfeedback; this.delaysend[0] = delayL * this.delayfeedback;
this.delaysend[1] = delayR * this.delayfeedback; this.delaysend[1] = delayR * this.delayfeedback;
this.out[0] += delayL; this.out[0] += delayL;