nodeskini 1.0.2 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,2052 +1,2090 @@
1
- /**
2
- * @fileOverview Websocket management. This is the main part of Skini for messages
3
- * management and control. Something like a main switch.
4
- * Most of the API and functions here are local.
5
- * @copyright (C) 2022-2024 Bertrand Petit-Hédelin
6
- *
7
- * This program is free software: you can redistribute it and/or modify
8
- * it under the terms of the GNU General Public License as published by
9
- * the Free Software Foundation, either version 3 of the License, or
10
- * any later version.
11
- *
12
- * This program is distributed in the hope that it will be useful,
13
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
- * GNU General Public License for more details.
16
- *
17
- * You should have received a copy of the GNU General Public License
18
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
19
- *
20
- * avec capteurIZTest.csv et TestCapteurIZ.als
21
- *
22
- * @author Bertrand Petit-Hédelin <bertrand@hedelin.fr>
23
- * @version 1.4
24
- */
25
- // @ts-check
26
- 'use strict'
27
- import { createRequire } from 'module';
28
- const require = createRequire(import.meta.url);
29
-
30
- import { compile } from "@hop/hiphop/lib/hhc-compiler.mjs";
31
- import * as fs from "fs";
32
- import * as compScore from './computeScore.mjs';
33
- import * as gameOSC from './gameOSC.mjs';
34
- import * as oscMidiLocal from './OSCandMidi.mjs';
35
- import * as saveParam from './saveParam.mjs';
36
-
37
- const ipConfig = require('./ipConfig.json');
38
- const midiConfig = require("./midiConfig.json");
39
-
40
- const decache = require('decache');
41
- const { stringify } = require('querystring');
42
- import { Worker } from 'worker_threads';
43
- import { fork } from "child_process";
44
-
45
- const defaultOrchestrationName = "orchestrationHH.mjs";
46
- let par;
47
- let DAW;
48
- let midimix;
49
- let sessionFile; // Pour le chemin complet de la session en cours (descripteur en ".csv")
50
- let parametersFile;
51
- let parametersFileGlobal;
52
- const origine = "./serveur/defaultSkiniParametres.js";
53
- const defaultSession = "./serveur/defaultSession.csv";
54
- let HipHopSrc; // Fichier HipHop éditer en texte et à compiler
55
- let decacheParameters;
56
- let childSimulator;
57
- const targetHH = "./myReact/orchestrationHH.mjs"; // Redondant à revoir
58
- // Attention en dur car le chemin est utilisé ailleurs, dans groupClientsSons.js
59
- // pour orchestrationHH.js
60
- const generatedDir = "./myReact/";
61
-
62
- // Declarations to move from CJS to ES6
63
- let _getBroadCastServer, _sendSignalFromDAW, _sendSignalFromMIDI, _sendSignalStopFromMIDI;
64
- let _sendSignalStartFromMIDI, _sendSignalFromMidiMix, _sendOSCTick, _getAutomatePossible;
65
- let _setPatternListLength;
66
-
67
- export {
68
- _getBroadCastServer as getBroadCastServer,
69
- _sendSignalFromDAW as sendSignalFromDAW,
70
- _sendSignalFromMIDI as sendSignalFromMIDI,
71
- _sendSignalStopFromMIDI as sendSignalStopFromMIDI,
72
- _sendSignalStartFromMIDI as sendSignalStartFromMIDI,
73
- _sendSignalFromMidiMix as sendSignalFromMidiMix,
74
- _sendOSCTick as sendOSCTick,
75
- _getAutomatePossible as getAutomatePossible,
76
- _setPatternListLength as setPatternListLength
77
- };
78
-
79
- // Répertoires par défaut, ils sont à fixer dans le fichier de configuration.
80
- // Où se trouvent les fichiers XML d'orchestration
81
- // On ne peut pas donner de chemin absolu dans un browser.
82
- // Ce sont les fichiers csv "descripteurs" des patterns
83
- // et les fichiers de configuration ".js"
84
- // Bug de principe (21/06/2022): On ne peut pas changer ces paramètres dans le fichier .js
85
- // puisque ces paramètres sont fixés avant tous choix de pièces ....
86
- // Il devrait s'agir de paramètres globaux et non liés aux fichiers de config de chaque pièce.
87
- const sessionPath = ipConfig.sessionPath; //"./pieces/";
88
- const piecePath = ipConfig.piecePath; //"./pieces/";
89
-
90
- import * as groupesClientSon from './groupeClientsSons.mjs';
91
-
92
- /**
93
- * To load some modules.
94
- * Used only at Skini launch.
95
- * @param {Object} midimixage reference
96
- */
97
- export async function setParameters(midimixage) {
98
- midimix = midimixage;
99
- await import('./controleDAW.mjs').then((daw) => {
100
- DAW = daw;
101
- groupesClientSon.setMidimix(midimix);
102
- initMidiPort();
103
- startWebSocketServer();
104
- });
105
- }
106
-
107
- /**
108
- * The simulator can be updated at serveral places
109
- */
110
- function updateSimulatorParameters(param) {
111
-
112
- if (childSimulator !== undefined) {
113
- let message = {
114
- type: "PARAMETERS",
115
- data: param
116
- }
117
- try {
118
- childSimulator.send(message);
119
- } catch (err) {
120
- console.log("ERR: websocketserver: updateSimulatorParameters:", err);
121
- }
122
- } else {
123
- if (debug1) console.log("INFO: websocketServer: updateSimulatorParameters :No Fork Simulator");
124
- }
125
- }
126
-
127
- /**
128
- * Convert data in the good format
129
- * and reload new parametrers in the different
130
- * modules during a Skini session.
131
- *
132
- * Cette fonction n'est pas bonne.
133
- * Il faut regénerer complétement les paramètres et envoyer
134
- * la nouvelle version.
135
- *
136
- * @param {object} param
137
- */
138
- function reloadParametersOld(param) {
139
- let par = param; // C'est seulement pour la lecture car on a le même objet
140
-
141
- // Le transfert des parametre passe tout en chaine de caractère qui ne
142
- // sont pas traduite en int.
143
- par.nbeDeGroupesClients = parseInt(param.nbeDeGroupesClients, 10);
144
- par.algoGestionFifo = parseInt(param.algoGestionFifo, 10);
145
- par.tempoMax = parseInt(param.tempoMax, 10);
146
- par.tempoMin = parseInt(param.tempoMin, 10);
147
- par.limiteDureeAttente = parseInt(param.limiteDureeAttente, 10);
148
-
149
- // Pas vraiment utile pour les booléens ?
150
- par.shufflePatterns = param.shufflePatterns;
151
- par.avecMusicien = param.avecMusicien;
152
- par.reactOnPlay = param.reactOnPlay;
153
-
154
- // Typage pour les antécédents dans Score. En rechargeant depuis le client
155
- // de parametrage on a une chaine de caractères et pas un tableau.
156
- for (let i = 0; i < param.groupesDesSons.length; i++) {
157
- if (typeof param.groupesDesSons[i][7] === 'string' || param.groupesDesSons[i][7] instanceof String) {
158
- par.groupesDesSons[i][7] = param.groupesDesSons[i][7].split(',');
159
- }
160
- for (let j = 0; j < par.groupesDesSons[i][7].length; j++) {
161
- par.groupesDesSons[i][7][j] = parseInt(par.groupesDesSons[i][7][j]);
162
- }
163
- }
164
-
165
- oscMidiLocal.setParameters(par);
166
- DAW.setParameters(par);
167
- groupesClientSon.setParameters(par);
168
- midimix.setParameters(par);
169
- updateSimulatorParameters(par);
170
-
171
- initMidiPort();
172
- }
173
-
174
- function reloadParameters(param) {
175
- // Création manuelle d'une copie
176
- let parlocal = {
177
- ...param, // copie superficielle : fonctions et propriétés simples sont conservées
178
- groupesDesSons: param.groupesDesSons.map(group => [...group]) // copie des sous-tableaux
179
- };
180
-
181
- // Conversion des types
182
- parlocal.nbeDeGroupesClients = parseInt(param.nbeDeGroupesClients, 10);
183
- parlocal.algoGestionFifo = parseInt(param.algoGestionFifo, 10);
184
- parlocal.tempoMax = parseInt(param.tempoMax, 10);
185
- parlocal.tempoMin = parseInt(param.tempoMin, 10);
186
- parlocal.limiteDureeAttente = parseInt(param.limiteDureeAttente, 10);
187
-
188
- parlocal.shufflePatterns = param.shufflePatterns;
189
- parlocal.avecMusicien = param.avecMusicien;
190
- parlocal.reactOnPlay = param.reactOnPlay;
191
-
192
- // Traitement des antécédents (index 7)
193
- for (let i = 0; i < parlocal.groupesDesSons.length; i++) {
194
- const entry = parlocal.groupesDesSons[i][7];
195
- if (typeof entry === 'string') {
196
- parlocal.groupesDesSons[i][7] = entry.split(',').map(x => parseInt(x, 10));
197
- } else if (Array.isArray(entry)) {
198
- parlocal.groupesDesSons[i][7] = entry.map(x => parseInt(x, 10));
199
- }
200
- }
201
-
202
- // Envoi aux modules
203
- oscMidiLocal.setParameters(parlocal);
204
- DAW.setParameters(parlocal);
205
- groupesClientSon.setParameters(parlocal);
206
- midimix.setParameters(parlocal);
207
- updateSimulatorParameters(parlocal);
208
-
209
- // Il faut remettre à jour les paramètre de
210
- // la variable global de websocketServer
211
- par = parlocal;
212
-
213
- initMidiPort();
214
- }
215
-
216
- /**
217
- * Simple conversion from Array to csv
218
- * @param {Array} arr
219
- * @param {String} delimiter
220
- * @returns {String} csv
221
- */
222
- const arrayToCSV = (arr, delimiter = ',') =>
223
- arr.map(v => v.join(delimiter)
224
- ).join('\n');
225
-
226
- // INITIALISATION DES DONNEES D'INTERACTION DU SEQUENCEUR
227
- const tripleCrocheTR = 2;
228
- const tripleCrocheR = 3;
229
- const doubleCrocheTR = 4;
230
- const doubleCrocheR = 6;
231
- const crocheTR = 8;
232
- const crocheR = 12;
233
- const noireTR = 16;
234
- const noireR = 24;
235
- const blancheTR = 32;
236
- const blancheR = 48;
237
- const rondeTR = 64;
238
- const rondeR = 96;
239
-
240
- let tempsMesure = 4; // Partie haute de la mesure, nombre de temps dans la mesure
241
- let divisionMesure = noireR; // Partie basse de la mesure
242
- let nbeDeMesures = 1;
243
- let tempo = 60; // à la minute
244
- let canalMidi = 1;
245
- let dureeDuTick = ((60 / tempo) / divisionMesure) * 1000; // Exprimé ici en millisecondes
246
-
247
- let previousTime = 0;
248
- let currentTime = 0;
249
- let timeToPlay = 0;
250
- let previousTimeToPlay = 0;
251
- let defautDeLatence;
252
-
253
- const debug = false;
254
- const debug1 = true;
255
- const warnings = false;
256
- let timerSynchro;
257
-
258
- // Automate des possibles
259
- let DAWStatus = 0; // 0 inactif, sinon actif (originellement pour distinguer des orchestrations, distinction pas utile à présent)
260
- let setTimer;
261
- let timerDivision = 1; // Default value for the number of pulses for a tick, can evolve during an orchestration
262
- let offsetDivision = 0;
263
- let compteurDivisionMesure = 0;
264
- let nbeDeGroupesSons = 0;
265
- let socketControleur;
266
- let groupeName = "";
267
- let automatePossibleMachine;
268
-
269
- // Scoring pour les jeux
270
- let computeScorePolicy = 0;
271
- let computeScoreClass = 0;
272
-
273
- // CONTROLEUR
274
- let DAWTableReady = false; // Pour pouvoir vérifier que la pièce a bien été chargée.
275
-
276
- let clientsEnCours = [];
277
- let groupeEncours = 0;
278
-
279
- let currentTimePrevMidi = 0;
280
- let currentTimeMidi = 0;
281
-
282
- /*************************************************
283
- INITIALISATION DU PORT MIDI OUT (si paramétré)
284
- **************************************************/
285
- /**
286
- * Init MIDI OUT port if defined in the parameters
287
- */
288
- function initMidiPort() {
289
-
290
- // Check the midi config before doing something
291
- if (midiConfig[0] === undefined) {
292
- return;
293
- }
294
-
295
- if (par !== undefined) {
296
- let directMidi = false;
297
- if (par.directMidiON !== undefined) {
298
- directMidi = par.directMidiON;
299
- }
300
- if (directMidi) {
301
- oscMidiLocal.initMidiOUT();
302
- }
303
- }
304
- }
305
-
306
- /************************************************
307
- WEBSOCKET
308
- **************************************************/
309
- /**
310
- * Main function to manage the websocket
311
- */
312
- function startWebSocketServer() {
313
- /** @namespace Websocketserver */
314
- const WebSocketServer = require('ws');
315
- const serv = new WebSocketServer.Server({ port: ipConfig.websocketServeurPort });
316
-
317
- /*************************************************************************************
318
- Worker for synchro if no DAW (ne fonctionne pas dans VSC mais dans le terminal)
319
- **************************************************************************************/
320
- var workerSync;
321
-
322
- /**
323
- * Function to start a worker for the synchro when not using a midi sync coming from a DAW
324
- * @function
325
- * @memberof Websocketserver
326
- * @param {string} filepath path
327
- * @param {number} timer
328
- * @inner
329
- */
330
- function workerSynchroInit(filepath, timer) {
331
- if (workerSync !== undefined) {
332
- workerSync.postMessage(['startSynchro', timer]);
333
- return;
334
- }
335
-
336
- return new Promise((resolve, reject) => {
337
- workerSync = new Worker(filepath);
338
- if (debug) console.log('Launching worker Synchro', filepath);
339
-
340
- workerSync.on('online', () => {
341
- workerSync.postMessage(['startSynchro', timer]);
342
- if (debug) console.log('Launching worker Synchro');
343
- })
344
- workerSync.on('message', messageFromWorker => {
345
- switch (messageFromWorker) {
346
- case "synchroWorker":
347
- receivedTickFromSynchro();
348
- break;
349
-
350
- default:
351
- break;
352
- }
353
- return resolve;
354
- });
355
- workerSync.on('error', reject);
356
- workerSync.on('exit', code => {
357
- if (code !== 0) {
358
- reject(new Error(`Worker stopped with exit code:` + code));
359
- }
360
- });
361
- });
362
- }
363
-
364
- /*************************************************************************************
365
- Worker for the Interface Z management
366
- **************************************************************************************/
367
- var workerInterfaceZ;
368
- var workerInterfaceZRunning = false;
369
-
370
- /**
371
- * Function to start a worker for the Interface Z management
372
- * @function
373
- * @memberof Websocketserver
374
- * @param {string} filepath worker path
375
- * @inner
376
- */
377
- function workerInterfaceZInit(filepath,
378
- serverAddress,
379
- interfaceZIPaddress,
380
- portOSCFromInterfaceZData,
381
- portOSCFromInterfaceZMidi,
382
- portOSCFromInterfaceZMiniWi,
383
- portOSCToInterfaceZ,
384
- tempoSensorsInit,
385
- sensorsSensibilities) {
386
-
387
- if (workerInterfaceZRunning) {
388
- if (debug1) console.log("INFO: workerInterfaceZRunning");
389
- if (workerInterfaceZ !== undefined) {
390
- workerInterfaceZ.postMessage(['startInterfaceZ',
391
- serverAddress,
392
- interfaceZIPaddress,
393
- portOSCFromInterfaceZData,
394
- portOSCFromInterfaceZMidi,
395
- portOSCFromInterfaceZMiniWi,
396
- portOSCToInterfaceZ,
397
- tempoSensorsInit,
398
- sensorsSensibilities]);
399
- return;
400
- }
401
- }
402
- workerInterfaceZRunning = true;
403
-
404
- if (
405
- interfaceZIPaddress === undefined ||
406
- portOSCFromInterfaceZData === undefined ||
407
- portOSCFromInterfaceZMiniWi === undefined ||
408
- tempoSensorsInit === undefined ||
409
- sensorsSensibilities === undefined) {
410
- console.log("WARN: You try to use the Interface Z sensors but do not configure ipConfig");
411
- return;
412
- }
413
-
414
- return new Promise((resolve, reject) => {
415
- workerInterfaceZ = new Worker(filepath);
416
- if (debug) console.log('Launching worker InterfaceZ', filepath);
417
-
418
- workerInterfaceZ.on('online', () => {
419
- workerInterfaceZ.postMessage(['startInterfaceZ',
420
- serverAddress,
421
- interfaceZIPaddress,
422
- portOSCFromInterfaceZData,
423
- portOSCFromInterfaceZMidi,
424
- portOSCFromInterfaceZMiniWi,
425
- portOSCToInterfaceZ,
426
- tempoSensorsInit,
427
- sensorsSensibilities]);
428
- if (debug) console.log('Launching worker InterfaceZ');
429
- })
430
- workerInterfaceZ.on('message', messageFromWorker => {
431
- if (debug) console.log("Websoclketserver: messageFromWorker: ", messageFromWorker);
432
-
433
- switch (messageFromWorker.type) {
434
- case "INTERFACEZ_RC":
435
- if (debug) console.log("websocketServer:message from worker:", messageFromWorker);
436
- inputAutomatePossible({ INTERFACEZ_RC: [messageFromWorker.sensor, messageFromWorker.value] });
437
- break;
438
-
439
- case "INTERFACEZ_RC0":
440
- if (debug) console.log("websocketServer:message from worker:", messageFromWorker);
441
- reactAutomatePossible({ INTERFACEZ_RC0: [messageFromWorker.sensor, messageFromWorker.value] });
442
- break;
443
- case "INTERFACEZ_RC1":
444
- if (debug) console.log("websocketServer:message from worker:", messageFromWorker);
445
- reactAutomatePossible({ INTERFACEZ_RC1: [messageFromWorker.sensor, messageFromWorker.value] });
446
- break;
447
- case "INTERFACEZ_RC2":
448
- if (debug) console.log("websocketServer:message from worker:", messageFromWorker);
449
- reactAutomatePossible({ INTERFACEZ_RC2: [messageFromWorker.sensor, messageFromWorker.value] });
450
- break;
451
- case "INTERFACEZ_RC3":
452
- if (debug) console.log("websocketServer:message from worker:", messageFromWorker);
453
- reactAutomatePossible({ INTERFACEZ_RC3: [messageFromWorker.sensor, messageFromWorker.value] });
454
- break;
455
- case "INTERFACEZ_RC4":
456
- if (debug) console.log("websocketServer:message from worker:", messageFromWorker);
457
- reactAutomatePossible({ INTERFACEZ_RC4: [messageFromWorker.sensor, messageFromWorker.value] });
458
- break;
459
- case "INTERFACEZ_RC5":
460
- if (debug) console.log("websocketServer:message from worker:", messageFromWorker);
461
- reactAutomatePossible({ INTERFACEZ_RC5: [messageFromWorker.sensor, messageFromWorker.value] });
462
- break;
463
- case "INTERFACEZ_RC6":
464
- if (debug) console.log("websocketServer:message from worker:", messageFromWorker);
465
- reactAutomatePossible({ INTERFACEZ_RC6: [messageFromWorker.sensor, messageFromWorker.value] });
466
- break;
467
- case "INTERFACEZ_RC7":
468
- if (debug) console.log("websocketServer:message from worker:", messageFromWorker);
469
- reactAutomatePossible({ INTERFACEZ_RC7: [messageFromWorker.sensor, messageFromWorker.value] });
470
- break;
471
- case "INTERFACEZ_RC8":
472
- if (debug) console.log("websocketServer:message from worker:", messageFromWorker);
473
- reactAutomatePossible({ INTERFACEZ_RC8: [messageFromWorker.sensor, messageFromWorker.value] });
474
- break;
475
- case "INTERFACEZ_RC9":
476
- if (debug) console.log("websocketServer:message from worker:", messageFromWorker);
477
- reactAutomatePossible({ INTERFACEZ_RC9: [messageFromWorker.sensor, messageFromWorker.value] });
478
- break;
479
- case "INTERFACEZ_RC10":
480
- if (debug) console.log("websocketServer:message from worker:", messageFromWorker);
481
- reactAutomatePossible({ INTERFACEZ_RC10: [messageFromWorker.sensor, messageFromWorker.value] });
482
- break;
483
- case "INTERFACEZ_RC11":
484
- if (debug) console.log("websocketServer:message from worker:", messageFromWorker);
485
- reactAutomatePossible({ INTERFACEZ_RC11: [messageFromWorker.sensor, messageFromWorker.value] });
486
- break;
487
-
488
- default:
489
- break;
490
- }
491
- return resolve;
492
- });
493
- workerInterfaceZ.on('error', reject);
494
- workerInterfaceZ.on('exit', code => {
495
- if (code !== 0) {
496
- reject(new Error(`Worker InterfaceZ stopped with exit code:` + code));
497
- }
498
- });
499
- });
500
- }
501
-
502
- /**
503
- * Define the function in order to Broadcast to all clients.
504
- * @function
505
- * @memberof Websocketserver
506
- * @param {string} data message
507
- * @inner
508
- */
509
- serv.broadcast = function broadcast(data) {
510
- //if(debug) console.log("Web Socket Server: broadcast: ", data);
511
- serv.clients.forEach(function each(client) {
512
- if (client.readyState === WebSocketServer.OPEN) {
513
- try {
514
- client.send(data);
515
- } catch (err) {
516
- console.log("ERR: websocketserver.js: broadcast", err);
517
- throw err;
518
- }
519
- }
520
- });
521
- }
522
-
523
- // Pour les broadcasts depuis controle DAW, c'est la structure dans HOP que je garde.
524
- DAW.initBroadCastServer(serv);
525
- groupesClientSon.initBroadCastServer(serv);
526
-
527
- /**
528
- * In order to get the server used for broadcasting
529
- * @returns {serv} - return the server for Broadcasting
530
- * @function
531
- * @memberof Websocketserver
532
- * @inner
533
- */
534
- function getBroadCastServer() {
535
- if (serv === undefined) {
536
- console.log("ERR: websocketServer: getBroadCastServer: serv undefined");
537
- return false;
538
- }
539
- return serv;
540
- }
541
- _getBroadCastServer = getBroadCastServer;
542
-
543
- /************************************************************************************
544
- Fonction pour emission de signaux depuis Ableton vers l'automatePossibleMachine.
545
- *************************************************************************************/
546
- /**
547
- * Send a signal to the orchestration according to the skini note
548
- * @function
549
- * @memberof Websocketserver
550
- * @param {number} noteSkini
551
- * @inner
552
- */
553
- function sendSignalFromDAW(noteSkini) {
554
- if (debug) console.log("websocketserver.js: sendSignalFromDAW:", noteSkini);
555
- var patternName = DAW.getPatternNameFromNote(noteSkini);
556
- if (debug) console.log("INFO: websocketserver.js: sendSignalFromDAW:", noteSkini, patternName);
557
- if (patternName !== undefined) {
558
- reactAutomatePossible({ patternSignal: [noteSkini, patternName] });
559
- } else {
560
- if (warnings) console.log("WARN: webSocketServeur: sendSignalFromDAW:", noteSkini, patternName);
561
- }
562
- }
563
- _sendSignalFromDAW = sendSignalFromDAW;
564
-
565
- /**
566
- * Send a signal "midiSignal" to the orchestration
567
- * tanks to a skini note.
568
- * @function
569
- * @memberof Websocketserver
570
- * @inner
571
- * @param {number} noteSkini
572
- */
573
- function sendSignalFromMIDI(noteSkini) {
574
- if (debug1) console.log("webSocketServeur: sendSignalFromMIDI:", noteSkini);
575
- if (!reactAutomatePossible({ midiSignal: [noteSkini] })) {
576
- console.log("WARN: webSocketServeur: sendSignalFromMIDI:", noteSkini);
577
- }
578
- }
579
- _sendSignalFromMIDI = sendSignalFromMIDI;
580
-
581
- /**
582
- * Send a signal "halt" to the orchestration.
583
- * @memberof Websocketserver
584
- * @function
585
- * @inner
586
- */
587
- function sendSignalStopFromMIDI() {
588
- if (!reactAutomatePossible({ halt: undefined })) {
589
- if (warnings) console.log("WARN: webSocketServeur: sendSignalStopFromMIDI");
590
- }
591
- }
592
- _sendSignalStopFromMIDI = sendSignalStopFromMIDI;
593
-
594
- /**
595
- * Send a signal "start" to the orchestration.
596
- * @memberof Websocketserver
597
- * @function
598
- * @inner
599
- */
600
- function sendSignalStartFromMIDI() {
601
- if (!reactAutomatePossible({ start: undefined })) {
602
- if (warnings) console.log("WARN: webSocketServeur: sendSignalStartFromMIDI");
603
- }
604
- }
605
- _sendSignalStartFromMIDI = sendSignalStartFromMIDI;
606
-
607
- /************************************************************************************
608
- Fonction pour émission de signaux depuis midimix.js vers l'automatePossibleMachine.
609
- Utilisable pour synchro vidéo ou jeu via des notes Midi
610
- *************************************************************************************/
611
- /**
612
- * Send a signal "controlFromVideo" to the orchestration
613
- * tanks to a skini note.
614
- * @memberof Websocketserver
615
- * @function
616
- * @inner
617
- * @param {number} noteSkini
618
- */
619
- function sendSignalFromMidiMix(noteSkini) {
620
- reactAutomatePossible({ controlFromVideo: [noteSkini] });
621
- }
622
- _sendSignalFromMidiMix = sendSignalFromMidiMix;
623
-
624
- /*************************************************************************************
625
- RECEPTION DES TICK MIDI OU BITWIG
626
- **************************************************************************************/
627
- var previousTimeClockMidi = 0;
628
- var currentTimeClockMidi = 0;
629
- var tempoTime = 0;
630
-
631
- // Vient de midiMix.js et directement de Bitwig ou de processing
632
- /**
633
- * Called by midimix.js, for OSC, MIDI, and Link messages.
634
- * @memberof Websocketserver
635
- * @function
636
- * @inner
637
- */
638
- function sendOSCTick() {
639
- if (debug1) {
640
- //console.log("websocketserver: sendOSCTick");
641
- serv.broadcast(JSON.stringify({
642
- type: "synchroSkini",
643
- text: ""
644
- }));
645
- }
646
- receivedTickFromSynchro();
647
- }
648
- _sendOSCTick = sendOSCTick;
649
-
650
- /**
651
- * Called on synchro messages received each quarter note
652
- * emitted by the DAW using MIDI or OSC, or the synchro worker.
653
- * No parameters the variables are global to the whole module.
654
- * @memberof Websocketserver
655
- * @function
656
- * @inner
657
- */
658
- function receivedTickFromSynchro() {
659
- if (debug) console.log("websocketserver : receivedTickFromSynchro: tick received");
660
- currentTimeClockMidi = Date.now();
661
- tempoTime = currentTimeClockMidi - previousTimeClockMidi; // Real duration of a quarter note
662
- if (debug) console.log("websocketserver:dureeDuTickHorlogeMidi:tempoTime=", tempoTime,
663
- compteurDivisionMesure,
664
- groupesClientSon.getTimerDivision());
665
- previousTimeClockMidi = currentTimeClockMidi;
666
-
667
- if (par.pulsationON) {
668
- reactAutomatePossible({ pulsation: undefined });
669
- }
670
- // La remise à jour de la durée des ticks est possible depuis les automates.
671
- // Si les automates ne mettent pas timerDivision à jour, on garde la valeur par défaut
672
- // donnée dans le fichier de config de la pièce. (compatibilté ascendante)
673
- var timerLocal = groupesClientSon.getTimerDivision();
674
- if (debug) console.log("websocketserver: receivedTickFromSynchro: timerLocal:", timerLocal);
675
-
676
- if (timerLocal !== undefined) {
677
- timerDivision = timerLocal;
678
- } else {
679
- //console.log("WARN: websocketServer: receivedTickFromSynchro: timerDivision undefined");
680
- }
681
-
682
- if (debug) console.log("websocketserver: receivedTickFromSynchro: timerDivision:", timerDivision);
683
-
684
- //offsetDivision = timerDivision/2;
685
- // actionOnTick() is called based on the tick not the pulse issued from the synchro.
686
- if (compteurDivisionMesure === 0) { // offsetDivision
687
- actionOnTick(timerDivision);
688
- }
689
- // Ceci est la définition du tick de l'orchestration
690
- // Il s'agit d'une conversion de la pulsation MIDI ou worker en "tick".
691
- compteurDivisionMesure = (compteurDivisionMesure + 1) % timerDivision;
692
- }
693
-
694
- /*************************************************************************************
695
- MATRICE DES POSSIBLES, AUTOMATE
696
- **************************************************************************************/
697
- /**
698
- * Get the HipHop machine.
699
- * @memberof Websocketserver
700
- * @function
701
- * @inner
702
- * @returns {automatePossibleMachine} - the HipHop machine
703
- */
704
- function getAutomatePossible() {
705
- if (automatePossibleMachine !== undefined) {
706
- return automatePossibleMachine;
707
- } else {
708
- console.log("ERR: websocketserverSini.js: getAutomatePossible: automatePossibleMachine undefined")
709
- }
710
- }
711
- _getAutomatePossible = getAutomatePossible;
712
-
713
- /**
714
- * React on the orchestration
715
- * @memberof Websocketserver
716
- * @param {*} signal
717
- * @returns {boolean} true if no problem
718
- */
719
- function reactAutomatePossible(signal) {
720
-
721
- if (debug) console.log("reactAutomatePossible 1:", signal, automatePossibleMachine);
722
- if (debug) console.log("reactAutomatePossible 1:", signal);
723
-
724
- if (automatePossibleMachine !== undefined) {
725
- try {
726
- if (debug) console.log("INFO: webSocketServer.js: reactAutomatePossible 2:", signal);
727
- automatePossibleMachine.react(signal);
728
- } catch (err) {
729
- console.log("ERROR: webSocketServer.js: reactAutomatePossible: Error on react for signal:", signal, err.toString());
730
- var msg = {
731
- type: "alertBlocklySkini",
732
- text: err.toString()
733
- }
734
- //throw err;
735
- serv.broadcast(JSON.stringify(msg));
736
- return false;
737
- }
738
- return true;
739
- } else {
740
- if (warnings) console.log("WARN: websocketserver: reactAutomatePossible: automate undefined");
741
- return false;
742
- }
743
- }
744
-
745
- function inputAutomatePossible(signal) {
746
- if (automatePossibleMachine !== undefined) {
747
- try {
748
- if (debug) console.log("INFO: webSocketServer.js: inputAutomatePossible:", signal);
749
- automatePossibleMachine.input(signal);
750
- } catch (err) {
751
- console.log("ERROR: webSocketServer.js: inputAutomatePossible: Error on react:", signal, err.toString());
752
- serv.broadcast(JSON.stringify({
753
- type: "alertBlocklySkini",
754
- text: err.toString()
755
- }));
756
- return false;
757
- }
758
- return true;
759
- } else {
760
- if (warnings) console.log("WARN: websocketserver: inputAutomatePossible: automate undefined");
761
- return false;
762
- }
763
- }
764
-
765
- // Pas au bon endroit, musicien pas en place dans cette version
766
- //if (par.avecMusicien !== undefined && par.decalageFIFOavecMusicien !== undefined) {p.
767
- //DAW.setAvecMusicien(par.avecMusicien, par.decalageFIFOavecMusicien);
768
- //}
769
-
770
- /**
771
- * Initialisation if the "matrice des possibles" which is a two dimensional array for
772
- * the groups of pattern according to the groups of users. It represents the status
773
- * of the orchestration.
774
- * @memberof Websocketserver
775
- * @param {number} DAWState Informs if an orchestration has been selected or not
776
- */
777
- function initMatriceDesPossibles(DAWState) {
778
-
779
- if (warnings) console.log("WARNING: websocketserver:initMatriceDesPossibles:DAWState:", DAWState);
780
-
781
- if (DAWState == 0) {
782
- if (warnings) console.log("WARNING: websocketserver:initMatriceDesPossibles:DAWState à 0");
783
- return;
784
- }
785
- nbeDeGroupesSons = DAW.getNbeDeGroupesSons();
786
- if(debug1) if (!Number.isInteger(nbeDeGroupesSons) || nbeDeGroupesSons <= 0 )
787
- {
788
- console.log("websocketServer:initMatriceDesPossibles:pb nbeDeGroupesons", nbeDeGroupesSons );
789
- return;
790
- }
791
-
792
- groupesClientSon.setNbeDeGroupesSons(nbeDeGroupesSons);
793
- if (groupesClientSon.setGroupesSon(DAWState) == -1) {
794
- if (warnings) console.log("WARNING: websocketserveur:initMatriceDesPossibles: setGroupesSon: vide");
795
- }
796
- groupesClientSon.createMatriceDesPossibles();
797
-
798
- let mesReponse = {
799
- type: "setControlerPadSize",
800
- nbeDeGroupesClients: par.nbeDeGroupesClients,
801
- nbeDeGroupesSons: nbeDeGroupesSons
802
- }
803
-
804
- if (socketControleur !== undefined) {
805
- if (socketControleur.readyState == 1) {
806
- socketControleur.send(JSON.stringify(mesReponse));
807
- } else {
808
- if (debug) console.log("WARN: websocketserveur:initMatriceDesPossibles: socketControler status:", socketControleur.readyState);
809
- }
810
- }
811
- }
812
-
813
- /**
814
- * Action called every quarter note of the MIDI synchro or worker synchro if no MIDI sync.
815
- * @memberof Websocketserver
816
- * @param {number} timerDivision
817
- * @returns {boolean} true if the reaction of the orchestration is ok
818
- */
819
- function actionOnTick(timerDivision) {
820
- if (debug) {
821
- currentTimePrevMidi = currentTimeMidi;
822
- currentTimeMidi = Date.now();
823
- console.log("webSocketServeur:actionOnTick:diff de temps:", currentTimeMidi - currentTimePrevMidi, ":", timerDivision);
824
- }
825
-
826
- if (!reactAutomatePossible({ tick: undefined })) {
827
- if (warnings) console.log("WARN: websocketserver: actionOnTick: automate not ready");
828
- return false;
829
- }
830
-
831
- if (timerDivision == undefined) console.log("WARN:websocketServer:actionOnTick:timerDivision undefined")
832
-
833
- DAW.playAndShiftEventDAW(timerDivision);
834
- DAW.displayQueues();
835
- return true;
836
- }
837
-
838
- /**
839
- * Fix the timer when using the synchro from Node.js
840
- * not used when the worker runs.
841
- * @memberof Websocketserver
842
- * @function
843
- * @inner
844
- * @param {number} timer in ms
845
- */
846
- function setMonTimer(timer) {
847
- if (!par.synchoOnMidiClock) {
848
- setTimer = setInterval(function () {
849
- if (debug1) { let v0 = Date.now(); }
850
- actionOnTick(timerDivision);
851
- if (debug1) {
852
- console.log("websocketserver: setMonTimer timer:", timer, "ms,Temps de réaction de l'automate:", Date.now() - v0, "ms");
853
- }
854
- }, timer);
855
- }
856
- }
857
-
858
- /**
859
- * To update the variable on the list lengths of memorySortable
860
- * Clients need this variable when connecting. It can change during an orchestration.
861
- * @memberof Websocketserver
862
- * @function
863
- * @inner
864
- * @param {number} value length of the list of the client
865
- */
866
- function setPatternListLength(value) {
867
- if (debug1) console.log("websocketserver.js : setPatternListLength : value :", value);
868
- }
869
- _setPatternListLength = setPatternListLength;
870
-
871
- /*************************************************************************************
872
- WEB SOCKET MANAGEMENT
873
- **************************************************************************************/
874
- serv.on('connection', function (ws) {
875
-
876
- let messageLog = {
877
- date: "",
878
- source: "websocketServerSkini.js",
879
- type: "log",
880
- note: "",
881
- pseudo: "",
882
- id: ""
883
- }
884
-
885
- // Pour informer que l'on est bien connecté
886
- if (debug) console.log("INFO: Web Socket Server: a connection established");
887
- ws.send(JSON.stringify({
888
- type: "message",
889
- value: "Bienvenue chez Skini !"
890
- }));
891
-
892
- /* // Pour dire à l'ouverture au client si on est ou pas dans une scène où DAW est actif.
893
- if (debug) console.log("Web Socket Server: DAWON:", par.DAWON);
894
- var msg = {
895
- msg.type: "DAWON",
896
- msg.value: DAWON
897
- }
898
-
899
- msg.type = "DAWON";
900
- msg.value = par.DAWON; // variable true, false, ou un chiffre
901
- ws.send(JSON.stringify(msg));*/
902
-
903
- // DONNEES DE TEMPO pour les séquenceurs.
904
- ws.send(JSON.stringify({
905
- type: "setConfigSequenceur",
906
- tempsMesure: tempsMesure,
907
- divisionMesure: divisionMesure,
908
- nbeDeMesures: nbeDeMesures,
909
- tempo: tempo,
910
- canalMidi: canalMidi,
911
- dureeDuTick: dureeDuTick
912
- }));
913
- ws.on('close', function () {
914
- if (debug) console.log("Web Socket Server: Socket closed by client.");
915
- });
916
-
917
- ws.on('error', function (event) {
918
- console.log("Web Socket Server: Erreur sur socket:", ws.socket, " ", event);
919
- });
920
-
921
- /**
922
- * This is where the pattern (clip) descriptor becomes an element in a FIFO.
923
- * @memberof Websocketserver
924
- * @function
925
- * @inner
926
- * @param {array} clip pattern description according to the csv file.
927
- * @param {string} signal Hiphop signal
928
- * @param {number} leGroupe user group (web client group)
929
- * @param {string} pseudo
930
- * @param {number} monId
931
- * @returns {number} waiting time
932
- */
933
- function pushClipDAW(clip, signal, leGroupe, pseudo, monId) {
934
- let DAWNote = clip[0];
935
- let DAWChannel = Math.floor(DAWNote / 127) + 1;
936
- DAWNote = DAWNote % 127;
937
- if (DAWChannel > 15) {
938
- if (debug) console.log("Web Socket Server.js : pushNoteOnDAW: Nombre de canaux midi dépassé.");
939
- return 0;
940
- }
941
- let nom = clip[3];
942
- let DAWInstrument = clip[5];
943
- let typePattern = clip[7];
944
- let dureeClip = clip[10];
945
- let adresseIP = clip[11];
946
- let numeroBuffer = clip[12];
947
- let patternLevel = clip[13];
948
- let typeVertPattern = clip[8];
949
-
950
- let signalComplet = { [signal]: clip[3] }; // on ajouté le nom du pattern au signal
951
- let dureeAttente = DAW.pushEventDAW(par.busMidiDAW, DAWChannel,
952
- DAWInstrument, DAWNote, 125, monId, pseudo, dureeClip, nom,
953
- signalComplet, typePattern, adresseIP,
954
- numeroBuffer, patternLevel, typeVertPattern);
955
-
956
- // Envoi du signal vers l'automate au moment de la demande si reactOnPlay n'existe pas ou est false.
957
- // Il y a un autre scénario dans controleAbleton.js où on envoie le signal au moment ou la commande Midi part
958
- // Ce sont deux scénarios différents, celui fonctionne mieux en termes de synchro Midi car les demandes sont réparties dans
959
- // le temps au fur et à mesure qu'elles arrivent. Il n'y a pas de risque de react successifs au même moment du tick midi.
960
- // Il traite les activations avant que les patterns aient été jouées.
961
- if (par.reactOnPlay === undefined) {
962
- reactAutomatePossible(signalComplet);
963
- } else if (!par.reactOnPlay) {
964
- if (debug) console.log("websocketServeur: pushClipDAW: reactOnPlay:", par.reactOnPlay, signalComplet);
965
- reactAutomatePossible(signalComplet);
966
- }
967
- if (debug) console.log("Web Socket Server.js : pushClipDAW :nom ", nom, " pseudo: ", pseudo);
968
-
969
- if (debug) console.log("Web Socket Server.js: pushClipDAW: DAWInstrument", DAWInstrument, " durée: ", dureeAttente);
970
-
971
- return dureeAttente;
972
- }
973
-
974
- /**
975
- * HipHop reaction on the orchestration and compute delay.
976
- * @memberof Websocketserver
977
- * @function
978
- * @inner
979
- * @param {string} unPseudo
980
- * @param {number} groupe
981
- * @param {array} pattern
982
- * @param {number} monId
983
- */
984
- function playPattern(unPseudo, groupe, pattern, monId) {
985
-
986
- if (pattern === undefined) return; // Protection si pas de selection sur le client
987
- if (debug) console.log("Web Socket Serveur: playPattern: clipChoisi", pattern, " pour ID: ", monId);
988
- if (debug) console.log('Websocket serveur : playPattern: demandeDeSonParPseudo : ', unPseudo, "groupe:", groupe, "pattern:", pattern[4]);
989
- if (debug) console.log("-----webSocketServeur: playPattern: Pattern reçu:", pattern[0]);
990
-
991
- let signal = groupesClientSon.getSignalFromGroup(pattern[9]) + "IN";
992
-
993
- if (signal === "-1IN") {
994
- console.log("WARN: websocketserveur: playPattern : no group declared :", groupe);
995
- return;
996
- }
997
- if (debug) console.log("webSocketServeur: playPattern, signal reçu:", pattern, signal);
998
-
999
- let legroupe = groupe; // groupe d'utilisateur
1000
-
1001
- // Pour la gestion des messages qui ne sont pas des patterns, on utilise des patterns dont les
1002
- // commandes MIDI sont négatives. Dans ce cas on émet des signaux sans faire appel au player de patterns
1003
- // On appelle jamais pushClipAbleton avec une note négative issue de la config.
1004
- if (pattern[0] < 0) {
1005
- // Pour associer le nom du pattern au signal de groupe IN
1006
- // C'est plus pour être cohérent que par besoin.
1007
- reactAutomatePossible({ [signal]: pattern[3] });
1008
- return;
1009
- }
1010
-
1011
- let dureeAttente = pushClipDAW(pattern, signal, legroupe, unPseudo, monId);
1012
-
1013
- // DureeAttente est la somme des durées de la FIFO de l'instrument.
1014
- if (dureeAttente === -1) {
1015
- return; // On est dans un cas de note sans durée
1016
- }
1017
-
1018
- // Conversion in real waiting time
1019
- dureeAttente = Math.floor(dureeAttente * tempoTime / 1000);
1020
- if (debug) console.log("Web Socket Serveur: abletonStartClip:dureeAttente", dureeAttente);
1021
- // On communique au client le temps d'attente en sec. avant d'entendre.
1022
- ws.send(JSON.stringify({
1023
- type: "dureeAttente",
1024
- text: dureeAttente,
1025
- son: pattern[3]
1026
- }));
1027
-
1028
- /*
1029
- // Informe tout le monde
1030
- var messageBroadcast = {
1031
- soundName: pattern[3],
1032
- soundFileName: pattern[4],
1033
- instrument: pattern[5],
1034
- group: pattern[9],
1035
- pseudo: unPseudo,
1036
- idClient: monId
1037
- }
1038
-
1039
- hop.broadcast('demandeDeSonParPseudo', JSON.stringify(messageBroadcast));
1040
-
1041
- var messageInstrument = {
1042
- instrument: pattern[5],
1043
- attente : dureeAttente
1044
- }
1045
- hop.broadcast('attenteInstrument', JSON.stringify(messageInstrument));
1046
- */
1047
- // Log la manip
1048
- messageLog.note = pattern[3];
1049
- messageLog.id = ws.id;
1050
- messageLog.pseudo = unPseudo;
1051
- logInfoSocket(messageLog);
1052
-
1053
- delete messageLog.note;
1054
- delete messageLog.text;
1055
- }
1056
-
1057
- /**
1058
- * Build the HipHop Programm.
1059
- * @memberof Websocketserver
1060
- * @function
1061
- * @inner
1062
- */
1063
- async function compileHH() {
1064
- DAWTableReady = false;
1065
- try {
1066
- automatePossibleMachine = await groupesClientSon.makeOneAutomatePossibleMachine();
1067
- } catch (err) {
1068
- //console.log("ERR: websocketserver.js: pb makeOneAutomatePossibleMachine", err);
1069
- console.log("\n-------------------------------------------------------------");
1070
- console.log(`ATTENTION:
1071
- Problem when compiling the Orchestration
1072
- maybe an hiphop compile Error`);
1073
- console.log("-------------------------------------------------------------");
1074
-
1075
- serv.broadcast(JSON.stringify({
1076
- type: "consoleBlocklySkini",
1077
- text: "See your console, pb on compilation"
1078
- }));
1079
- return;
1080
- }
1081
-
1082
- DAW.setAutomatePossible(automatePossibleMachine);
1083
- console.log("INFO: websocketServer: loadDAWTable: table loaded\n");
1084
- serv.broadcast(JSON.stringify({
1085
- type: "consoleBlocklySkini",
1086
- text: "Orchestration loaded"
1087
- }));
1088
-
1089
- // Pour l'emission des commandes OSC entre l'orchestration et un jeu ou des capteurs
1090
- if (par.gameOSCSignals) {
1091
- gameOSC.setOrchestration(automatePossibleMachine);
1092
- gameOSC.init();
1093
- } else {
1094
- // Pour fermer la socket si on change pour une pièce sans gameOSC
1095
- gameOSC.closeSocket();
1096
- }
1097
-
1098
- try {
1099
- //reactAutomatePossible( {DAWON: 1} ); // !!! en cours
1100
- } catch (e) {
1101
- console.log("websocketServerSkini:loadDAWTable:catch react:", e);
1102
- }
1103
- DAWTableReady = true;
1104
- }
1105
-
1106
- /**
1107
- * Load the parameters
1108
- * @memberof Websocketserver
1109
- * @function
1110
- * @inner
1111
- */
1112
- function loadParameters(fileName) {
1113
- // Chargement des paramètres
1114
- // à partir du fichier de config de la pièce
1115
- // qui a le même nom que le fichier d'orchestration
1116
-
1117
- if (debug1) console.log("INFO: loadBlocks: parametersFile: ", fileName);
1118
- // Attention decache n'utilise pas le même path que parametersFile
1119
- decacheParameters = "../" + fileName;
1120
-
1121
- try {
1122
- if (fs.existsSync(fileName)) {
1123
- let extension = fileName.slice(-3);
1124
- if (extension !== ".js") {
1125
- console.log("ERR: Not an js file:", fileName);
1126
- ws.send(JSON.stringify({
1127
- type: "alertBlocklySkini",
1128
- text: "Parameter not an JavaScript file " + fileName
1129
- }));
1130
- return;
1131
- }
1132
- } else {
1133
- console.log("ERR: No parameter file:", fileName);
1134
- ws.send(JSON.stringify({
1135
- type: "alertBlocklySkini",
1136
- text: "The parameter file " + fileName + " is not updated, don't run the program before modifying it."
1137
- }));
1138
- // Initialise un fichier de parametres par défaut
1139
- // C'est à dire en copie un dans un parametersFile temporaire
1140
- try {
1141
- fs.copyFileSync(origine, fileName);
1142
- } catch (err) {
1143
- console.log("websocketServer: Pb ecriture: ", fileName, err);
1144
- }
1145
- return;
1146
- }
1147
- } catch (err) {
1148
- console.log("ERR: Pb Reading parameter file:", fileName, err);
1149
- ws.send(JSON.stringify({
1150
- type: "alertBlocklySkini",
1151
- text: "Pb Reading parameter file " + fileName
1152
- }));
1153
- return;
1154
- }
1155
-
1156
- decache(decacheParameters);
1157
- // Le fait de faire un require ici, annule la référence de par dans
1158
- // les autres modules. Il faut faire un reload dans tous les modules.
1159
- par = require(decacheParameters);
1160
- if (debug) console.log("websocketserveur.js: loadbloaks; après require de dechacheParameters:", par.groupesDesSons);
1161
- reloadParameters(par);
1162
-
1163
- // On initialise les interfaces Midi ou via OSC et Synchro quand les paramètres sont chargés.
1164
- midimix.midimix(automatePossibleMachine);
1165
- ws.send(JSON.stringify({
1166
- type: "consoleBlocklySkini",
1167
- text: "Orchestration loaded"
1168
- }));
1169
- }
1170
-
1171
- /**
1172
- * Process the websocket messages. The protocols are here.
1173
- * @memberof Websocketserver
1174
- * @function
1175
- * @inner
1176
- */
1177
- ws.on('message', async function (message) {
1178
- if (debug) console.log('received: %s', message);
1179
- var msgRecu = JSON.parse(message);
1180
-
1181
- // Pour le Log des messages reçus
1182
- messageLog.date = getDateTime();
1183
- messageLog.type = msgRecu.type;
1184
-
1185
- switch (msgRecu.type) {
1186
-
1187
- case "checkSession":
1188
- DAW.displaySession();
1189
- console.log("------------------------------------");
1190
- console.log(par);
1191
- break;
1192
-
1193
- case "cleanQueues":
1194
- DAW.cleanQueues();
1195
- break;
1196
-
1197
- case "clientPseudo":
1198
- if (debug) console.log("websocketserver: clientPseudo", msgRecu);
1199
- compScore.putInClientsEnCours(msgRecu.pseudo, ws.id, msgRecu.groupe, clientsEnCours);
1200
- if (debug) console.log("websocketserver: clientPseudo : clientsEnCours:", clientsEnCours);
1201
- break;
1202
-
1203
- case "clientScenes": // Pas trés utile, c'est dans scenes.js
1204
- ws.id = msgRecu.value;
1205
- break;
1206
-
1207
- case "closeSpectateur":
1208
- if (debug) console.log("Web Socket Server: received message system : [%s]",
1209
- msgRecu.text, " from Client ID:", ws.id);
1210
- //DAW.libereDansTableDesCommandes(ws.id);
1211
-
1212
- messageLog.id = ws.id;
1213
- messageLog.pseudo = msgRecu.pseudo;
1214
- delete messageLog.text;
1215
- delete messageLog.note;
1216
- logInfoSocket(messageLog);
1217
-
1218
- ws.close();
1219
- break;
1220
-
1221
- case "combienDeSpectateurs":
1222
- var value = DAW.nbeDeSpectateursConnectes();
1223
- ws.send(JSON.stringify({ type: "nbeDeSpectateurs", value }));
1224
- break;
1225
-
1226
- case "configuration": // Message converti en signal pour l'automate central
1227
- if (debug) console.log("Web Socket Server: received message configuration : [%s]",
1228
- msgRecu.text, " from Client ID:", ws.id);
1229
- //machineServeur.inputAndReact( msgRecu.text, msgRecu.extra ); // ENVOI DU SIGNAL VERS HIPHOP
1230
- break;
1231
-
1232
- case "configDAWMidiNote":
1233
- if (debug1) console.log("websocketServer: configDAWMidiNote:", msgRecu);
1234
- //Rappel des paramètres: par.busMidiDAW, DAWChannel, DAWNote, velocity
1235
- oscMidiLocal.sendNoteOn(msgRecu.bus, msgRecu.channel, msgRecu.note, 120);
1236
- break;
1237
-
1238
- case "configDAWCC":
1239
- if (debug1) console.log("websocketServer: configDAWCC:", msgRecu);
1240
- oscMidiLocal.sendControlChange(msgRecu.bus, msgRecu.channel, msgRecu.CC, msgRecu.CCValue);
1241
- break;
1242
-
1243
- case "compileHH":
1244
- try {
1245
- compileHH();
1246
- } catch (err) {
1247
- console.error(err);
1248
- }
1249
- break;
1250
-
1251
- case "compileHHEditionFile":
1252
- if (debug1) console.log("websocketServer: compileHHEditionFile:", msgRecu,
1253
- ":", piecePath + HipHopSrc, ":", targetHH);
1254
-
1255
- try {
1256
- // Etape de parsing vers targetHH
1257
- let fragment = compile(piecePath + HipHopSrc, {});
1258
- await fragment.output(targetHH);
1259
- await fragment.sourcemap(targetHH);
1260
- } catch (err) {
1261
- console.log("ERR: Erreur dans la compilation du programme hiphop")
1262
- console.log("websocketServerSkini:compileHHEditionFile:fragment:", err);
1263
- break; // Pas la peine d'aller plus loin dans la compilation
1264
- }
1265
-
1266
- // Compilation du programme généré en dur dans targetHH.
1267
- // On utilise le même procédé que pour les programmes générés par Blockly
1268
- try {
1269
- compileHH();
1270
- } catch (err) {
1271
- console.log("websocketServerSkini:compileHHEditionFile:compileHH:", err);
1272
- }
1273
- break;
1274
-
1275
- case "createSession":
1276
- if (msgRecu.fileName === '') {
1277
- console.log("WARN: No descriptor file name");
1278
- break;
1279
- }
1280
- if (debug1) console.log("createSession:", sessionPath + msgRecu.fileName + ".csv");
1281
- sessionFile = sessionPath + msgRecu.fileName + ".csv";
1282
- // Initialise un fichier de parametres par défaut
1283
- // C'est à dire en copie un dans un parametersFile temporaire
1284
- try {
1285
- fs.copyFileSync(defaultSession, sessionFile);
1286
- } catch (err) {
1287
- console.log("websocketServer: Pb ecriture: ", sessionFile, err);
1288
- }
1289
- try {
1290
- DAW.loadDAWTable(sessionFile);
1291
- } catch (err) {
1292
- console.log("websocketServer: erreur de chargement:createSession: ", sessionFile, err);
1293
- }
1294
-
1295
- ws.send(JSON.stringify({ type: "consoleBlocklySkini", text: "session loaded: " + sessionFile }));
1296
- break;
1297
-
1298
- case "DAWPseudo":
1299
- break;
1300
-
1301
- case "DAWSelectListClips":
1302
- let listAllClips = new Array(); // Devient alors utilisable pour les controles dans DAW
1303
- listAllClips = DAW.getListClips(msgRecu.niveaux);
1304
- //if (debug) console.log("Web Socket Serveur: niveaux pour recherche ", msgRecu.niveaux, listClips);
1305
- ws.send(JSON.stringify({
1306
- type: "listClips",
1307
- listAllClips
1308
- }));
1309
- break;
1310
-
1311
- /* case "DAWStartClip":
1312
- pseudo = msgRecu.pseudo;
1313
- if (msgRecu.clipChoisi === undefined ) break; // Protection si pas de selection sur le client
1314
- if (debug) console.log("Web Socket Serveur: DAWStartClip: clipChoisi", msgRecu.clipChoisi, " pour ID: ", msgRecu.id);
1315
- if (debug) console.log('Websocket serveur : DAWStartClip: demandeDeSonParPseudo : ', msgRecu.pseudo, msgRecu.clipChoisi[4]);
1316
-
1317
- // !! Attention pas à jour dans les paramètre de pushClipDAW
1318
- var dureeAttente = pushClipDAW(msgRecu.clipChoisi);
1319
- if ( dureeAttente === -1) {
1320
- break; // On est dans un cas de note répétée
1321
- }
1322
-
1323
- var msg = {
1324
- type: "dureeAttente",
1325
- text: dureeAttente,
1326
- son: msgRecu.clipChoisi[3]
1327
- }
1328
- // On communique au client le temps d'attente avant d'entendre.
1329
- ws.send(JSON.stringify(msg));
1330
-
1331
- oscMidiLocal.sendProcessing( "/DAWPseudo", msgRecu.pseudo );
1332
-
1333
- // Informe tout le monde
1334
- var messageBroadcast = msgRecu.pseudo + " a choisi " + msgRecu.clipChoisi[3];
1335
- msg.type = "demandeDeSonParPseudo";
1336
- msg.text = messageBroadcast;
1337
- delete msg.listClips;
1338
- serv.broadcast(JSON.stringify(msg));
1339
-
1340
- // Log la manip
1341
- messageLog.note = msgRecu.clipChoisi[3];
1342
- messageLog.id = ws.id;
1343
- messageLog.pseudo = msgRecu.pseudo;
1344
- messageLog.text = msg.text;
1345
- logInfoSocket(messageLog);
1346
- break;*/
1347
-
1348
- case "dureeDuTickHorlogeMidi": // Reçu de Processing chaque 24 pulses de l'horloge Midi (une noire)
1349
- receivedTickFromSynchro();
1350
- break;
1351
-
1352
- case "getDelayInstrument":
1353
- if (debug) console.log("Web Socket Serveur: getDelayInstrument", msgRecu.clipChoisi, " pour ID: ", ws.id);
1354
- let msgDelay = {
1355
- type: "delaiInstrument"
1356
- }
1357
-
1358
- if (msgRecu.clipChoisi === undefined) {
1359
- msgDelay.text = -1;
1360
- msgDelay.son = "pattern undefined";
1361
- ws.send(JSON.stringify(msgDelay));
1362
- break;
1363
- } else {
1364
- let dureeAttente = DAW.getDelayEventDAW(msgRecu.clipChoisi[5]);
1365
- if (dureeAttente === -1) {
1366
- break; // On est dans un cas de note répétée
1367
- }
1368
- msgDelay.text = dureeAttente;
1369
- }
1370
- // On communique au client le délai avant d'entendre.
1371
- msgDelay.son = msgRecu.clipChoisi[3];
1372
- ws.send(JSON.stringify(msgDelay));
1373
- break;
1374
-
1375
- case "getGroupesClientLength":
1376
- var longueurs = groupesClientSon.getGroupesClientLength();
1377
-
1378
- if (debug) console.log("websocketserver: getGroupesClientLength: ", longueurs);
1379
- ws.send(JSON.stringify({ type: "groupesClientLength", longueurs }));
1380
-
1381
- break;
1382
-
1383
- case "getNombreDePatternsPossibleEnListe": // Pour l'initialisation de memorySortable
1384
- let nombreDePatternsPossible = groupesClientSon.getNombreDePatternsPossibleEnListe();
1385
- ws.send(JSON.stringify({
1386
- type: "nombreDePatternsPossibleEnListe",
1387
- nombreDePatternsPossible: nombreDePatternsPossible
1388
- }));
1389
- break;
1390
-
1391
- case "getPatternGroups":
1392
- // It happends when calling this function before loading a piece
1393
- if (par === undefined) break;
1394
-
1395
- if (DAWStatus !== undefined) {
1396
- ws.send(JSON.stringify({
1397
- type: "setPatternGroups",
1398
- value: par.groupesDesSons
1399
- }));
1400
- } else {
1401
- if (warnings) console.log("WARN: websocketserver: getPatternGroups: DAWStatus not yet defined");
1402
- }
1403
- break;
1404
-
1405
- case "loadBlocks":
1406
- //
1407
- // Il manque ici un ménage de ce qui peut se trouver déjà chargé !!!
1408
- //
1409
- if (msgRecu.fileName === '') {
1410
- console.log("WARN: No orchestration");
1411
- break;
1412
- }
1413
-
1414
- let orchestrationFile = piecePath + msgRecu.fileName;
1415
-
1416
- try {
1417
- if (fs.existsSync(orchestrationFile)) {
1418
- let extension = orchestrationFile.slice(-4);
1419
- if (extension !== ".xml") {
1420
- console.log("ERR: Not an xml file:", orchestrationFile);
1421
- ws.send(JSON.stringify({
1422
- type: "consoleBlocklySkini",
1423
- text: "Not an XML file " + orchestrationFile
1424
- }));
1425
- break;
1426
- }
1427
- } else {
1428
- console.log("ERR: No orchestration file:", orchestrationFile);
1429
- ws.send(JSON.stringify({
1430
- type: "consoleBlocklySkini",
1431
- text: "No orchestration file " + orchestrationFile
1432
- }));
1433
- break;
1434
- }
1435
- } catch (err) {
1436
- console.log("ERR: No orchestration file:", orchestrationFile, err);
1437
- ws.send(JSON.stringify({
1438
- type: "consoleBlocklySkini",
1439
- text: "Error reading orchestration file " + orchestrationFile
1440
- }));
1441
- break;
1442
- }
1443
-
1444
- fs.readFile(orchestrationFile, 'utf8', (err, data) => {
1445
- if (err) {
1446
- console.error(err);
1447
- return;
1448
- }
1449
- if (debug1) console.log("INFO: loadBlocks: orchestrationFile:", orchestrationFile);
1450
- ws.send(JSON.stringify({
1451
- type: "blocksLoaded",
1452
- data
1453
- }));
1454
- });
1455
-
1456
- // Chargement des paramètres
1457
- // à partir du fichier de config de la pièce
1458
- // qui a le même nom que le fichier d'orchestration avec un extension js
1459
- // au lieu de xml
1460
-
1461
- // Entre autre pour la mise à jour des parametres dans le browser
1462
- parametersFileGlobal = msgRecu.fileName.slice(0, -4) + ".js";
1463
- parametersFile = sessionPath + msgRecu.fileName;
1464
- // Construction du nom à partir du fichier xml
1465
- parametersFile = parametersFile.slice(0, -4) + ".js";
1466
- loadParameters(parametersFile);
1467
- break;
1468
-
1469
- case "loadHHFile":
1470
- if (msgRecu.fileName === '') {
1471
- console.log("WARN: No Hiphop file selected");
1472
- break;
1473
- }
1474
- if (debug1) console.log("INFO: loadHHFile:", msgRecu.fileName);
1475
- HipHopSrc = msgRecu.fileName;
1476
-
1477
- let extension = HipHopSrc.slice(-6);
1478
- if (extension !== ".hh.js") {
1479
- console.log("ERR: Not an HipHop js file:", HipHopSrc);
1480
- ws.send(JSON.stringify({
1481
- type: "alertBlocklySkini",
1482
- text: "You try to load a not HipHop JavaScript file : " + HipHopSrc
1483
- }));
1484
- }
1485
-
1486
- // try {
1487
- // compileHH();
1488
- // } catch (err) {
1489
- // console.log("websocketServerSkini:loadHHFile:catch:", err);
1490
- // }
1491
-
1492
- // Chargement des paramètres
1493
- // à partir du fichier de config de la pièce
1494
- // qui a le même nom que le fichier d'orchestration avec un extension js
1495
- // au lieu de hh.js
1496
- // Entre autre pour la mise à jour des parametres dans le browser
1497
- parametersFileGlobal = msgRecu.fileName.slice(0, -6) + ".js";
1498
- parametersFile = sessionPath + msgRecu.fileName;
1499
- // Construction du nom à partir du fichier hh.js
1500
- parametersFile = parametersFile.slice(0, -6) + ".js";
1501
- loadParameters(parametersFile);
1502
- break;
1503
-
1504
- case "loadSession": // Pour les descripteurs de clips
1505
- if (msgRecu.fileName === '') {
1506
- console.log("WARN: No descriptor selected");
1507
- break;
1508
- }
1509
-
1510
- if (debug1) console.log("INFO: loadSession:", sessionPath + msgRecu.fileName);
1511
- sessionFile = sessionPath + msgRecu.fileName;
1512
-
1513
- try {
1514
- if (fs.existsSync(sessionFile)) {
1515
- let extension = sessionFile.slice(-4);
1516
- if (extension !== ".csv") {
1517
- console.log("ERR: Not an csv file:", sessionFile);
1518
- ws.send(JSON.stringify({
1519
- type: "alertBlocklySkini",
1520
- text: "Descriptor not a CSV file " + sessionFile
1521
- }));
1522
- break;
1523
- }
1524
- } else {
1525
- console.log("ERR: No session file:", sessionFile);
1526
- ws.send(JSON.stringify({
1527
- type: "alertBlocklySkini",
1528
- text: "No session file " + sessionFile
1529
- }));
1530
- break;
1531
- }
1532
- } catch (err) {
1533
- console.log("ERR: Pb Reading session file:", sessionFile, err);
1534
- ws.send(JSON.stringify({
1535
- type: "alertBlocklySkini",
1536
- text: "Pb Reading session file " + sessionPath
1537
- }));
1538
- break;
1539
- }
1540
-
1541
- try {
1542
- DAW.loadDAWTable(sessionPath + msgRecu.fileName);
1543
- } catch (err) {
1544
- console.log("websocketServer: erreur de chargement:loadSession: ", sessionFile, err);
1545
- }
1546
- ws.send(JSON.stringify({
1547
- type: "consoleBlocklySkini",
1548
- text: "session loaded: " + msgRecu.fileName
1549
- }));
1550
- break;
1551
-
1552
- case "putInMatriceDesPossibles":
1553
- if (debug) console.log("websocketserver:putInMatriceDesPossibles:", msgRecu);
1554
- groupesClientSon.setInMatriceDesPossibles(msgRecu.clients, msgRecu.sons, msgRecu.status); // groupe de clients, n° du groupe de sons, booleen
1555
- groupeName = groupesClientSon.getNameGroupeSons(msgRecu.sons);
1556
-
1557
- if (debug) groupesClientSon.displayMatriceDesPossibles();
1558
-
1559
- serv.broadcast(JSON.stringify({
1560
- type: "groupeClientStatus",
1561
- groupeClient: msgRecu.clients, // Pour identifier le groupe de clients
1562
- groupeName,
1563
- status: msgRecu.status
1564
- }));
1565
- break;
1566
-
1567
- case "ResetMatriceDesPossibles":
1568
- if (debug1) console.log("websocketserver: ResetMatriceDesPossibles");
1569
- groupesClientSon.resetMatriceDesPossibles();
1570
- groupeName = "";
1571
- serv.broadcast(JSON.stringify({
1572
- type: "groupeClientStatus",
1573
- groupeClient: 255,
1574
- groupeName,
1575
- status: false
1576
- }));
1577
- break;
1578
-
1579
- case "saveBlocklyGeneratedFile":
1580
- if (msgRecu.fileName === '') {
1581
- console.log("WARN: No Orchestration");
1582
- break;
1583
- }
1584
-
1585
- // Si on crée une orchestration à partir de rien le fichier de paramètre n'existe pas
1586
- // On en crée un par défaut.
1587
- parametersFileGlobal = msgRecu.fileName + ".js";
1588
- try {
1589
- if (!fs.existsSync(sessionPath + parametersFileGlobal)) {
1590
- console.log("ERR: No parameter file:", parametersFileGlobal);
1591
- ws.send(JSON.stringify({
1592
- type: "alertBlocklySkini",
1593
- text: "The parameter file " + parametersFileGlobal + " is created, don't run the program before modifying it."
1594
- }));
1595
- // Initialise un fichier de parametres par défaut
1596
- try {
1597
- fs.copyFileSync(origine, sessionPath + parametersFileGlobal);
1598
- // On recharge les nouveaux paramètres avant la compilation.
1599
- // Attention decache n'utilise pas le même path que parametersFile
1600
- decacheParameters = "../" + sessionPath + parametersFileGlobal;
1601
- decache(decacheParameters);
1602
- par = require(decacheParameters);
1603
- reloadParameters(par);
1604
- } catch (err) {
1605
- console.log("websocketServer: Pb ecriture: ", parametersFileGlobal, err.toString());
1606
- break;
1607
- }
1608
- } else {
1609
- if (debug) console.log("websocketserveur.js: saveBlocklyGeneratedFile: si OK:", par.groupesDesSons);
1610
- }
1611
- } catch (err) {
1612
- console.log("ERR: Pb creating parameter file:", parametersFileGlobal, err.toString());
1613
- ws.send(JSON.stringify({
1614
- type: "alertBlocklySkini",
1615
- text: "Pb creating parameter file " + parametersFileGlobal
1616
- }));
1617
- break;
1618
- }
1619
-
1620
- // Ecrit le programme HH pour compilation
1621
- fs.writeFile(generatedDir + defaultOrchestrationName, msgRecu.text, function (err) {
1622
- if (err) {
1623
- ws.send(JSON.stringify({
1624
- type: "alertBlocklySkini",
1625
- text: err.toString()
1626
- }));
1627
- return console.log(err);
1628
- }
1629
- if (debug1) console.log("INFO: websocketServer:", generatedDir + defaultOrchestrationName, " written");
1630
- });
1631
-
1632
- // Ecrit le fichier XML Blockly
1633
- fs.writeFile(piecePath + msgRecu.fileName + ".xml", msgRecu.xmlBlockly, function (err) {
1634
- if (err) {
1635
- return console.log(err.toString());
1636
- }
1637
- console.log("INFO: websocketServer:", msgRecu.fileName + ".xml", " written");
1638
- ws.send(JSON.stringify({
1639
- type: "consoleBlocklySkini",
1640
- text: msgRecu.fileName + ".xml written"
1641
- }));
1642
- // Compile l'orchestration
1643
- try {
1644
- compileHH();
1645
- } catch (err) {
1646
- console.log("websocketServerSkini:saveBlocklyGeneratedFile:catch:", err.toString());
1647
- }
1648
- });
1649
- break;
1650
-
1651
- case "saveSessionAs":
1652
- if (debug1) console.log("save descriptors as: ", msgRecu.fileName);
1653
- try {
1654
- fs.copyFileSync(sessionFile, sessionPath + msgRecu.fileName);
1655
- } catch (err) {
1656
- console.log("websocketServer: Pb ecriture save descriptors as: ", msgRecu.fileName, err);
1657
- }
1658
- break;
1659
-
1660
- case "selectAllClips":
1661
- var listClips = DAW.getAllClips(msgRecu.groupe, groupesClientSon.matriceDesPossibles);
1662
- if (listClips !== -1) {
1663
- if (debug) console.log("Web Socket Serveur: selectAllClips for id:", ws.id, "groupe:", msgRecu.groupe, " premier pattern:", listClips[0]);
1664
- ws.send(JSON.stringify({
1665
- type: "listClips",
1666
- listClips
1667
- }));
1668
- }
1669
- break;
1670
-
1671
- case "sendOSC":
1672
- oscMidiLocal.sendOSCRasp(msgRecu.message, msgRecu.value1,
1673
- par.raspOSCPort, msgRecu.IpAddress);
1674
- break;
1675
-
1676
- case "sendPatternSequence":
1677
- var patternSequence = msgRecu.patternSequence;
1678
- if (debug) console.log("websocketserver: reçu : sendPatternSequence", patternSequence, msgRecu.pseudo);
1679
-
1680
- // Pour définir la façon dont sera calculé le score pour cette séquence de patterns
1681
- computeScorePolicy = groupesClientSon.getComputeScorePolicy();
1682
- computeScoreClass = groupesClientSon.getComputeScoreClass();
1683
- if (debug) console.log("websocketserver: reçu : sendPatternSequence: computeScorePolicy, computeScoreClass:", computeScorePolicy, computeScoreClass);
1684
-
1685
- let maPreSequence = compScore.getPreSequence(msgRecu.pseudo, clientsEnCours); //Une liste d'index (notes Skini midi)
1686
- if (debug) console.log("websocketserver: reçu : sendPatternSequence", patternSequence, msgRecu.pseudo, maPreSequence);
1687
-
1688
- let monScore = compScore.evaluateSequenceOfPatterns(patternSequence, maPreSequence, computeScorePolicy, computeScoreClass);
1689
-
1690
- // Met à jour la mémorisation des listes des index de pattern associée au pseudo pour
1691
- // le calcul du score.
1692
- compScore.setPreSequence(msgRecu.pseudo, patternSequence, clientsEnCours);
1693
-
1694
- //Mise à jour du score total en fonction du pseudo
1695
- let scoreTotal = compScore.updateScore(msgRecu.pseudo, monScore, clientsEnCours);
1696
-
1697
- for (let i = 0; i < patternSequence.length; i++) {
1698
- let pattern = DAW.getPatternFromNote(patternSequence[i]);
1699
- if (pattern === undefined) {
1700
- if (warnings) console.log("WARN: websocketserver: sendPatternSequence: pattern undefined");
1701
- ws.send(JSON.stringify({
1702
- type: "patternSequenceAck",
1703
- value: false
1704
- }));
1705
- }
1706
- if (debug) console.log("websocketserver: sendPatternSequence: pattern: ", patternSequence[i], pattern);
1707
- playPattern(msgRecu.pseudo, msgRecu.groupe, pattern, msgRecu.idClient);
1708
- }
1709
-
1710
- // On a besoin d'un acknowledge car on pourrait perdre des commandes du client (?? en TCP)
1711
- // On envoie le score pour la séquence choisie
1712
- ws.send(JSON.stringify({
1713
- type: "patternSequenceAck",
1714
- score: scoreTotal,
1715
- value: true
1716
- }));
1717
- // On passe par groupeClientSon pour informer l'orchestration
1718
- // Il n'y a pas de lien depuis l'orchestration vers websocketServer.js
1719
- // (Il y en a dans l'autre sens via des react())
1720
- groupesClientSon.setClientsEncours(clientsEnCours);
1721
- break;
1722
-
1723
- case "setAllMatriceDesPossibles":
1724
- if (debug1) console.log("websocketserver: setAllMatriceDesPossibles");
1725
- groupesClientSon.setMatriceDesPossibles();
1726
- groupeName = "";
1727
- serv.broadcast(JSON.stringify({
1728
- type: "groupeClientStatus",
1729
- groupeClient: 255,
1730
- groupeName,
1731
- status: true
1732
- }));
1733
- break;
1734
-
1735
- // DAWON est le SIGNAL d'activation ou désactivation de l'orchestration
1736
- // DAWStatus est une VARIABLE qui permet de savoir quelle est l'orchestration choisie
1737
- // (Dans cette version on n'utilise qu'une seule orchestration
1738
- // mais dans une première version DAWStatus pouvait avoir des valeurs de 1 à 3.)
1739
- // DAWStatus = 0 signifie pas d'orchestration en cours.
1740
- case "setDAWON":
1741
- // msgRecu.value > 0 => DAW Active
1742
- DAWStatus = msgRecu.value;
1743
- if (DAWTableReady) {
1744
- if (debug) console.log("websocketServer:setDAWON:", DAWStatus);
1745
- DAW.cleanQueues();
1746
- serv.broadcast(JSON.stringify({
1747
- type: "DAWStatus",
1748
- value: msgRecu.value
1749
- }));
1750
- initMatriceDesPossibles(DAWStatus);
1751
- // Pour être en phase avec la création du pad controleur
1752
- groupesClientSon.resetMatriceDesPossibles();
1753
- } else {
1754
- if (warnings) console.log("WARNING: Table des commandes DAW pas encore chargée: ", DAWStatus);
1755
- ws.send(JSON.stringify({
1756
- type: "DAWTableNotReady",
1757
- text: "Table des commandes DAW pas encore chargée"
1758
- }));
1759
- }
1760
- break;
1761
-
1762
- case "startAutomate": // Lance l'automate orchestrateur de la matrice des possibles
1763
- if (par.timer !== undefined) {
1764
- timerSynchro = par.timer;
1765
- }
1766
-
1767
- if (DAWTableReady) {
1768
- if (debug1) console.log("INFO: webSocketServeur:startAutomate: DAWstatus:", DAWStatus);
1769
- reactAutomatePossible({ start: undefined });
1770
-
1771
- // S'il n'y a pas de synchro Midi ni Link on lance un worker
1772
- if (!par.synchoOnMidiClock && !par.synchroLink && par.synchroSkini) {
1773
- //setMonTimer(timerSynchro); // Pour un timer dans le thread principal, pas utile avec les workers
1774
- if (debug1) console.log("websocketserver: startAutomate:worker synchro");
1775
- workerSynchroInit('./serveur/workerSynchro.mjs', timerSynchro); // Avec un worker
1776
- }
1777
-
1778
- if (par.sensorOSC) {
1779
- if (debug1) console.log("INFO: webSocketServeur: With Interface Z sensors");
1780
- workerInterfaceZInit('./serveur/workerInterfaceZ.mjs',
1781
- ipConfig.serverIPAddress,
1782
- ipConfig.interfaceZIPaddress,
1783
- ipConfig.portOSCFromInterfaceZData,
1784
- ipConfig.portOSCFromInterfaceZMidi,
1785
- ipConfig.portOSCFromInterfaceZMiniWi,
1786
- ipConfig.portOSCToInterfaceZ,
1787
- par.tempoSensorsInit,
1788
- par.sensorsSensibilities);
1789
- } else {
1790
- if (debug) console.log("INFO: webSocketServeur: stopInterfaceZ", workerInterfaceZ);
1791
- if (workerInterfaceZ !== undefined) {
1792
- workerInterfaceZ.postMessage(["stopInterfaceZ"]);
1793
- }
1794
- }
1795
- }
1796
-
1797
- compScore.resetClientEnCours(clientsEnCours);
1798
- groupesClientSon.setClientsEncours(clientsEnCours);
1799
-
1800
- // Sinon n'est envoyé qu'au onopen de la Socket
1801
- // nécessaire pour les couleurs sur les clients
1802
- serv.broadcast(JSON.stringify({
1803
- type: "setPatternGroups",
1804
- value: par.groupesDesSons
1805
- }));
1806
- // Au cas où le client serait connecté avant le début de l'orchestration.
1807
- if (debug) console.log("Web Socket Server: startAutomate DAWON:", DAWStatus);
1808
- ws.send(JSON.stringify({
1809
- type: "DAWON",
1810
- value: DAWStatus
1811
- }));
1812
- break;
1813
-
1814
- case "startByMidiClock":
1815
- //compteurDivisionMesure = 0; // Remise à zero de la position dans le motif
1816
- break;
1817
-
1818
- case "startSpectateur": // On récupère l'ID du client
1819
- // On autorise la configuration des patterns même sans piece chargée
1820
- if (msgRecu.text === "configurateur") {
1821
- if (debug1) console.log("INFO: webSocketServeur: startSpectateur: un configurateur connecté", msgRecu.id);
1822
- ws.send(JSON.stringify({
1823
- type: "skiniParametres",
1824
- descriptors: DAW.getSession(),
1825
- value: par
1826
- }));
1827
- }
1828
-
1829
- if (par === undefined) {
1830
- console.log("WARN: A client try to connect but no piece launched");
1831
- break;
1832
- }
1833
-
1834
- ws.id = msgRecu.id;
1835
- if (debug) console.log("INFO: websocketserbeur: startSpectateur: ", msgRecu.id);
1836
-
1837
- // On ne permet donc qu'un seul controleur.
1838
- // Attention: La connexion d'un deuxième contrôleur, fait perdre la première et réinitialise la matrice des possible.
1839
- if (msgRecu.text === "controleur") {
1840
- if (debug1) console.log("INFO: webSocketServeur: startSpectateur: un controleur connecté");
1841
- socketControleur = ws;
1842
- groupesClientSon.setSocketControleur(ws);
1843
- initMatriceDesPossibles(DAWStatus);
1844
- ws.send(JSON.stringify({
1845
- type: "skiniParametres",
1846
- value: par
1847
- }));
1848
- break;
1849
- }
1850
-
1851
- if (msgRecu.text === "pieceParameters") {
1852
- if (debug1) console.log("INFO: webSocketServeur: startSpectateur: Parametre connecté", msgRecu.id);
1853
- ws.send(JSON.stringify({
1854
- type: "skiniParametres",
1855
- value: par
1856
- }));
1857
- }
1858
-
1859
- if (msgRecu.text === "clientListe") {
1860
- if (debug1) console.log("INFO: webSocketServeur: startSpectateur: un clientListe connecté", msgRecu.id);
1861
- ws.send(JSON.stringify({
1862
- type: "skiniParametres",
1863
- value: par
1864
- }));
1865
- }
1866
-
1867
- if (msgRecu.text === "simulateur") {
1868
- if (par.simulatorInAseperateGroup) {
1869
- // Assignation d'un groupe au client
1870
- ws.send(JSON.stringify({
1871
- type: "groupe",
1872
- noDeGroupe: par.nbeDeGroupesClients - 1
1873
- }));
1874
- groupesClientSon.putIdInGroupClient(ws.id, par.nbeDeGroupesClients - 1);
1875
-
1876
- // Pour dire à l'ouverture au simulateur si on est ou pas dans une scène où DAW est actif.
1877
- if (debug1) console.log("INFO: Web Socket Server: startSpectateur: simulateur: DAWON:", DAWStatus);
1878
- ws.send(JSON.stringify({
1879
- type: "DAWON",
1880
- value: DAWStatus
1881
- }));
1882
- break;
1883
- }
1884
- }
1885
-
1886
- if (debug) console.log("websocket serveur: startSpectateur: ", ws.id, "dans groupe:", groupeEncours, msgRecu, clientsEnCours);
1887
-
1888
- // Assignation d'un groupe au client
1889
- ws.send(JSON.stringify({
1890
- type: "groupe",
1891
- noDeGroupe: groupeEncours
1892
- }));
1893
- groupesClientSon.putIdInGroupClient(ws.id, groupeEncours);
1894
-
1895
- if (debug) console.log("websocket serveur: startSpectateur: groupesClientSon:", groupesClientSon.getGroupesClient());
1896
-
1897
- // Pour une distribution équilibrée entre les groupes
1898
- // Si on souhaite avoir le simulateur sur un groupe non distribué à l'audience,
1899
- // dans le fichier de configuration on met simulatorInAseperateGroup = true;
1900
- // Ceci réserve le dernier groupe Client pour le simulateur puisque groupeEnCours n'aura jamais
1901
- // la valeur maximale nbeDeGroupesClients
1902
- if (par.nbeDeGroupesClients === 1 && par.simulatorInAseperateGroup) {
1903
- if (warnings) console.log("WARN: ATENTION PAS DE GROUPE ASSIGNE A L'AUDIENCE !");
1904
- }
1905
-
1906
- groupeEncours++;
1907
- if (par.simulatorInAseperateGroup) {
1908
- groupeEncours %= par.nbeDeGroupesClients - 1;
1909
- }
1910
- else {
1911
- groupeEncours %= par.nbeDeGroupesClients;
1912
- }
1913
- // Pour dire à l'ouverture au client si on est ou pas dans une scène où DAW est actif.
1914
- if (debug) console.log("Web Socket Server: startSpectateur: emission DAWStatus:", DAWStatus);
1915
- ws.send(JSON.stringify({
1916
- type: "DAWStatus",
1917
- value: DAWStatus
1918
- }));
1919
- break;
1920
-
1921
- case "startSimulator":
1922
- if (debug) console.log("Web Socket Server: start Simulator");
1923
- childSimulator = fork("./client/simulateurListe/simulateurFork.mjs");
1924
- childSimulator.send({ type: "START_SIMULATOR" });
1925
- updateSimulatorParameters(par);
1926
- break;
1927
-
1928
- case "stopAutomate":
1929
- if (DAWTableReady) {
1930
- //if (setTimer !== undefined && !par.synchoOnMidiClock) clearInterval(setTimer);
1931
- reactAutomatePossible({ halt: undefined });
1932
- DAWStatus = 0;
1933
- serv.broadcast(JSON.stringify({
1934
- type: "DAWStatus",
1935
- value: false
1936
- }));
1937
- }
1938
- break;
1939
-
1940
- case "stopSimulator":
1941
- if (debug) console.log("INFO: Web Socket Server: stop Simulator");
1942
- childSimulator.send({ type: "STOP_SIMULATOR" });
1943
- break;
1944
-
1945
- case "system": // Message converti en signal pour l'automate central
1946
- if (debug) console.log("Web Socket Server: received message : [%s]",
1947
- msgRecu.text, " from Client ID:", ws.id);
1948
- machineServeur.inputAndReact(msgRecu.text, ws.id); // ENVOI DU SIGNAL VERS HIPHOP
1949
- break;
1950
-
1951
- case "updateSession":
1952
- if (sessionFile !== undefined) {
1953
- if (debug) console.log("updateSession pour:", sessionFile, ": ", arrayToCSV(msgRecu.data));
1954
- // Ecrire le fichier
1955
- fs.writeFile(sessionFile, arrayToCSV(msgRecu.data), function (err) {
1956
- if (err) {
1957
- ws.send(JSON.stringify({
1958
- type: "alertBlocklySkini",
1959
- text: err.toString()
1960
- }));
1961
- return console.log("ERR: websocketserver.js: updateSession: ", err);
1962
- } else {
1963
- // Le recharger dans DAW
1964
- try {
1965
- DAW.loadDAWTable(sessionFile);
1966
- } catch (err) {
1967
- console.log("websocketServer: erreur de chargement:createSession: ", sessionFile, err);
1968
- }
1969
- ws.send(JSON.stringify({
1970
- type: "consoleBlocklySkini",
1971
- text: "session loaded: " + sessionFile
1972
- }));
1973
- }
1974
- });
1975
- } else {
1976
- console.log("WARN: No descriptor file specified");
1977
- break;
1978
- }
1979
- break;
1980
-
1981
- case "updateParameters":
1982
- // Save the previous parameters
1983
- fs.copyFile(sessionPath + parametersFileGlobal, "./backup/" + parametersFileGlobal + ".back", function (err) {
1984
- if (err) {
1985
- return console.log(err);
1986
- }
1987
- if (debug1) console.log("INFO: websocketServer: updateParameters", parametersFileGlobal + ".back written");
1988
- });
1989
-
1990
- if (debug1) console.log("INFO: websocketserveur: Update of the piece parameters", msgRecu.data, "in", sessionPath + parametersFileGlobal);
1991
- if (parametersFileGlobal !== undefined) {
1992
- saveParam.saveParameters(sessionPath + parametersFileGlobal, msgRecu.data);
1993
- reloadParameters(msgRecu.data);
1994
- }
1995
-
1996
- // On recrée le fichier pour son utilisation par l'orchestration
1997
- // avant recompilation.
1998
- // let destinationUpdate = "./serveur/skiniParametres.js";
1999
- // try {
2000
- // fs.copyFileSync(sessionPath + parametersFileGlobal, destinationUpdate);
2001
- // } catch (err) {
2002
- // console.log("ERR: websocketserver: destinationUpdate : Pb ecriture", destinationUpdate, err);
2003
- // }
2004
-
2005
- // Recompile the orchestration pour intégrer les modifications
2006
- // des paramétres de groupes et tanks.
2007
- try {
2008
- compileHH();
2009
- } catch (err) {
2010
- console.log("websocketServerSkini:updateParameters:catch:", err);
2011
- }
2012
- break;
2013
-
2014
- default: console.log("INFO: Web Socket Serveur: Type de message inconnu : ", msgRecu);
2015
- }
2016
- });
2017
- });
2018
-
2019
- /****** FIN WEBSOCKET ************/
2020
- }
2021
-
2022
- function logInfoSocket(message) {
2023
- fs.appendFile('skinilog.json', JSON.stringify(message) + "\n", "UTF-8", function (err) {
2024
- if (err) {
2025
- console.error(err);
2026
- throw err;
2027
- }
2028
- });
2029
- }
2030
-
2031
- function getDateTime() {
2032
- var date = new Date();
2033
-
2034
- var hour = date.getHours();
2035
- hour = (hour < 10 ? "0" : "") + hour;
2036
-
2037
- var min = date.getMinutes();
2038
- min = (min < 10 ? "0" : "") + min;
2039
-
2040
- var sec = date.getSeconds();
2041
- sec = (sec < 10 ? "0" : "") + sec;
2042
-
2043
- var year = date.getFullYear();
2044
-
2045
- var month = date.getMonth() + 1;
2046
- month = (month < 10 ? "0" : "") + month;
2047
-
2048
- var day = date.getDate();
2049
- day = (day < 10 ? "0" : "") + day;
2050
-
2051
- return day + ":" + month + ":" + year + ":" + hour + ":" + min + ":" + sec;
2052
- }
1
+ /**
2
+ * @fileOverview Websocket management. This is the main part of Skini for messages
3
+ * management and control. Something like a main switch.
4
+ * Most of the API and functions here are local.
5
+ * @copyright (C) 2022-2024 Bertrand Petit-Hédelin
6
+ *
7
+ * This program is free software: you can redistribute it and/or modify
8
+ * it under the terms of the GNU General Public License as published by
9
+ * the Free Software Foundation, either version 3 of the License, or
10
+ * any later version.
11
+ *
12
+ * This program is distributed in the hope that it will be useful,
13
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ * GNU General Public License for more details.
16
+ *
17
+ * You should have received a copy of the GNU General Public License
18
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
19
+ *
20
+ * avec capteurIZTest.csv et TestCapteurIZ.als
21
+ *
22
+ * @author Bertrand Petit-Hédelin <bertrand@hedelin.fr>
23
+ * @version 1.4
24
+ */
25
+ // @ts-check
26
+ 'use strict'
27
+ import { createRequire } from 'module';
28
+ const require = createRequire(import.meta.url);
29
+
30
+ import { compile } from "@hop/hiphop/lib/hhc-compiler.mjs";
31
+ import * as fs from "fs";
32
+ import * as compScore from './computeScore.mjs';
33
+ import * as gameOSC from './gameOSC.mjs';
34
+ import * as oscMidiLocal from './OSCandMidi.mjs';
35
+ import * as saveParam from './saveParam.mjs';
36
+
37
+ const ipConfig = require('./ipConfig.json');
38
+ const midiConfig = require("./midiConfig.json");
39
+
40
+ const decache = require('decache');
41
+ const { stringify } = require('querystring');
42
+ import { Worker } from 'worker_threads';
43
+ import { fork } from "child_process";
44
+
45
+ const defaultOrchestrationName = "orchestrationHH.mjs";
46
+ const defaultOrchestrationNameHH = "orchestrationHH.hh.js";
47
+
48
+ let par;
49
+ let DAW;
50
+ let midimix;
51
+ let sessionFile; // Pour le chemin complet de la session en cours (descripteur en ".csv")
52
+ let parametersFile;
53
+ let parametersFileGlobal;
54
+ const origine = "./serveur/defaultSkiniParametres.js";
55
+ const defaultSession = "./serveur/defaultSession.csv";
56
+ let HipHopSrc; // Fichier HipHop éditer en texte et à compiler
57
+ let decacheParameters;
58
+ let childSimulator;
59
+ const targetHH = "./myReact/orchestrationHH.mjs"; // Redondant à revoir
60
+ // Attention en dur car le chemin est utilisé ailleurs, dans groupClientsSons.js
61
+ // pour orchestrationHH.js
62
+ const generatedDir = "./myReact/";
63
+
64
+ // Declarations to move from CJS to ES6
65
+ let _getBroadCastServer, _sendSignalFromDAW, _sendSignalFromMIDI, _sendSignalStopFromMIDI;
66
+ let _sendSignalStartFromMIDI, _sendSignalFromMidiMix, _sendOSCTick, _getAutomatePossible;
67
+ let _setPatternListLength;
68
+
69
+ export {
70
+ _getBroadCastServer as getBroadCastServer,
71
+ _sendSignalFromDAW as sendSignalFromDAW,
72
+ _sendSignalFromMIDI as sendSignalFromMIDI,
73
+ _sendSignalStopFromMIDI as sendSignalStopFromMIDI,
74
+ _sendSignalStartFromMIDI as sendSignalStartFromMIDI,
75
+ _sendSignalFromMidiMix as sendSignalFromMidiMix,
76
+ _sendOSCTick as sendOSCTick,
77
+ _getAutomatePossible as getAutomatePossible,
78
+ _setPatternListLength as setPatternListLength
79
+ };
80
+
81
+ // Répertoires par défaut, ils sont à fixer dans le fichier de configuration.
82
+ // se trouvent les fichiers XML d'orchestration
83
+ // On ne peut pas donner de chemin absolu dans un browser.
84
+ // Ce sont les fichiers csv "descripteurs" des patterns
85
+ // et les fichiers de configuration ".js"
86
+ // Bug de principe (21/06/2022): On ne peut pas changer ces paramètres dans le fichier .js
87
+ // puisque ces paramètres sont fixés avant tous choix de pièces ....
88
+ // Il devrait s'agir de paramètres globaux et non liés aux fichiers de config de chaque pièce.
89
+ const sessionPath = ipConfig.sessionPath; //"./pieces/";
90
+ const piecePath = ipConfig.piecePath; //"./pieces/";
91
+
92
+ import * as groupesClientSon from './groupeClientsSons.mjs';
93
+
94
+ /**
95
+ * To load some modules.
96
+ * Used only at Skini launch.
97
+ * @param {Object} midimixage reference
98
+ */
99
+ export async function setParameters(midimixage) {
100
+ midimix = midimixage;
101
+ await import('./controleDAW.mjs').then((daw) => {
102
+ DAW = daw;
103
+ groupesClientSon.setMidimix(midimix);
104
+ initMidiPort();
105
+ startWebSocketServer();
106
+ });
107
+ }
108
+
109
+ /**
110
+ * The simulator can be updated at serveral places
111
+ */
112
+ function updateSimulatorParameters(param) {
113
+
114
+ if (childSimulator !== undefined) {
115
+ let message = {
116
+ type: "PARAMETERS",
117
+ data: param
118
+ }
119
+ try {
120
+ childSimulator.send(message);
121
+ } catch (err) {
122
+ console.log("ERR: websocketserver: updateSimulatorParameters:", err);
123
+ }
124
+ } else {
125
+ if (debug1) console.log("INFO: websocketServer: updateSimulatorParameters :No Fork Simulator");
126
+ }
127
+ }
128
+
129
+ /**
130
+ * Convert data in the good format
131
+ * and reload new parametrers in the different
132
+ * modules during a Skini session.
133
+ *
134
+ * Cette fonction n'est pas bonne.
135
+ * Il faut regénerer complétement les paramètres et envoyer
136
+ * la nouvelle version.
137
+ *
138
+ * @param {object} param
139
+ */
140
+ function reloadParametersOld(param) {
141
+ let par = param; // C'est seulement pour la lecture car on a le même objet
142
+
143
+ // Le transfert des parametre passe tout en chaine de caractère qui ne
144
+ // sont pas traduite en int.
145
+ par.nbeDeGroupesClients = parseInt(param.nbeDeGroupesClients, 10);
146
+ par.algoGestionFifo = parseInt(param.algoGestionFifo, 10);
147
+ par.tempoMax = parseInt(param.tempoMax, 10);
148
+ par.tempoMin = parseInt(param.tempoMin, 10);
149
+ par.limiteDureeAttente = parseInt(param.limiteDureeAttente, 10);
150
+
151
+ // Pas vraiment utile pour les booléens ?
152
+ par.shufflePatterns = param.shufflePatterns;
153
+ par.avecMusicien = param.avecMusicien;
154
+ par.reactOnPlay = param.reactOnPlay;
155
+
156
+ // Typage pour les antécédents dans Score. En rechargeant depuis le client
157
+ // de parametrage on a une chaine de caractères et pas un tableau.
158
+ for (let i = 0; i < param.groupesDesSons.length; i++) {
159
+ if (typeof param.groupesDesSons[i][7] === 'string' || param.groupesDesSons[i][7] instanceof String) {
160
+ par.groupesDesSons[i][7] = param.groupesDesSons[i][7].split(',');
161
+ }
162
+ for (let j = 0; j < par.groupesDesSons[i][7].length; j++) {
163
+ par.groupesDesSons[i][7][j] = parseInt(par.groupesDesSons[i][7][j]);
164
+ }
165
+ }
166
+
167
+ oscMidiLocal.setParameters(par);
168
+ DAW.setParameters(par);
169
+ groupesClientSon.setParameters(par);
170
+ midimix.setParameters(par);
171
+ updateSimulatorParameters(par);
172
+
173
+ initMidiPort();
174
+ }
175
+
176
+ function reloadParameters(param) {
177
+ // Création manuelle d'une copie
178
+ let parlocal = {
179
+ ...param, // copie superficielle : fonctions et propriétés simples sont conservées
180
+ groupesDesSons: param.groupesDesSons.map(group => [...group]) // copie des sous-tableaux
181
+ };
182
+
183
+ // Conversion des types
184
+ parlocal.nbeDeGroupesClients = parseInt(param.nbeDeGroupesClients, 10);
185
+ parlocal.algoGestionFifo = parseInt(param.algoGestionFifo, 10);
186
+ parlocal.tempoMax = parseInt(param.tempoMax, 10);
187
+ parlocal.tempoMin = parseInt(param.tempoMin, 10);
188
+ parlocal.limiteDureeAttente = parseInt(param.limiteDureeAttente, 10);
189
+
190
+ parlocal.shufflePatterns = param.shufflePatterns;
191
+ parlocal.avecMusicien = param.avecMusicien;
192
+ parlocal.reactOnPlay = param.reactOnPlay;
193
+
194
+ // Traitement des antécédents (index 7)
195
+ for (let i = 0; i < parlocal.groupesDesSons.length; i++) {
196
+ const entry = parlocal.groupesDesSons[i][7];
197
+ if (typeof entry === 'string') {
198
+ parlocal.groupesDesSons[i][7] = entry.split(',').map(x => parseInt(x, 10));
199
+ } else if (Array.isArray(entry)) {
200
+ parlocal.groupesDesSons[i][7] = entry.map(x => parseInt(x, 10));
201
+ }
202
+ }
203
+
204
+ // Envoi aux modules
205
+ oscMidiLocal.setParameters(parlocal);
206
+ DAW.setParameters(parlocal);
207
+ groupesClientSon.setParameters(parlocal);
208
+ midimix.setParameters(parlocal);
209
+ updateSimulatorParameters(parlocal);
210
+
211
+ // Il faut remettre à jour les paramètre de
212
+ // la variable global de websocketServer
213
+ par = parlocal;
214
+ if (debug1) console.log("INFO: websocketServer: reloadParameters:tempo Max",par.tempoMax);
215
+ initMidiPort();
216
+ }
217
+
218
+ /**
219
+ * Simple conversion from Array to csv
220
+ * @param {Array} arr
221
+ * @param {String} delimiter
222
+ * @returns {String} csv
223
+ */
224
+ const arrayToCSV = (arr, delimiter = ',') =>
225
+ arr.map(v => v.join(delimiter)
226
+ ).join('\n');
227
+
228
+ // INITIALISATION DES DONNEES D'INTERACTION DU SEQUENCEUR
229
+ const tripleCrocheTR = 2;
230
+ const tripleCrocheR = 3;
231
+ const doubleCrocheTR = 4;
232
+ const doubleCrocheR = 6;
233
+ const crocheTR = 8;
234
+ const crocheR = 12;
235
+ const noireTR = 16;
236
+ const noireR = 24;
237
+ const blancheTR = 32;
238
+ const blancheR = 48;
239
+ const rondeTR = 64;
240
+ const rondeR = 96;
241
+
242
+ let tempsMesure = 4; // Partie haute de la mesure, nombre de temps dans la mesure
243
+ let divisionMesure = noireR; // Partie basse de la mesure
244
+ let nbeDeMesures = 1;
245
+ let tempo = 60; // à la minute
246
+ let canalMidi = 1;
247
+ let dureeDuTick = ((60 / tempo) / divisionMesure) * 1000; // Exprimé ici en millisecondes
248
+
249
+ let previousTime = 0;
250
+ let currentTime = 0;
251
+ let timeToPlay = 0;
252
+ let previousTimeToPlay = 0;
253
+ let defautDeLatence;
254
+
255
+ const debug = false;
256
+ const debug1 = true;
257
+ const warnings = false;
258
+ let timerSynchro;
259
+
260
+ // Automate des possibles
261
+ let DAWStatus = 0; // 0 inactif, sinon actif (originellement pour distinguer des orchestrations, distinction pas utile à présent)
262
+ let setTimer;
263
+ let timerDivision = 1; // Default value for the number of pulses for a tick, can evolve during an orchestration
264
+ let offsetDivision = 0;
265
+ let compteurDivisionMesure = 0;
266
+ let nbeDeGroupesSons = 0;
267
+ let socketControleur;
268
+ let groupeName = "";
269
+ let automatePossibleMachine;
270
+
271
+ // Scoring pour les jeux
272
+ let computeScorePolicy = 0;
273
+ let computeScoreClass = 0;
274
+
275
+ // CONTROLEUR
276
+ let DAWTableReady = false; // Pour pouvoir vérifier que la pièce a bien été chargée.
277
+
278
+ let clientsEnCours = [];
279
+ let groupeEncours = 0;
280
+
281
+ let currentTimePrevMidi = 0;
282
+ let currentTimeMidi = 0;
283
+
284
+ /*************************************************
285
+ INITIALISATION DU PORT MIDI OUT (si paramétré)
286
+ **************************************************/
287
+ /**
288
+ * Init MIDI OUT port if defined in the parameters
289
+ */
290
+ function initMidiPort() {
291
+
292
+ // Check the midi config before doing something
293
+ if (midiConfig[0] === undefined) {
294
+ return;
295
+ }
296
+
297
+ if (par !== undefined) {
298
+ let directMidi = false;
299
+ if (par.directMidiON !== undefined) {
300
+ directMidi = par.directMidiON;
301
+ }
302
+ if (directMidi) {
303
+ oscMidiLocal.initMidiOUT();
304
+ }
305
+ }
306
+ }
307
+
308
+ /************************************************
309
+ WEBSOCKET
310
+ **************************************************/
311
+ /**
312
+ * Main function to manage the websocket
313
+ */
314
+ function startWebSocketServer() {
315
+ /** @namespace Websocketserver */
316
+ const WebSocketServer = require('ws');
317
+ const serv = new WebSocketServer.Server({ port: ipConfig.websocketServeurPort });
318
+
319
+ /*************************************************************************************
320
+ Worker for synchro if no DAW (ne fonctionne pas dans VSC mais dans le terminal)
321
+ **************************************************************************************/
322
+ var workerSync;
323
+
324
+ /**
325
+ * Function to start a worker for the synchro when not using a midi sync coming from a DAW
326
+ * @function
327
+ * @memberof Websocketserver
328
+ * @param {string} filepath path
329
+ * @param {number} timer
330
+ * @inner
331
+ */
332
+ function workerSynchroInit(filepath, timer) {
333
+ if (workerSync !== undefined) {
334
+ workerSync.postMessage(['startSynchro', timer]);
335
+ return;
336
+ }
337
+
338
+ return new Promise((resolve, reject) => {
339
+ workerSync = new Worker(filepath);
340
+ if (debug) console.log('Launching worker Synchro', filepath);
341
+
342
+ workerSync.on('online', () => {
343
+ workerSync.postMessage(['startSynchro', timer]);
344
+ if (debug) console.log('Launching worker Synchro');
345
+ })
346
+ workerSync.on('message', messageFromWorker => {
347
+ switch (messageFromWorker) {
348
+ case "synchroWorker":
349
+ receivedTickFromSynchro();
350
+ break;
351
+
352
+ default:
353
+ break;
354
+ }
355
+ return resolve;
356
+ });
357
+ workerSync.on('error', reject);
358
+ workerSync.on('exit', code => {
359
+ if (code !== 0) {
360
+ reject(new Error(`Worker stopped with exit code:` + code));
361
+ }
362
+ });
363
+ });
364
+ }
365
+
366
+ /*************************************************************************************
367
+ Worker for the Interface Z management
368
+ **************************************************************************************/
369
+ var workerInterfaceZ;
370
+ var workerInterfaceZRunning = false;
371
+
372
+ /**
373
+ * Function to start a worker for the Interface Z management
374
+ * @function
375
+ * @memberof Websocketserver
376
+ * @param {string} filepath worker path
377
+ * @inner
378
+ */
379
+ function workerInterfaceZInit(filepath,
380
+ serverAddress,
381
+ interfaceZIPaddress,
382
+ portOSCFromInterfaceZData,
383
+ portOSCFromInterfaceZMidi,
384
+ portOSCFromInterfaceZMiniWi,
385
+ portOSCToInterfaceZ,
386
+ tempoSensorsInit,
387
+ sensorsSensibilities) {
388
+
389
+ if (workerInterfaceZRunning) {
390
+ if (debug1) console.log("INFO: workerInterfaceZRunning");
391
+ if (workerInterfaceZ !== undefined) {
392
+ workerInterfaceZ.postMessage(['startInterfaceZ',
393
+ serverAddress,
394
+ interfaceZIPaddress,
395
+ portOSCFromInterfaceZData,
396
+ portOSCFromInterfaceZMidi,
397
+ portOSCFromInterfaceZMiniWi,
398
+ portOSCToInterfaceZ,
399
+ tempoSensorsInit,
400
+ sensorsSensibilities]);
401
+ return;
402
+ }
403
+ }
404
+ workerInterfaceZRunning = true;
405
+
406
+ if (
407
+ interfaceZIPaddress === undefined ||
408
+ portOSCFromInterfaceZData === undefined ||
409
+ portOSCFromInterfaceZMiniWi === undefined ||
410
+ tempoSensorsInit === undefined ||
411
+ sensorsSensibilities === undefined) {
412
+ console.log("WARN: You try to use the Interface Z sensors but do not configure ipConfig");
413
+ return;
414
+ }
415
+
416
+ return new Promise((resolve, reject) => {
417
+ workerInterfaceZ = new Worker(filepath);
418
+ if (debug) console.log('Launching worker InterfaceZ', filepath);
419
+
420
+ workerInterfaceZ.on('online', () => {
421
+ workerInterfaceZ.postMessage(['startInterfaceZ',
422
+ serverAddress,
423
+ interfaceZIPaddress,
424
+ portOSCFromInterfaceZData,
425
+ portOSCFromInterfaceZMidi,
426
+ portOSCFromInterfaceZMiniWi,
427
+ portOSCToInterfaceZ,
428
+ tempoSensorsInit,
429
+ sensorsSensibilities]);
430
+ if (debug) console.log('Launching worker InterfaceZ');
431
+ })
432
+ workerInterfaceZ.on('message', messageFromWorker => {
433
+ if (debug) console.log("Websoclketserver: messageFromWorker: ", messageFromWorker);
434
+
435
+ switch (messageFromWorker.type) {
436
+ case "INTERFACEZ_RC":
437
+ if (debug) console.log("websocketServer:message from worker:", messageFromWorker);
438
+ inputAutomatePossible({ INTERFACEZ_RC: [messageFromWorker.sensor, messageFromWorker.value] });
439
+ break;
440
+
441
+ case "INTERFACEZ_RC0":
442
+ if (debug) console.log("websocketServer:message from worker:", messageFromWorker);
443
+ reactAutomatePossible({ INTERFACEZ_RC0: [messageFromWorker.sensor, messageFromWorker.value] });
444
+ break;
445
+ case "INTERFACEZ_RC1":
446
+ if (debug) console.log("websocketServer:message from worker:", messageFromWorker);
447
+ reactAutomatePossible({ INTERFACEZ_RC1: [messageFromWorker.sensor, messageFromWorker.value] });
448
+ break;
449
+ case "INTERFACEZ_RC2":
450
+ if (debug) console.log("websocketServer:message from worker:", messageFromWorker);
451
+ reactAutomatePossible({ INTERFACEZ_RC2: [messageFromWorker.sensor, messageFromWorker.value] });
452
+ break;
453
+ case "INTERFACEZ_RC3":
454
+ if (debug) console.log("websocketServer:message from worker:", messageFromWorker);
455
+ reactAutomatePossible({ INTERFACEZ_RC3: [messageFromWorker.sensor, messageFromWorker.value] });
456
+ break;
457
+ case "INTERFACEZ_RC4":
458
+ if (debug) console.log("websocketServer:message from worker:", messageFromWorker);
459
+ reactAutomatePossible({ INTERFACEZ_RC4: [messageFromWorker.sensor, messageFromWorker.value] });
460
+ break;
461
+ case "INTERFACEZ_RC5":
462
+ if (debug) console.log("websocketServer:message from worker:", messageFromWorker);
463
+ reactAutomatePossible({ INTERFACEZ_RC5: [messageFromWorker.sensor, messageFromWorker.value] });
464
+ break;
465
+ case "INTERFACEZ_RC6":
466
+ if (debug) console.log("websocketServer:message from worker:", messageFromWorker);
467
+ reactAutomatePossible({ INTERFACEZ_RC6: [messageFromWorker.sensor, messageFromWorker.value] });
468
+ break;
469
+ case "INTERFACEZ_RC7":
470
+ if (debug) console.log("websocketServer:message from worker:", messageFromWorker);
471
+ reactAutomatePossible({ INTERFACEZ_RC7: [messageFromWorker.sensor, messageFromWorker.value] });
472
+ break;
473
+ case "INTERFACEZ_RC8":
474
+ if (debug) console.log("websocketServer:message from worker:", messageFromWorker);
475
+ reactAutomatePossible({ INTERFACEZ_RC8: [messageFromWorker.sensor, messageFromWorker.value] });
476
+ break;
477
+ case "INTERFACEZ_RC9":
478
+ if (debug) console.log("websocketServer:message from worker:", messageFromWorker);
479
+ reactAutomatePossible({ INTERFACEZ_RC9: [messageFromWorker.sensor, messageFromWorker.value] });
480
+ break;
481
+ case "INTERFACEZ_RC10":
482
+ if (debug) console.log("websocketServer:message from worker:", messageFromWorker);
483
+ reactAutomatePossible({ INTERFACEZ_RC10: [messageFromWorker.sensor, messageFromWorker.value] });
484
+ break;
485
+ case "INTERFACEZ_RC11":
486
+ if (debug) console.log("websocketServer:message from worker:", messageFromWorker);
487
+ reactAutomatePossible({ INTERFACEZ_RC11: [messageFromWorker.sensor, messageFromWorker.value] });
488
+ break;
489
+
490
+ default:
491
+ break;
492
+ }
493
+ return resolve;
494
+ });
495
+ workerInterfaceZ.on('error', reject);
496
+ workerInterfaceZ.on('exit', code => {
497
+ if (code !== 0) {
498
+ reject(new Error(`Worker InterfaceZ stopped with exit code:` + code));
499
+ }
500
+ });
501
+ });
502
+ }
503
+
504
+ /**
505
+ * Define the function in order to Broadcast to all clients.
506
+ * @function
507
+ * @memberof Websocketserver
508
+ * @param {string} data message
509
+ * @inner
510
+ */
511
+ serv.broadcast = function broadcast(data) {
512
+ //if(debug) console.log("Web Socket Server: broadcast: ", data);
513
+ serv.clients.forEach(function each(client) {
514
+ if (client.readyState === WebSocketServer.OPEN) {
515
+ try {
516
+ client.send(data);
517
+ } catch (err) {
518
+ console.log("ERR: websocketserver.js: broadcast", err);
519
+ throw err;
520
+ }
521
+ }
522
+ });
523
+ }
524
+
525
+ // Pour les broadcasts depuis controle DAW, c'est la structure dans HOP que je garde.
526
+ DAW.initBroadCastServer(serv);
527
+ groupesClientSon.initBroadCastServer(serv);
528
+
529
+ /**
530
+ * In order to get the server used for broadcasting
531
+ * @returns {serv} - return the server for Broadcasting
532
+ * @function
533
+ * @memberof Websocketserver
534
+ * @inner
535
+ */
536
+ function getBroadCastServer() {
537
+ if (serv === undefined) {
538
+ console.log("ERR: websocketServer: getBroadCastServer: serv undefined");
539
+ return false;
540
+ }
541
+ return serv;
542
+ }
543
+ _getBroadCastServer = getBroadCastServer;
544
+
545
+ /************************************************************************************
546
+ Fonction pour emission de signaux depuis Ableton vers l'automatePossibleMachine.
547
+ *************************************************************************************/
548
+ /**
549
+ * Send a signal to the orchestration according to the skini note
550
+ * @function
551
+ * @memberof Websocketserver
552
+ * @param {number} noteSkini
553
+ * @inner
554
+ */
555
+ function sendSignalFromDAW(noteSkini) {
556
+ if (debug) console.log("websocketserver.js: sendSignalFromDAW:", noteSkini);
557
+ var patternName = DAW.getPatternNameFromNote(noteSkini);
558
+ if (debug) console.log("INFO: websocketserver.js: sendSignalFromDAW:", noteSkini, patternName);
559
+ if (patternName !== undefined) {
560
+ reactAutomatePossible({ patternSignal: [noteSkini, patternName] });
561
+ } else {
562
+ if (warnings) console.log("WARN: webSocketServeur: sendSignalFromDAW:", noteSkini, patternName);
563
+ }
564
+ }
565
+ _sendSignalFromDAW = sendSignalFromDAW;
566
+
567
+ /**
568
+ * Send a signal "midiSignal" to the orchestration
569
+ * tanks to a skini note.
570
+ * @function
571
+ * @memberof Websocketserver
572
+ * @inner
573
+ * @param {number} noteSkini
574
+ */
575
+ function sendSignalFromMIDI(noteSkini) {
576
+ if (debug1) console.log("webSocketServeur: sendSignalFromMIDI:", noteSkini);
577
+ if (!reactAutomatePossible({ midiSignal: [noteSkini] })) {
578
+ console.log("WARN: webSocketServeur: sendSignalFromMIDI:", noteSkini);
579
+ }
580
+ }
581
+ _sendSignalFromMIDI = sendSignalFromMIDI;
582
+
583
+ /**
584
+ * Send a signal "halt" to the orchestration.
585
+ * @memberof Websocketserver
586
+ * @function
587
+ * @inner
588
+ */
589
+ function sendSignalStopFromMIDI() {
590
+ if (!reactAutomatePossible({ halt: undefined })) {
591
+ if (warnings) console.log("WARN: webSocketServeur: sendSignalStopFromMIDI");
592
+ }
593
+ }
594
+ _sendSignalStopFromMIDI = sendSignalStopFromMIDI;
595
+
596
+ /**
597
+ * Send a signal "start" to the orchestration.
598
+ * @memberof Websocketserver
599
+ * @function
600
+ * @inner
601
+ */
602
+ function sendSignalStartFromMIDI() {
603
+ if (!reactAutomatePossible({ start: undefined })) {
604
+ if (warnings) console.log("WARN: webSocketServeur: sendSignalStartFromMIDI");
605
+ }
606
+ }
607
+ _sendSignalStartFromMIDI = sendSignalStartFromMIDI;
608
+
609
+ /************************************************************************************
610
+ Fonction pour émission de signaux depuis midimix.js vers l'automatePossibleMachine.
611
+ Utilisable pour synchro vidéo ou jeu via des notes Midi
612
+ *************************************************************************************/
613
+ /**
614
+ * Send a signal "controlFromVideo" to the orchestration
615
+ * tanks to a skini note.
616
+ * @memberof Websocketserver
617
+ * @function
618
+ * @inner
619
+ * @param {number} noteSkini
620
+ */
621
+ function sendSignalFromMidiMix(noteSkini) {
622
+ reactAutomatePossible({ controlFromVideo: [noteSkini] });
623
+ }
624
+ _sendSignalFromMidiMix = sendSignalFromMidiMix;
625
+
626
+ /*************************************************************************************
627
+ RECEPTION DES TICK MIDI OU BITWIG
628
+ **************************************************************************************/
629
+ var previousTimeClockMidi = 0;
630
+ var currentTimeClockMidi = 0;
631
+ var tempoTime = 0;
632
+
633
+ // Vient de midiMix.js et directement de Bitwig ou de processing
634
+ /**
635
+ * Called by midimix.js, for OSC, MIDI, and Link messages.
636
+ * @memberof Websocketserver
637
+ * @function
638
+ * @inner
639
+ */
640
+ function sendOSCTick() {
641
+ if (debug1) {
642
+ //console.log("websocketserver: sendOSCTick");
643
+ serv.broadcast(JSON.stringify({
644
+ type: "synchroSkini",
645
+ text: ""
646
+ }));
647
+ }
648
+ receivedTickFromSynchro();
649
+ }
650
+ _sendOSCTick = sendOSCTick;
651
+
652
+ /**
653
+ * Called on synchro messages received each quarter note
654
+ * emitted by the DAW using MIDI or OSC, or the synchro worker.
655
+ * No parameters the variables are global to the whole module.
656
+ * @memberof Websocketserver
657
+ * @function
658
+ * @inner
659
+ */
660
+ function receivedTickFromSynchro() {
661
+ if (debug) console.log("websocketserver : receivedTickFromSynchro: tick received");
662
+ currentTimeClockMidi = Date.now();
663
+ tempoTime = currentTimeClockMidi - previousTimeClockMidi; // Real duration of a quarter note
664
+ if (debug) console.log("websocketserver:dureeDuTickHorlogeMidi:tempoTime=", tempoTime,
665
+ compteurDivisionMesure,
666
+ groupesClientSon.getTimerDivision());
667
+ previousTimeClockMidi = currentTimeClockMidi;
668
+
669
+ if (par.pulsationON) {
670
+ reactAutomatePossible({ pulsation: undefined });
671
+ }
672
+ // La remise à jour de la durée des ticks est possible depuis les automates.
673
+ // Si les automates ne mettent pas timerDivision à jour, on garde la valeur par défaut
674
+ // donnée dans le fichier de config de la pièce. (compatibilté ascendante)
675
+ var timerLocal = groupesClientSon.getTimerDivision();
676
+ if (debug) console.log("websocketserver: receivedTickFromSynchro: timerLocal:", timerLocal);
677
+
678
+ if (timerLocal !== undefined) {
679
+ timerDivision = timerLocal;
680
+ } else {
681
+ //console.log("WARN: websocketServer: receivedTickFromSynchro: timerDivision undefined");
682
+ }
683
+
684
+ if (debug) console.log("websocketserver: receivedTickFromSynchro: timerDivision:", timerDivision);
685
+
686
+ //offsetDivision = timerDivision/2;
687
+ // actionOnTick() is called based on the tick not the pulse issued from the synchro.
688
+ if (compteurDivisionMesure === 0) { // offsetDivision
689
+ actionOnTick(timerDivision);
690
+ }
691
+ // Ceci est la définition du tick de l'orchestration
692
+ // Il s'agit d'une conversion de la pulsation MIDI ou worker en "tick".
693
+ compteurDivisionMesure = (compteurDivisionMesure + 1) % timerDivision;
694
+ }
695
+
696
+ /*************************************************************************************
697
+ MATRICE DES POSSIBLES, AUTOMATE
698
+ **************************************************************************************/
699
+ /**
700
+ * Get the HipHop machine.
701
+ * @memberof Websocketserver
702
+ * @function
703
+ * @inner
704
+ * @returns {automatePossibleMachine} - the HipHop machine
705
+ */
706
+ function getAutomatePossible() {
707
+ if (automatePossibleMachine !== undefined) {
708
+ return automatePossibleMachine;
709
+ } else {
710
+ console.log("ERR: websocketserverSini.js: getAutomatePossible: automatePossibleMachine undefined")
711
+ }
712
+ }
713
+ _getAutomatePossible = getAutomatePossible;
714
+
715
+ /**
716
+ * React on the orchestration
717
+ * @memberof Websocketserver
718
+ * @param {*} signal
719
+ * @returns {boolean} true if no problem
720
+ */
721
+ function reactAutomatePossible(signal) {
722
+
723
+ if (debug) console.log("reactAutomatePossible 1:", signal, automatePossibleMachine);
724
+ if (debug) console.log("reactAutomatePossible 1:", signal);
725
+
726
+ if (automatePossibleMachine !== undefined) {
727
+ try {
728
+ if (debug) console.log("INFO: webSocketServer.js: reactAutomatePossible 2:", signal);
729
+ automatePossibleMachine.react(signal);
730
+ } catch (err) {
731
+ console.log("ERROR: webSocketServer.js: reactAutomatePossible: Error on react for signal:", signal, err.toString());
732
+ var msg = {
733
+ type: "alertBlocklySkini",
734
+ text: err.toString()
735
+ }
736
+ //throw err;
737
+ serv.broadcast(JSON.stringify(msg));
738
+ return false;
739
+ }
740
+ return true;
741
+ } else {
742
+ if (warnings) console.log("WARN: websocketserver: reactAutomatePossible: automate undefined");
743
+ return false;
744
+ }
745
+ }
746
+
747
+ function inputAutomatePossible(signal) {
748
+ if (automatePossibleMachine !== undefined) {
749
+ try {
750
+ if (debug) console.log("INFO: webSocketServer.js: inputAutomatePossible:", signal);
751
+ automatePossibleMachine.input(signal);
752
+ } catch (err) {
753
+ console.log("ERROR: webSocketServer.js: inputAutomatePossible: Error on react:", signal, err.toString());
754
+ serv.broadcast(JSON.stringify({
755
+ type: "alertBlocklySkini",
756
+ text: err.toString()
757
+ }));
758
+ return false;
759
+ }
760
+ return true;
761
+ } else {
762
+ if (warnings) console.log("WARN: websocketserver: inputAutomatePossible: automate undefined");
763
+ return false;
764
+ }
765
+ }
766
+
767
+ // Pas au bon endroit, musicien pas en place dans cette version
768
+ //if (par.avecMusicien !== undefined && par.decalageFIFOavecMusicien !== undefined) {p.
769
+ //DAW.setAvecMusicien(par.avecMusicien, par.decalageFIFOavecMusicien);
770
+ //}
771
+
772
+ /**
773
+ * Initialisation if the "matrice des possibles" which is a two dimensional array for
774
+ * the groups of pattern according to the groups of users. It represents the status
775
+ * of the orchestration.
776
+ * @memberof Websocketserver
777
+ * @param {number} DAWState Informs if an orchestration has been selected or not
778
+ */
779
+ function initMatriceDesPossibles(DAWState) {
780
+
781
+ if (warnings) console.log("WARNING: websocketserver:initMatriceDesPossibles:DAWState:", DAWState);
782
+
783
+ if (DAWState == 0) {
784
+ if (warnings) console.log("WARNING: websocketserver:initMatriceDesPossibles:DAWState à 0");
785
+ return;
786
+ }
787
+ nbeDeGroupesSons = DAW.getNbeDeGroupesSons();
788
+ if(debug1) if (!Number.isInteger(nbeDeGroupesSons) || nbeDeGroupesSons <= 0 )
789
+ {
790
+ console.log("websocketServer:initMatriceDesPossibles:pb nbeDeGroupesons", nbeDeGroupesSons );
791
+ return;
792
+ }
793
+
794
+ groupesClientSon.setNbeDeGroupesSons(nbeDeGroupesSons);
795
+ if (groupesClientSon.setGroupesSon(DAWState) == -1) {
796
+ if (warnings) console.log("WARNING: websocketserveur:initMatriceDesPossibles: setGroupesSon: vide");
797
+ }
798
+ groupesClientSon.createMatriceDesPossibles();
799
+
800
+ let mesReponse = {
801
+ type: "setControlerPadSize",
802
+ nbeDeGroupesClients: par.nbeDeGroupesClients,
803
+ nbeDeGroupesSons: nbeDeGroupesSons
804
+ }
805
+
806
+ if (socketControleur !== undefined) {
807
+ if (socketControleur.readyState == 1) {
808
+ socketControleur.send(JSON.stringify(mesReponse));
809
+ } else {
810
+ if (debug) console.log("WARN: websocketserveur:initMatriceDesPossibles: socketControler status:", socketControleur.readyState);
811
+ }
812
+ }
813
+ }
814
+
815
+ /**
816
+ * Action called every quarter note of the MIDI synchro or worker synchro if no MIDI sync.
817
+ * @memberof Websocketserver
818
+ * @param {number} timerDivision
819
+ * @returns {boolean} true if the reaction of the orchestration is ok
820
+ */
821
+ function actionOnTick(timerDivision) {
822
+ if (debug) {
823
+ currentTimePrevMidi = currentTimeMidi;
824
+ currentTimeMidi = Date.now();
825
+ console.log("webSocketServeur:actionOnTick:diff de temps:", currentTimeMidi - currentTimePrevMidi, ":", timerDivision);
826
+ }
827
+
828
+ if (!reactAutomatePossible({ tick: undefined })) {
829
+ if (warnings) console.log("WARN: websocketserver: actionOnTick: automate not ready");
830
+ return false;
831
+ }
832
+
833
+ if (timerDivision == undefined) console.log("WARN:websocketServer:actionOnTick:timerDivision undefined")
834
+
835
+ DAW.playAndShiftEventDAW(timerDivision);
836
+ DAW.displayQueues();
837
+ return true;
838
+ }
839
+
840
+ /**
841
+ * Fix the timer when using the synchro from Node.js
842
+ * not used when the worker runs.
843
+ * @memberof Websocketserver
844
+ * @function
845
+ * @inner
846
+ * @param {number} timer in ms
847
+ */
848
+ function setMonTimer(timer) {
849
+ if (!par.synchoOnMidiClock) {
850
+ setTimer = setInterval(function () {
851
+ if (debug1) { let v0 = Date.now(); }
852
+ actionOnTick(timerDivision);
853
+ if (debug1) {
854
+ console.log("websocketserver: setMonTimer timer:", timer, "ms,Temps de réaction de l'automate:", Date.now() - v0, "ms");
855
+ }
856
+ }, timer);
857
+ }
858
+ }
859
+
860
+ /**
861
+ * To update the variable on the list lengths of memorySortable
862
+ * Clients need this variable when connecting. It can change during an orchestration.
863
+ * @memberof Websocketserver
864
+ * @function
865
+ * @inner
866
+ * @param {number} value length of the list of the client
867
+ */
868
+ function setPatternListLength(value) {
869
+ if (debug1) console.log("websocketserver.js : setPatternListLength : value :", value);
870
+ }
871
+ _setPatternListLength = setPatternListLength;
872
+
873
+ /*************************************************************************************
874
+ WEB SOCKET MANAGEMENT
875
+ **************************************************************************************/
876
+ serv.on('connection', function (ws) {
877
+
878
+ let messageLog = {
879
+ date: "",
880
+ source: "websocketServerSkini.js",
881
+ type: "log",
882
+ note: "",
883
+ pseudo: "",
884
+ id: ""
885
+ }
886
+
887
+ // Pour informer que l'on est bien connecté
888
+ if (debug) console.log("INFO: Web Socket Server: a connection established");
889
+ ws.send(JSON.stringify({
890
+ type: "message",
891
+ value: "Bienvenue chez Skini !"
892
+ }));
893
+
894
+ /* // Pour dire à l'ouverture au client si on est ou pas dans une scène où DAW est actif.
895
+ if (debug) console.log("Web Socket Server: DAWON:", par.DAWON);
896
+ var msg = {
897
+ msg.type: "DAWON",
898
+ msg.value: DAWON
899
+ }
900
+
901
+ msg.type = "DAWON";
902
+ msg.value = par.DAWON; // variable true, false, ou un chiffre
903
+ ws.send(JSON.stringify(msg));*/
904
+
905
+ // DONNEES DE TEMPO pour les séquenceurs.
906
+ ws.send(JSON.stringify({
907
+ type: "setConfigSequenceur",
908
+ tempsMesure: tempsMesure,
909
+ divisionMesure: divisionMesure,
910
+ nbeDeMesures: nbeDeMesures,
911
+ tempo: tempo,
912
+ canalMidi: canalMidi,
913
+ dureeDuTick: dureeDuTick
914
+ }));
915
+ ws.on('close', function () {
916
+ if (debug) console.log("Web Socket Server: Socket closed by client.");
917
+ });
918
+
919
+ ws.on('error', function (event) {
920
+ console.log("Web Socket Server: Erreur sur socket:", ws.socket, " ", event);
921
+ });
922
+
923
+ /**
924
+ * This is where the pattern (clip) descriptor becomes an element in a FIFO.
925
+ * @memberof Websocketserver
926
+ * @function
927
+ * @inner
928
+ * @param {array} clip pattern description according to the csv file.
929
+ * @param {string} signal Hiphop signal
930
+ * @param {number} leGroupe user group (web client group)
931
+ * @param {string} pseudo
932
+ * @param {number} monId
933
+ * @returns {number} waiting time
934
+ */
935
+ function pushClipDAW(clip, signal, leGroupe, pseudo, monId) {
936
+ let DAWNote = clip[0];
937
+ let DAWChannel = Math.floor(DAWNote / 127) + 1;
938
+ DAWNote = DAWNote % 127;
939
+ if (DAWChannel > 15) {
940
+ if (debug) console.log("Web Socket Server.js : pushNoteOnDAW: Nombre de canaux midi dépassé.");
941
+ return 0;
942
+ }
943
+ let nom = clip[3];
944
+ let DAWInstrument = clip[5];
945
+ let typePattern = clip[7];
946
+ let dureeClip = clip[10];
947
+ let adresseIP = clip[11];
948
+ let numeroBuffer = clip[12];
949
+ let patternLevel = clip[13];
950
+ let typeVertPattern = clip[8];
951
+
952
+ let signalComplet = { [signal]: clip[3] }; // on ajouté le nom du pattern au signal
953
+ let dureeAttente = DAW.pushEventDAW(par.busMidiDAW, DAWChannel,
954
+ DAWInstrument, DAWNote, 125, monId, pseudo, dureeClip, nom,
955
+ signalComplet, typePattern, adresseIP,
956
+ numeroBuffer, patternLevel, typeVertPattern);
957
+
958
+ // Envoi du signal vers l'automate au moment de la demande si reactOnPlay n'existe pas ou est false.
959
+ // Il y a un autre scénario dans controleAbleton.js on envoie le signal au moment ou la commande Midi part
960
+ // Ce sont deux scénarios différents, celui fonctionne mieux en termes de synchro Midi car les demandes sont réparties dans
961
+ // le temps au fur et à mesure qu'elles arrivent. Il n'y a pas de risque de react successifs au même moment du tick midi.
962
+ // Il traite les activations avant que les patterns aient été jouées.
963
+ if (par.reactOnPlay === undefined) {
964
+ reactAutomatePossible(signalComplet);
965
+ } else if (!par.reactOnPlay) {
966
+ if (debug) console.log("websocketServeur: pushClipDAW: reactOnPlay:", par.reactOnPlay, signalComplet);
967
+ reactAutomatePossible(signalComplet);
968
+ }
969
+ if (debug) console.log("Web Socket Server.js : pushClipDAW :nom ", nom, " pseudo: ", pseudo);
970
+
971
+ if (debug) console.log("Web Socket Server.js: pushClipDAW: DAWInstrument", DAWInstrument, " durée: ", dureeAttente);
972
+
973
+ return dureeAttente;
974
+ }
975
+
976
+ /**
977
+ * HipHop reaction on the orchestration and compute delay.
978
+ * @memberof Websocketserver
979
+ * @function
980
+ * @inner
981
+ * @param {string} unPseudo
982
+ * @param {number} groupe
983
+ * @param {array} pattern
984
+ * @param {number} monId
985
+ */
986
+ function playPattern(unPseudo, groupe, pattern, monId) {
987
+
988
+ if (pattern === undefined) return; // Protection si pas de selection sur le client
989
+ if (debug) console.log("Web Socket Serveur: playPattern: clipChoisi", pattern, " pour ID: ", monId);
990
+ if (debug) console.log('Websocket serveur : playPattern: demandeDeSonParPseudo : ', unPseudo, "groupe:", groupe, "pattern:", pattern[4]);
991
+ if (debug) console.log("-----webSocketServeur: playPattern: Pattern reçu:", pattern[0]);
992
+
993
+ let signal = groupesClientSon.getSignalFromGroup(pattern[9]) + "IN";
994
+
995
+ if (signal === "-1IN") {
996
+ console.log("WARN: websocketserveur: playPattern : no group declared :", groupe);
997
+ return;
998
+ }
999
+ if (debug) console.log("webSocketServeur: playPattern, signal reçu:", pattern, signal);
1000
+
1001
+ let legroupe = groupe; // groupe d'utilisateur
1002
+
1003
+ // Pour la gestion des messages qui ne sont pas des patterns, on utilise des patterns dont les
1004
+ // commandes MIDI sont négatives. Dans ce cas on émet des signaux sans faire appel au player de patterns
1005
+ // On appelle jamais pushClipAbleton avec une note négative issue de la config.
1006
+ if (pattern[0] < 0) {
1007
+ // Pour associer le nom du pattern au signal de groupe IN
1008
+ // C'est plus pour être cohérent que par besoin.
1009
+ reactAutomatePossible({ [signal]: pattern[3] });
1010
+ return;
1011
+ }
1012
+
1013
+ let dureeAttente = pushClipDAW(pattern, signal, legroupe, unPseudo, monId);
1014
+
1015
+ // DureeAttente est la somme des durées de la FIFO de l'instrument.
1016
+ if (dureeAttente === -1) {
1017
+ return; // On est dans un cas de note sans durée
1018
+ }
1019
+
1020
+ // Conversion in real waiting time
1021
+ dureeAttente = Math.floor(dureeAttente * tempoTime / 1000);
1022
+ if (debug) console.log("Web Socket Serveur: abletonStartClip:dureeAttente", dureeAttente);
1023
+ // On communique au client le temps d'attente en sec. avant d'entendre.
1024
+ ws.send(JSON.stringify({
1025
+ type: "dureeAttente",
1026
+ text: dureeAttente,
1027
+ son: pattern[3]
1028
+ }));
1029
+
1030
+ /*
1031
+ // Informe tout le monde
1032
+ var messageBroadcast = {
1033
+ soundName: pattern[3],
1034
+ soundFileName: pattern[4],
1035
+ instrument: pattern[5],
1036
+ group: pattern[9],
1037
+ pseudo: unPseudo,
1038
+ idClient: monId
1039
+ }
1040
+
1041
+ hop.broadcast('demandeDeSonParPseudo', JSON.stringify(messageBroadcast));
1042
+
1043
+ var messageInstrument = {
1044
+ instrument: pattern[5],
1045
+ attente : dureeAttente
1046
+ }
1047
+ hop.broadcast('attenteInstrument', JSON.stringify(messageInstrument));
1048
+ */
1049
+ // Log la manip
1050
+ messageLog.note = pattern[3];
1051
+ messageLog.id = ws.id;
1052
+ messageLog.pseudo = unPseudo;
1053
+ logInfoSocket(messageLog);
1054
+
1055
+ delete messageLog.note;
1056
+ delete messageLog.text;
1057
+ }
1058
+
1059
+ /**
1060
+ * Build the HipHop Programm.
1061
+ * compile is not the good terminology.
1062
+ * It should be buildMachine, or makeMachine...
1063
+ *
1064
+ * @memberof Websocketserver
1065
+ * @function
1066
+ * @inner
1067
+ */
1068
+ async function compileHH() {
1069
+ DAWTableReady = false;
1070
+ try {
1071
+ automatePossibleMachine = await groupesClientSon.makeOneAutomatePossibleMachine();
1072
+ } catch (err) {
1073
+ //console.log("ERR: websocketserver.js: pb makeOneAutomatePossibleMachine", err);
1074
+ console.log("\n-------------------------------------------------------------");
1075
+ console.log(`ATTENTION:
1076
+ Problem when compiling the Orchestration
1077
+ maybe an hiphop compile Error`);
1078
+ console.log("-------------------------------------------------------------");
1079
+
1080
+ serv.broadcast(JSON.stringify({
1081
+ type: "consoleBlocklySkini",
1082
+ text: "See your console, pb on compilation"
1083
+ }));
1084
+ return;
1085
+ }
1086
+
1087
+ DAW.setAutomatePossible(automatePossibleMachine);
1088
+ console.log("INFO: websocketServer: loadDAWTable: table loaded\n");
1089
+ serv.broadcast(JSON.stringify({
1090
+ type: "consoleBlocklySkini",
1091
+ text: "Orchestration loaded"
1092
+ }));
1093
+
1094
+ // Pour l'emission des commandes OSC entre l'orchestration et un jeu ou des capteurs
1095
+ if (par.gameOSCSignals) {
1096
+ gameOSC.setOrchestration(automatePossibleMachine);
1097
+ gameOSC.init();
1098
+ } else {
1099
+ // Pour fermer la socket si on change pour une pièce sans gameOSC
1100
+ gameOSC.closeSocket();
1101
+ }
1102
+
1103
+ try {
1104
+ //reactAutomatePossible( {DAWON: 1} ); // !!! en cours
1105
+ } catch (e) {
1106
+ console.log("websocketServerSkini:loadDAWTable:catch react:", e);
1107
+ }
1108
+ DAWTableReady = true;
1109
+ }
1110
+
1111
+ /**
1112
+ * Load the parameters
1113
+ * @memberof Websocketserver
1114
+ * @function
1115
+ * @inner
1116
+ */
1117
+ function loadParameters(fileName) {
1118
+ // Chargement des paramètres
1119
+ // à partir du fichier de config de la pièce
1120
+ // qui a le même nom que le fichier d'orchestration
1121
+
1122
+ if (debug1) console.log("INFO: loadBlocks: parametersFile: ", fileName);
1123
+ // Attention decache n'utilise pas le même path que parametersFile
1124
+ decacheParameters = "../" + fileName;
1125
+
1126
+ try {
1127
+ if (fs.existsSync(fileName)) {
1128
+ let extension = fileName.slice(-3);
1129
+ if (extension !== ".js") {
1130
+ console.log("ERR: Not an js file:", fileName);
1131
+ ws.send(JSON.stringify({
1132
+ type: "alertBlocklySkini",
1133
+ text: "Parameter not an JavaScript file " + fileName
1134
+ }));
1135
+ return;
1136
+ }
1137
+ } else {
1138
+ console.log("ERR: No parameter file:", fileName);
1139
+ ws.send(JSON.stringify({
1140
+ type: "alertBlocklySkini",
1141
+ text: "The parameter file " + fileName + " is not updated, don't run the program before modifying it."
1142
+ }));
1143
+ // Initialise un fichier de parametres par défaut
1144
+ // C'est à dire en copie un dans un parametersFile temporaire
1145
+ try {
1146
+ fs.copyFileSync(origine, fileName);
1147
+ } catch (err) {
1148
+ console.log("websocketServer: Pb ecriture: ", fileName, err);
1149
+ }
1150
+ return;
1151
+ }
1152
+ } catch (err) {
1153
+ console.log("ERR: Pb Reading parameter file:", fileName, err);
1154
+ ws.send(JSON.stringify({
1155
+ type: "alertBlocklySkini",
1156
+ text: "Pb Reading parameter file " + fileName
1157
+ }));
1158
+ return;
1159
+ }
1160
+
1161
+ // Solution proposée par Qodo pour recharger proprement
1162
+ try {
1163
+ const absPathParam = require.resolve(decacheParameters);
1164
+ try { delete require.cache[absPathParam]; } catch (_) {}
1165
+ try { decache(absPathParam); } catch (_) {}
1166
+ par = require(absPathParam);
1167
+ } catch (e) {
1168
+ console.log("ERR: websocketserver: loadParameters: failed to (re)load parameters:", decacheParameters, e.toString());
1169
+ ws.send(JSON.stringify({
1170
+ type: "alertBlocklySkini",
1171
+ text: "Error reloading parameters " + decacheParameters
1172
+ }));
1173
+ return;
1174
+ }
1175
+
1176
+ // Le fait de faire un require ici, annule la référence de par dans
1177
+ // les autres modules. Il faut faire un reload dans tous les modules.
1178
+ if (debug) console.log("websocketserveur.js: loadbloaks; après require de dechacheParameters:", par);
1179
+ reloadParameters(par);
1180
+
1181
+ // On initialise les interfaces Midi ou via OSC et Synchro quand les paramètres sont chargés.
1182
+ midimix.midimix(automatePossibleMachine);
1183
+ ws.send(JSON.stringify({
1184
+ type: "consoleBlocklySkini",
1185
+ text: "Orchestration loaded"
1186
+ }));
1187
+ }
1188
+
1189
+ /**
1190
+ * Process the websocket messages. The protocols are here.
1191
+ * @memberof Websocketserver
1192
+ * @function
1193
+ * @inner
1194
+ */
1195
+ ws.on('message', async function (message) {
1196
+ if (debug) console.log('received: %s', message);
1197
+ var msgRecu = JSON.parse(message);
1198
+
1199
+ // Pour le Log des messages reçus
1200
+ messageLog.date = getDateTime();
1201
+ messageLog.type = msgRecu.type;
1202
+
1203
+ switch (msgRecu.type) {
1204
+
1205
+ case "checkSession":
1206
+ DAW.displaySession();
1207
+ console.log("------------------------------------");
1208
+ console.log(par);
1209
+ break;
1210
+
1211
+ case "cleanQueues":
1212
+ DAW.cleanQueues();
1213
+ break;
1214
+
1215
+ case "clientPseudo":
1216
+ if (debug) console.log("websocketserver: clientPseudo", msgRecu);
1217
+ compScore.putInClientsEnCours(msgRecu.pseudo, ws.id, msgRecu.groupe, clientsEnCours);
1218
+ if (debug) console.log("websocketserver: clientPseudo : clientsEnCours:", clientsEnCours);
1219
+ break;
1220
+
1221
+ case "clientScenes": // Pas trés utile, c'est dans scenes.js
1222
+ ws.id = msgRecu.value;
1223
+ break;
1224
+
1225
+ case "closeSpectateur":
1226
+ if (debug) console.log("Web Socket Server: received message system : [%s]",
1227
+ msgRecu.text, " from Client ID:", ws.id);
1228
+ //DAW.libereDansTableDesCommandes(ws.id);
1229
+
1230
+ messageLog.id = ws.id;
1231
+ messageLog.pseudo = msgRecu.pseudo;
1232
+ delete messageLog.text;
1233
+ delete messageLog.note;
1234
+ logInfoSocket(messageLog);
1235
+
1236
+ ws.close();
1237
+ break;
1238
+
1239
+ case "combienDeSpectateurs":
1240
+ var value = DAW.nbeDeSpectateursConnectes();
1241
+ ws.send(JSON.stringify({ type: "nbeDeSpectateurs", value }));
1242
+ break;
1243
+
1244
+ case "configuration": // Message converti en signal pour l'automate central
1245
+ if (debug) console.log("Web Socket Server: received message configuration : [%s]",
1246
+ msgRecu.text, " from Client ID:", ws.id);
1247
+ //machineServeur.inputAndReact( msgRecu.text, msgRecu.extra ); // ENVOI DU SIGNAL VERS HIPHOP
1248
+ break;
1249
+
1250
+ case "configDAWMidiNote":
1251
+ if (debug1) console.log("websocketServer: configDAWMidiNote:", msgRecu);
1252
+ //Rappel des paramètres: par.busMidiDAW, DAWChannel, DAWNote, velocity
1253
+ oscMidiLocal.sendNoteOn(msgRecu.bus, msgRecu.channel, msgRecu.note, 120);
1254
+ break;
1255
+
1256
+ case "configDAWCC":
1257
+ if (debug1) console.log("websocketServer: configDAWCC:", msgRecu);
1258
+ oscMidiLocal.sendControlChange(msgRecu.bus, msgRecu.channel, msgRecu.CC, msgRecu.CCValue);
1259
+ break;
1260
+
1261
+ case "compileHH":
1262
+ try {
1263
+ compileHH();
1264
+ } catch (err) {
1265
+ console.error(err);
1266
+ }
1267
+ break;
1268
+
1269
+ case "compileHHEditionFile":
1270
+ if (debug1) console.log("websocketServer: compileHHEditionFile:", msgRecu,
1271
+ ":", piecePath + HipHopSrc, ":", targetHH);
1272
+
1273
+ try {
1274
+ // Etape de compilation vers targetHH
1275
+ let fragment = compile(piecePath + HipHopSrc, {});
1276
+ await fragment.output(targetHH);
1277
+ await fragment.sourcemap(targetHH);
1278
+ } catch (err) {
1279
+ console.log("ERR: Erreur dans la compilation du programme hiphop")
1280
+ console.log("websocketServerSkini:compileHHEditionFile:fragment:", err);
1281
+ break; // Pas la peine d'aller plus loin dans la compilation
1282
+ }
1283
+
1284
+ // Fabrique la machine à partir du programme généré en dur dans targetHH.
1285
+ // On utilise le même procédé que pour les programmes générés par Blockly
1286
+ try {
1287
+ compileHH();
1288
+ } catch (err) {
1289
+ console.log("websocketServerSkini:compileHHEditionFile:compileHH:", err);
1290
+ }
1291
+ break;
1292
+
1293
+ case "createSession":
1294
+ if (msgRecu.fileName === '') {
1295
+ console.log("WARN: No descriptor file name");
1296
+ break;
1297
+ }
1298
+ if (debug1) console.log("createSession:", sessionPath + msgRecu.fileName + ".csv");
1299
+ sessionFile = sessionPath + msgRecu.fileName + ".csv";
1300
+ // Initialise un fichier de parametres par défaut
1301
+ // C'est à dire en copie un dans un parametersFile temporaire
1302
+ try {
1303
+ fs.copyFileSync(defaultSession, sessionFile);
1304
+ } catch (err) {
1305
+ console.log("websocketServer: Pb ecriture: ", sessionFile, err);
1306
+ }
1307
+ try {
1308
+ DAW.loadDAWTable(sessionFile);
1309
+ } catch (err) {
1310
+ console.log("websocketServer: erreur de chargement:createSession: ", sessionFile, err);
1311
+ }
1312
+
1313
+ ws.send(JSON.stringify({ type: "consoleBlocklySkini", text: "session loaded: " + sessionFile }));
1314
+ break;
1315
+
1316
+ case "DAWPseudo":
1317
+ break;
1318
+
1319
+ case "DAWSelectListClips":
1320
+ let listAllClips = new Array(); // Devient alors utilisable pour les controles dans DAW
1321
+ listAllClips = DAW.getListClips(msgRecu.niveaux);
1322
+ //if (debug) console.log("Web Socket Serveur: niveaux pour recherche ", msgRecu.niveaux, listClips);
1323
+ ws.send(JSON.stringify({
1324
+ type: "listClips",
1325
+ listAllClips
1326
+ }));
1327
+ break;
1328
+
1329
+ /* case "DAWStartClip":
1330
+ pseudo = msgRecu.pseudo;
1331
+ if (msgRecu.clipChoisi === undefined ) break; // Protection si pas de selection sur le client
1332
+ if (debug) console.log("Web Socket Serveur: DAWStartClip: clipChoisi", msgRecu.clipChoisi, " pour ID: ", msgRecu.id);
1333
+ if (debug) console.log('Websocket serveur : DAWStartClip: demandeDeSonParPseudo : ', msgRecu.pseudo, msgRecu.clipChoisi[4]);
1334
+
1335
+ // !! Attention pas à jour dans les paramètre de pushClipDAW
1336
+ var dureeAttente = pushClipDAW(msgRecu.clipChoisi);
1337
+ if ( dureeAttente === -1) {
1338
+ break; // On est dans un cas de note répétée
1339
+ }
1340
+
1341
+ var msg = {
1342
+ type: "dureeAttente",
1343
+ text: dureeAttente,
1344
+ son: msgRecu.clipChoisi[3]
1345
+ }
1346
+ // On communique au client le temps d'attente avant d'entendre.
1347
+ ws.send(JSON.stringify(msg));
1348
+
1349
+ oscMidiLocal.sendProcessing( "/DAWPseudo", msgRecu.pseudo );
1350
+
1351
+ // Informe tout le monde
1352
+ var messageBroadcast = msgRecu.pseudo + " a choisi " + msgRecu.clipChoisi[3];
1353
+ msg.type = "demandeDeSonParPseudo";
1354
+ msg.text = messageBroadcast;
1355
+ delete msg.listClips;
1356
+ serv.broadcast(JSON.stringify(msg));
1357
+
1358
+ // Log la manip
1359
+ messageLog.note = msgRecu.clipChoisi[3];
1360
+ messageLog.id = ws.id;
1361
+ messageLog.pseudo = msgRecu.pseudo;
1362
+ messageLog.text = msg.text;
1363
+ logInfoSocket(messageLog);
1364
+ break;*/
1365
+
1366
+ case "dureeDuTickHorlogeMidi": // Reçu de Processing chaque 24 pulses de l'horloge Midi (une noire)
1367
+ receivedTickFromSynchro();
1368
+ break;
1369
+
1370
+ case "getDelayInstrument":
1371
+ if (debug) console.log("Web Socket Serveur: getDelayInstrument", msgRecu.clipChoisi, " pour ID: ", ws.id);
1372
+ let msgDelay = {
1373
+ type: "delaiInstrument"
1374
+ }
1375
+
1376
+ if (msgRecu.clipChoisi === undefined) {
1377
+ msgDelay.text = -1;
1378
+ msgDelay.son = "pattern undefined";
1379
+ ws.send(JSON.stringify(msgDelay));
1380
+ break;
1381
+ } else {
1382
+ let dureeAttente = DAW.getDelayEventDAW(msgRecu.clipChoisi[5]);
1383
+ if (dureeAttente === -1) {
1384
+ break; // On est dans un cas de note répétée
1385
+ }
1386
+ msgDelay.text = dureeAttente;
1387
+ }
1388
+ // On communique au client le délai avant d'entendre.
1389
+ msgDelay.son = msgRecu.clipChoisi[3];
1390
+ ws.send(JSON.stringify(msgDelay));
1391
+ break;
1392
+
1393
+ case "getGroupesClientLength":
1394
+ var longueurs = groupesClientSon.getGroupesClientLength();
1395
+
1396
+ if (debug) console.log("websocketserver: getGroupesClientLength: ", longueurs);
1397
+ ws.send(JSON.stringify({ type: "groupesClientLength", longueurs }));
1398
+
1399
+ break;
1400
+
1401
+ case "getNombreDePatternsPossibleEnListe": // Pour l'initialisation de memorySortable
1402
+ let nombreDePatternsPossible = groupesClientSon.getNombreDePatternsPossibleEnListe();
1403
+ ws.send(JSON.stringify({
1404
+ type: "nombreDePatternsPossibleEnListe",
1405
+ nombreDePatternsPossible: nombreDePatternsPossible
1406
+ }));
1407
+ break;
1408
+
1409
+ case "getPatternGroups":
1410
+ // It happends when calling this function before loading a piece
1411
+ if (par === undefined) break;
1412
+
1413
+ if (DAWStatus !== undefined) {
1414
+ ws.send(JSON.stringify({
1415
+ type: "setPatternGroups",
1416
+ value: par.groupesDesSons
1417
+ }));
1418
+ } else {
1419
+ if (warnings) console.log("WARN: websocketserver: getPatternGroups: DAWStatus not yet defined");
1420
+ }
1421
+ break;
1422
+
1423
+ case "loadBlocks":
1424
+ //
1425
+ // Il manque ici un ménage de ce qui peut se trouver déjà chargé !!!
1426
+ //
1427
+ if(debug1) console.log("INFO: loadBlocks: msgRecu.fileName: ", msgRecu.fileName);
1428
+ if (msgRecu.fileName === '') {
1429
+ console.log("WARN: No orchestration");
1430
+ break;
1431
+ }
1432
+
1433
+ let orchestrationFile = piecePath + msgRecu.fileName;
1434
+
1435
+ try {
1436
+ if (fs.existsSync(orchestrationFile)) {
1437
+ let extension = orchestrationFile.slice(-4);
1438
+ if (extension !== ".xml") {
1439
+ console.log("ERR: Not an xml file:", orchestrationFile);
1440
+ ws.send(JSON.stringify({
1441
+ type: "consoleBlocklySkini",
1442
+ text: "Not an XML file " + orchestrationFile
1443
+ }));
1444
+ break;
1445
+ }
1446
+ } else {
1447
+ console.log("ERR: No orchestration file:", orchestrationFile);
1448
+ ws.send(JSON.stringify({
1449
+ type: "consoleBlocklySkini",
1450
+ text: "No orchestration file " + orchestrationFile
1451
+ }));
1452
+ break;
1453
+ }
1454
+ } catch (err) {
1455
+ console.log("ERR: No orchestration file:", orchestrationFile, err);
1456
+ ws.send(JSON.stringify({
1457
+ type: "consoleBlocklySkini",
1458
+ text: "Error reading orchestration file " + orchestrationFile
1459
+ }));
1460
+ break;
1461
+ }
1462
+
1463
+ fs.readFile(orchestrationFile, 'utf8', (err, data) => {
1464
+ if (err) {
1465
+ console.error(err);
1466
+ return;
1467
+ }
1468
+ if (debug1) console.log("INFO: loadBlocks: orchestrationFile:", orchestrationFile);
1469
+ ws.send(JSON.stringify({
1470
+ type: "blocksLoaded",
1471
+ data
1472
+ }));
1473
+ });
1474
+
1475
+ // Chargement des paramètres
1476
+ // à partir du fichier de config de la pièce
1477
+ // qui a le même nom que le fichier d'orchestration avec un extension js
1478
+ // au lieu de xml
1479
+
1480
+ // Entre autre pour la mise à jour des parametres dans le browser
1481
+ parametersFileGlobal = msgRecu.fileName.slice(0, -4) + ".js";
1482
+ parametersFile = sessionPath + msgRecu.fileName;
1483
+ // Construction du nom à partir du fichier xml
1484
+ parametersFile = parametersFile.slice(0, -4) + ".js";
1485
+ if(debug1) console.log("INFO: loadBlocks: parametersFile: ***** avant loadParameters", parametersFile);
1486
+ loadParameters(parametersFile);
1487
+ break;
1488
+
1489
+ case "loadHHFile":
1490
+ if (msgRecu.fileName === '') {
1491
+ console.log("WARN: No Hiphop file selected");
1492
+ break;
1493
+ }
1494
+ if (debug1) console.log("INFO: loadHHFile:", msgRecu.fileName);
1495
+ HipHopSrc = msgRecu.fileName;
1496
+
1497
+ let extension = HipHopSrc.slice(-6);
1498
+ if (extension !== ".hh.js") {
1499
+ console.log("ERR: Not an HipHop js file:", HipHopSrc);
1500
+ ws.send(JSON.stringify({
1501
+ type: "alertBlocklySkini",
1502
+ text: "You try to load a not HipHop JavaScript file : " + HipHopSrc
1503
+ }));
1504
+ }
1505
+
1506
+ // try {
1507
+ // compileHH();
1508
+ // } catch (err) {
1509
+ // console.log("websocketServerSkini:loadHHFile:catch:", err);
1510
+ // }
1511
+
1512
+ // Chargement des paramètres
1513
+ // à partir du fichier de config de la pièce
1514
+ // qui a le même nom que le fichier d'orchestration avec un extension js
1515
+ // au lieu de hh.js
1516
+ // Entre autre pour la mise à jour des parametres dans le browser
1517
+ parametersFileGlobal = msgRecu.fileName.slice(0, -6) + ".js";
1518
+ parametersFile = sessionPath + msgRecu.fileName;
1519
+ // Construction du nom à partir du fichier hh.js
1520
+ parametersFile = parametersFile.slice(0, -6) + ".js";
1521
+ loadParameters(parametersFile);
1522
+ break;
1523
+
1524
+ case "loadSession": // Pour les descripteurs de clips
1525
+ if (msgRecu.fileName === '') {
1526
+ console.log("WARN: No descriptor selected");
1527
+ break;
1528
+ }
1529
+
1530
+ if (debug1) console.log("INFO: loadSession:", sessionPath + msgRecu.fileName);
1531
+ sessionFile = sessionPath + msgRecu.fileName;
1532
+
1533
+ try {
1534
+ if (fs.existsSync(sessionFile)) {
1535
+ let extension = sessionFile.slice(-4);
1536
+ if (extension !== ".csv") {
1537
+ console.log("ERR: Not an csv file:", sessionFile);
1538
+ ws.send(JSON.stringify({
1539
+ type: "alertBlocklySkini",
1540
+ text: "Descriptor not a CSV file " + sessionFile
1541
+ }));
1542
+ break;
1543
+ }
1544
+ } else {
1545
+ console.log("ERR: No session file:", sessionFile);
1546
+ ws.send(JSON.stringify({
1547
+ type: "alertBlocklySkini",
1548
+ text: "No session file " + sessionFile
1549
+ }));
1550
+ break;
1551
+ }
1552
+ } catch (err) {
1553
+ console.log("ERR: Pb Reading session file:", sessionFile, err);
1554
+ ws.send(JSON.stringify({
1555
+ type: "alertBlocklySkini",
1556
+ text: "Pb Reading session file " + sessionPath
1557
+ }));
1558
+ break;
1559
+ }
1560
+
1561
+ try {
1562
+ DAW.loadDAWTable(sessionPath + msgRecu.fileName);
1563
+ } catch (err) {
1564
+ console.log("websocketServer: erreur de chargement:loadSession: ", sessionFile, err);
1565
+ }
1566
+ ws.send(JSON.stringify({
1567
+ type: "consoleBlocklySkini",
1568
+ text: "session loaded: " + msgRecu.fileName
1569
+ }));
1570
+ break;
1571
+
1572
+ case "putInMatriceDesPossibles":
1573
+ if (debug) console.log("websocketserver:putInMatriceDesPossibles:", msgRecu);
1574
+ groupesClientSon.setInMatriceDesPossibles(msgRecu.clients, msgRecu.sons, msgRecu.status); // groupe de clients, n° du groupe de sons, booleen
1575
+ groupeName = groupesClientSon.getNameGroupeSons(msgRecu.sons);
1576
+
1577
+ if (debug) groupesClientSon.displayMatriceDesPossibles();
1578
+
1579
+ serv.broadcast(JSON.stringify({
1580
+ type: "groupeClientStatus",
1581
+ groupeClient: msgRecu.clients, // Pour identifier le groupe de clients
1582
+ groupeName,
1583
+ status: msgRecu.status
1584
+ }));
1585
+ break;
1586
+
1587
+ case "ResetMatriceDesPossibles":
1588
+ if (debug1) console.log("websocketserver: ResetMatriceDesPossibles");
1589
+ groupesClientSon.resetMatriceDesPossibles();
1590
+ groupeName = "";
1591
+ serv.broadcast(JSON.stringify({
1592
+ type: "groupeClientStatus",
1593
+ groupeClient: 255,
1594
+ groupeName,
1595
+ status: false
1596
+ }));
1597
+ break;
1598
+
1599
+ case "saveBlocklyGeneratedFile":
1600
+ if (msgRecu.fileName === '') {
1601
+ console.log("WARN: No Orchestration");
1602
+ break;
1603
+ }
1604
+
1605
+ // Si on crée une orchestration à partir de rien le fichier de paramètre n'existe pas
1606
+ // On en crée un par défaut.
1607
+ parametersFileGlobal = msgRecu.fileName + ".js";
1608
+ try {
1609
+ if (!fs.existsSync(sessionPath + parametersFileGlobal)) {
1610
+ console.log("ERR: No parameter file:", parametersFileGlobal);
1611
+ ws.send(JSON.stringify({
1612
+ type: "alertBlocklySkini",
1613
+ text: "The parameter file " + parametersFileGlobal + " is created, don't run the program before modifying it."
1614
+ }));
1615
+ // Initialise un fichier de parametres par défaut
1616
+ try {
1617
+ fs.copyFileSync(origine, sessionPath + parametersFileGlobal);
1618
+ // On recharge les nouveaux paramètres avant la compilation.
1619
+ // Attention decache n'utilise pas le même path que parametersFile
1620
+ decacheParameters = "../" + sessionPath + parametersFileGlobal;
1621
+ decache(decacheParameters);
1622
+ par = require(decacheParameters);
1623
+ reloadParameters(par);
1624
+ } catch (err) {
1625
+ console.log("websocketServer: Pb ecriture: ", parametersFileGlobal, err.toString());
1626
+ break;
1627
+ }
1628
+ } else {
1629
+ if (debug) console.log("websocketserveur.js: saveBlocklyGeneratedFile: si OK:", par.groupesDesSons);
1630
+ }
1631
+ } catch (err) {
1632
+ console.log("ERR: Pb creating parameter file:", parametersFileGlobal, err.toString());
1633
+ ws.send(JSON.stringify({
1634
+ type: "alertBlocklySkini",
1635
+ text: "Pb creating parameter file " + parametersFileGlobal
1636
+ }));
1637
+ break;
1638
+ }
1639
+
1640
+ // Ecrit le programme HH pour création de la machine.
1641
+ // Le programme créé par blockly est dans msgRecu.text
1642
+ fs.writeFile(generatedDir + defaultOrchestrationNameHH, msgRecu.text, function (err) {
1643
+ if (err) {
1644
+ ws.send(JSON.stringify({
1645
+ type: "alertBlocklySkini",
1646
+ text: err.toString()
1647
+ }));
1648
+ return console.log(err);
1649
+ }
1650
+ if (debug1) console.log("INFO: websocketServer:", generatedDir + defaultOrchestrationNameHH, " written");
1651
+ });
1652
+
1653
+ // Ecrit le fichier XML Blockly
1654
+ fs.writeFile(piecePath + msgRecu.fileName + ".xml", msgRecu.xmlBlockly, async function (err) {
1655
+ if (err) {
1656
+ return console.log(err.toString());
1657
+ }
1658
+ console.log("INFO: websocketServer:", msgRecu.fileName + ".xml", " written");
1659
+ ws.send(JSON.stringify({
1660
+ type: "consoleBlocklySkini",
1661
+ text: msgRecu.fileName + ".xml written"
1662
+ }));
1663
+
1664
+ // Pour développement
1665
+ if(debug) console.log("websocketServerSkini:saveBlocklyGeneratedFile:", msgRecu.text);
1666
+
1667
+ try {
1668
+ // Etape de compilation vers targetHH, même principe que
1669
+ // pour "compileHHEditionFile"
1670
+ let fragmentBlocky = compile(generatedDir + defaultOrchestrationNameHH, {});
1671
+ await fragmentBlocky.output(targetHH);
1672
+ await fragmentBlocky.sourcemap(targetHH);
1673
+ } catch (err) {
1674
+ console.log("ERR: Erreur dans la compilation du programme hiphop")
1675
+ console.log("websocketServerSkini:saveBlocklyGeneratedFile:fragment:", err);
1676
+ return; // Pas la peine d'aller plus loin dans la compilation
1677
+ }
1678
+
1679
+ // Création de la machine HH de l'orchestration
1680
+ try {
1681
+ compileHH();
1682
+ } catch (err) {
1683
+ console.log("websocketServerSkini:saveBlocklyGeneratedFile:catch:", err.toString());
1684
+ }
1685
+ });
1686
+ break;
1687
+
1688
+ case "saveSessionAs":
1689
+ if (debug1) console.log("save descriptors as: ", msgRecu.fileName);
1690
+ try {
1691
+ fs.copyFileSync(sessionFile, sessionPath + msgRecu.fileName);
1692
+ } catch (err) {
1693
+ console.log("websocketServer: Pb ecriture save descriptors as: ", msgRecu.fileName, err);
1694
+ }
1695
+ break;
1696
+
1697
+ case "selectAllClips":
1698
+ var listClips = DAW.getAllClips(msgRecu.groupe, groupesClientSon.matriceDesPossibles);
1699
+ if (listClips !== -1) {
1700
+ if (debug) console.log("Web Socket Serveur: selectAllClips for id:", ws.id, "groupe:", msgRecu.groupe, " premier pattern:", listClips[0]);
1701
+ ws.send(JSON.stringify({
1702
+ type: "listClips",
1703
+ listClips
1704
+ }));
1705
+ }
1706
+ break;
1707
+
1708
+ case "sendOSC":
1709
+ oscMidiLocal.sendOSCRasp(msgRecu.message, msgRecu.value1,
1710
+ par.raspOSCPort, msgRecu.IpAddress);
1711
+ break;
1712
+
1713
+ case "sendPatternSequence":
1714
+ var patternSequence = msgRecu.patternSequence;
1715
+ if (debug) console.log("websocketserver: reçu : sendPatternSequence", patternSequence, msgRecu.pseudo);
1716
+
1717
+ // Pour définir la façon dont sera calculé le score pour cette séquence de patterns
1718
+ computeScorePolicy = groupesClientSon.getComputeScorePolicy();
1719
+ computeScoreClass = groupesClientSon.getComputeScoreClass();
1720
+ if (debug) console.log("websocketserver: reçu : sendPatternSequence: computeScorePolicy, computeScoreClass:", computeScorePolicy, computeScoreClass);
1721
+
1722
+ let maPreSequence = compScore.getPreSequence(msgRecu.pseudo, clientsEnCours); //Une liste d'index (notes Skini midi)
1723
+ if (debug) console.log("websocketserver: reçu : sendPatternSequence", patternSequence, msgRecu.pseudo, maPreSequence);
1724
+
1725
+ let monScore = compScore.evaluateSequenceOfPatterns(patternSequence, maPreSequence, computeScorePolicy, computeScoreClass);
1726
+
1727
+ // Met à jour la mémorisation des listes des index de pattern associée au pseudo pour
1728
+ // le calcul du score.
1729
+ compScore.setPreSequence(msgRecu.pseudo, patternSequence, clientsEnCours);
1730
+
1731
+ //Mise à jour du score total en fonction du pseudo
1732
+ let scoreTotal = compScore.updateScore(msgRecu.pseudo, monScore, clientsEnCours);
1733
+
1734
+ for (let i = 0; i < patternSequence.length; i++) {
1735
+ let pattern = DAW.getPatternFromNote(patternSequence[i]);
1736
+ if (pattern === undefined) {
1737
+ if (warnings) console.log("WARN: websocketserver: sendPatternSequence: pattern undefined");
1738
+ ws.send(JSON.stringify({
1739
+ type: "patternSequenceAck",
1740
+ value: false
1741
+ }));
1742
+ }
1743
+ if (debug) console.log("websocketserver: sendPatternSequence: pattern: ", patternSequence[i], pattern);
1744
+ playPattern(msgRecu.pseudo, msgRecu.groupe, pattern, msgRecu.idClient);
1745
+ }
1746
+
1747
+ // On a besoin d'un acknowledge car on pourrait perdre des commandes du client (?? en TCP)
1748
+ // On envoie le score pour la séquence choisie
1749
+ ws.send(JSON.stringify({
1750
+ type: "patternSequenceAck",
1751
+ score: scoreTotal,
1752
+ value: true
1753
+ }));
1754
+ // On passe par groupeClientSon pour informer l'orchestration
1755
+ // Il n'y a pas de lien depuis l'orchestration vers websocketServer.js
1756
+ // (Il y en a dans l'autre sens via des react())
1757
+ groupesClientSon.setClientsEncours(clientsEnCours);
1758
+ break;
1759
+
1760
+ case "setAllMatriceDesPossibles":
1761
+ if (debug1) console.log("websocketserver: setAllMatriceDesPossibles");
1762
+ groupesClientSon.setMatriceDesPossibles();
1763
+ groupeName = "";
1764
+ serv.broadcast(JSON.stringify({
1765
+ type: "groupeClientStatus",
1766
+ groupeClient: 255,
1767
+ groupeName,
1768
+ status: true
1769
+ }));
1770
+ break;
1771
+
1772
+ // DAWON est le SIGNAL d'activation ou désactivation de l'orchestration
1773
+ // DAWStatus est une VARIABLE qui permet de savoir quelle est l'orchestration choisie
1774
+ // (Dans cette version on n'utilise qu'une seule orchestration
1775
+ // mais dans une première version DAWStatus pouvait avoir des valeurs de 1 à 3.)
1776
+ // DAWStatus = 0 signifie pas d'orchestration en cours.
1777
+ case "setDAWON":
1778
+ // msgRecu.value > 0 => DAW Active
1779
+ DAWStatus = msgRecu.value;
1780
+ if (DAWTableReady) {
1781
+ if (debug) console.log("websocketServer:setDAWON:", DAWStatus);
1782
+ DAW.cleanQueues();
1783
+ serv.broadcast(JSON.stringify({
1784
+ type: "DAWStatus",
1785
+ value: msgRecu.value
1786
+ }));
1787
+ initMatriceDesPossibles(DAWStatus);
1788
+ // Pour être en phase avec la création du pad controleur
1789
+ groupesClientSon.resetMatriceDesPossibles();
1790
+ } else {
1791
+ if (warnings) console.log("WARNING: Table des commandes DAW pas encore chargée: ", DAWStatus);
1792
+ ws.send(JSON.stringify({
1793
+ type: "DAWTableNotReady",
1794
+ text: "Table des commandes DAW pas encore chargée"
1795
+ }));
1796
+ }
1797
+ break;
1798
+
1799
+ case "startAutomate": // Lance l'automate orchestrateur de la matrice des possibles
1800
+ if (par.timer !== undefined) {
1801
+ timerSynchro = par.timer;
1802
+ }
1803
+
1804
+ if (DAWTableReady) {
1805
+ if (debug1) console.log("INFO: webSocketServeur:startAutomate: DAWstatus:", DAWStatus);
1806
+ reactAutomatePossible({ start: undefined });
1807
+
1808
+ // S'il n'y a pas de synchro Midi ni Link on lance un worker
1809
+ if (!par.synchoOnMidiClock && !par.synchroLink && par.synchroSkini) {
1810
+ //setMonTimer(timerSynchro); // Pour un timer dans le thread principal, pas utile avec les workers
1811
+ if (debug1) console.log("websocketserver: startAutomate:worker synchro");
1812
+ workerSynchroInit('./serveur/workerSynchro.mjs', timerSynchro); // Avec un worker
1813
+ }
1814
+
1815
+ if (par.sensorOSC) {
1816
+ if (debug1) console.log("INFO: webSocketServeur: With Interface Z sensors");
1817
+ workerInterfaceZInit('./serveur/workerInterfaceZ.mjs',
1818
+ ipConfig.serverIPAddress,
1819
+ ipConfig.interfaceZIPaddress,
1820
+ ipConfig.portOSCFromInterfaceZData,
1821
+ ipConfig.portOSCFromInterfaceZMidi,
1822
+ ipConfig.portOSCFromInterfaceZMiniWi,
1823
+ ipConfig.portOSCToInterfaceZ,
1824
+ par.tempoSensorsInit,
1825
+ par.sensorsSensibilities);
1826
+ } else {
1827
+ if (debug) console.log("INFO: webSocketServeur: stopInterfaceZ", workerInterfaceZ);
1828
+ if (workerInterfaceZ !== undefined) {
1829
+ workerInterfaceZ.postMessage(["stopInterfaceZ"]);
1830
+ }
1831
+ }
1832
+ }
1833
+
1834
+ compScore.resetClientEnCours(clientsEnCours);
1835
+ groupesClientSon.setClientsEncours(clientsEnCours);
1836
+
1837
+ // Sinon n'est envoyé qu'au onopen de la Socket
1838
+ // nécessaire pour les couleurs sur les clients
1839
+ serv.broadcast(JSON.stringify({
1840
+ type: "setPatternGroups",
1841
+ value: par.groupesDesSons
1842
+ }));
1843
+ // Au cas où le client serait connecté avant le début de l'orchestration.
1844
+ if (debug) console.log("Web Socket Server: startAutomate DAWON:", DAWStatus);
1845
+ ws.send(JSON.stringify({
1846
+ type: "DAWON",
1847
+ value: DAWStatus
1848
+ }));
1849
+ break;
1850
+
1851
+ case "startByMidiClock":
1852
+ //compteurDivisionMesure = 0; // Remise à zero de la position dans le motif
1853
+ break;
1854
+
1855
+ case "startSpectateur": // On récupère l'ID du client
1856
+ // On autorise la configuration des patterns même sans piece chargée
1857
+ if (msgRecu.text === "configurateur") {
1858
+ if (debug1) console.log("INFO: webSocketServeur: startSpectateur: un configurateur connecté", msgRecu.id);
1859
+ ws.send(JSON.stringify({
1860
+ type: "skiniParametres",
1861
+ descriptors: DAW.getSession(),
1862
+ value: par
1863
+ }));
1864
+ }
1865
+
1866
+ if (par === undefined) {
1867
+ console.log("WARN: A client try to connect but no piece launched");
1868
+ break;
1869
+ }
1870
+
1871
+ ws.id = msgRecu.id;
1872
+ if (debug) console.log("INFO: websocketserbeur: startSpectateur: ", msgRecu.id);
1873
+
1874
+ // On ne permet donc qu'un seul controleur.
1875
+ // Attention: La connexion d'un deuxième contrôleur, fait perdre la première et réinitialise la matrice des possible.
1876
+ if (msgRecu.text === "controleur") {
1877
+ if (debug1) console.log("INFO: webSocketServeur: startSpectateur: un controleur connecté");
1878
+ socketControleur = ws;
1879
+ groupesClientSon.setSocketControleur(ws);
1880
+ initMatriceDesPossibles(DAWStatus);
1881
+ ws.send(JSON.stringify({
1882
+ type: "skiniParametres",
1883
+ value: par
1884
+ }));
1885
+ break;
1886
+ }
1887
+
1888
+ if (msgRecu.text === "pieceParameters") {
1889
+ if (debug1) console.log("INFO: webSocketServeur: startSpectateur: Parametre connecté", msgRecu.id);
1890
+ ws.send(JSON.stringify({
1891
+ type: "skiniParametres",
1892
+ value: par
1893
+ }));
1894
+
1895
+ if (debug) console.log("INFO: webSocketServeur: startSpectateur: Parametre connecté", par);
1896
+ }
1897
+
1898
+ if (msgRecu.text === "clientListe") {
1899
+ if (debug1) console.log("INFO: webSocketServeur: startSpectateur: un clientListe connecté", msgRecu.id);
1900
+ ws.send(JSON.stringify({
1901
+ type: "skiniParametres",
1902
+ value: par
1903
+ }));
1904
+ }
1905
+
1906
+ if (msgRecu.text === "simulateur") {
1907
+ if (par.simulatorInAseperateGroup) {
1908
+ // Assignation d'un groupe au client
1909
+ ws.send(JSON.stringify({
1910
+ type: "groupe",
1911
+ noDeGroupe: par.nbeDeGroupesClients - 1
1912
+ }));
1913
+ groupesClientSon.putIdInGroupClient(ws.id, par.nbeDeGroupesClients - 1);
1914
+
1915
+ // Pour dire à l'ouverture au simulateur si on est ou pas dans une scène où DAW est actif.
1916
+ if (debug1) console.log("INFO: Web Socket Server: startSpectateur: simulateur: DAWON:", DAWStatus);
1917
+ ws.send(JSON.stringify({
1918
+ type: "DAWON",
1919
+ value: DAWStatus
1920
+ }));
1921
+ break;
1922
+ }
1923
+ }
1924
+
1925
+ if (debug) console.log("websocket serveur: startSpectateur: ", ws.id, "dans groupe:", groupeEncours, msgRecu, clientsEnCours);
1926
+
1927
+ // Assignation d'un groupe au client
1928
+ ws.send(JSON.stringify({
1929
+ type: "groupe",
1930
+ noDeGroupe: groupeEncours
1931
+ }));
1932
+ groupesClientSon.putIdInGroupClient(ws.id, groupeEncours);
1933
+
1934
+ if (debug) console.log("websocket serveur: startSpectateur: groupesClientSon:", groupesClientSon.getGroupesClient());
1935
+
1936
+ // Pour une distribution équilibrée entre les groupes
1937
+ // Si on souhaite avoir le simulateur sur un groupe non distribué à l'audience,
1938
+ // dans le fichier de configuration on met simulatorInAseperateGroup = true;
1939
+ // Ceci réserve le dernier groupe Client pour le simulateur puisque groupeEnCours n'aura jamais
1940
+ // la valeur maximale nbeDeGroupesClients
1941
+ if (par.nbeDeGroupesClients === 1 && par.simulatorInAseperateGroup) {
1942
+ if (warnings) console.log("WARN: ATENTION PAS DE GROUPE ASSIGNE A L'AUDIENCE !");
1943
+ }
1944
+
1945
+ groupeEncours++;
1946
+ if (par.simulatorInAseperateGroup) {
1947
+ groupeEncours %= par.nbeDeGroupesClients - 1;
1948
+ }
1949
+ else {
1950
+ groupeEncours %= par.nbeDeGroupesClients;
1951
+ }
1952
+ // Pour dire à l'ouverture au client si on est ou pas dans une scène où DAW est actif.
1953
+ if (debug) console.log("Web Socket Server: startSpectateur: emission DAWStatus:", DAWStatus);
1954
+ ws.send(JSON.stringify({
1955
+ type: "DAWStatus",
1956
+ value: DAWStatus
1957
+ }));
1958
+ break;
1959
+
1960
+ case "startSimulator":
1961
+ if (debug) console.log("Web Socket Server: start Simulator");
1962
+ childSimulator = fork("./client/simulateurListe/simulateurFork.mjs");
1963
+ childSimulator.send({ type: "START_SIMULATOR" });
1964
+ updateSimulatorParameters(par);
1965
+ break;
1966
+
1967
+ case "stopAutomate":
1968
+ if (DAWTableReady) {
1969
+ //if (setTimer !== undefined && !par.synchoOnMidiClock) clearInterval(setTimer);
1970
+ reactAutomatePossible({ halt: undefined });
1971
+ DAWStatus = 0;
1972
+ serv.broadcast(JSON.stringify({
1973
+ type: "DAWStatus",
1974
+ value: false
1975
+ }));
1976
+ }
1977
+ break;
1978
+
1979
+ case "stopSimulator":
1980
+ if (debug) console.log("INFO: Web Socket Server: stop Simulator");
1981
+ childSimulator.send({ type: "STOP_SIMULATOR" });
1982
+ break;
1983
+
1984
+ case "system": // Message converti en signal pour l'automate central
1985
+ if (debug) console.log("Web Socket Server: received message : [%s]",
1986
+ msgRecu.text, " from Client ID:", ws.id);
1987
+ machineServeur.inputAndReact(msgRecu.text, ws.id); // ENVOI DU SIGNAL VERS HIPHOP
1988
+ break;
1989
+
1990
+ case "updateSession":
1991
+ if (sessionFile !== undefined) {
1992
+ if (debug) console.log("updateSession pour:", sessionFile, ": ", arrayToCSV(msgRecu.data));
1993
+ // Ecrire le fichier
1994
+ fs.writeFile(sessionFile, arrayToCSV(msgRecu.data), function (err) {
1995
+ if (err) {
1996
+ ws.send(JSON.stringify({
1997
+ type: "alertBlocklySkini",
1998
+ text: err.toString()
1999
+ }));
2000
+ return console.log("ERR: websocketserver.js: updateSession: ", err);
2001
+ } else {
2002
+ // Le recharger dans DAW
2003
+ try {
2004
+ DAW.loadDAWTable(sessionFile);
2005
+ } catch (err) {
2006
+ console.log("websocketServer: erreur de chargement:createSession: ", sessionFile, err);
2007
+ }
2008
+ ws.send(JSON.stringify({
2009
+ type: "consoleBlocklySkini",
2010
+ text: "session loaded: " + sessionFile
2011
+ }));
2012
+ }
2013
+ });
2014
+ } else {
2015
+ console.log("WARN: No descriptor file specified");
2016
+ break;
2017
+ }
2018
+ break;
2019
+
2020
+ case "updateParameters":
2021
+ //Save the previous parameters
2022
+ fs.copyFile(sessionPath + parametersFileGlobal, "./backup/" + parametersFileGlobal + ".back", function (err) {
2023
+ if (err) {
2024
+ return console.log(err);
2025
+ }
2026
+ if (debug1) console.log("INFO: websocketServer: updateParameters", parametersFileGlobal + ".back written");
2027
+ });
2028
+
2029
+ if (debug1) console.log("INFO: websocketserveur: Update of the piece parameters", msgRecu.data, "in", sessionPath + parametersFileGlobal);
2030
+ if (parametersFileGlobal !== undefined) {
2031
+ saveParam.saveParameters(sessionPath + parametersFileGlobal, msgRecu.data);
2032
+ reloadParameters(msgRecu.data);
2033
+ }
2034
+
2035
+ // On recrée le fichier pour son utilisation par le simulateurListe.js
2036
+ let destinationUpdate = "./serveur/skiniParametres.js";
2037
+ try {
2038
+ fs.copyFileSync(sessionPath + parametersFileGlobal, destinationUpdate);
2039
+ } catch (err) {
2040
+ console.log("ERR: websocketserver: destinationUpdate : Pb ecriture", destinationUpdate, err);
2041
+ }
2042
+
2043
+ // Recréé la machine d'orchestration pour intégrer les modifications
2044
+ // des paramétres de groupes et tanks.
2045
+ try {
2046
+ compileHH();
2047
+ } catch (err) {
2048
+ console.log("websocketServerSkini:updateParameters:catch:", err);
2049
+ }
2050
+ break;
2051
+
2052
+ default: console.log("INFO: Web Socket Serveur: Type de message inconnu : ", msgRecu);
2053
+ }
2054
+ });
2055
+ });
2056
+
2057
+ /****** FIN WEBSOCKET ************/
2058
+ }
2059
+
2060
+ function logInfoSocket(message) {
2061
+ fs.appendFile('skinilog.json', JSON.stringify(message) + "\n", "UTF-8", function (err) {
2062
+ if (err) {
2063
+ console.error(err);
2064
+ throw err;
2065
+ }
2066
+ });
2067
+ }
2068
+
2069
+ function getDateTime() {
2070
+ var date = new Date();
2071
+
2072
+ var hour = date.getHours();
2073
+ hour = (hour < 10 ? "0" : "") + hour;
2074
+
2075
+ var min = date.getMinutes();
2076
+ min = (min < 10 ? "0" : "") + min;
2077
+
2078
+ var sec = date.getSeconds();
2079
+ sec = (sec < 10 ? "0" : "") + sec;
2080
+
2081
+ var year = date.getFullYear();
2082
+
2083
+ var month = date.getMonth() + 1;
2084
+ month = (month < 10 ? "0" : "") + month;
2085
+
2086
+ var day = date.getDate();
2087
+ day = (day < 10 ? "0" : "") + day;
2088
+
2089
+ return day + ":" + month + ":" + year + ":" + hour + ":" + min + ":" + sec;
2090
+ }