-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmidikeys.js
185 lines (156 loc) · 4.25 KB
/
midikeys.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
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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
/**
* allows us to set the data property, which is read-only in MIDIMessageEvent
*/
class MyMIDIMessageEvent extends MIDIMessageEvent {
data = null
}
export class MidiKeys {
static KEY_TYPE_NONE = 0
static KEY_TYPE_CONTROL = 1
static KEY_TYPE_NOTE = 2
static map = {
qwertz: {
// note keys
65: 0, // A -> C
87: 1, // W -> C#
83: 2, // S -> D
69: 3, // E -> D#
68: 4, // D -> E
70: 5, // F -> F
84: 6, // T -> F#
71: 7, // G -> G
90: 8, // Z -> G#
72: 9, // H -> A
85: 10, // U -> A#
74: 11, // J -> B
75: 12, // K -> C
79: 13, // O -> C#
76: 14, // L -> D
80: 15, // P -> D#
186: 16, // Ö -> E
222: 17, // Ä -> F
// control keys
89: {keydown: () => this.octaveDown()}, // Y
88: {keydown: () => this.octaveUp()}, // X
67: {keydown: () => this.velDown()}, // C
86: {keydown: () => this.velUp()}, // V
16: {keydown: () => this.pedal(1), keyup: () => this.pedal(0)} // Shift
}
}
static MIN_OCTAVE = 0
static MAX_OCTAVE = 9
static MIN_VEL = 1
static MAX_VEL = 127
static VEL_STEP = 10
static verbose = false
static octave = 3
static vel = 127
static keys_pressed = []
static COMMAND_KEYDOWN = 0x9
static COMMAND_KEYUP = 0x8
static COMMAND_CC = 0xB
static COMMAND_CC_PEDAL = 0x40
static log(...o) {
if (!this.verbose) return
console.log("MidiKeys: OCT", this.octave, " VEL", this.vel, "pressed", this.keys_pressed, ...o)
}
static getKeyboardLayout() {
return 'qwertz'
}
static getMap() {
if (!(this.getKeyboardLayout() in this.map)) {
throw "undefined keyboard layout"
}
return this.map[this.getKeyboardLayout()]
}
static setup(api = (msg) => console.log("MidiKeys: no api to deliver message", msg)) {
this.api = api
document.addEventListener("keydown", function(event) {
MidiKeys.handleKeyEvent(event)
});
document.addEventListener("keyup", function(event) {
MidiKeys.handleKeyEvent(event)
});
}
static handleKeyEvent(event) {
let keyId = this.getKeyIdOfKeyEvent(event)
let keyType = this.getKeyType(keyId)
// some other key has been pressed or released
if (keyType === this.KEY_TYPE_NONE) return
// prevent keys that are pressed from constantly firing keypress event
if (event.type === 'keydown') {
if (this.keys_pressed[keyId]) return
this.keys_pressed[keyId] = true
} else {
this.keys_pressed[keyId] = false
}
let keyMapped = this.getMap()[keyId]
// control key?
if (keyType === this.KEY_TYPE_CONTROL) {
if (event.type in keyMapped) {
(keyMapped[event.type])()
this.log()
}
}
// note key?
if (keyType === this.KEY_TYPE_NOTE) {
let pitch = this.getPitch(keyMapped)
if (event.type === 'keydown') {
this.noteTrigger(pitch)
} else {
this.noteRelease(pitch)
}
}
this.log()
}
static getKeyIdOfKeyEvent(event) {
return (typeof event.which === "number") ? event.which : event.keyCode
}
static getKeyType(keyId) {
if (!(keyId in this.getMap())) return this.KEY_TYPE_NONE
let res = this.getMap()[keyId]
if (typeof res === 'number') return this.KEY_TYPE_NOTE
return this.KEY_TYPE_CONTROL
}
static noteTrigger(pitch) {
this.sendMessage(this.COMMAND_KEYDOWN, pitch, this.vel)
}
static noteRelease(pitch) {
this.sendMessage(this.COMMAND_KEYUP, pitch)
}
static sendMessage(command, pitch, vel) {
let data = new Uint8Array(3)
data[0] = (command << 4) + 0x00; // Send the command on channel 1
data[1] = pitch; // Attach the midi note
data[2] = vel;
let msg = new MyMIDIMessageEvent('MIDIMessageEvent')
msg.data = data
this.api(msg)
}
static getPitch(note) {
return (this.octave * 12) + note
}
/** CONTROLS **********************************************/
static octaveDown() {
this.octave = Math.max(this.octave - 1, this.MIN_OCTAVE)
}
static octaveUp() {
this.octave = Math.min(this.octave + 1, this.MAX_OCTAVE)
}
static velDown() {
this.vel = Math.max(this.vel - this.VEL_STEP, this.MIN_VEL)
}
static velUp() {
this.vel = Math.min(this.vel + this.VEL_STEP, this.MAX_VEL)
}
static pedal(on) {
if (on) {
// vel >= 64: pedal on
this.sendMessage(this.COMMAND_CC, this.COMMAND_CC_PEDAL, 127)
} else {
// vel < 64: pedal off
this.sendMessage(this.COMMAND_CC, this.COMMAND_CC_PEDAL, 0)
}
this.log("PEDAL", !!on)
}
}