summaryrefslogtreecommitdiff
path: root/tools/mq_editor
diff options
context:
space:
mode:
Diffstat (limited to 'tools/mq_editor')
-rw-r--r--tools/mq_editor/mq_synth.js24
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]));