dsp-collection
Version:
A collection of JavaScript modules for digital signal processing (written in TypeScript)
73 lines • 3.41 kB
JavaScript
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