UNPKG

node-pitchfinder

Version:
192 lines (160 loc) 5.32 kB
'use strict'; var DEFAULT_SAMPLE_RATE = 44100; var MAX_FLWT_LEVELS = 6; var MAX_F = 3000; var DIFFERENCE_LEVELS_N = 3; var MAXIMA_THRESHOLD_RATIO = 0.75; module.exports = function () { var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var sampleRate = config.sampleRate || DEFAULT_SAMPLE_RATE; return function DynamicWaveletDetector(data) { 'use strict'; var float32AudioBuffer = data; if (!(data instanceof Float64Array)) float32AudioBuffer = Float64Array.from(data); var mins = []; var maxs = []; var bufferLength = float32AudioBuffer.length; var freq = null; var theDC = 0; var minValue = 0; var maxValue = 0; // Compute max amplitude, amplitude threshold, and the DC. for (var i = 0; i < bufferLength; i++) { var sample = float32AudioBuffer[i]; theDC = theDC + sample; maxValue = Math.max(maxValue, sample); minValue = Math.min(minValue, sample); } theDC /= bufferLength; minValue -= theDC; maxValue -= theDC; var amplitudeMax = maxValue > -1 * minValue ? maxValue : -1 * minValue; var amplitudeThreshold = amplitudeMax * MAXIMA_THRESHOLD_RATIO; // levels, start without downsampling... var curLevel = 0; var curModeDistance = -1; var curSamNb = float32AudioBuffer.length; var delta = void 0, nbMaxs = void 0, nbMins = void 0; // Search: while (true) { delta = ~~(sampleRate / (Math.pow(2, curLevel) * MAX_F)); if (curSamNb < 2) break; var dv = void 0; var previousDV = -1000; var lastMinIndex = -1000000; var lastMaxIndex = -1000000; var findMax = false; var findMin = false; nbMins = 0; nbMaxs = 0; for (var _i = 2; _i < curSamNb; _i++) { var si = float32AudioBuffer[_i] - theDC; var si1 = float32AudioBuffer[_i - 1] - theDC; if (si1 <= 0 && si > 0) findMax = true; if (si1 >= 0 && si < 0) findMin = true; // min or max ? dv = si - si1; if (previousDV > -1000) { if (findMin && previousDV < 0 && dv >= 0) { // minimum if (Math.abs(si) >= amplitudeThreshold) { if (_i > lastMinIndex + delta) { mins[nbMins++] = _i; lastMinIndex = _i; findMin = false; } } } if (findMax && previousDV > 0 && dv <= 0) { // maximum if (Math.abs(si) >= amplitudeThreshold) { if (_i > lastMaxIndex + delta) { maxs[nbMaxs++] = _i; lastMaxIndex = _i; findMax = false; } } } } previousDV = dv; } if (nbMins === 0 && nbMaxs === 0) { // No best distance found! break; } var d = void 0; var distances = []; for (var _i2 = 0; _i2 < curSamNb; _i2++) { distances[_i2] = 0; } for (var _i3 = 0; _i3 < nbMins; _i3++) { for (var j = 1; j < DIFFERENCE_LEVELS_N; j++) { if (_i3 + j < nbMins) { d = Math.abs(mins[_i3] - mins[_i3 + j]); distances[d] += 1; } } } var bestDistance = -1; var bestValue = -1; for (var _i4 = 0; _i4 < curSamNb; _i4++) { var summed = 0; for (var _j = -1 * delta; _j <= delta; _j++) { if (_i4 + _j >= 0 && _i4 + _j < curSamNb) { summed += distances[_i4 + _j]; } } if (summed === bestValue) { if (_i4 === 2 * bestDistance) { bestDistance = _i4; } } else if (summed > bestValue) { bestValue = summed; bestDistance = _i4; } } // averaging var distAvg = 0; var nbDists = 0; for (var _j2 = -delta; _j2 <= delta; _j2++) { if (bestDistance + _j2 >= 0 && bestDistance + _j2 < bufferLength) { var nbDist = distances[bestDistance + _j2]; if (nbDist > 0) { nbDists += nbDist; distAvg += (bestDistance + _j2) * nbDist; } } } // This is our mode distance. distAvg /= nbDists; // Continue the levels? if (curModeDistance > -1) { if (Math.abs(distAvg * 2 - curModeDistance) <= 2 * delta) { // two consecutive similar mode distances : ok ! freq = sampleRate / (Math.pow(2, curLevel - 1) * curModeDistance); break; } } // not similar, continue next level; curModeDistance = distAvg; curLevel++; if (curLevel >= MAX_FLWT_LEVELS || curSamNb < 2) { break; } // do not modify original audio buffer, make a copy buffer, if // downsampling is needed (only once). var newFloat32AudioBuffer = float32AudioBuffer.subarray(0); if (curSamNb === distances.length) { newFloat32AudioBuffer = new Float32Array(curSamNb / 2); } for (var _i5 = 0; _i5 < curSamNb / 2; _i5++) { newFloat32AudioBuffer[_i5] = (float32AudioBuffer[2 * _i5] + float32AudioBuffer[2 * _i5 + 1]) / 2; } float32AudioBuffer = newFloat32AudioBuffer; curSamNb /= 2; } return freq; }; };