generated from bitfocus/companion-module-template-js
-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathmidi.ts
167 lines (148 loc) · 4.07 KB
/
midi.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
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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
import * as node_midi from '@julusian/midi'
import { EventEmitter } from 'events'
import { MidiMessage, Mtc } from './msgtypes.js'
export class Input extends EventEmitter {
private _input
private _smpte: number[]
private _pendingSysex: boolean
private _sysex: number[]
private _inputPortNumberedNames: string[]
public name: string
constructor(name: string, virtual?: boolean) {
super()
this._input = new node_midi.Input()
this._input.ignoreTypes(false, false, false) // Allow all message types
this._pendingSysex = false
this._sysex = []
this._smpte = []
this.name = name
this._inputPortNumberedNames = getInputs()
if (virtual) {
this._input.openVirtualPort(name)
} else {
const numInputs = this._input.getPortCount()
for (let i = 0; i < numInputs; i++) {
if (name === this._inputPortNumberedNames[i]) {
try {
this._input.openPort(i)
} catch (err) {
console.log(`Error opening port ${name}.\nError: ${err}`)
}
}
}
}
this._input.on('message', (deltaTime: number, bytes: number[]): void => {
// not dealing with sysex chunks longer than the buffer ATM
const msg = MidiMessage.parseMessage(bytes)
if (msg === undefined) return
this.emit('message', deltaTime, msg)
if (msg.id == 'mtc') {
this.parseMtc(msg as Mtc)
}
})
}
close(): void {
this._input.closePort()
this._input.destroy()
}
isPortOpen(): boolean {
return this._input.isPortOpen()
}
parseMtc(data: Mtc): void {
const FRAME_RATES = [24, 25, 29.97, 30]
const byteNumber: number = data.type
let value: number = data.value
let smpteFrameRate: number
this._smpte[byteNumber] = value
// Check if we have 8 complete messages. If this._smpte[0] is undefined, then we started in the middle!
if (byteNumber === 7 && typeof this._smpte[0] === 'number') {
const bits: number[] = []
for (let i = 3; i >= 0; i--) {
const bit = value & (1 << i) ? 1 : 0
bits.push(bit)
}
value = bits[3]
smpteFrameRate = FRAME_RATES[(bits[1] << 1) + bits[2]]
this._smpte[byteNumber] = value
const smpte = this._smpte
const smpteFormatted =
((smpte[7] << 4) + smpte[6]).toString().padStart(2, '0') +
':' +
((smpte[5] << 4) + smpte[4]).toString().padStart(2, '0') +
':' +
((smpte[3] << 4) + smpte[2]).toString().padStart(2, '0') +
'.' +
((smpte[1] << 4) + smpte[0]).toString().padStart(2, '0')
this.emit('smpte', {
smpte: smpteFormatted,
smpteFR: smpteFrameRate,
})
}
}
}
export class Output {
private _output
public name: string
constructor(name: string, virtual?: boolean) {
this._output = new node_midi.Output()
this.name = name
const outputPortNumberedNames: string[] = getOutputs()
if (virtual) {
this._output.openVirtualPort(name)
} else {
const numOutputs = this._output.getPortCount()
for (let i = 0; i < numOutputs; i++) {
if (name === outputPortNumberedNames[i]) {
try {
this._output.openPort(i)
} catch (err) {
console.log(`Error opening port ${name}.\nError: ${err}`)
}
}
}
}
}
close(): void {
this._output.closePort()
this._output.destroy()
}
isPortOpen(): boolean {
return this._output.isPortOpen()
}
send(msg: MidiMessage): void {
this._output.sendMessage(msg.bytes)
}
}
// utilities
export function getInputs(): string[] {
const input = new node_midi.Input()
const inputs: string[] = []
for (let i = 0; i < input.getPortCount(); i++) {
let counter = 0
const portName = input.getPortName(i)
let numberedPortName = portName
while (inputs.includes(numberedPortName)) {
counter++
numberedPortName += ` ${counter}`
}
inputs.push(numberedPortName)
}
input.closePort()
return inputs
}
export function getOutputs(): string[] {
const output = new node_midi.Output()
const outputs: string[] = []
for (let i = 0; i < output.getPortCount(); i++) {
let counter = 0
const portName = output.getPortName(i)
let numberedPortName = portName
while (outputs.includes(numberedPortName)) {
counter++
numberedPortName += ` ${counter}`
}
outputs.push(numberedPortName)
}
output.closePort()
return outputs
}