export const toMIDI = (f: number) => ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"][(f % 12 + 12) % 12] + Math.round(f / 12 - 2); export const toRad = (degrees: number) => degrees * Math.PI / 180; export const atodb = (a: number) => 20 * Math.log10(a); export const dbtoa = (db: number) => 10 ** (db / 20); export const denormalize = (x: number, min: number, max: number) => min + (max - min) * x; export const normalize = (x: number, min: number, max: number) => (x - min) / (max - min) || 0; export const normLog = (x: number, min: number, max: number) => { const normalized = normalize(x, min, max); const logMin = Math.log(Math.max(Number.EPSILON, min)); const logMax = Math.log(Math.max(Number.EPSILON, max)); const vLog = denormalize(normalized, logMin, logMax); const v = Math.exp(vLog); return Math.max(min, Math.min(max, v)); }; export const iNormLog = (vIn: number, min: number, max: number) => { const v = Math.max(min, Math.min(max, vIn)); const vLog = Math.log(Math.max(Number.EPSILON, v)); const logMin = Math.log(Math.max(Number.EPSILON, min)); const logMax = Math.log(Math.max(Number.EPSILON, max)); const normalized = normalize(vLog, logMin, logMax); return denormalize(normalized, min, max); }; export const normExp = iNormLog; export const iNormExp = normLog; export const roundedRect = (ctx: CanvasRenderingContext2D, x: number, y: number, width: number, height: number, radius: number | number[]) => { const radii = [0, 0, 0, 0]; if (typeof radius === "number") radii.fill(radius); else radius.forEach((v, i) => radii[i] = v); ctx.beginPath(); ctx.moveTo(x + radii[0], y); ctx.lineTo(x + width - radii[1], y); ctx.quadraticCurveTo(x + width, y, x + width, y + radii[1]); ctx.lineTo(x + width, y + height - radii[2]); ctx.quadraticCurveTo(x + width, y + height, x + width - radii[2], y + height); ctx.lineTo(x + radii[3], y + height); ctx.quadraticCurveTo(x, y + height, x, y + height - radii[3]); ctx.lineTo(x, y + radii[0]); ctx.quadraticCurveTo(x, y, x + radii[0], y); ctx.closePath(); ctx.stroke(); }; export const fillRoundedRect = (ctx: CanvasRenderingContext2D, x: number, y: number, width: number, height: number, radius: number | number[]) => { const radii = [0, 0, 0, 0]; if (typeof radius === "number") radii.fill(radius); else radius.forEach((v, i) => radii[i] = v); ctx.beginPath(); ctx.moveTo(x + radii[0], y); ctx.lineTo(x + width - radii[1], y); ctx.quadraticCurveTo(x + width, y, x + width, y + radii[1]); ctx.lineTo(x + width, y + height - radii[2]); ctx.quadraticCurveTo(x + width, y + height, x + width - radii[2], y + height); ctx.lineTo(x + radii[3], y + height); ctx.quadraticCurveTo(x, y + height, x, y + height - radii[3]); ctx.lineTo(x, y + radii[0]); ctx.quadraticCurveTo(x, y, x + radii[0], y); ctx.closePath(); ctx.fill(); };