diff options
Diffstat (limited to 'tools/mq_editor/mq_synth.js')
| -rw-r--r-- | tools/mq_editor/mq_synth.js | 24 |
1 files changed, 24 insertions, 0 deletions
diff --git a/tools/mq_editor/mq_synth.js b/tools/mq_editor/mq_synth.js index 2d3111b..f298392 100644 --- a/tools/mq_editor/mq_synth.js +++ b/tools/mq_editor/mq_synth.js @@ -26,6 +26,8 @@ function randFloat(seed, min, max) { // resonator: {enabled, r, gainComp} — two-pole resonator mode per partial // integratePhase: true = accumulate 2π*f/SR per sample (correct for varying freq) // false = 2π*f*t (simpler, only correct for constant freq) +// options.k1: LP coefficient in (0,1] — omit to bypass +// options.k2: HP coefficient in (0,1] — omit to bypass function synthesizeMQ(partials, sampleRate, duration, integratePhase = true, options = {}) { const numSamples = Math.floor(sampleRate * duration); const pcm = new Float32Array(numSamples); @@ -137,6 +139,28 @@ function synthesizeMQ(partials, sampleRate, duration, integratePhase = true, opt pcm[i] = sample; } + // Post-synthesis filters (applied before normalization) + // LP: y[n] = k1*x[n] + (1-k1)*y[n-1] — options.k1 in (0,1], omit to bypass + // HP: y[n] = k2*(y[n-1] + x[n] - x[n-1]) — options.k2 in (0,1], omit to bypass + if (options.k1 != null) { + const k1 = Math.max(0, Math.min(1, options.k1)); + let y = 0.0; + for (let i = 0; i < numSamples; ++i) { + y = k1 * pcm[i] + (1.0 - k1) * y; + pcm[i] = y; + } + } + if (options.k2 != null) { + const k2 = Math.max(0, Math.min(1, options.k2)); + let y = 0.0, xp = 0.0; + for (let i = 0; i < numSamples; ++i) { + const x = pcm[i]; + y = k2 * (y + x - xp); + xp = x; + pcm[i] = y; + } + } + // Normalize let maxAbs = 0; for (let i = 0; i < numSamples; ++i) maxAbs = Math.max(maxAbs, Math.abs(pcm[i])); |
