whistle-switch
Version:
Control Homekit by whistling 🎶
123 lines (106 loc) • 2.19 kB
text/typescript
import { DefaultReferencePitchHz } from "./frequency";
export type NoteName =
| "c"
| "c#"
| "dâ™"
| "d"
| "d#"
| "eâ™"
| "e"
| "f"
| "f#"
| "gâ™"
| "g"
| "g#"
| "aâ™"
| "a"
| "a#"
| "bâ™"
| "b";
export const NoteNumberByName: Record<NoteName, number> = {
c: 0,
"c#": 1,
"dâ™": 1,
d: 2,
"d#": 3,
"eâ™": 3,
e: 4,
f: 5,
"f#": 6,
"gâ™": 6,
g: 7,
"g#": 8,
"aâ™": 8,
a: 9,
"a#": 10,
"bâ™": 10,
b: 11,
};
export const NoteNames: NoteName[] = [
"c", // 0
"c#", // 1
"d",
"d#",
"e",
"f",
"f#",
"g",
"g#",
"a", // 9
"a#",
"b",
];
export function noteNumberFromFrequency(
referencePitchHz: number,
frequencyHz: number
) {
if (frequencyHz < 1) {
return 0;
}
const c0Hz = referencePitchHz * Math.pow(2, -4.75);
return Math.round(12 * Math.log2(frequencyHz / c0Hz));
}
export function noteOctave(noteNumber: number) {
return Math.floor(noteNumber / 12);
}
export function noteName(noteNumber: number) {
const noteI = noteNumber % 12;
return NoteNames[noteI];
}
export function noteFullName(noteNumber: number) {
return `${noteName(noteNumber)}${noteOctave(noteNumber)}`;
}
export function noteNameFromFrequency(noteNumber: number) {
const noteI = noteNumber % 12;
return NoteNames[noteI];
}
export class FrequencyToNoteConverter {
private _refHz: number;
private _numberByFreq = new Map();
get referenceFrequencyHz() {
return this._refHz;
}
constructor(referenceFrequencyHz: number) {
this._refHz = referenceFrequencyHz;
}
private _getNumberMemo = (frequencyHz: number) => {
if (!this._numberByFreq.has(frequencyHz)) {
this._numberByFreq.set(
frequencyHz,
noteNumberFromFrequency(this._refHz, frequencyHz)
);
}
return this._numberByFreq.get(frequencyHz)!;
};
name(frequencyHz: number) {
const noteNumber = this._getNumberMemo(frequencyHz);
return noteName(noteNumber);
}
number(frequencyHz: number) {
return this._getNumberMemo(frequencyHz);
}
octave(frequencyHz: number) {
const noteNumber = this._getNumberMemo(frequencyHz);
return noteOctave(noteNumber);
}
}