Working version -- remove mips, switch back to simple voice release

This commit is contained in:
Aria
2025-11-22 21:43:27 -06:00
parent 835659213e
commit 34620ffed2
4 changed files with 18 additions and 80 deletions
+6 -11
View File
@@ -41,25 +41,20 @@ const voicePools = {};
// Cycles through the pool attempting to find an inactive voice that hasn't been GC'd
export const claimVoice = (key) => {
voicePools[key] ??= {};
voicePools[key] ??= [];
const pool = voicePools[key];
let node;
for (const [id, ref] of Object.entries(pool)) {
while (pool.length && !node) {
const ref = pool.pop();
node = ref?.deref?.();
delete pool[id];
if (node !== null) break;
}
return node;
};
// Releases a voice from use and adds to the inactive pool
export const releaseVoice = (key, id, node) => {
voicePools[key] ??= {};
voicePools[key][id] = new WeakRef(node);
};
export const removeVoice = (key, id) => {
delete voicePools[key][id];
export const releaseVoice = (key, node) => {
voicePools[key] ??= [];
voicePools[key].push(new WeakRef(node));
};
export const getParamADSR = (
+1 -10
View File
@@ -15,7 +15,6 @@ import {
getWorklet,
setParams,
releaseVoice,
removeVoice,
noises,
webAudioTimeout,
} from './helpers.mjs';
@@ -180,13 +179,11 @@ export function registerSynthSounds() {
voices,
};
if (o == null) {
console.log('Building');
o = getWorklet(ac, 'supersaw-oscillator', params, {
outputChannelCount: [2],
processorOptions,
});
} else {
console.log('Reusing');
setParams(o, params);
o.port.postMessage({ type: 'reset', payload: { processorOptions } });
}
@@ -209,14 +206,8 @@ export function registerSynthSounds() {
const timeoutNode = webAudioTimeout(
ac,
() => {
const id = Math.floor(Math.random() * 10000);
destroyAudioWorkletNode(o);
releaseVoice('supersaw', id, o);
o.port.onmessage = (e) => {
if (e.data.type === 'finished') {
removeVoice('supersaw', id);
}
};
releaseVoice('supersaw', o);
onended();
fm?.stop();
vibratoOscillator?.stop();
+1 -13
View File
@@ -12,7 +12,6 @@ import {
getWorklet,
setParams,
releaseVoice,
removeVoice,
webAudioTimeout,
destroyAudioWorkletNode,
} from './helpers.mjs';
@@ -255,11 +254,6 @@ export async function onTriggerSynth(t, value, onended, tables, cps, frameLen) {
logger(`[wavetable] still loading sound "${s}:${n}"`, 'highlight');
destroyAudioWorkletNode(source);
releaseVoice(voiceKey, source);
source.port.onmessage = (e) => {
if (e.data.type === 'finished') {
removeVoice(voiceKey, id);
}
};
return;
}
const posADSRParams = [value.wtattack, value.wtdecay, value.wtsustain, value.wtrelease];
@@ -337,13 +331,7 @@ export async function onTriggerSynth(t, value, onended, tables, cps, frameLen) {
wtPosModulators?.disconnect();
wtWarpModulators?.disconnect();
destroyAudioWorkletNode(source);
const id = Math.floor(Math.random() * 10000);
releaseVoice(voiceKey, id, source);
source.port.onmessage = (e) => {
if (e.data.type === 'finished') {
removeVoice(voiceKey, id);
}
};
releaseVoice(voiceKey, source);
onended();
},
t,
+10 -46
View File
@@ -525,14 +525,9 @@ class SuperSawOscillatorProcessor extends AudioWorkletProcessor {
}
process(_input, outputs, params) {
const started = currentTime >= params.begin[0];
const end = params.end[0];
const ended = currentTime >= end + 5;
const inGracePeriod = currentTime >= end && !ended;
if (!started || inGracePeriod) {
const ended = currentTime >= params.end[0];
if (!started || ended) {
return true;
} else if (ended) {
this.port.postMessage({ type: 'finished' });
return false;
}
const output = outputs[0];
// fast k-rate paths
@@ -1174,25 +1169,10 @@ class WavetableOscillatorProcessor extends AudioWorkletProcessor {
const key = payload.key;
this.frameLen = payload.frameLen;
if (!tablesCache[key]) {
const tables = [payload.frames];
let table = tables[0];
for (let level = 1; level < 6; level++) {
const nextLen = table[0].length >> 1;
const nextTable = table.map((frame) => {
const avg = new Float32Array(nextLen);
for (let i = 0; i < nextLen; i++) {
avg[i] = (frame[2 * i] + frame[2 * i + 1]) / 2;
}
return avg;
});
tables.push(nextTable);
table = nextTable;
if (nextLen <= 32) break;
}
tablesCache[key] = tables;
tablesCache[key] = payload.frames;
}
this.tables = tablesCache[key];
this.numFrames = this.tables[0].length;
this.table = tablesCache[key];
this.numFrames = this.table.length;
} else if (type === 'reset') {
this.reset(payload.processorOptions);
}
@@ -1352,24 +1332,11 @@ class WavetableOscillatorProcessor extends AudioWorkletProcessor {
return a + (b - a) * frac;
}
_chooseMip(dphi) {
const approxHarm = clamp(dphi, 1e-6, 64);
let level = 0;
while (level + 1 < (this.tables?.length || 1) && approxHarm < this.tables[level][0].length / 8) {
level++;
}
return level;
}
process(_inputs, outputs, params) {
const started = currentTime >= params.begin[0];
const end = params.end[0];
const ended = currentTime >= end + 5;
const inGracePeriod = currentTime >= end && !ended;
if (!this.tables || !started || inGracePeriod) {
const ended = currentTime >= params.end[0];
if (!started || ended) {
return true;
} else if (ended) {
return false;
}
const outL = outputs[0][0];
const outR = outputs[0][1] || outputs[0][0];
@@ -1409,13 +1376,10 @@ class WavetableOscillatorProcessor extends AudioWorkletProcessor {
for (let n = 0; n < this.voices; n++) {
const freqVoice = applyFastDetune(freq, detuner(n)); // voice detune
const dPhase = freqVoice * INVSR;
const level = this._chooseMip(dPhase);
const table = this.tables[level];
// warp phase then sample
const ph = this._warpPhase(this.phases[n], warpAmount, warpMode);
const s0 = this._sampleFrame(table[fIdx], ph);
const s1 = this._sampleFrame(table[Math.min(this.numFrames - 1, fIdx + 1)], ph);
const s0 = this._sampleFrame(this.table[fIdx], ph);
const s1 = this._sampleFrame(this.table[Math.min(this.numFrames - 1, fIdx + 1)], ph);
let s = lerp(s0, s1, interpT);
if (warpMode === WarpMode.FLIP && this.phases[n] < warpAmount) {
s = -s;
@@ -1426,7 +1390,7 @@ class WavetableOscillatorProcessor extends AudioWorkletProcessor {
// invert right and left gain
const tmp = gainL;
gainR = gainL;
gainL = gainR;
gainL = tmp;
}
}
return true;