forked from tonaljs/tonal
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.ts
99 lines (91 loc) · 2.88 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
import { note as toNote, NoteName } from "@tonaljs/tonal";
type Midi = number;
export function isMidi(arg: any): arg is Midi {
return +arg >= 0 && +arg <= 127;
}
/**
* Get the note midi number (a number between 0 and 127)
*
* It returns undefined if not valid note name
*
* @function
* @param {string|number} note - the note name or midi number
* @return {Integer} the midi number or undefined if not valid note
* @example
* import { toMidi } from '@tonaljs/midi'
* toMidi("C4") // => 60
* toMidi(60) // => 60
* toMidi('60') // => 60
*/
export function toMidi(note: NoteName | number): number | null {
if (isMidi(note)) {
return +note;
}
const n = toNote(note);
return n.empty ? null : n.midi;
}
/**
* Get the frequency in hertzs from midi number
*
* @param {number} midi - the note midi number
* @param {number} [tuning = 440] - A4 tuning frequency in Hz (440 by default)
* @return {number} the frequency or null if not valid note midi
* @example
* import { midiToFreq} from '@tonaljs/midi'
* midiToFreq(69) // => 440
*/
export function midiToFreq(midi: number, tuning = 440): number {
return Math.pow(2, (midi - 69) / 12) * tuning;
}
const L2 = Math.log(2);
const L440 = Math.log(440);
/**
* Get the midi number from a frequency in hertz. The midi number can
* contain decimals (with two digits precission)
*
* @param {number} frequency
* @return {number}
* @example
* import { freqToMidi} from '@tonaljs/midi'
* freqToMidi(220)); //=> 57
* freqToMidi(261.62)); //=> 60
* freqToMidi(261)); //=> 59.96
*/
export function freqToMidi(freq: number): number {
const v = (12 * (Math.log(freq) - L440)) / L2 + 69;
return Math.round(v * 100) / 100;
}
export interface ToNoteNameOptions {
pitchClass?: boolean;
sharps?: boolean;
}
const SHARPS = "C C# D D# E F F# G G# A A# B".split(" ");
const FLATS = "C Db D Eb E F Gb G Ab A Bb B".split(" ");
/**
* Given a midi number, returns a note name. The altered notes will have
* flats unless explicitly set with the optional `useSharps` parameter.
*
* @function
* @param {number} midi - the midi note number
* @param {Object} options = default: `{ sharps: false, pitchClass: false }`
* @param {boolean} useSharps - (Optional) set to true to use sharps instead of flats
* @return {string} the note name
* @example
* import { midiToNoteName } from '@tonaljs/midi'
* midiToNoteName(61) // => "Db4"
* midiToNoteName(61, { pitchClass: true }) // => "Db"
* midiToNoteName(61, { sharps: true }) // => "C#4"
* midiToNoteName(61, { pitchClass: true, sharps: true }) // => "C#"
* // it rounds to nearest note
* midiToNoteName(61.7) // => "D4"
*/
export function midiToNoteName(midi: number, options: ToNoteNameOptions = {}) {
midi = Math.round(midi);
const pcs = options.sharps === true ? SHARPS : FLATS;
const pc = pcs[midi % 12];
if (options.pitchClass) {
return pc;
}
const o = Math.floor(midi / 12) - 1;
return pc + o;
}