tuner-core 2026.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +113 -0
  3. package/dist/detectors/autocorrelation.d.ts +19 -0
  4. package/dist/detectors/autocorrelation.d.ts.map +1 -0
  5. package/dist/detectors/autocorrelation.js +60 -0
  6. package/dist/detectors/autocorrelation.js.map +1 -0
  7. package/dist/detectors/create-pitch-detector.d.ts +4 -0
  8. package/dist/detectors/create-pitch-detector.d.ts.map +1 -0
  9. package/dist/detectors/create-pitch-detector.js +39 -0
  10. package/dist/detectors/create-pitch-detector.js.map +1 -0
  11. package/dist/detectors/mpm.d.ts +21 -0
  12. package/dist/detectors/mpm.d.ts.map +1 -0
  13. package/dist/detectors/mpm.js +95 -0
  14. package/dist/detectors/mpm.js.map +1 -0
  15. package/dist/detectors/pyin.d.ts +49 -0
  16. package/dist/detectors/pyin.d.ts.map +1 -0
  17. package/dist/detectors/pyin.js +205 -0
  18. package/dist/detectors/pyin.js.map +1 -0
  19. package/dist/detectors/yin-cmnd.d.ts +13 -0
  20. package/dist/detectors/yin-cmnd.d.ts.map +1 -0
  21. package/dist/detectors/yin-cmnd.js +53 -0
  22. package/dist/detectors/yin-cmnd.js.map +1 -0
  23. package/dist/detectors/yin.d.ts +20 -0
  24. package/dist/detectors/yin.d.ts.map +1 -0
  25. package/dist/detectors/yin.js +66 -0
  26. package/dist/detectors/yin.js.map +1 -0
  27. package/dist/index.d.ts +13 -0
  28. package/dist/index.d.ts.map +1 -0
  29. package/dist/index.js +16 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/notes.d.ts +25 -0
  32. package/dist/notes.d.ts.map +1 -0
  33. package/dist/notes.js +56 -0
  34. package/dist/notes.js.map +1 -0
  35. package/dist/preferences.d.ts +8 -0
  36. package/dist/preferences.d.ts.map +1 -0
  37. package/dist/preferences.js +7 -0
  38. package/dist/preferences.js.map +1 -0
  39. package/dist/session.d.ts +25 -0
  40. package/dist/session.d.ts.map +1 -0
  41. package/dist/session.js +131 -0
  42. package/dist/session.js.map +1 -0
  43. package/dist/tuner-settings.d.ts +58 -0
  44. package/dist/tuner-settings.d.ts.map +1 -0
  45. package/dist/tuner-settings.js +69 -0
  46. package/dist/tuner-settings.js.map +1 -0
  47. package/dist/tunings.d.ts +5 -0
  48. package/dist/tunings.d.ts.map +1 -0
  49. package/dist/tunings.js +231 -0
  50. package/dist/tunings.js.map +1 -0
  51. package/dist/types.d.ts +68 -0
  52. package/dist/types.d.ts.map +1 -0
  53. package/dist/types.js +3 -0
  54. package/dist/types.js.map +1 -0
  55. package/dist/utils/median.d.ts +3 -0
  56. package/dist/utils/median.d.ts.map +1 -0
  57. package/dist/utils/median.js +15 -0
  58. package/dist/utils/median.js.map +1 -0
  59. package/package.json +69 -0
@@ -0,0 +1,205 @@
1
+ import { frequencyToMidi, midiToFrequency } from '../notes.js';
2
+ import { computeYinCmnd, yinAtTau } from './yin-cmnd.js';
3
+ const LOG_FLOOR = -1e8;
4
+ /**
5
+ * Probabilistic YIN–style observations + discrete-pitch HMM (Viterbi forward step).
6
+ * Stateful: call `reset()` when stopping capture so the Markov chain does not bridge sessions.
7
+ */
8
+ export class PyinDetector {
9
+ minFrequency;
10
+ maxFrequency;
11
+ rmsThreshold;
12
+ yinSharpness;
13
+ transitionSigmaSemitones;
14
+ maxJumpSemitones;
15
+ logPitchToUv;
16
+ logUvToPitch;
17
+ logUvToUv;
18
+ firstDipSemitoneWeight;
19
+ firstDipThreshold;
20
+ midiMin;
21
+ midiMax;
22
+ nPitch;
23
+ uvIndex;
24
+ nStates;
25
+ prevLog;
26
+ hasPrev = false;
27
+ constructor(options = {}) {
28
+ this.minFrequency = options.minFrequency ?? 60;
29
+ this.maxFrequency = options.maxFrequency ?? 1400;
30
+ this.rmsThreshold = options.rmsThreshold ?? 0.01;
31
+ this.yinSharpness = options.yinSharpness ?? 12;
32
+ this.transitionSigmaSemitones = options.transitionSigmaSemitones ?? 1.35;
33
+ this.maxJumpSemitones = options.maxJumpSemitones ?? 10;
34
+ this.logPitchToUv = options.logPitchToUv ?? -3.2;
35
+ this.logUvToPitch = options.logUvToPitch ?? -2.8;
36
+ this.logUvToUv = options.logUvToUv ?? -0.15;
37
+ this.firstDipSemitoneWeight = options.firstDipSemitoneWeight ?? 0.14;
38
+ this.firstDipThreshold = options.firstDipThreshold ?? 0.18;
39
+ this.midiMin = Math.floor(frequencyToMidi(this.minFrequency));
40
+ this.midiMax = Math.ceil(frequencyToMidi(this.maxFrequency));
41
+ this.nPitch = this.midiMax - this.midiMin + 1;
42
+ this.uvIndex = this.nPitch;
43
+ this.nStates = this.nPitch + 1;
44
+ this.prevLog = new Float32Array(this.nStates);
45
+ }
46
+ reset() {
47
+ this.hasPrev = false;
48
+ this.prevLog.fill(0);
49
+ }
50
+ detect(samples, sampleRate) {
51
+ const cmnd = computeYinCmnd(samples, sampleRate, this.minFrequency, this.maxFrequency);
52
+ if (!cmnd) {
53
+ this.hasPrev = false;
54
+ return { frequency: null, confidence: 0 };
55
+ }
56
+ if (cmnd.rms < this.rmsThreshold) {
57
+ this.hasPrev = false;
58
+ return { frequency: null, confidence: 0 };
59
+ }
60
+ const { yin, tauMin, tauMax } = cmnd;
61
+ let yMin = 1;
62
+ for (let tau = tauMin; tau <= tauMax; tau++) {
63
+ yMin = Math.min(yMin, yin[tau] ?? 1);
64
+ }
65
+ const midiHint = firstDipMidiHint(yin, tauMin, tauMax, sampleRate, this.firstDipThreshold);
66
+ const emitPitch = new Float32Array(this.nPitch);
67
+ for (let i = 0; i < this.nPitch; i++) {
68
+ const midi = this.midiMin + i;
69
+ const f = midiToFrequency(midi);
70
+ if (f < this.minFrequency || f > this.maxFrequency) {
71
+ emitPitch[i] = LOG_FLOOR;
72
+ continue;
73
+ }
74
+ const tau = sampleRate / f;
75
+ if (tau < tauMin || tau > tauMax) {
76
+ emitPitch[i] = LOG_FLOOR;
77
+ continue;
78
+ }
79
+ const y = yinAtTau(yin, tauMax, tau);
80
+ let logP = -this.yinSharpness * y;
81
+ if (midiHint !== null) {
82
+ const dm = midi - midiHint;
83
+ logP -= this.firstDipSemitoneWeight * dm * dm;
84
+ }
85
+ emitPitch[i] = logP;
86
+ }
87
+ const emitUv = -this.yinSharpness * (0.35 + 0.65 * yMin);
88
+ const cur = new Float32Array(this.nStates);
89
+ const twoSigma2 = 2 * this.transitionSigmaSemitones * this.transitionSigmaSemitones;
90
+ if (!this.hasPrev) {
91
+ const uniform = -Math.log(this.nStates);
92
+ for (let j = 0; j < this.nPitch; j++) {
93
+ cur[j] = uniform + (emitPitch.at(j) ?? LOG_FLOOR);
94
+ }
95
+ cur[this.uvIndex] = uniform + emitUv;
96
+ this.hasPrev = true;
97
+ }
98
+ else {
99
+ for (let j = 0; j < this.nPitch; j++) {
100
+ let best = LOG_FLOOR;
101
+ const mj = this.midiMin + j;
102
+ for (let i = 0; i < this.nPitch; i++) {
103
+ const mi = this.midiMin + i;
104
+ const dj = Math.abs(mi - mj);
105
+ if (dj > this.maxJumpSemitones) {
106
+ continue;
107
+ }
108
+ const trans = -(dj * dj) / twoSigma2;
109
+ const v = (this.prevLog.at(i) ?? LOG_FLOOR) + trans;
110
+ if (v > best) {
111
+ best = v;
112
+ }
113
+ }
114
+ const fromUv = (this.prevLog.at(this.uvIndex) ?? LOG_FLOOR) + this.logUvToPitch;
115
+ if (fromUv > best) {
116
+ best = fromUv;
117
+ }
118
+ cur[j] = best + (emitPitch.at(j) ?? LOG_FLOOR);
119
+ }
120
+ let bestUv = LOG_FLOOR;
121
+ for (let i = 0; i < this.nPitch; i++) {
122
+ const v = (this.prevLog.at(i) ?? LOG_FLOOR) + this.logPitchToUv;
123
+ if (v > bestUv) {
124
+ bestUv = v;
125
+ }
126
+ }
127
+ const uvStay = (this.prevLog.at(this.uvIndex) ?? LOG_FLOOR) + this.logUvToUv;
128
+ if (uvStay > bestUv) {
129
+ bestUv = uvStay;
130
+ }
131
+ cur[this.uvIndex] = bestUv + emitUv;
132
+ }
133
+ let bestIdx = 0;
134
+ let bestVal = cur.at(0) ?? LOG_FLOOR;
135
+ for (let j = 1; j < this.nStates; j++) {
136
+ const cj = cur.at(j) ?? LOG_FLOOR;
137
+ if (cj > bestVal) {
138
+ bestVal = cj;
139
+ bestIdx = j;
140
+ }
141
+ }
142
+ for (let j = 0; j < this.nStates; j++) {
143
+ this.prevLog[j] = cur.at(j) ?? LOG_FLOOR;
144
+ }
145
+ if (bestIdx === this.uvIndex) {
146
+ const second = this.secondBestLog(cur, this.uvIndex);
147
+ const confidence = logProbToConfidence(bestVal, second);
148
+ return { frequency: null, confidence };
149
+ }
150
+ const midi = this.midiMin + bestIdx;
151
+ const frequency = midiToFrequency(midi);
152
+ const second = this.secondBestLog(cur, bestIdx);
153
+ const confidence = logProbToConfidence(bestVal, second);
154
+ return { frequency, confidence };
155
+ }
156
+ secondBestLog(cur, exclude) {
157
+ let s = LOG_FLOOR;
158
+ for (let j = 0; j < this.nStates; j++) {
159
+ if (j === exclude) {
160
+ continue;
161
+ }
162
+ const cj = cur.at(j) ?? LOG_FLOOR;
163
+ if (cj > s) {
164
+ s = cj;
165
+ }
166
+ }
167
+ return s;
168
+ }
169
+ }
170
+ function logProbToConfidence(best, second) {
171
+ const margin = best - second;
172
+ const c = 1 / (1 + Math.exp(-margin));
173
+ return Math.max(0, Math.min(1, c));
174
+ }
175
+ /** MIDI note at the first YIN dip (classic YIN τ pick), for a subharmonic-resistant hint */
176
+ function firstDipMidiHint(yin, tauMin, tauMax, sampleRate, threshold) {
177
+ let bestTau = -1;
178
+ for (let tau = tauMin; tau <= tauMax; tau++) {
179
+ const y = yin[tau] ?? 1;
180
+ if (y < threshold) {
181
+ let t = tau;
182
+ while (t + 1 <= tauMax && (yin[t + 1] ?? 1) < (yin[t] ?? 1)) {
183
+ t++;
184
+ }
185
+ bestTau = t;
186
+ break;
187
+ }
188
+ }
189
+ if (bestTau < 0) {
190
+ return null;
191
+ }
192
+ let refinedTau = bestTau;
193
+ if (bestTau > 1 && bestTau < tauMax) {
194
+ const y0 = yin[bestTau - 1] ?? 1;
195
+ const y1 = yin[bestTau] ?? 1;
196
+ const y2 = yin[bestTau + 1] ?? 1;
197
+ const denom = y0 - 2 * y1 + y2;
198
+ if (Math.abs(denom) > 1e-14) {
199
+ const offset = (y0 - y2) / (2 * denom);
200
+ refinedTau = bestTau + Math.max(-1, Math.min(1, offset));
201
+ }
202
+ }
203
+ return frequencyToMidi(sampleRate / refinedTau);
204
+ }
205
+ //# sourceMappingURL=pyin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pyin.js","sourceRoot":"","sources":["../../src/detectors/pyin.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Shared YIN cumulative mean normalized difference (Cheveigné & Kawahara).
3
+ * Used by YinDetector and PyinDetector.
4
+ */
5
+ export interface YinCmndBuffer {
6
+ yin: Float32Array;
7
+ tauMin: number;
8
+ tauMax: number;
9
+ rms: number;
10
+ }
11
+ export declare function computeYinCmnd(samples: Float32Array, sampleRate: number, minFrequency: number, maxFrequency: number): YinCmndBuffer | null;
12
+ export declare function yinAtTau(yin: Float32Array, tauMax: number, tau: number): number;
13
+ //# sourceMappingURL=yin-cmnd.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"yin-cmnd.d.ts","sourceRoot":"","sources":["../../src/detectors/yin-cmnd.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,YAAY,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,GAAG,EAAE,MAAM,CAAA;CACZ;AAED,wBAAgB,cAAc,CAC5B,OAAO,EAAE,YAAY,EACrB,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,MAAM,GACnB,aAAa,GAAG,IAAI,CAwCtB;AAED,wBAAgB,QAAQ,CACtB,GAAG,EAAE,YAAY,EACjB,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,GACV,MAAM,CAWR"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Shared YIN cumulative mean normalized difference (Cheveigné & Kawahara).
3
+ * Used by YinDetector and PyinDetector.
4
+ */
5
+ export function computeYinCmnd(samples, sampleRate, minFrequency, maxFrequency) {
6
+ const n = samples.length;
7
+ if (n < 4) {
8
+ return null;
9
+ }
10
+ let rms = 0;
11
+ for (let i = 0; i < n; i++) {
12
+ const v = samples[i] ?? 0;
13
+ rms += v * v;
14
+ }
15
+ rms = Math.sqrt(rms / n);
16
+ const tauMin = Math.max(1, Math.ceil(sampleRate / maxFrequency));
17
+ const tauMaxFreq = Math.floor(sampleRate / minFrequency);
18
+ const tauMaxWin = Math.floor(n / 2) - 1;
19
+ const tauMax = Math.min(tauMaxFreq, tauMaxWin);
20
+ if (tauMin > tauMax) {
21
+ return null;
22
+ }
23
+ const d = new Float32Array(tauMax + 1);
24
+ for (let tau = 1; tau <= tauMax; tau++) {
25
+ let sum = 0;
26
+ for (let j = 0; j < n - tau; j++) {
27
+ const diff = (samples[j] ?? 0) - (samples[j + tau] ?? 0);
28
+ sum += diff * diff;
29
+ }
30
+ d[tau] = sum;
31
+ }
32
+ const yin = new Float32Array(tauMax + 1);
33
+ yin[0] = 1;
34
+ let running = 0;
35
+ for (let tau = 1; tau <= tauMax; tau++) {
36
+ running += d[tau] ?? 0;
37
+ yin[tau] = running > 0 ? ((d[tau] ?? 0) * tau) / running : 1;
38
+ }
39
+ return { yin, tauMin, tauMax, rms };
40
+ }
41
+ export function yinAtTau(yin, tauMax, tau) {
42
+ if (tau <= 1) {
43
+ return yin[1] ?? 1;
44
+ }
45
+ if (tau >= tauMax) {
46
+ return yin[tauMax] ?? 1;
47
+ }
48
+ const t0 = Math.floor(tau);
49
+ const t1 = t0 + 1;
50
+ const f = tau - t0;
51
+ return (yin[t0] ?? 1) * (1 - f) + (yin[t1] ?? 1) * f;
52
+ }
53
+ //# sourceMappingURL=yin-cmnd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"yin-cmnd.js","sourceRoot":"","sources":["../../src/detectors/yin-cmnd.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import type { PitchDetection, PitchDetector } from '../types.js';
2
+ /**
3
+ * YIN pitch estimator (Cheveigné & Kawahara).
4
+ * Generally more robust than raw autocorrelation on real signals, especially low notes.
5
+ */
6
+ export declare class YinDetector implements PitchDetector {
7
+ private readonly minFrequency;
8
+ private readonly maxFrequency;
9
+ /** Cumulative mean normalized difference; typical 0.10–0.20 */
10
+ private readonly threshold;
11
+ private readonly rmsThreshold;
12
+ constructor({ minFrequency, maxFrequency, threshold, rmsThreshold, }?: {
13
+ minFrequency?: number | undefined;
14
+ maxFrequency?: number | undefined;
15
+ threshold?: number | undefined;
16
+ rmsThreshold?: number | undefined;
17
+ });
18
+ detect(samples: Float32Array, sampleRate: number): PitchDetection;
19
+ }
20
+ //# sourceMappingURL=yin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"yin.d.ts","sourceRoot":"","sources":["../../src/detectors/yin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAGhE;;;GAGG;AACH,qBAAa,WAAY,YAAW,aAAa;IAC/C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAQ;IACrC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAQ;IACrC,+DAA+D;IAC/D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAQ;IAClC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAQ;gBAEzB,EACV,YAAiB,EACjB,YAAmB,EACnB,SAAgB,EAChB,YAAmB,GACpB;;;;;KAAK;IAON,MAAM,CAAC,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,GAAG,cAAc;CA0DlE"}
@@ -0,0 +1,66 @@
1
+ import { computeYinCmnd } from './yin-cmnd.js';
2
+ /**
3
+ * YIN pitch estimator (Cheveigné & Kawahara).
4
+ * Generally more robust than raw autocorrelation on real signals, especially low notes.
5
+ */
6
+ export class YinDetector {
7
+ minFrequency;
8
+ maxFrequency;
9
+ /** Cumulative mean normalized difference; typical 0.10–0.20 */
10
+ threshold;
11
+ rmsThreshold;
12
+ constructor({ minFrequency = 60, maxFrequency = 1400, threshold = 0.15, rmsThreshold = 0.01, } = {}) {
13
+ this.minFrequency = minFrequency;
14
+ this.maxFrequency = maxFrequency;
15
+ this.threshold = threshold;
16
+ this.rmsThreshold = rmsThreshold;
17
+ }
18
+ detect(samples, sampleRate) {
19
+ const cmnd = computeYinCmnd(samples, sampleRate, this.minFrequency, this.maxFrequency);
20
+ if (!cmnd || cmnd.rms < this.rmsThreshold) {
21
+ return { frequency: null, confidence: 0 };
22
+ }
23
+ const { yin, tauMin, tauMax } = cmnd;
24
+ let bestTau = -1;
25
+ let bestVal = 1;
26
+ for (let tau = tauMin; tau <= tauMax; tau++) {
27
+ const y = yin[tau] ?? 1;
28
+ if (y < this.threshold) {
29
+ let t = tau;
30
+ while (t + 1 <= tauMax && (yin[t + 1] ?? 1) < (yin[t] ?? 1)) {
31
+ t++;
32
+ }
33
+ bestTau = t;
34
+ bestVal = yin[t] ?? 1;
35
+ break;
36
+ }
37
+ }
38
+ if (bestTau < 0 || bestVal >= this.threshold || !Number.isFinite(bestVal)) {
39
+ const confidence = Number.isFinite(bestVal)
40
+ ? Math.max(0, Math.min(1, 1 - bestVal))
41
+ : 0;
42
+ return { frequency: null, confidence };
43
+ }
44
+ let refinedTau = bestTau;
45
+ if (bestTau > 1 && bestTau < tauMax) {
46
+ const y0 = yin[bestTau - 1] ?? 1;
47
+ const y1 = yin[bestTau] ?? 1;
48
+ const y2 = yin[bestTau + 1] ?? 1;
49
+ const denom = y0 - 2 * y1 + y2;
50
+ if (Math.abs(denom) > 1e-14) {
51
+ const offset = (y0 - y2) / (2 * denom);
52
+ refinedTau = bestTau + Math.max(-1, Math.min(1, offset));
53
+ }
54
+ }
55
+ const frequency = sampleRate / refinedTau;
56
+ if (frequency < this.minFrequency || frequency > this.maxFrequency) {
57
+ return {
58
+ frequency: null,
59
+ confidence: Math.max(0, Math.min(1, 1 - bestVal)),
60
+ };
61
+ }
62
+ const confidence = Math.max(0, Math.min(1, 1 - bestVal));
63
+ return { frequency, confidence };
64
+ }
65
+ }
66
+ //# sourceMappingURL=yin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"yin.js","sourceRoot":"","sources":["../../src/detectors/yin.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ export type { AudioProvider, PitchDetection, PitchDetector, StringDef, Tuning, Instrument, ClosestString, TunerResult, HapticsProvider, UserPreferences, PreferencesProvider, TunerEventMap, TunerEventListener, } from './types.js';
2
+ export { TunerSession } from './session.js';
3
+ export { frequencyToMidi, midiToFrequency, midiToNote, midiToOctave, getCents, getCentsFromTarget, } from './notes.js';
4
+ export { AutocorrelationDetector } from './detectors/autocorrelation.js';
5
+ export { YinDetector } from './detectors/yin.js';
6
+ export { PyinDetector } from './detectors/pyin.js';
7
+ export { MpmDetector } from './detectors/mpm.js';
8
+ export { createPitchDetector } from './detectors/create-pitch-detector.js';
9
+ export { INSTRUMENTS, findInstrument, findTuning } from './tunings.js';
10
+ export { DEFAULT_PREFERENCES } from './preferences.js';
11
+ export type { TunerSettings, DetectorSettings, PitchDetectorKind, PyinHMMSettings, } from './tuner-settings.js';
12
+ export { DEFAULT_TUNER_SETTINGS, mergeTunerSettings, applySettingsPatch, } from './tuner-settings.js';
13
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EACV,aAAa,EACb,cAAc,EACd,aAAa,EACb,SAAS,EACT,MAAM,EACN,UAAU,EACV,aAAa,EACb,WAAW,EACX,eAAe,EACf,eAAe,EACf,mBAAmB,EACnB,aAAa,EACb,kBAAkB,GACnB,MAAM,YAAY,CAAA;AAGnB,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAG3C,OAAO,EACL,eAAe,EACf,eAAe,EACf,UAAU,EACV,YAAY,EACZ,QAAQ,EACR,kBAAkB,GACnB,MAAM,YAAY,CAAA;AAGnB,OAAO,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAA;AACxE,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAChD,OAAO,EAAE,mBAAmB,EAAE,MAAM,sCAAsC,CAAA;AAG1E,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAGtE,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAA;AAGtD,YAAY,EACV,aAAa,EACb,gBAAgB,EAChB,iBAAiB,EACjB,eAAe,GAChB,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EACL,sBAAsB,EACtB,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,qBAAqB,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,16 @@
1
+ // Session
2
+ export { TunerSession } from './session.js';
3
+ // Note utilities
4
+ export { frequencyToMidi, midiToFrequency, midiToNote, midiToOctave, getCents, getCentsFromTarget, } from './notes.js';
5
+ // Detectors
6
+ export { AutocorrelationDetector } from './detectors/autocorrelation.js';
7
+ export { YinDetector } from './detectors/yin.js';
8
+ export { PyinDetector } from './detectors/pyin.js';
9
+ export { MpmDetector } from './detectors/mpm.js';
10
+ export { createPitchDetector } from './detectors/create-pitch-detector.js';
11
+ // Tuning data
12
+ export { INSTRUMENTS, findInstrument, findTuning } from './tunings.js';
13
+ // Preferences
14
+ export { DEFAULT_PREFERENCES } from './preferences.js';
15
+ export { DEFAULT_TUNER_SETTINGS, mergeTunerSettings, applySettingsPatch, } from './tuner-settings.js';
16
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAiBA,UAAU;AACV,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAE3C,iBAAiB;AACjB,OAAO,EACL,eAAe,EACf,eAAe,EACf,UAAU,EACV,YAAY,EACZ,QAAQ,EACR,kBAAkB,GACnB,MAAM,YAAY,CAAA;AAEnB,YAAY;AACZ,OAAO,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAA;AACxE,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAChD,OAAO,EAAE,mBAAmB,EAAE,MAAM,sCAAsC,CAAA;AAE1E,cAAc;AACd,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAEtE,cAAc;AACd,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAA;AAStD,OAAO,EACL,sBAAsB,EACtB,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,qBAAqB,CAAA"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Convert a frequency in Hz to a MIDI note number (float).
3
+ */
4
+ export declare function frequencyToMidi(frequency: number): number;
5
+ /**
6
+ * Convert a MIDI note number to frequency in Hz.
7
+ */
8
+ export declare function midiToFrequency(midi: number): number;
9
+ /**
10
+ * Get the note name for a MIDI note number.
11
+ */
12
+ export declare function midiToNote(midi: number): string;
13
+ /**
14
+ * Get the octave for a MIDI note number.
15
+ */
16
+ export declare function midiToOctave(midi: number): number;
17
+ /**
18
+ * Get cents deviation from the nearest semitone (-50 to +50).
19
+ */
20
+ export declare function getCents(frequency: number): number;
21
+ /**
22
+ * Get cents deviation between a detected frequency and a target frequency.
23
+ */
24
+ export declare function getCentsFromTarget(detected: number, target: number): number;
25
+ //# sourceMappingURL=notes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notes.d.ts","sourceRoot":"","sources":["../src/notes.ts"],"names":[],"mappings":"AAkBA;;GAEG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAG/C;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAIlD;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAE3E"}
package/dist/notes.js ADDED
@@ -0,0 +1,56 @@
1
+ const NOTE_NAMES = [
2
+ 'C',
3
+ 'C#',
4
+ 'D',
5
+ 'D#',
6
+ 'E',
7
+ 'F',
8
+ 'F#',
9
+ 'G',
10
+ 'G#',
11
+ 'A',
12
+ 'A#',
13
+ 'B',
14
+ ];
15
+ const A4_FREQUENCY = 440;
16
+ const A4_MIDI = 69;
17
+ /**
18
+ * Convert a frequency in Hz to a MIDI note number (float).
19
+ */
20
+ export function frequencyToMidi(frequency) {
21
+ return 12 * Math.log2(frequency / A4_FREQUENCY) + A4_MIDI;
22
+ }
23
+ /**
24
+ * Convert a MIDI note number to frequency in Hz.
25
+ */
26
+ export function midiToFrequency(midi) {
27
+ return A4_FREQUENCY * 2 ** ((midi - A4_MIDI) / 12);
28
+ }
29
+ /**
30
+ * Get the note name for a MIDI note number.
31
+ */
32
+ export function midiToNote(midi) {
33
+ const name = NOTE_NAMES[Math.round(midi) % 12];
34
+ return name ?? 'A';
35
+ }
36
+ /**
37
+ * Get the octave for a MIDI note number.
38
+ */
39
+ export function midiToOctave(midi) {
40
+ return Math.floor(Math.round(midi) / 12) - 1;
41
+ }
42
+ /**
43
+ * Get cents deviation from the nearest semitone (-50 to +50).
44
+ */
45
+ export function getCents(frequency) {
46
+ const midi = frequencyToMidi(frequency);
47
+ const nearestMidi = Math.round(midi);
48
+ return Math.round((midi - nearestMidi) * 100);
49
+ }
50
+ /**
51
+ * Get cents deviation between a detected frequency and a target frequency.
52
+ */
53
+ export function getCentsFromTarget(detected, target) {
54
+ return Math.round(1200 * Math.log2(detected / target));
55
+ }
56
+ //# sourceMappingURL=notes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notes.js","sourceRoot":"","sources":["../src/notes.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,GAAG;IACjB,GAAG;IACH,IAAI;IACJ,GAAG;IACH,IAAI;IACJ,GAAG;IACH,GAAG;IACH,IAAI;IACJ,GAAG;IACH,IAAI;IACJ,GAAG;IACH,IAAI;IACJ,GAAG;CACK,CAAA;AAEV,MAAM,YAAY,GAAG,GAAG,CAAA;AACxB,MAAM,OAAO,GAAG,EAAE,CAAA;AAElB;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,SAAiB;IAC/C,OAAO,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,GAAG,YAAY,CAAC,GAAG,OAAO,CAAA;AAC3D,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,OAAO,YAAY,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,CAAA;AACpD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAA;IAC9C,OAAO,IAAI,IAAI,GAAG,CAAA;AACpB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAA;AAC9C,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,QAAQ,CAAC,SAAiB;IACxC,MAAM,IAAI,GAAG,eAAe,CAAC,SAAS,CAAC,CAAA;IACvC,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IACpC,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,GAAG,WAAW,CAAC,GAAG,GAAG,CAAC,CAAA;AAC/C,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAAC,QAAgB,EAAE,MAAc;IACjE,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAC,CAAC,CAAA;AACxD,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Persisted app choices (instrument, tuning, cents threshold, custom tunings) — the kind of data
3
+ * a `PreferencesProvider` loads/saves. This is not engine configuration; for buffer size, detector
4
+ * kind, and smoothing see `tuner-settings.ts` and `TunerSettings`.
5
+ */
6
+ import type { UserPreferences } from './types.js';
7
+ export declare const DEFAULT_PREFERENCES: UserPreferences;
8
+ //# sourceMappingURL=preferences.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preferences.d.ts","sourceRoot":"","sources":["../src/preferences.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAEjD,eAAO,MAAM,mBAAmB,EAAE,eAKjC,CAAA"}
@@ -0,0 +1,7 @@
1
+ export const DEFAULT_PREFERENCES = {
2
+ centsThreshold: 5,
3
+ customTunings: [],
4
+ lastInstrumentId: null,
5
+ lastTuningId: null,
6
+ };
7
+ //# sourceMappingURL=preferences.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preferences.js","sourceRoot":"","sources":["../src/preferences.ts"],"names":[],"mappings":"AAOA,MAAM,CAAC,MAAM,mBAAmB,GAAoB;IAClD,cAAc,EAAE,CAAC;IACjB,aAAa,EAAE,EAAE;IACjB,gBAAgB,EAAE,IAAI;IACtB,YAAY,EAAE,IAAI;CACnB,CAAA"}
@@ -0,0 +1,25 @@
1
+ import type { TunerSettings } from './tuner-settings.js';
2
+ import type { AudioProvider, PitchDetector, TunerEventListener, TunerEventMap, Tuning, UserPreferences } from './types.js';
3
+ export declare class TunerSession {
4
+ private readonly audio;
5
+ private readonly detector;
6
+ private readonly settings;
7
+ private activeTuning;
8
+ private centsThreshold;
9
+ private running;
10
+ private freqWindow;
11
+ private lastHeldHz;
12
+ private listeners;
13
+ constructor(audio: AudioProvider, detector: PitchDetector, settings?: Partial<TunerSettings>);
14
+ setTuning(tuning: Tuning | null): void;
15
+ applyPreferences(prefs: Partial<UserPreferences>): void;
16
+ start(): Promise<void>;
17
+ stop(): void;
18
+ get isRunning(): boolean;
19
+ on<K extends keyof TunerEventMap>(event: K, listener: TunerEventListener<K>): void;
20
+ off<K extends keyof TunerEventMap>(event: K, listener: TunerEventListener<K>): void;
21
+ private emit;
22
+ private processFrame;
23
+ private findClosestString;
24
+ }
25
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAExD,OAAO,KAAK,EACV,aAAa,EACb,aAAa,EACb,kBAAkB,EAClB,aAAa,EAEb,MAAM,EACN,eAAe,EAChB,MAAM,YAAY,CAAA;AASnB,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;IACrC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAe;IACxC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAe;IACxC,OAAO,CAAC,YAAY,CAAsB;IAC1C,OAAO,CAAC,cAAc,CAAkC;IACxD,OAAO,CAAC,OAAO,CAAQ;IAEvB,OAAO,CAAC,UAAU,CAAe;IACjC,OAAO,CAAC,UAAU,CAAsB;IAExC,OAAO,CAAC,SAAS,CAKhB;gBAGC,KAAK,EAAE,aAAa,EACpB,QAAQ,EAAE,aAAa,EACvB,QAAQ,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC;IASnC,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAItC,gBAAgB,CAAC,KAAK,EAAE,OAAO,CAAC,eAAe,CAAC,GAAG,IAAI;IAQjD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAY5B,IAAI,IAAI,IAAI;IAUZ,IAAI,SAAS,IAAI,OAAO,CAEvB;IAID,EAAE,CAAC,CAAC,SAAS,MAAM,aAAa,EAC9B,KAAK,EAAE,CAAC,EACR,QAAQ,EAAE,kBAAkB,CAAC,CAAC,CAAC,GAC9B,IAAI;IAIP,GAAG,CAAC,CAAC,SAAS,MAAM,aAAa,EAC/B,KAAK,EAAE,CAAC,EACR,QAAQ,EAAE,kBAAkB,CAAC,CAAC,CAAC,GAC9B,IAAI;IAIP,OAAO,CAAC,IAAI;IAWZ,OAAO,CAAC,YAAY;IAyCpB,OAAO,CAAC,iBAAiB;CAsB1B"}