Flute, Oboe, Clarinet
Cello
Viola
Violin I
4 loops, 1 filter (5 workers, 1 AudioWorklet) tick: 0.000, time: 0.000 (60 bpm)
Fauré with reverb
Combining my reverb filter with my flute synth
Log in to post a comment.
Reverb
mix
0.80
time
3.00
timeHi
1.30
cutoff
4000
Flute
gain
1.23
offset
0.16
noiseAmount
0.05
const ISQRT2 = Math.sqrt(0.5);const SQRT8 = Math.sqrt(8);const ISQRT8 = 1/SQRT8;// const fibodelays = [1410,1662,1872,1993,2049,2114,2280,2610]; // X^8 = X+1const fibodelays = [1467,1691,1932,2138,2286,2567,3141,3897]; // X^8 = X+2let meandelay = fibodelays[3] * ditty.dt;class Delayline {constructor(n) {this.n = n;this.p = 0;this.data = new Float32Array(n);}current() {return this.data[this.p]; // using lastOut results in 1 sample excess delay}clock(input) {this.data[this.p] = input;if(++this.p >= this.n) {this.p = 0;}}}const reverb = filter.def(class {constructor(options) {this.outgain = 0.3;this.dls = [];this.s0 = new Float32Array(8); // Lowpass filter memoryfor(let i=0; i<8; i++) {this.dls.push(new Delayline(fibodelays[i]));this.s0[i] = 0;}}process(input, options) {let rt60 = options.rt60;let loopgain = 10 ** (-3*meandelay / rt60) * ISQRT8;let higain = 10 ** (-3*meandelay / options.rtHi) * ISQRT8;let v = this.dls.map( (dl) => dl.current());// Fast Walsh-Hadamard transform// https://formulasearchengine.com/wiki/Fast_Walsh%E2%80%93Hadamard_transformlet w = [v[0]+v[4], v[1]+v[5], v[2]+v[6], v[3]+v[7],v[0]-v[4], v[1]-v[5], v[2]-v[6], v[3]-v[7]];let x = [w[0]+w[2], w[1]+w[3], w[0]-w[2], w[1]-w[3],w[4]+w[6], w[5]+w[7], w[4]-w[6], w[5]-w[7]];let y = [x[0]+x[1], x[0]-x[1], x[2]+x[3], x[2]-x[3],x[4]+x[5], x[4]-x[5], x[6]+x[7], x[6]-x[7]];y[0] += input[0]*SQRT8;y[2] += input[1]*SQRT8;let a0 = clamp01(2*Math.PI*options.cutoff*ditty.dt);for(let i=0; i<8; i++) {let hipass = y[i] - this.s0[i];this.dls[i].clock(this.s0[i] * loopgain + hipass * higain);this.s0[i] += a0 * hipass;}return [lerp(input[0], v[0], options.mix),lerp(input[1], v[1], options.mix)];}}, {mix:0.2, rt60:1, cutoff:5000, rtHi:0.8});input.reverbMix = 0.8; // min=0, max=1, step=0.01input.reverbTime = 3.0; // min=0.1, max=10, step=0.1input.reverbTimeHi = 1.3; // min=0.1, max=10, step=0.1input.reverbCutoff = 4000; // min=200, max=10000, step=10const hallverb = reverb.createShared({mix:() => input.reverbMix,rt60:() => input.reverbTime,cutoff:() => input.reverbCutoff,rtHi:() => input.reverbTimeHi});// A very simplified "physical modeling" flute using a single delay line, a nonlinearity, a wide bandpass filter, and noise.// Basically://// [ noise ] ---> (+) ---> [ nonlinearity ] ---> [ bandpass filter ] ---> output// ^ |// | |// |------------------ [ delay line ] <----------------//// This model can do flute-like attacks quite nicely.// However it can be quite out of tune due to the filters.// One way to compensate for it is to let the filters follow the note as well (but that's cheating!)// It is unable to perform transitions between notes, and// does not account for the jump to higher harmonics when increasing blowing pressure.// Make sure to play with the sliders!// Fluteinput.fluteGain = 1.23; // min=1, max=1.5, step=0.01input.fluteOffset = 0.16; // min=0, max=0.5, step=0.01input.fluteNoiseAmount = 0.05; // min=0.005, max=0.5, step=0.005const saturate = (x) => x / Math.sqrt(1 + x*x);// Adapted from https://forums.codeguru.com/showthread.php?473996-How-to-do-cubic-interpolation-with-an-audio-sampleconst cubic_interpolate = (y0, y1, y2, y3, mu ) => {let mu2 = mu*mu;let a0 = y3 - y2 - y0 + y1; //plet a1 = y0 - y1 - a0;let a2 = y2 - y0;let a3 = y1;return ( a0*mu*mu2 + a1*mu2 + a2*mu + a3 );};const flute = synth.def(class {constructor(options) {let freq = midi_to_hz(options.note);let delay_samples = 1 / (freq * ditty.dt); // Duration of one period in samplesthis.len = Math.floor(delay_samples) + 2; // buffer sizethis.fd = delay_samples % 1; // fractional delay to interpolate between samplesthis.buf = new Float32Array(this.len); // buffer used to create a delaythis.pos = 0; // current position of the reading/writing headlet lpcutoff = 5*freq;this.a0 = clamp01(2 * Math.PI * lpcutoff * ditty.dt); // Lowpass filter the reinjectionlet hpcutoff = 0.1*freq;this.a1 = clamp01(2 * Math.PI * hpcutoff * ditty.dt); // hipass filter the reinjectionthis.s0 = 0; // Signal value history for the lowpass filterthis.s1 = 0; // Signal value history for the hipass filterthis.windEnv = adsr.create({attack: 0.05, release: 0.1, duration:options.duration});}process(note, env, tick, options) {let pos = this.pos;/*let value = lerp(this.buf[pos],this.buf[(pos+1)%this.len],this.fd); // linear interpolation for the fractional delay*/let value = cubic_interpolate(this.buf[pos],this.buf[(pos+1)%this.len],this.buf[(pos+2)%this.len],this.buf[(pos+3)%this.len],this.fd);/*if(tick < options.duration) {// Add noisevalue += input.fluteNoiseAmount * (Math.random() - 0.5);// Nonlinearity to gain amplitudevalue = input.fluteGain * saturate(value + input.fluteOffset);} else {value *= 0.95;}*/// Add noisevalue += this.windEnv.value * input.fluteNoiseAmount * (Math.random() - 0.5);// Nonlinearity to gain amplitudelet offset = (options.offset >= 0) ? options.offset : input.fluteOffset;value = saturate(value + offset);// Lower amplitude when we stop blowingvalue *= lerp(0.95, input.fluteGain, this.windEnv.value);this.s0 += this.a0 * (value - this.s0); // lowpass filterthis.s1 += this.a1 * (this.s0 - this.s1); // hipass filterthis.buf[pos] = this.s0 - this.s1;this.pos = (pos+1)%this.len;return this.buf[pos] * env.value; // Apply envelope to avoid clicks}}, {env:adsr, attack:0.05, offset:-1, duration:1, release:1});// lpcutoff:2000, hpcutoff:50ditty.bpm = 60;// Manually adjusted retuning, starting from C4// Only valid for the default parameters!const out_of_tune = [-31,-30,-29,-26,-26,-26,-28,-24,-27,-25,-25,-18,-22, -31, -15, -16-7-9, -14-2,-26+2, -29, -19-7+16, -28+8+16, -27+15+5,-13+5-20+1, -24-8+22-4, -9, -11-22, -15,0, -10, -25, 6, 22,-36, 29];// Try to correct the pitch of the noteconst retune = (nn) => {let i = nn - 48;if(i < 0) {return nn - 0.01*out_of_tune[0];}if(i < out_of_tune.length) {return nn - 0.01*out_of_tune[i];
4818 chars