UNPKG

dsp-collection

Version:

A collection of JavaScript modules for digital signal processing (written in TypeScript)

73 lines 3.41 kB
import * as MathUtils from "../math/MathUtils.js"; import * as NumApprox from "../math/NumApprox.js"; import * as WindowFunctions from "./WindowFunctions.js"; import * as AdaptiveStft from "./AdaptiveStft.js"; import * as InstFreq from "./InstFreq.js"; export function getDefaultHarmonicSumParms() { return { amplitudeCompressionExponent: 0.5, harmonicsDeclineRate: 0.2, harmonicsDeclineExponent: 0, fCutoff: 5000, relWindowWidth: 16, windowFunction: WindowFunctions.getFunctionbyId("hann", { tableCacheCostLimit: 1 }) }; } export function getDefaultHarmonicInstSumParms() { return { ...getDefaultHarmonicSumParms(), shiftFactor: 0.25, peakWidthFactor: 0.2 }; } export function harmonicSum(samples, sampleRate, position, f0, parms = getDefaultHarmonicSumParms()) { const n = Math.floor(parms.fCutoff / f0); const a = AdaptiveStft.getHarmonicAmplitudes(samples, position * sampleRate, f0 / sampleRate, n, parms.relWindowWidth, parms.windowFunction); if (!a) { return NaN; } return evaluateHarmonicAmplitudes(a, parms); } export function harmonicInstSum(samples, sampleRate, position, f0, parms = getDefaultHarmonicInstSumParms()) { const peakWidth = f0 * parms.peakWidthFactor / sampleRate; const n = Math.floor(parms.fCutoff / f0); let sum = 0; let count = 0; for (let harmonic = 1; harmonic <= n; harmonic++) { const f = harmonic * f0 / sampleRate; const r = InstFreq.instFreqSingle_relWindow(samples, position * sampleRate, f, parms.shiftFactor, parms.relWindowWidth * harmonic, parms.windowFunction); if (r) { const absDelta = Math.abs(r.measuringFrequency - r.instFrequency); const a = r.amplitude * Math.max(0, 1 - (2 * absDelta) / peakWidth); sum += evaluateHarmonicAmplitude(a, harmonic, parms); count++; } } return (count > 0) ? sum : NaN; } export function evaluateHarmonicAmplitude(amplitude, harmonic, parms) { const attenuation = MathUtils.hyperbolicDecline(harmonic - 1, parms.harmonicsDeclineRate, parms.harmonicsDeclineExponent); return amplitude ** parms.amplitudeCompressionExponent * attenuation; } function evaluateHarmonicAmplitudes(amplitudes, parms) { let sum = 0; for (let harmonic = 1; harmonic <= amplitudes.length; harmonic++) { sum += evaluateHarmonicAmplitude(amplitudes[harmonic - 1], harmonic, parms); } return sum; } export function findPitchSalienceFunctionArgMax(salienceFunction, f0Min, f0Max, { scanFactor = 1.005, relTolerance = 1E-3, absTolerance = 0.05 } = {}) { const f1 = NumApprox.argMax_scanGeom(salienceFunction, f0Min, f0Max, scanFactor); if (!isFinite(f1)) { return NaN; } const f1Lo = Math.max(f1 / scanFactor, f0Min); const f1Hi = Math.min(f1 * scanFactor, f0Max); const tolerance = Math.min(f1 * relTolerance, absTolerance); return NumApprox.argMax_goldenSectionSearch(salienceFunction, f1Lo, f1Hi, tolerance); } export function estimatePitch_harmonicSum(samples, sampleRate, position, f0Min = 75, f0Max = 900, parms = getDefaultHarmonicSumParms()) { const salienceFunction = (f0) => harmonicSum(samples, sampleRate, position, f0, parms); return findPitchSalienceFunctionArgMax(salienceFunction, f0Min, f0Max); } //# sourceMappingURL=PitchDetectionHarm.js.map