Move fns to util file; optimized clamp

This commit is contained in:
Aria
2025-11-23 15:25:08 -06:00
parent 96d92b4728
commit 2ce09cfd32
5 changed files with 66 additions and 32 deletions
+6 -1
View File
@@ -253,7 +253,12 @@ export const pairs = function (xs) {
return result;
};
export const clamp = (num, min, max) => Math.min(Math.max(num, min), max);
// Optimized clamp (~1.7x faster than Math.max/min approach)
export const clamp = (num, min, max) => {
if (num < min) return min;
if (num > max) return max;
return num;
};
/* solmization, not used yet */
const solfeggio = ['Do', 'Reb', 'Re', 'Mib', 'Mi', 'Fa', 'Solb', 'Sol', 'Lab', 'La', 'Sib', 'Si']; /*solffegio notes*/
+38 -2
View File
@@ -3,6 +3,8 @@ import { logger } from './logger.mjs';
// currently duplicate with core util.mjs to skip dependency
// TODO: add separate util module?
export const LN2 = Math.LN2;
export const tokenizeNote = (note) => {
if (typeof note !== 'string') {
return [];
@@ -32,10 +34,9 @@ export const noteToMidi = (note, defaultOctave = 3) => {
export const midiToFreq = (n) => {
return Math.pow(2, (n - 69) / 12) * 440;
};
export const clamp = (num, min, max) => Math.min(Math.max(num, min), max);
export const freqToMidi = (freq) => {
return (12 * Math.log(freq / 440)) / Math.LN2 + 69;
return (12 * Math.log(freq / 440)) / LN2 + 69;
};
export const valueToMidi = (value, fallbackValue) => {
@@ -114,3 +115,38 @@ export function getCommonSampleInfo(hapValue, bank) {
export const pickAndRename = (source, map) => {
return Object.fromEntries(Object.entries(map).map(([newKey, oldKey]) => [newKey, source[oldKey]]));
};
// Optimized / numerical functions (ideal for hot dsp loops)
// Optimized clamp (~1.7x faster than Math.max/min approach)
export const clamp = (num, min, max) => {
if (num < min) return min;
if (num > max) return max;
return num;
};
export const mod = (n, m) => ((n % m) + m) % m;
export const lerp = (a, b, n) => n * (b - a) + a;
export const frac = (x) => x - Math.floor(x);
// Fast integer ops for non-negative values
export const ffloor = (x) => x | 0;
export const fround = (x) => ffloor(x + 0.5);
export const fceil = (x) => ffloor(x + 1);
export const ffrac = (x) => x - ffloor(x);
export const fastexpm1 = (x) => x * (1 + x + 0.5 * x * x); // Taylor approximation
export const fastpow2 = (x) => {
// Taylor approximation of 2 ^ x
const a = x * LN2;
return 1 + a * (1 + a * (0.5 + a / 6));
};
export const applySemitoneDetuneToFrequency = (frequency, detune) => {
return frequency * Math.pow(2, detune / 12);
};
export const applyFastDetune = (frequency, detune) => {
if (detune < 4) return frequency * fastpow2(detune / 12);
return applySemitoneDetuneToFrequency(frequency, detune);
};
+12 -27
View File
@@ -5,6 +5,18 @@
import OLAProcessor from './ola-processor';
import FFT from './fft.js';
import { getDistortionAlgorithm } from './helpers.mjs';
import {
applyFastDetune,
applySemitoneDetuneToFrequency,
clamp,
fastexpm1,
fceil,
ffloor,
ffrac,
frac,
fround,
lerp,
} from './util.mjs';
const blockSize = 128;
const PI = Math.PI;
@@ -12,23 +24,11 @@ const TWO_PI = 2 * PI;
const INVSR = 1 / sampleRate;
const MAX_VOICES = 64;
const clamp = (num, min, max) => Math.min(Math.max(num, min), max);
const mod = (n, m) => ((n % m) + m) % m;
const lerp = (a, b, n) => n * (b - a) + a;
const pv = (arr, n) => arr[n] ?? arr[0];
const frac = (x) => x - Math.floor(x);
// Fast integer ops for non-negative values
const ffloor = (x) => x | 0;
const fround = (x) => ffloor(x + 0.5);
const fceil = (x) => ffloor(x + 1);
const ffrac = (x) => x - ffloor(x);
const fast_tanh = (x) => {
const x2 = x * x;
return (x * (27 + x2)) / (27 + 9 * x2);
};
const fastexpm1 = (x) => x * (1 + x + 0.5 * x * x); // Taylor approximation
// Optimized per-voice detuner which precomputes constants
const getDetuner = (unison, detune) => {
@@ -40,21 +40,6 @@ const getDetuner = (unison, detune) => {
return (voiceIdx) => voiceIdx * scale - center;
};
function fastPow2(x) {
// Taylor approximation of 2 ^ x
const a = x * 0.6931471805599453; // ln(2)
return 1 + a * (1 + a * (0.5 + a / 6));
}
const applySemitoneDetuneToFrequency = (frequency, detune) => {
return frequency * Math.pow(2, detune / 12);
};
const applyFastDetune = (frequency, detune) => {
if (detune < 4) return frequency * fastPow2(detune / 12);
return applySemitoneDetuneToFrequency(frequency, detune);
};
// Smooth waveshape near discontinuities to remove frequencies above Nyquist and prevent aliasing
// referenced from https://www.kvraudio.com/forum/viewtopic.php?t=375517
function polyBlep(phase, dt) {
+5 -1
View File
@@ -1,6 +1,10 @@
import { Dough } from './dough.mjs';
const clamp = (num, min, max) => Math.min(Math.max(num, min), max);
const clamp = (num, min, max) => {
if (num < min) return min;
if (num > max) return max;
return num;
};
class DoughProcessor extends AudioWorkletProcessor {
constructor() {
+5 -1
View File
@@ -6,7 +6,11 @@ const PI_DIV_SR = Math.PI / SAMPLE_RATE;
const ISR = 1 / SAMPLE_RATE;
let gainCurveFunc = (val) => Math.pow(val, 2);
const clamp = (num, min, max) => Math.min(Math.max(num, min), max);
const clamp = (num, min, max) => {
if (num < min) return min;
if (num > max) return max;
return num;
};
function applyGainCurve(val) {
return gainCurveFunc(val);