-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathmidiclock.js
109 lines (91 loc) Β· 2.52 KB
/
midiclock.js
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
100
101
102
103
104
105
106
107
108
109
const CLOCK = 248;
const START = 250;
const STOP = 252;
export class MidiClockInput {
constructor() {
this.midi = undefined;
this.input = undefined;
this.onClockMessage = () => undefined;
}
async requestAccess() {
this.midi = await navigator.requestMIDIAccess({
sysex: false,
software: false,
});
return this.midi;
}
getInputs() {
return new Promise((resolve) => {
const resolveIfPresent = () => {
const inputs = this.midi.inputs;
if (inputs.size) {
this.midi.removeEventListener("statechange", resolveIfPresent);
resolve(Array.from(inputs.values()));
}
};
this.midi.addEventListener("statechange", resolveIfPresent);
resolveIfPresent();
});
}
listenTo(inputId) {
if (this.input) {
this.input.removeEventListener("midimessage", this.onMidiMessage);
}
this.messageCounter = 0;
this.synchronized = false;
this.lastQuarterAt = undefined;
this.bpm = undefined;
this.input = this.midi.inputs.get(inputId);
this.input.addEventListener("midimessage", (msg) =>
this.onMidiMessage(msg)
);
}
onMidiMessage(event) {
if (event.data.length != 1) {
console.warn("Unknown message", event);
return;
}
const rawMessage = event.data[0];
if (rawMessage === STOP) {
this.messageCounter = 0;
this.synchronized = false;
this.lastQuarterAt = undefined;
} else if (rawMessage === START) {
this.messageCounter = 0;
this.synchronized = true;
this.lastQuarterAt = undefined;
} else if (rawMessage === CLOCK) {
this.messageCounter += 1;
} else {
console.warn("Unknown message", rawMessage);
return;
}
const quarterNotes = this.messageCounter / 24;
if (this.messageCounter % 24 === 0) {
const now = event.receivedTime || event.timeStamp;
if (this.lastQuarterAt) {
this.bpm = 60000 / (event.timeStamp - this.lastQuarterAt);
}
this.lastQuarterAt = now;
}
const clockMessage = {
messageCounter: this.messageCounter,
note: quarterNotes / 4,
bpm: this.bpm,
synchronized: this.synchronized,
};
this.onClockMessage(clockMessage);
}
}
export function calculateBars(notes) {
const quarterNotes = parseInt(notes * 4);
const beat = quarterNotes % 4,
bar = parseInt(quarterNotes / 4),
segment = parseInt(bar / 4);
return {
beat: beat + 1,
bar: (bar % 4) + 1,
segment: segment + 1,
fraction: (notes * 4) % 1,
};
}