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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
|
'use strict'
function IO (terminal) {
const dgram = require('dgram')
this.midi = { device: 0 }
this.outputs = []
this.stack = { keys: [] }
this.start = function () {
this.clear()
this.midiSetup()
}
this.clear = function () {
this.stack.udp = []
this.stack.midi = []
}
this.run = function () {
if (this.length() < 1) { return }
for (const id in this.stack.udp) {
this.playUdp(this.stack.udp[id])
}
for (const id in this.stack.midi) {
this.playMidi(this.stack.midi[id])
}
}
// Keyboard
this.sendKey = function (key) {
this.stack.keys = [key]
}
this.playKey = function (key) {
// Idle. Read by the ! operator.
}
// UDP
this.sendUdp = function (msg) {
this.stack.udp.push(msg)
}
this.playUdp = function (data) {
const udp = dgram.createSocket('udp4')
udp.send(Buffer.from(`${data}`), 49160, 'localhost', (err) => {
udp.close()
})
}
// Midi
this.sendMidi = function (channel, octave, note, velocity, length) {
this.stack.midi.push([channel, octave, note, velocity, length])
}
this.playMidi = function (data) {
const device = this.midi.device
const channel = convertChannel(data[0])
const note = convertNote(data[1], data[2])
const velocity = data[3]
const length = window.performance.now() + convertLength(data[4], terminal.bpm)
if (!this.outputs[device]) { console.warn('No midi device!'); return }
this.outputs[device].send([channel[0], note, velocity])
this.outputs[device].send([channel[1], note, velocity], length)
}
this.setMidiDevice = function (id = 0) {
this.midi.device = clamp(id, 0, this.outputs.length - 1)
console.log(this.outputs[this.midi.device] ? `Set device to #${this.midi.device} — ${this.outputs[this.midi.device].name}` : 'Missing midi device with id.')
return this.outputs[this.midi.device]
}
this.listMidiDevices = function () {
return this.outputs
}
//
function convertChannel (id) {
// return [id + 144, id + 128].toString(16);
if (id === 0) { return [0x90, 0x80] } // ch1
if (id === 1) { return [0x91, 0x81] } // ch2
if (id === 2) { return [0x92, 0x82] } // ch3
if (id === 3) { return [0x93, 0x83] } // ch4
if (id === 4) { return [0x94, 0x84] } // ch5
if (id === 5) { return [0x95, 0x85] } // ch6
if (id === 6) { return [0x96, 0x86] } // ch7
if (id === 7) { return [0x97, 0x87] } // ch8
if (id === 8) { return [0x98, 0x88] } // ch9
if (id === 9) { return [0x99, 0x89] } // ch10
if (id === 10) { return [0x9A, 0x8A] } // ch11
if (id === 11) { return [0x9B, 0x8B] } // ch12
if (id === 12) { return [0x9C, 0x8C] } // ch13
if (id === 13) { return [0x9D, 0x8D] } // ch14
if (id === 14) { return [0x9E, 0x8E] } // ch15
if (id === 15) { return [0x9F, 0x8F] } // ch16
}
function convertNote (octave, note) {
return 24 + (octave * 12) + note // 60 = C3
}
function convertLength (val, bpm) {
return (60000 / bpm) / val
}
// Setup
this.midiSetup = function () {
if (!navigator.requestMIDIAccess) { return }
navigator.requestMIDIAccess({ sysex: false }).then(this.midiActive, this.midiInactive)
}
this.midiActive = function (midiAccess) {
const iter = midiAccess.outputs.values()
for (let i = iter.next(); i && !i.done; i = iter.next()) {
terminal.io.outputs.push(i.value)
}
console.log(terminal.io.outputs[terminal.io.midi.device] ? `Midi is active, devices(${terminal.io.midi.device + 1}/${terminal.io.outputs.length}): ${terminal.io.outputs[terminal.io.midi.device].name}` : 'No Midi device')
}
this.midiInactive = function (err) {
console.warn('No Midi', err)
}
this.length = function () {
return this.stack.udp.length + this.stack.midi.length + this.stack.keys.length
}
this.toString = function () {
let text = ''
for (let i = 0; i < this.length(); i++) {
text += '|'
}
while (text.length - 1 <= terminal.size.grid.w) {
text += '-'
}
return text
}
function clamp (v, min, max) { return v < min ? min : v > max ? max : v }
}
module.exports = IO
|