-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
95 lines (78 loc) · 2.91 KB
/
index.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
const JZZ = require("jzz")
module.exports = class RinkLink {
constructor(mock) {
this.input = JZZ().openMidiIn(/ice(skate|link|rink).*/gi);
this.output = JZZ().openMidiIn(/ice(skate|link|rink).*/gi);
if((this.input._err.length > 0 || this.output._err > 0) && !mock) {
throw new Error("could not open midi device!");
}
this.subscribers = [];
this.message_buffer = [];
this.input.connect(message => this._onMidiMessage(message));
}
close() {
this.input.close();
this.output.close();
}
_onMidiMessage(d) {
if (d[0] >> 4 == 0xA) { // end of packet
this._flush_buffer();
} else if (d[0] >> 4 == 0x8) { // single byte
this.message_buffer.push(d[1] | ((d[0] & 0b00000001) << 7));
} else if (d[0] >> 4 == 0x9) { // two bytes
this.message_buffer.push(d[1] | ((d[0] & 0b00000001) << 7));
this.message_buffer.push(d[2] | ((d[0] & 0b00000010) << 6));
} else {
console.error("unknown message recived on icelink");
}
}
_flush_buffer() {
if(this.message_buffer)
this.subscribers.forEach(s => s(this.message_buffer));
this.message_buffer = [];
}
subscribe(callback) {
this.subscribers.push(callback);
}
unsubscribe(callback) {
this.subscribers = this.subscribers.filter(s => s != callback);
}
async recive() {
return new Promise(resolve => {
const callback = data => {
this.unsubscribe(callback);
resolve(data);
}
this.subscribe(callback);
})
}
send(data) {
// preprocess the input
if (typeof (data) === "string") data = data.split("").map(c => c.charCodeAt(0));
else if (typeof (data) === "number") data = [data];
if (data.reduce((acc, cur) => true ? typeof (cur) != "number" : acc, false)) {
throw Error("datatype is not sendable via icerink!");
}
// group into pairs of two to speed up transmission
const grouped_data = data.reduce((acc, cur) => {
if (acc.length == 0 || acc[acc.length - 1].length > 1) {
return [...acc, cur]
} else {
return [...acc.slice(0, -1), [...acc.slice(-1), cur]]
}
}, []);
grouped_data.forEach(b => {
if (typeof(b) != "number") {
this.output.send(new JZZ.MIDI([0x90 | b[0] >> 7 | (b[1] >> 7) << 1, b[0] & 0b01111111, b[1] & 0b01111111]))
} else {
this.output.send(new JZZ.MIDI([0x80 | b >> 7, b & 0b01111111, 0x00]))
}
})
this.output.send(new JZZ.MIDI([0xA0, 0x00, 0x00])); // flush the line
}
command(c) {
// flushing the buffer when nothing is inside causes a reboot
this._flush_buffer();
this.send(c);
}
};