PageSourceSearch

https://www.hamienet.com/assets/vendor-basicpitch-Bd1IWE9_.js

js hamienet.com collected 2026-09-24 19:01:13 UTC 15,033 bytes, 371 lines download raw bytes

1import { aJ as loadGraphModel, aK as slice, aL as concat1d, aM as zeros, aN as tensor, aO as expandDims, aP as signal } from "./vendor-dVNaLJx_.js";
2import "./vendor-audio-DH3w0BzW.js";
3import "./vendor-react-Bay9Y7Lb.js";
4import "./vendor-i18n-R-bMoYp_.js";
5const OUTPUT_TO_TENSOR_NAME = {
6  contours: "Identity",
7  onsets: "Identity_2",
8  frames: "Identity_1"
9};
10const NUM_CHANNELS = 1;
11const AUDIO_SAMPLE_RATE$1 = 22050;
12const FFT_HOP$1 = 256;
13const ANNOTATIONS_FPS$1 = Math.floor(AUDIO_SAMPLE_RATE$1 / FFT_HOP$1);
14const AUDIO_WINDOW_LENGTH_SECONDS = 2;
15const AUDIO_N_SAMPLES$1 = AUDIO_SAMPLE_RATE$1 * AUDIO_WINDOW_LENGTH_SECONDS - FFT_HOP$1;
16const N_OVERLAPPING_FRAMES = 30;
17const N_OVERLAP_OVER_2 = Math.floor(N_OVERLAPPING_FRAMES / 2);
18const OVERLAP_LENGTH_FRAMES = N_OVERLAPPING_FRAMES * FFT_HOP$1;
19const HOP_SIZE = AUDIO_N_SAMPLES$1 - OVERLAP_LENGTH_FRAMES;
20class BasicPitch {
21  constructor(modelOrModelPath) {
22    this.model = typeof modelOrModelPath === "string" ? loadGraphModel(modelOrModelPath) : modelOrModelPath;
23  }
24  adjustNoteStart(notes, offsetSeconds) {
25    return notes.map((note) => ({
26      startTimeSeconds: note.startTimeSeconds + offsetSeconds,
27      durationSeconds: note.durationSeconds,
28      pitch_midi: note.pitchMidi,
29      amplitude: note.amplitude,
30      pitchBends: note.pitchBends
31    }));
32  }
33  async evaluateSingleFrame(reshapedInput, batchNumber) {
34    const model = await this.model;
35    const singleBatch = slice(reshapedInput, batchNumber, 1);
36    const results = model.execute(singleBatch, [
37      OUTPUT_TO_TENSOR_NAME.frames,
38      OUTPUT_TO_TENSOR_NAME.onsets,
39      OUTPUT_TO_TENSOR_NAME.contours
40    ]);
41    return [results[0], results[1], results[2]];
42  }
43  async prepareData(singleChannelAudioData) {
44    const wavSamples = concat1d([
45      zeros([Math.floor(OVERLAP_LENGTH_FRAMES / 2)], "float32"),
46      tensor(singleChannelAudioData)
47    ]);
48    return [
49      expandDims(signal.frame(wavSamples, AUDIO_N_SAMPLES$1, HOP_SIZE, true, 0), -1),
50      singleChannelAudioData.length
51    ];
52  }
53  unwrapOutput(result) {
54    let rawOutput = result;
55    rawOutput = result.slice([0, N_OVERLAP_OVER_2, 0], [-1, result.shape[1] - 2 * N_OVERLAP_OVER_2, -1]);
56    const outputShape = rawOutput.shape;
57    return rawOutput.reshape([outputShape[0] * outputShape[1], outputShape[2]]);
58  }
59  async evaluateModel(resampledBuffer, onComplete, percentCallback) {
60    let singleChannelAudioData;
61    if (resampledBuffer instanceof Float32Array) {
62      singleChannelAudioData = resampledBuffer;
63    } else {
64      if (resampledBuffer.sampleRate !== AUDIO_SAMPLE_RATE$1) {
65        throw new Error(`Input audio buffer is not at correct sample rate! Is ${resampledBuffer.sampleRate}. Should be ${AUDIO_SAMPLE_RATE$1}`);
66      }
67      if (resampledBuffer.numberOfChannels !== NUM_CHANNELS) {
68        throw new Error(`Input audio buffer is not mono! Number of channels is ${resampledBuffer.numberOfChannels}. Should be ${NUM_CHANNELS}`);
69      }
70      singleChannelAudioData = resampledBuffer.getChannelData(0);
71    }
72    const [reshapedInput, audioOriginalLength] = await this.prepareData(singleChannelAudioData);
73    const nOutputFramesOriginal = Math.floor(audioOriginalLength * (ANNOTATIONS_FPS$1 / AUDIO_SAMPLE_RATE$1));
74    let calculatedFrames = 0;
75    for (let i = 0; i < reshapedInput.shape[0]; ++i) {
76      percentCallback(i / reshapedInput.shape[0]);
77      const [resultingFrames, resultingOnsets, resultingContours] = await this.evaluateSingleFrame(reshapedInput, i);
78      let unwrappedResultingFrames = this.unwrapOutput(resultingFrames);
79      let unwrappedResultingOnsets = this.unwrapOutput(resultingOnsets);
80      let unwrappedResultingContours = this.unwrapOutput(resultingContours);
81      const calculatedFramesTmp = unwrappedResultingFrames.shape[0];
82      if (calculatedFrames >= nOutputFramesOriginal) {
83        continue;
84      }
85      if (calculatedFramesTmp + calculatedFrames >= nOutputFramesOriginal) {
86        const framesToOutput = nOutputFramesOriginal - calculatedFrames;
87        unwrappedResultingFrames = unwrappedResultingFrames.slice([0, 0], [framesToOutput, -1]);
88        unwrappedResultingOnsets = unwrappedResultingOnsets.slice([0, 0], [framesToOutput, -1]);
89        unwrappedResultingContours = unwrappedResultingContours.slice([0, 0], [framesToOutput, -1]);
90      }
91      calculatedFrames += calculatedFramesTmp;
92      onComplete(await unwrappedResultingFrames.array(), await unwrappedResultingOnsets.array(), await unwrappedResultingContours.array());
93    }
94    percentCallback(1);
95  }
96}
97const MIDI_OFFSET = 21;
98const AUDIO_SAMPLE_RATE = 22050;
99const AUDIO_WINDOW_LENGTH = 2;
100const FFT_HOP = 256;
101const ANNOTATIONS_FPS = Math.floor(AUDIO_SAMPLE_RATE / FFT_HOP);
102const ANNOT_N_FRAMES = ANNOTATIONS_FPS * AUDIO_WINDOW_LENGTH;
103const AUDIO_N_SAMPLES = AUDIO_SAMPLE_RATE * AUDIO_WINDOW_LENGTH - FFT_HOP;
104const WINDOW_OFFSET = FFT_HOP / AUDIO_SAMPLE_RATE * (ANNOT_N_FRAMES - AUDIO_N_SAMPLES / FFT_HOP) + 18e-4;
105const MAX_FREQ_IDX = 87;
106const CONTOURS_BINS_PER_SEMITONE = 3;
107const ANNOTATIONS_BASE_FREQUENCY = 27.5;
108const ANNOTATIONS_N_SEMITONES = 88;
109const N_FREQ_BINS_CONTOURS = ANNOTATIONS_N_SEMITONES * CONTOURS_BINS_PER_SEMITONE;
110const hzToMidi = (hz) => 12 * (Math.log2(hz) - Math.log2(440)) + 69;
111const midiToHz = (midi) => 440 * 2 ** ((midi - 69) / 12);
112const modelFrameToTime = (frame) => frame * FFT_HOP / AUDIO_SAMPLE_RATE - WINDOW_OFFSET * Math.floor(frame / ANNOT_N_FRAMES);
113function argMax(arr) {
114  return arr.length === 0 ? null : arr.reduce((maxIndex, currentValue, index) => arr[maxIndex] > currentValue ? maxIndex : index, -1);
115}
116const argMaxAxis1 = (arr) => arr.map((row) => argMax(row));
117function whereGreaterThanAxis1(arr2d, threshold) {
118  const outputX = [];
119  const outputY = [];
120  for (let i = 0; i < arr2d.length; i++) {
121    for (let j = 0; j < arr2d[i].length; j++) {
122      if (arr2d[i][j] > threshold) {
123        outputX.push(i);
124        outputY.push(j);
125      }
126    }
127  }
128  return [outputX, outputY];
129}
130function meanStdDev(array) {
131  const [sum, sumSquared, count] = array.reduce((prev, row) => {
132    const [rowSum, rowSumsSquared, rowCount] = row.reduce((p, value) => [p[0] + value, p[1] + value * value, p[2] + 1], [0, 0, 0]);
133    return [prev[0] + rowSum, prev[1] + rowSumsSquared, prev[2] + rowCount];
134  }, [0, 0, 0]);
135  const mean = sum / count;
136  const std = Math.sqrt(1 / (count - 1) * (sumSquared - sum * sum / count));
137  return [mean, std];
138}
139function globalMax(array) {
140  return array.reduce((prev, row) => Math.max(prev, ...row), 0);
141}
142function min3dForAxis0(array) {
143  const minArray = array[0].map((v) => v.slice());
144  for (let x = 1; x < array.length; ++x) {
145    for (let y = 0; y < array[0].length; ++y) {
146      for (let z = 0; z < array[0][0].length; ++z) {
147        minArray[y][z] = Math.min(minArray[y][z], array[x][y][z]);
148      }
149    }
150  }
151  return minArray;
152}
153function argRelMax(array, order = 1) {
154  const result = [];
155  for (let col = 0; col < array[0].length; ++col) {
156    for (let row = 0; row < array.length; ++row) {
157      let isRelMax = true;
158      for (let comparisonRow = Math.max(0, row - order); isRelMax && comparisonRow <= Math.min(array.length - 1, row + order); ++comparisonRow) {
159        if (comparisonRow !== row) {
160          isRelMax = isRelMax && array[row][col] > array[comparisonRow][col];
161        }
162      }
163      if (isRelMax) {
164        result.push([row, col]);
165      }
166    }
167  }
168  return result;
169}
170function max3dForAxis0(array) {
171  const maxArray = array[0].map((v) => v.slice());
172  for (let x = 1; x < array.length; ++x) {
173    for (let y = 0; y < array[0].length; ++y) {
174      for (let z = 0; z < array[0][0].length; ++z) {
175        maxArray[y][z] = Math.max(maxArray[y][z], array[x][y][z]);
176      }
177    }
178  }
179  return maxArray;
180}
181function isNotNull(t) {
182  return t !== null;
183}
184function constrainFrequency(onsets, frames, maxFreq, minFreq) {
185  if (maxFreq) {
186    const maxFreqIdx = hzToMidi(maxFreq) - MIDI_OFFSET;
187    for (let i = 0; i < onsets.length; i++) {
188      onsets[i].fill(0, maxFreqIdx);
189    }
190    for (let i = 0; i < frames.length; i++) {
191      frames[i].fill(0, maxFreqIdx);
192    }
193  }
194  if (minFreq) {
195    const minFreqIdx = hzToMidi(minFreq) - MIDI_OFFSET;
196    for (let i = 0; i < onsets.length; i++) {
197      onsets[i].fill(0, 0, minFreqIdx);
198    }
199    for (let i = 0; i < frames.length; i++) {
200      frames[i].fill(0, 0, minFreqIdx);
201    }
202  }
203}
204function getInferredOnsets(onsets, frames, nDiff = 2) {
205  const diffs = Array.from(Array(nDiff).keys()).map((n) => n + 1).map((n) => {
206    const framesAppended = Array(n).fill(Array(frames[0].length).fill(0)).concat(frames);
207    const nPlus = framesAppended.slice(n);
208    const minusN = framesAppended.slice(0, -n);
209    if (nPlus.length !== minusN.length) {
210      throw new Error(`nPlus length !== minusN length: ${nPlus.length} !== ${minusN.length}`);
211    }
212    return nPlus.map((row, r) => row.map((v, c) => v - minusN[r][c]));
213  });
214  let frameDiff = min3dForAxis0(diffs);
215  frameDiff = frameDiff.map((row) => row.map((v) => Math.max(v, 0)));
216  frameDiff = frameDiff.map((row, r) => r < nDiff ? row.fill(0) : row);
217  const onsetMax = globalMax(onsets);
218  const frameDiffMax = globalMax(frameDiff);
219  frameDiff = frameDiff.map((row) => row.map((v) => onsetMax * v / frameDiffMax));
220  return max3dForAxis0([onsets, frameDiff]);
221}
222function outputToNotesPoly(frames, onsets, onsetThresh = 0.5, frameThresh = 0.3, minNoteLen = 5, inferOnsets = true, maxFreq = null, minFreq = null, melodiaTrick = true, energyTolerance = 11) {
223  let inferredFrameThresh = frameThresh;
224  if (inferredFrameThresh === null) {
225    const [mean, std] = meanStdDev(frames);
226    inferredFrameThresh = mean + std;
227  }
228  const nFrames = frames.length;
229  constrainFrequency(onsets, frames, maxFreq, minFreq);
230  let inferredOnsets = onsets;
231  if (inferOnsets) {
232    inferredOnsets = getInferredOnsets(onsets, frames);
233  }
234  const peakThresholdMatrix = inferredOnsets.map((o) => o.map(() => 0));
235  argRelMax(inferredOnsets).forEach(([row, col]) => {
236    peakThresholdMatrix[row][col] = inferredOnsets[row][col];
237  });
238  const [noteStarts, freqIdxs] = whereGreaterThanAxis1(peakThresholdMatrix, onsetThresh);
239  noteStarts.reverse();
240  freqIdxs.reverse();
241  const remainingEnergy = frames.map((frame) => frame.slice());
242  const noteEvents = noteStarts.map((noteStartIdx, idx) => {
243    const freqIdx = freqIdxs[idx];
244    if (noteStartIdx >= nFrames - 1) {
245      return null;
246    }
247    let i = noteStartIdx + 1;
248    let k = 0;
249    while (i < nFrames - 1 && k < energyTolerance) {
250      if (remainingEnergy[i][freqIdx] < inferredFrameThresh) {
251        k += 1;
252      } else {
253        k = 0;
254      }
255      i += 1;
256    }
257    i -= k;
258    if (i - noteStartIdx <= minNoteLen) {
259      return null;
260    }
261    for (let j = noteStartIdx; j < i; ++j) {
262      remainingEnergy[j][freqIdx] = 0;
263      if (freqIdx < MAX_FREQ_IDX) {
264        remainingEnergy[j][freqIdx + 1] = 0;
265      }
266      if (freqIdx > 0) {
267        remainingEnergy[j][freqIdx - 1] = 0;
268      }
269    }
270    const amplitude = frames.slice(noteStartIdx, i).reduce((prev, row) => prev + row[freqIdx], 0) / (i - noteStartIdx);
271    return {
272      startFrame: noteStartIdx,
273      durationFrames: i - noteStartIdx,
274      pitchMidi: freqIdx + MIDI_OFFSET,
275      amplitude
276    };
277  }).filter(isNotNull);
278  if (melodiaTrick === true) {
279    while (globalMax(remainingEnergy) > inferredFrameThresh) {
280      const [iMid, freqIdx] = remainingEnergy.reduce((prevCoord, currRow, rowIdx) => {
281        const colMaxIdx = argMax(currRow);
282        return currRow[colMaxIdx] > remainingEnergy[prevCoord[0]][prevCoord[1]] ? [rowIdx, colMaxIdx] : prevCoord;
283      }, [0, 0]);
284      remainingEnergy[iMid][freqIdx] = 0;
285      let i = iMid + 1;
286      let k = 0;
287      while (i < nFrames - 1 && k < energyTolerance) {
288        if (remainingEnergy[i][freqIdx] < inferredFrameThresh) {
289          k += 1;
290        } else {
291          k = 0;
292        }
293        remainingEnergy[i][freqIdx] = 0;
294        if (freqIdx < MAX_FREQ_IDX) {
295          remainingEnergy[i][freqIdx + 1] = 0;
296        }
297        if (freqIdx > 0) {
298          remainingEnergy[i][freqIdx - 1] = 0;
299        }
300        i += 1;
301      }
302      const iEnd = i - 1 - k;
303      i = iMid - 1;
304      k = 0;
305      while (i > 0 && k < energyTolerance) {
306        if (remainingEnergy[i][freqIdx] < inferredFrameThresh) {
307          k += 1;
308        } else {
309          k = 0;
310        }
311        remainingEnergy[i][freqIdx] = 0;
312        if (freqIdx < MAX_FREQ_IDX) {
313          remainingEnergy[i][freqIdx + 1] = 0;
314        }
315        if (freqIdx > 0) {
316          remainingEnergy[i][freqIdx - 1] = 0;
317        }
318        i -= 1;
319      }
320      const iStart = i + 1 + k;
321      if (iStart < 0) {
322        throw new Error(`iStart is not positive! value: ${iStart}`);
323      }
324      if (iEnd >= nFrames) {
325        throw new Error(`iEnd is past end of times. (iEnd, times.length): (${iEnd}, ${nFrames})`);
326      }
327      const amplitude = frames.slice(iStart, iEnd).reduce((sum, row) => sum + row[freqIdx], 0) / (iEnd - iStart);
328      if (iEnd - iStart <= minNoteLen) {
329        continue;
330      }
331      noteEvents.push({
332        startFrame: iStart,
333        durationFrames: iEnd - iStart,
334        pitchMidi: freqIdx + MIDI_OFFSET,
335        amplitude
336      });
337    }
338  }
339  return noteEvents;
340}
341const gaussian = (M, std) => Array.from(Array(M).keys()).map((n) => Math.exp(-1 * (n - (M - 1) / 2) ** 2 / (2 * std ** 2)));
342const midiPitchToContourBin = (pitchMidi) =>
342 12 * CONTOURS_BINS_PER_SEMITONE * Math.log2(midiToHz(pitchMidi) / ANNOTATIONS_BASE_FREQUENCY);
343function addPitchBendsToNoteEvents(contours, notes, nBinsTolerance = 25) {
344  const windowLength = nBinsTolerance * 2 + 1;
345  const freqGaussian = gaussian(windowLength, 5);
346  return notes.map((note) => {
347    const freqIdx = Math.floor(Math.round(midiPitchToContourBin(note.pitchMidi)));
348    const freqStartIdx = Math.max(freqIdx - nBinsTolerance, 0);
349    const freqEndIdx = Math.min(N_FREQ_BINS_CONTOURS, freqIdx + nBinsTolerance + 1);
350    const freqGuassianSubMatrix = freqGaussian.slice(Math.max(0, nBinsTolerance - freqIdx), windowLength - Math.max(0, freqIdx - (N_FREQ_BINS_CONTOURS - nBinsTolerance - 1)));
351    const pitchBendSubmatrix = contours.slice(note.startFrame, note.startFrame + note.durationFrames).map((d) => d.slice(freqStartIdx, freqEndIdx).map((v, col) => v * freqGuassianSubMatrix[col]));
352    const pbShift = nBinsTolerance - Math.max(0, nBinsTolerance - freqIdx);
353    const bends = argMaxAxis1(pitchBendSubmatrix).map((v) => v - pbShift);
354    return Object.assign(Object.assign({}, note), { pitchBends: bends });
355  });
356}
357const noteFramesToTime = (notes) => notes.map((note) => {
358  return {
359    pitchMidi: note.pitchMidi,
360    amplitude: note.amplitude,
361    pitchBends: note.pitchBends,
362    startTimeSeconds: modelFrameToTime(note.startFrame),
363    durationSeconds: modelFrameToTime(note.startFrame + note.durationFrames) - modelFrameToTime(note.startFrame)
364  };
365});
366export {
367  BasicPitch,
368  addPitchBendsToNoteEvents,
369  noteFramesToTime,
370  outputToNotesPoly
371};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.