· 4 years ago · Apr 11, 2021, 04:58 PM
1'use strict';
2var audioDevices = [];
3
4navigator.mediaDevices.enumerateDevices().then(function (devices) {
5 for (var i = 0; i !== devices.length; ++i) {
6 var device = devices[i];
7 if (device.kind === 'audiooutput') {
8 audioDevices.push(device);
9 }
10 }
11});
12
13navigator.mediaDevices
14 .getUserMedia({
15 audio: true,
16 })
17 .then(function (stream) {
18 stream.getTracks().forEach(function (track) {
19 track.stop();
20 });
21 });
22
23/* eslint no-inline-comments: 0 */
24/* eslint no-unused-vars: 0 */
25/* eslint no-undef: 0 */
26/* eslint no-console: 0 */
27
28// All midi and gui key events land here
29function keyEvent(source, key, action, edit) {
30 // console.log(`${source} key ${action === 'press' ? 'pressed' : 'release'}: ${key}`); // Log the key action
31 const keyConfig = getKeyConfig(key);
32 if (!edit) {
33 // Only perform these actions if left-click on key - no right-click
34 colorKey(key, action, keyConfig); // Color the key based on the action
35 playAudio(key, action, keyConfig); // Play Audio if used
36 sendAPI(key, action, keyConfig); // Send API event if used
37 sendCLR(key, action, keyConfig); // Send CLR event if used
38 sendHotkey(key, action, keyConfig); // Send Hotkey if used
39 }
40 if (action === 'press') {
41 $('.options .key_pos_label').text(`(${key.join(',')})`); // Change key label to show last pressed key
42 lastKey = key; // Update what the last key pressed was
43 setKeyOptions(); // Update the Edit key options fields
44 }
45}
46
47function readyLaunchpad() {
48 // On DOM ready
49 const launchpadGuiKey = $('.launchpad .key');
50 launchpadGuiKey.mousedown((event) => {
51 // Launchpad gui key was pressed
52 const key = getKeyPosition(event.currentTarget); // Get key position array
53 if (event.which === 3) {
54 // Mouse click was a 'right-click'
55 keyEvent('gui', key, 'press', 'right-click');
56 return;
57 }
58 keyEvent('gui', key, 'press'); // Forward to key event handler
59 });
60
61 launchpadGuiKey.mouseup((event) => {
62 // Launchpad gui key was released
63 releasedKey(event.currentTarget);
64 });
65
66 launchpadGuiKey.mouseleave((event) => {
67 // Mouse left Launchpad gui key
68 // Release if mouse left key while pressed
69 if ($(event.currentTarget).hasClass('pressed')) releasedKey(event.currentTarget);
70 });
71}
72
73// Find the respective gui element with the key position
74function getGuiKey(key) {
75 if (Array.isArray(key)) key = key.join(',');
76 return $(`.launchpad .key[data-pos='${key}']`);
77}
78
79function releasedKey(that) {
80 // Gui key was released
81 keyEvent('gui', getKeyPosition(that), 'release'); // Forward to key event handler
82}
83
84function getKeyPosition(that) {
85 const pos = $(that).data('pos').split(','); // Gets key position number
86 pos[0] = parseInt(pos[0]);
87 pos[1] = parseInt(pos[1]);
88 return pos; // Returns key position as an array
89}
90
91function setAllLights() {
92 // Sets all key lights to their released state color (background color)
93 for (let c = 0; c <= 8; c++) {
94 for (let r = 0; r <= 8; r++) {
95 if (c === 8 && r === 8) break; // 8,8 does not exist on the pad
96 const keyConfig = getKeyConfig([c, r]);
97 colorKey([c, r], 'release', keyConfig); // Color the button
98 setIcons([c, r], keyConfig);
99 }
100 }
101}
102
103function colorKey(key, action, keyConfig) {
104 const guiKey = getGuiKey(key); // Find key in DOM
105 guiKey.removeClass(guiKey.data('color')); // Remove old color class
106 const keyColor = keyConfig.color[action] || 'OFF'; // Try to get key color
107 guiKey.addClass(keyColor); // Add new key color class
108 guiKey.data('color', keyColor); // Store color to data attribute
109 if (action === 'press') {
110 guiKey.addClass('pressed');
111 $(`.c${key.join('-')}`).addClass('pressed');
112 } else {
113 guiKey.removeClass('pressed');
114 $(`.c${key.join('-')}`).removeClass('pressed');
115 }
116 if (launchpad) {
117 // Set midi color if launchpad is connected
118 const button = launchpad.getButton(key[0], key[1]); // Get button object
119 button.light(color[keyColor]); // Color the key
120 }
121}
122
123ipc.on('all_dark', () => {
124 // Message to turn off all the midi key lights
125 if (launchpad) launchpad.allDark(); // Turn off all lights if launchpad is connected
126});
127
128function stopAudio(track) {
129 // Stops the track
130 const tmp = track.src; // Stores the current source
131 track.src = ''; // Clears the source, this is what actually stops the audio
132 track.src = tmp; // Restore the source for next play
133}
134
135function playAudio(key, action, keyConfig) {
136 // Handle Audio playback
137 const audio = keyConfig.audio; // Get key audio settings if they exist
138 if (!audio || !audio.path) return; // Return if no settings or disabled
139 const track = tracks[key.join(',')]; // Get loaded track from memory
140 const audioPath = path.normalize(audio.path);
141 switch (action) {
142 case 'press':
143 if (!track) {
144 console.log('a');
145 // Track was just created in edit mode
146 var track1 = new Audio(audioPath);
147 var track2 = new Audio(audioPath);
148 tracks[key.join(',')] = track1; // Create and add new track to tracks
149 console.log(audioDevices[2].label + ' ' + audioDevices[2].groupId);
150 console.log(audioDevices[3].label + ' ' + audioDevices[3].groupId);
151 track1.setSinkId(audioDevices[3].groupId);
152 track1.play(); // Play Track
153 track2.setSinkId(audioDevices[2].groupId);
154 track2.play(); // Play Track
155 console.log('b');
156 return;
157 }
158 if (!track.played || track.played.length === 0 || track.ended) {
159 // Start the track if it hasn't played before or has finished playing
160 // track.play();
161 var track1 = track;
162 track1.setSinkId(audioDevices[3].groupId);
163 track1.play(); // Play Track
164 var track2 = track;
165 track2.setSinkId(audioDevices[2].groupId);
166 track2.play(); // Play Track
167 return;
168 }
169 if (!track.ended) {
170 // What do we do if the key is repressed while the track is playing
171 switch (audio.type) {
172 case 'toggle': // Stops the track
173 stopAudio(track);
174 break;
175 case 'restart': // Restarts the track
176 stopAudio(track);
177 track.play();
178 break;
179 default:
180 // Do Nothing
181 }
182 return;
183 }
184 break;
185 case 'release':
186 if (!track) return;
187 if (track && !track.ended && audio.type === 'hold') {
188 stopAudio(track); // Stop audio on release if that is what's set
189 }
190 break;
191 default:
192 // Do Nothing
193 }
194}
195
196function sendHotkey(key, action, keyConfig) {
197 const hotkey = keyConfig.hotkey; // Get key audio settings if they exist
198 if (!hotkey || !hotkey.string) return; // Return if no settings or disabled
199 const keys = hotkey.string.split(' + '); // Split hotkey string into an array
200 switch (hotkey.type) {
201 case 'send': // Send and release hotkeys
202 if (action !== 'press') return;
203 kbAction(keys, 'down', () => {
204 kbAction(keys, 'up');
205 });
206 break;
207 case 'hold':
208 switch (action) {
209 case 'press': // Hold hotkeys
210 kbAction(keys, 'down');
211 break;
212 case 'release': // Release Hotkeys
213 kbAction(keys, 'up');
214 break;
215 default:
216 // Do Nothing
217 }
218 break;
219 default:
220 // Do Nothing
221 }
222}
223
224function resolveKey(key) {
225 // Match up the different key names from the 2 different libraries we are using
226 key = key.toLowerCase();
227 if (key.startsWith('numpad')) {
228 switch (key.split(' ')[1]) {
229 case '/':
230 return 'numpad_divide';
231 case '*':
232 return 'numpad_multiply';
233 case '-':
234 return 'numpad_minus';
235 case '+':
236 return 'numpad_plus';
237 case '.':
238 return 'numpad_decimal';
239 default:
240 return key.replace(' ', '_');
241 }
242 }
243 switch (key) {
244 case 'l-ctrl':
245 return 'left_control';
246 case 'r-ctrl':
247 return 'right_control';
248 case 'l-shift':
249 return 'left_shift';
250 case 'r-shift':
251 return 'right_shift';
252 case 'l-alt':
253 return 'left_alt';
254 case 'r-alt':
255 return 'right_alt';
256 case 'esc':
257 return 'escape';
258 case 'page up':
259 return 'pageup';
260 case 'page down':
261 return 'pagedown';
262 default:
263 return key;
264 }
265}
266
267function kbAction(keys, action, callback) {
268 for (let i = 0; i < keys.length; i++) {
269 let c = keyboard[keys[i]] || 0;
270 switch (action) {
271 case 'down':
272 c++;
273 keyboard[keys[i]] = c;
274 if (c > 1) continue;
275 break;
276 case 'up':
277 c--;
278 keyboard[keys[i]] = c;
279 if (c !== 0) continue;
280 break;
281 default:
282 // Do Nothing
283 }
284 ipc.send('robot_key', { key: resolveKey(keys[i]), action: action });
285 }
286 if (callback) return callback();
287 return null;
288}
289
290function sendCLR(key, action, keyConfig) {
291 if (action === 'release') return;
292 if (!config.get('app.clr.enabled')) return;
293 const clr = keyConfig.clr;
294 if (!clr || !clr.path) return;
295 clrIO.emit('key_press', { key: key.join(','), options: clr });
296}
297
298function sendAPI(key, action, keyConfig) {
299 if (action === 'release') return;
300 const api = keyConfig.api;
301 if (!api || !api.path) return;
302 if (!isURL(api.path)) return;
303 const oldColor = keyConfig.color.release;
304 keyConfig.color.release = 'YELLOW';
305 colorKey(key, 'release', keyConfig);
306 fetch.get(api.path).then(
307 (res) => {
308 if (res.statusCode !== 200) {
309 err();
310 } else if (res.body.error) {
311 err();
312 } else {
313 ok();
314 }
315 },
316 () => {
317 err();
318 }
319 );
320
321 function ok() {
322 keyConfig.color.release = 'GREEN';
323 colorKey(key, 'release', keyConfig);
324 setTimeout(() => {
325 keyConfig.color.release = oldColor;
326 colorKey(key, 'release', keyConfig);
327 }, 1500);
328 }
329
330 function err() {
331 keyConfig.color.release = 'RED';
332 colorKey(key, 'release', keyConfig);
333 setTimeout(() => {
334 keyConfig.color.release = oldColor;
335 colorKey(key, 'release', keyConfig);
336 }, 1500);
337 }
338}
339