nodeskini 1.0.0

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.
Files changed (215) hide show
  1. package/README.md +31 -0
  2. package/client/archive/bundle.js +11528 -0
  3. package/client/archive/golem.html +60 -0
  4. package/client/archive/golemClient.js +595 -0
  5. package/client/archive/golembundle.js +11634 -0
  6. package/client/archive/sequencer.html +43 -0
  7. package/client/archive/sequenceurClient.js +815 -0
  8. package/client/archive/stylegolem.css +158 -0
  9. package/client/archive/stylesequenceur.css +204 -0
  10. package/client/clientListe/Sortable-master/.circleci/config.yml +33 -0
  11. package/client/clientListe/Sortable-master/.editorconfig +15 -0
  12. package/client/clientListe/Sortable-master/.github/ISSUE_TEMPLATE/bug-report.md +73 -0
  13. package/client/clientListe/Sortable-master/.github/ISSUE_TEMPLATE/custom-template.md +48 -0
  14. package/client/clientListe/Sortable-master/.github/ISSUE_TEMPLATE/feature-request.md +41 -0
  15. package/client/clientListe/Sortable-master/.jshintrc +25 -0
  16. package/client/clientListe/Sortable-master/.testcaferc.json +7 -0
  17. package/client/clientListe/Sortable-master/CONTRIBUTING.md +26 -0
  18. package/client/clientListe/Sortable-master/LICENSE +21 -0
  19. package/client/clientListe/Sortable-master/README.md +813 -0
  20. package/client/clientListe/Sortable-master/Sortable.js +3709 -0
  21. package/client/clientListe/Sortable-master/Sortable.min.js +2 -0
  22. package/client/clientListe/Sortable-master/Sortable.min.old.js +2 -0
  23. package/client/clientListe/Sortable-master/Sortable.min.old.old.js +2 -0
  24. package/client/clientListe/Sortable-master/babel.config.js +27 -0
  25. package/client/clientListe/Sortable-master/bower.json +30 -0
  26. package/client/clientListe/Sortable-master/entry/entry-complete.js +8 -0
  27. package/client/clientListe/Sortable-master/entry/entry-core.js +19 -0
  28. package/client/clientListe/Sortable-master/entry/entry-defaults.js +19 -0
  29. package/client/clientListe/Sortable-master/index.html +460 -0
  30. package/client/clientListe/Sortable-master/modular/sortable.complete.esm.js +3701 -0
  31. package/client/clientListe/Sortable-master/modular/sortable.core.esm.js +3698 -0
  32. package/client/clientListe/Sortable-master/modular/sortable.esm.js +3699 -0
  33. package/client/clientListe/Sortable-master/package-lock.json +5704 -0
  34. package/client/clientListe/Sortable-master/package.json +56 -0
  35. package/client/clientListe/Sortable-master/plugins/AutoScroll/AutoScroll.js +270 -0
  36. package/client/clientListe/Sortable-master/plugins/AutoScroll/README.md +80 -0
  37. package/client/clientListe/Sortable-master/plugins/AutoScroll/index.js +1 -0
  38. package/client/clientListe/Sortable-master/plugins/MultiDrag/MultiDrag.js +617 -0
  39. package/client/clientListe/Sortable-master/plugins/MultiDrag/README.md +96 -0
  40. package/client/clientListe/Sortable-master/plugins/MultiDrag/index.js +1 -0
  41. package/client/clientListe/Sortable-master/plugins/OnSpill/OnSpill.js +79 -0
  42. package/client/clientListe/Sortable-master/plugins/OnSpill/README.md +60 -0
  43. package/client/clientListe/Sortable-master/plugins/OnSpill/index.js +1 -0
  44. package/client/clientListe/Sortable-master/plugins/README.md +178 -0
  45. package/client/clientListe/Sortable-master/plugins/Swap/README.md +55 -0
  46. package/client/clientListe/Sortable-master/plugins/Swap/Swap.js +90 -0
  47. package/client/clientListe/Sortable-master/plugins/Swap/index.js +1 -0
  48. package/client/clientListe/Sortable-master/scripts/banner.js +8 -0
  49. package/client/clientListe/Sortable-master/scripts/build.js +17 -0
  50. package/client/clientListe/Sortable-master/scripts/esm-build.js +28 -0
  51. package/client/clientListe/Sortable-master/scripts/minify.js +11 -0
  52. package/client/clientListe/Sortable-master/scripts/test-compat.js +30 -0
  53. package/client/clientListe/Sortable-master/scripts/test.js +21 -0
  54. package/client/clientListe/Sortable-master/scripts/umd-build.js +15 -0
  55. package/client/clientListe/Sortable-master/src/Animation.js +175 -0
  56. package/client/clientListe/Sortable-master/src/BrowserInfo.js +12 -0
  57. package/client/clientListe/Sortable-master/src/EventDispatcher.js +57 -0
  58. package/client/clientListe/Sortable-master/src/PluginManager.js +87 -0
  59. package/client/clientListe/Sortable-master/src/Sortable.js +1971 -0
  60. package/client/clientListe/Sortable-master/src/utils.js +556 -0
  61. package/client/clientListe/Sortable-master/st/app.js +224 -0
  62. package/client/clientListe/Sortable-master/st/iframe/frame.html +32 -0
  63. package/client/clientListe/Sortable-master/st/iframe/index.html +49 -0
  64. package/client/clientListe/Sortable-master/st/logo.png +0 -0
  65. package/client/clientListe/Sortable-master/st/og-image.png +0 -0
  66. package/client/clientListe/Sortable-master/st/prettify/prettify.css +1 -0
  67. package/client/clientListe/Sortable-master/st/prettify/prettify.js +46 -0
  68. package/client/clientListe/Sortable-master/st/prettify/run_prettify.js +64 -0
  69. package/client/clientListe/Sortable-master/st/saucelabs.svg +1 -0
  70. package/client/clientListe/Sortable-master/st/theme.css +254 -0
  71. package/client/clientListe/Sortable-master/tests/Sortable.compat.test.js +39 -0
  72. package/client/clientListe/Sortable-master/tests/Sortable.test.js +386 -0
  73. package/client/clientListe/Sortable-master/tests/dual-list.html +34 -0
  74. package/client/clientListe/Sortable-master/tests/empty-list.html +30 -0
  75. package/client/clientListe/Sortable-master/tests/filter.html +27 -0
  76. package/client/clientListe/Sortable-master/tests/handles.html +27 -0
  77. package/client/clientListe/Sortable-master/tests/nested.html +67 -0
  78. package/client/clientListe/Sortable-master/tests/single-list.html +25 -0
  79. package/client/clientListe/Sortable-master/tests/style.css +18 -0
  80. package/client/clientListe/clientListe.html +148 -0
  81. package/client/clientListe/clientListe.js +1508 -0
  82. package/client/clientListe/clientListebundle.js +13164 -0
  83. package/client/clientListe/images/poubelle.png +0 -0
  84. package/client/clientListe/images/start.png +0 -0
  85. package/client/clientListe/images/stop.png +0 -0
  86. package/client/clientListe/images/submit.png +0 -0
  87. package/client/clientListe/sortable-theme-bootstrap.css +90 -0
  88. package/client/configurateur/configReact.js +273 -0
  89. package/client/configurateur/configReactbundle.js +295 -0
  90. package/client/configurateur/configurateur.css +95 -0
  91. package/client/configurateur/configurateur.html +48 -0
  92. package/client/configurateur/lib/jexcel.css +755 -0
  93. package/client/configurateur/lib/jexcel.js +14970 -0
  94. package/client/configurateur/lib/jsuites.css +2801 -0
  95. package/client/configurateur/lib/jsuites.js +11822 -0
  96. package/client/configurateur/lib/react-dom.production.min.js +239 -0
  97. package/client/configurateur/lib/react.production.min.js +32 -0
  98. package/client/configurateur/src/configReact.js +247 -0
  99. package/client/controleur/clientcontroleur.js +536 -0
  100. package/client/controleur/clientcontroleur.test.js +282 -0
  101. package/client/controleur/controleur.html +51 -0
  102. package/client/controleur/controleurbundle.js +565 -0
  103. package/client/controleur/stylecontroleur.css +236 -0
  104. package/client/controleurHH/controleurHH.html +71 -0
  105. package/client/controleurHH/controleurHH.js +252 -0
  106. package/client/controleurHH/styles/index.css +320 -0
  107. package/client/controleurHH/styles/material.css +11552 -0
  108. package/client/parametrage/paramReact.js +473 -0
  109. package/client/parametrage/paramReactbundle.js +500 -0
  110. package/client/parametrage/parametrage.css +111 -0
  111. package/client/parametrage/parametrage.html +163 -0
  112. package/client/parametrage/src/paramReact.js +459 -0
  113. package/client/score/hash.js +83 -0
  114. package/client/score/p5.min.js +3 -0
  115. package/client/score/parto1.js +1171 -0
  116. package/client/score/parto1bundle.js +1181 -0
  117. package/client/score/processing.min.js +431 -0
  118. package/client/score/score.html +15 -0
  119. package/client/score/score.js +34 -0
  120. package/client/simulateurListe/simulateurFork.js +750 -0
  121. package/client/simulateurListe/simulateurFork.mjs +681 -0
  122. package/client/simulateurListe/simulateurForkSansReorg.js +569 -0
  123. package/client/simulateurListe/simulateurListe.js +628 -0
  124. package/myReact/archive/Nodeemitvaluedlocal1.hh.js +52 -0
  125. package/myReact/archive/abort-parNode.js +79 -0
  126. package/myReact/archive/abroNode.js +169 -0
  127. package/myReact/archive/abroNode2.js +80 -0
  128. package/myReact/archive/atom.compile.hh.js +51 -0
  129. package/myReact/archive/await-countNode.js +67 -0
  130. package/myReact/archive/await-nowvalNode.js +44 -0
  131. package/myReact/archive/callHH.js +96 -0
  132. package/myReact/archive/emit-if2.hh.compiled.js +113 -0
  133. package/myReact/archive/every1Node.js +35 -0
  134. package/myReact/archive/if-runNode.js +74 -0
  135. package/myReact/archive/if1Node.js +43 -0
  136. package/myReact/archive/makeawait.js +0 -0
  137. package/myReact/archive/myReact.old.js +684 -0
  138. package/myReact/archive/orchestration.js +281 -0
  139. package/myReact/archive/orchestration1.js +132 -0
  140. package/myReact/archive/orchestration1.xml +465 -0
  141. package/myReact/archive/orchestration2.js +161 -0
  142. package/myReact/archive/orchestrationHH.mano.js +280 -0
  143. package/myReact/archive/orchestrationHHTest.js +428 -0
  144. package/myReact/archive/orchestrationHHTest.xml +234 -0
  145. package/myReact/archive/orchestrationHHTestRun.js +104 -0
  146. package/myReact/archive/orchestrationHHTestRun.xml +34 -0
  147. package/myReact/archive/orchestrationTest0.js +178 -0
  148. package/myReact/archive/orchestrationTest1.js +181 -0
  149. package/myReact/archive/orchestrationTest2.js +281 -0
  150. package/myReact/archive/run3pointsNode.js +59 -0
  151. package/myReact/archive/runNode.js +123 -0
  152. package/myReact/archive/runNode2.js +91 -0
  153. package/myReact/archive/testAwait1.js +141 -0
  154. package/myReact/archive/testAwait1.xml +86 -0
  155. package/myReact/archive/testEvery1.js +122 -0
  156. package/myReact/archive/testEvery1.xml +79 -0
  157. package/myReact/archive/testHH1.js +135 -0
  158. package/myReact/archive/testHH1.xml +86 -0
  159. package/myReact/archive/testHH1revu.js +104 -0
  160. package/myReact/archive/testHH2.js +122 -0
  161. package/myReact/archive/testHH2.xml +79 -0
  162. package/myReact/archive/testHH3.js +130 -0
  163. package/myReact/archive/testHH3.xml +86 -0
  164. package/myReact/archive/testHHabort.js +121 -0
  165. package/myReact/archive/testHHabort.xml +83 -0
  166. package/myReact/archive/testMakeawait.js +202 -0
  167. package/myReact/archive/testRun1.js +168 -0
  168. package/myReact/archive/testRun1.xml +142 -0
  169. package/myReact/archive/titi.js +28 -0
  170. package/myReact/archive/titi.xml +110 -0
  171. package/myReact/archive/toto.js +73 -0
  172. package/myReact/archive/toto.xml +198 -0
  173. package/myReact/archive/trap-await-parallelNode.js +123 -0
  174. package/myReact/inutiles/hiver2022.xml +804 -0
  175. package/myReact/inutiles/hopeNode.xml +459 -0
  176. package/myReact/inutiles/mars2022.xml +871 -0
  177. package/myReact/inutiles/mystique1.xml +318 -0
  178. package/myReact/inutiles/mystiqueOSC.xml +277 -0
  179. package/myReact/inutiles/opus5Node.xml +1271 -0
  180. package/myReact/inutiles/opus5NodeLinux.xml +1241 -0
  181. package/myReact/inutiles/orchestrationHH.xml +541 -0
  182. package/myReact/inutiles/orchestrationHH2.xml +547 -0
  183. package/myReact/inutiles/testHH.xml +95 -0
  184. package/myReact/inutiles/trouveLaPercuTenor.xml +349 -0
  185. package/myReact/myReact.js +744 -0
  186. package/myReact/myReact.min.js +1 -0
  187. package/myReact/orchestrationHH.js +311 -0
  188. package/myReact/orchestrationHH.mjs +436 -0
  189. package/myReact/orchestrationHH.mjs.map +1 -0
  190. package/package.json +46 -0
  191. package/serveur/OSCandMidi.mjs +361 -0
  192. package/serveur/computeScore.mjs +415 -0
  193. package/serveur/controleDAW.mjs +1149 -0
  194. package/serveur/defaultSession.csv +2 -0
  195. package/serveur/defaultSkiniParametres.js +119 -0
  196. package/serveur/gameOSC.mjs +96 -0
  197. package/serveur/groupeClientsSons.mjs +1014 -0
  198. package/serveur/ipConfig.json +24 -0
  199. package/serveur/ipConfig127.json +19 -0
  200. package/serveur/ipConfig75.json +17 -0
  201. package/serveur/ipConfigBH.json +19 -0
  202. package/serveur/ipConfigLocal.json +19 -0
  203. package/serveur/midiConfig.json +26 -0
  204. package/serveur/midiConfigBH.json +26 -0
  205. package/serveur/midiConfigVoid.json +3 -0
  206. package/serveur/midimix.mjs +570 -0
  207. package/serveur/saveParam.mjs +159 -0
  208. package/serveur/skiniParametres.good.js +132 -0
  209. package/serveur/skiniParametres.js +106 -0
  210. package/serveur/utilsHHSkini.hh.js +64 -0
  211. package/serveur/utilsSkini.mjs +137 -0
  212. package/serveur/websocketServer.mjs +2052 -0
  213. package/serveur/workerInterfaceZ.mjs +327 -0
  214. package/serveur/workerSynchro.mjs +49 -0
  215. package/skini.mjs +141 -0
@@ -0,0 +1,1149 @@
1
+ /**
2
+ * @fileOverview Control of the DAW
3
+ * @author Bertrand Petit-Hédelin <bertrand@hedelin.fr>
4
+ * @copyright (C) 2022-2025 Bertrand Petit-Hédelin
5
+ *
6
+ * This program is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * any later version.
10
+ *
11
+ * This program is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
18
+ * @version node.js 1.5
19
+ */
20
+ import { createRequire } from 'module';
21
+ const require = createRequire(import.meta.url);
22
+
23
+ const csv = require('csv-array');
24
+ import * as oscMidi from './OSCandMidi.mjs'
25
+ import * as fs from "fs";
26
+
27
+ // Index de la table des commandes (des clips ou patterns).
28
+ const NOTE_ID = 0;
29
+ const NOTESTOP_ID = 1;
30
+ const FLAG_ID = 2;
31
+ const TEXT_ID = 3;
32
+ const SOUND_ID = 4;
33
+ const INSTR_ID = 5;
34
+ const SLOT_ID = 6;
35
+ const TYPE_ID = 7;
36
+ const TYPE_V_ID = 8;
37
+ const GROUP_ID = 9;
38
+ const DURATION_ID = 10;
39
+ const IP_ID = 11;
40
+ const BUF_ID = 12;
41
+ const LEVEL_ID = 13;
42
+
43
+ // Index des éléments de file d'attente, commande DAW
44
+ const CD_BUS_ID = 0;
45
+ const CD_CHANNEL_ID = 1;
46
+ const CD_NOTE_ID = 2;
47
+ const CD_VEL_ID = 3;
48
+ const CD_WS_ID = 4;
49
+ const CD_PSEUDO_ID = 5;
50
+ const CD_DUREE_ID = 6;
51
+ const CD_NOM_ID = 7;
52
+ const CD_SIG_ID = 8;
53
+ const CD_TYPE_ID = 9;
54
+ const CD_IP_ID = 10;
55
+ const CD_BUF_ID = 11;
56
+ const CD_LEVEL_ID = 12;
57
+ const CD_TYPE_V_ID = 13;
58
+
59
+ var par;
60
+ export function setParameters(param) {
61
+ par = param;
62
+ }
63
+
64
+ import * as groupesClientSon from './groupeClientsSons.mjs'
65
+ const { Socket } = require('dgram');
66
+
67
+ const debug = false;
68
+ const debug1 = true;
69
+
70
+ let serv;
71
+ let tableDesCommandes;
72
+
73
+ // Il faut initialiser le tableau pour displayQueue au départ.
74
+ // Une file d'attente est composée d'arrays (de format différent de la table des commandes):
75
+ // [bus(0), channel(1), note(2), velocity(3), wsid(4),
76
+ // pseudo(5), dureeClip(6), nom(7), signal(8), type(9), IP(10), bufnum(11), level(12)]
77
+
78
+ let filesDattente = Array(20).fill().map(() => []);
79
+ let filesDattenteJouables = new Array(filesDattente.length);
80
+
81
+ // Il y en aura autant que de files d'attente
82
+ let compteursDattente = [];
83
+
84
+ let nbeDeFileDattentes = 0;
85
+ let nbeDeGroupesSons = 0;
86
+ let nombreInstruments = 0;
87
+ let automatePossibleMachine;
88
+
89
+ const avecMusicien = false;
90
+ let decalageFIFOavecMusicien = 0;
91
+
92
+ const typeDebut = 1;
93
+ const typeMilieu = 2;
94
+ const typeFin = 3;
95
+ const typeNeutre = 4;
96
+ const typeMauvais = 5;
97
+
98
+ /**
99
+ * Set locally the reference to the HipHop automaton
100
+ * @param {automaton} - HipHop machine
101
+ */
102
+ export function setAutomatePossible(automate) {
103
+ automatePossibleMachine = automate;
104
+ }
105
+
106
+ function logInfoDAW(message) {
107
+ message.date = getDateTime();
108
+ fs.appendFile('skinilog.json', JSON.stringify(message) + "\n", "UTF-8", function (err) {
109
+ if (err) {
110
+ console.error(err);
111
+ throw err;
112
+ }
113
+ });
114
+ }
115
+
116
+ function getDateTime() {
117
+ function padZero(num) {
118
+ return (num < 10 ? "0" : "") + num;
119
+ }
120
+
121
+ var date = new Date();
122
+
123
+ var hour = padZero(date.getHours());
124
+ var min = padZero(date.getMinutes());
125
+ var sec = padZero(date.getSeconds());
126
+
127
+ var year = date.getFullYear();
128
+ var month = padZero(date.getMonth() + 1);
129
+ var day = padZero(date.getDate());
130
+
131
+ // Return ISO 8601 formatted string for better interoperability
132
+ return `${year}-${month}-${day}T${hour}:${min}:${sec}`;
133
+ }
134
+
135
+ /**
136
+ * Load the patterns description from a file
137
+ * @param {string} fichier - file name of csv patterns description
138
+ */
139
+ export function loadDAWTable(fichier) {
140
+ let ajustement = false;
141
+ return new Promise(function (resolve, reject) {
142
+ if (fichier === undefined) {
143
+ reject("controleDAW: loadDAWTable: pas de fichier à lire");
144
+ }
145
+
146
+ nbeDeFileDattentes = 0;
147
+ tableDesCommandes = new Array();
148
+ csv.parseCSV(fichier, function (data) {
149
+ try {
150
+ // Pour reformater le tableau stocké en CSV
151
+ for (let i = 0; i < data.length; i++) {
152
+ data[i][0] = parseInt(data[i][NOTE_ID], 10);
153
+ data[i][1] = parseInt(data[i][NOTESTOP_ID], 10);
154
+ data[i][2] = parseInt(data[i][FLAG_ID], 10);
155
+ // On ne refomate pas les 3 et 4 qui restent des chaines de caractères
156
+
157
+ for (let j = 5; j < 11; j++) {
158
+ data[i][j] = parseInt(data[i][j], 10);
159
+ }
160
+
161
+ // On ne reformate pas l'adresse IP en 11, mais le numéro de buffer en 12
162
+ // et le niveau en 13
163
+ if (data[i][BUF_ID] !== undefined) data[i][BUF_ID] = parseInt(data[i][BUF_ID], 10);
164
+ if (data[i][LEVEL_ID] !== undefined) data[i][LEVEL_ID] = parseInt(data[i][LEVEL_ID], 10);
165
+
166
+ tableDesCommandes.push(data[i]); // ajoute la ligne au tableau
167
+
168
+ // Met à jour le nombre de files d'attente selon le numéro max des synthé dans le fichier de des descripteurs
169
+ // Il y a danger quand les instruments sont comptés à partir de 0, il va en en manquer 1.
170
+ if (tableDesCommandes[i][INSTR_ID] === 0) ajustement = true;
171
+ if (tableDesCommandes[i][INSTR_ID] > nbeDeFileDattentes) nbeDeFileDattentes = tableDesCommandes[i][INSTR_ID];
172
+ }
173
+ if (ajustement) nbeDeFileDattentes++;
174
+
175
+ if (debug1) console.log("INFO: controleDAW.mjs: loadDAWTable: Nbe d'nbeDeFileDattentes: ", nbeDeFileDattentes);
176
+
177
+ // Calcul du nombre de groupe de sons à partir des paramètres, mais il n'est pas certain
178
+ // que ceux-ci soient à jour avant de charger les patterns. D'où le test qui suit.
179
+ nbeDeGroupesSons = Math.max(...par.groupesDesSons.map(groupe => parseInt(groupe[1], 10))); // 1 = Index dans le tableau des paramètres
180
+ if (debug1) console.log("INFO: controleDAW.mjs: loadDAWTable: nbeDeGroupesSons: ", nbeDeGroupesSons);
181
+
182
+ if (debug1) console.log("INFO: controleDAW.mjs: loadDAWTable: Nbe d'instruments: avant test:", nombreInstruments);
183
+
184
+ // Cas d'une incohérence entre les paramètres et les patterns
185
+ // Le problème peut apparaitre quand on passe d'une pièce à une autre.
186
+ if (nombreInstruments < nbeDeFileDattentes) {
187
+ nombreInstruments = nbeDeFileDattentes;
188
+ }
189
+
190
+ //nombreInstruments = Math.max(...tableDesCommandes.map(commande => parseInt(commande[INSTR_ID]), 10));
191
+ if (debug1) console.log("INFO: controleDAW.mjs: loadDAWTable: Nbe d'instruments: ", nombreInstruments);
192
+
193
+ // On convertit l'index issu de la config des pattern en nombre de FIFO
194
+ nombreInstruments++;
195
+
196
+ // Initialisation
197
+ filesDattente = new Array(nombreInstruments).fill().map(() => []);
198
+ compteursDattente = new Array(nombreInstruments).fill(0);
199
+ filesDattenteJouables = new Array(nombreInstruments).fill(true);
200
+
201
+ if (debug) console.log("controleDAW.mjs: loadDAWTable: Lecture une ligne de: ", fichier, tableDesCommandes[0]);
202
+ if (debug) console.log("controleDAW.mjs: loadDAWTable: Nbe de files d'attente: ", nbeDeFileDattentes);
203
+ if (debug) console.log("controleDAW.mjs: filesDattenteJouables: ", filesDattenteJouables);
204
+ if (debug) console.log("controleDAW.mjs: nbeDeGroupesSons:", nbeDeGroupesSons);
205
+
206
+ resolve();
207
+ }
208
+ catch (e) {
209
+ console.log("controlDAW:loadDAW:catch", e);
210
+ throw (e);
211
+ }
212
+ }, false);
213
+ });
214
+ }
215
+
216
+ /**
217
+ * Display on the console the patterns description
218
+ */
219
+ export function displaySession() {
220
+ console.log(tableDesCommandes);
221
+ }
222
+
223
+ /**
224
+ * Get the ongoing pattern description (of the csv file loaded).
225
+ * @returns {array} patterns descriptors
226
+ */
227
+ export function getSession() {
228
+ return tableDesCommandes;
229
+ }
230
+
231
+ // ======================= Initialisation pour Broadcast =============================
232
+ /**
233
+ * Set the server to manage the Broadcast on the clients
234
+ * @param {Socket} serveur
235
+ */
236
+ export function initBroadCastServer(serveur) {
237
+ if (debug) console.log("controleDAW: initBroadCastServer ");
238
+ serv = serveur;
239
+ }
240
+
241
+ // ======================= Grestion des fichiers de config ===========================
242
+ // Au format CSV: 0= note, 1=note stop, 2=flag, 3=nom, 4=fichier son, 5=instrument, 6=Niveau1, 7=niveau2, 8=niveau3, 9=groupe, 10=durée
243
+
244
+ /**
245
+ * Give the number of groups of pattern
246
+ * @returns {number} - number of groups
247
+ */
248
+ export function getNbeDeGroupesSons() {
249
+ return nbeDeGroupesSons;
250
+ }
251
+
252
+ /**
253
+ * Retrieves the pattern name associated with a given note identifier.
254
+ *
255
+ * @param {string|number} noteSkini - The note identifier to search for.
256
+ * @returns {string|undefined} The corresponding pattern name if found, otherwise undefined.
257
+ */
258
+ export function getPatternNameFromNote(noteSkini) {
259
+ const ligne = tableDesCommandes.find(cmd => cmd[0] === parseInt(noteSkini, 10));
260
+ return ligne?.[TEXT_ID];
261
+ }
262
+
263
+ /**
264
+ * Get a complete pattern from a note
265
+ * @param {number | string} noteSkini
266
+ * @returns {Array} pattern
267
+ */
268
+ export function getPatternFromNote(noteSkini) {
269
+ return tableDesCommandes.find(cmd => cmd[NOTE_ID] === parseInt(noteSkini, 10));
270
+ }
271
+
272
+ /**
273
+ * <BR> - Pour mettre des patterns en file d'attente sans interaction.
274
+ * Cette fonction permet d'utiliser Skini comme un séquenceur sans interaction.
275
+ * Le mécanisme de FIFO est utilisé, ce qui permet une combinaison avec les interactions
276
+ * et permet tous les usages des mécanismes de lecture des FIFO.
277
+ * Assez similaire à pushClipDAW() de websocketServer.
278
+ * <BR> - To queue patterns without interaction. This function allows
279
+ * to use Skini as a sequencer without interaction.
280
+ * The FIFO mechanism is used, which allows a combination with
281
+ * the interactions and allows all the uses of the FIFO reading mechanisms.
282
+ * Quite similar to pushClipDAW() of websocketServer.
283
+ * @param {string} patternName
284
+ * @returns {number} Duree d'attente sur la queue.
285
+ */
286
+ export function putPatternInQueue(patternName) {
287
+ // On identifie le pattern par son nom, cad le texte du champ "nom"
288
+ let commande = tableDesCommandes.find(cmd => cmd[TEXT_ID] === patternName);
289
+
290
+ if (debug) console.log("INFO: controleDAW: putPatternInQueue: commande :", commande);
291
+
292
+ if (commande !== undefined) {
293
+ let DAWNote = commande[NOTE_ID];
294
+ let DAWChannel = Math.floor(DAWNote / 127) + 1;
295
+ DAWNote = DAWNote % 127;
296
+ if (DAWChannel > 15) {
297
+ if (debug1) console.log("Web Socket Server.js : pushClipDAW: Nombre de canaux midi dépassé.");
298
+ return;
299
+ }
300
+ let nom = commande[TEXT_ID];
301
+ let DAWInstrument = commande[INSTR_ID];
302
+ let dureeClip = commande[DURATION_ID];
303
+ let signal = groupesClientSon.getSignalFromGroup(commande[GROUP_ID]) + "IN";
304
+ let id = 0;
305
+ let adresseIP = commande[IP_ID];
306
+ let numeroBuffer = commande[BUF_ID];
307
+ let patternLevel = commande[LEVEL_ID];
308
+ let patternVertType = commande[TYPE_V_ID];
309
+ let patternType = commande[TYPE_ID];
310
+
311
+ // Contient le signal et le pattern
312
+ let signalComplet = { [signal]: nom };
313
+
314
+ if (debug) console.log("controleDAW:putPatternInQueue: signalComplet:", signalComplet);
315
+ if (debug) console.log("controleDAW:putPatternInQueue:", par.busMidiDAW, DAWChannel, DAWInstrument, DAWNote, 125, id, "Automate", dureeClip, nom);
316
+ let dureeAttente = pushEventDAW(par.busMidiDAW, DAWChannel, DAWInstrument,
317
+ DAWNote, 125, id, "Automate", dureeClip, nom, signalComplet, patternType,
318
+ adresseIP, numeroBuffer, patternLevel, patternVertType);
319
+ return dureeAttente;
320
+ } else {
321
+ console.log("WARN: constroleDAW.js: Le pattern n'existe pas:", patternName);
322
+ return undefined;
323
+ }
324
+ }
325
+
326
+ // ================= Gestion des files d'attente ===========================
327
+ /**
328
+ * Push the patterns parameters in the queue of the instrument.
329
+ * The queue is not as the pattern description.
330
+ * There are more parameters such as
331
+ * pseudo, signal, websocket id.
332
+ *
333
+ * @param {number} bus (0)
334
+ * @param {number} channel (1)
335
+ * @param {number} note (2)
336
+ * @param {number} velocity (3)
337
+ * @param {number} wsid (4)
338
+ * @param {string} pseudo (5)
339
+ * @param {number} dureeClip (6)
340
+ * @param {string} nom (7)
341
+ * @param {string} signal (8)
342
+ * @param {number} typePattern (9)
343
+ * @param {string} IPaddress (10)
344
+ * @param {number} bufferNumber (11)
345
+ * @param {number} pattern level (12)
346
+ * @param {number} vertical type (13)
347
+ */
348
+ export function pushEventDAW(bus, channel, instrument, note, velocity,
349
+ wsid, pseudo, dureeClip, nom, signal, typePattern,
350
+ adresseIP, numeroBuffer, patternLevel, typeVertPattern) {
351
+
352
+ let dureeAttente = 0;
353
+ if (debug) console.log("controleDAW.mjs: pushEventDAW ", bus, channel,
354
+ instrument, note, velocity, wsid, pseudo, nom, signal, typePattern,
355
+ adresseIP, numeroBuffer, patternLevel, typeVertPattern);
356
+ if (debug) console.log("controleDAW.mjs: pushEventDAW ", nom, signal);
357
+
358
+ let longeurDeLafile = filesDattente[instrument].length;
359
+
360
+ // Scénario spécifique pour les interfaces avec les musiciens.
361
+ if (longeurDeLafile === 0 && avecMusicien) {
362
+ // On met une demande vide dans la file d'attente pour décaler le démarrage du premier élément de la FIFO
363
+ // et laisser au musicien le temps de se préparer au pattern.
364
+ // file d'attente = [bus, channel, note, velocity, wsid, pseudo, dureeClip, nom, signal]
365
+ // On met une note négative qui ne sera pas jouée, bien que dans la FIFO.
366
+
367
+ // ATTENTION: Le pseudo devient l'instrument pour le décodage par les clients musiciens.
368
+ // Le serveur nomme le pattern "void" quand il s'agit d'un pattern qu'on ne doit pas jouer.
369
+ // et qui sert à permettre au musicien de se préparer.
370
+ // [bus, channel, note, velocity, wsid, pseudo, dureeClip, nom, signal]
371
+ filesDattente[instrument].push([0, 0, -1, 0, 0, instrument, decalageFIFOavecMusicien, "void", "void", "void", "void", "void", "void"]);
372
+ }
373
+
374
+ if (par.algoGestionFifo === 1) {
375
+ // Ici on prend en compte le type de pattern pour le placer dans la Fifo
376
+ ordonneFifo(filesDattente[instrument], [bus, channel, note, velocity,
377
+ wsid, pseudo, dureeClip, nom, signal, typePattern,
378
+ adresseIP, numeroBuffer, patternLevel, typeVertPattern]);
379
+ }
380
+ else if (par.algoGestionFifo === 2) {
381
+ ordonneVerticalFIFO(filesDattente, instrument,
382
+ [bus, channel, note, velocity,
383
+ wsid, pseudo, dureeClip, nom, signal, typePattern,
384
+ adresseIP, numeroBuffer, patternLevel, typeVertPattern]);
385
+ if (debug1) printInstruments(filesDattente);
386
+ } else {
387
+ // On met la demande dans la file d'attente sans traitement et sans tenir compte du type qui n'a pas de sens.
388
+ filesDattente[instrument].push([bus, channel, note, velocity,
389
+ wsid, pseudo, dureeClip, nom, signal, '',
390
+ adresseIP, numeroBuffer, patternLevel, typeVertPattern]); // Push à la fin du tableau
391
+ if (debug) printInstruments(filesDattente);
392
+ }
393
+
394
+ //Structure de la file: par.busMidiDAW en 0, DAW channel en 1, DAWNote en 2, velocity en 3, wsid 4, pseudo en 5, durée en 6
395
+ // Calcul de la durée d'attente en sommant les durées dans la file d'un instrument
396
+ for (let i = 0; i < longeurDeLafile; i++) {
397
+ dureeAttente = dureeAttente + filesDattente[instrument][i][CD_DUREE_ID];
398
+ }
399
+
400
+ // On retourne la longueur de la file d'attente pour une estimation de la durée d'attente qui sera transmise au spectateur
401
+ return dureeAttente;
402
+ }
403
+
404
+ /**
405
+ * To get the total duration of the patterns in a queue (instrument).
406
+ * @param {number} instrument
407
+ * @returns {number} a delay
408
+ */
409
+ export function getDelayEventDAW(instrument) {
410
+ if (debug) console.log("controleDAW.mjs: getDelayEventDAW ", instrument);
411
+ return filesDattente[instrument].reduce((total, evt) => total + evt[CD_DUREE_ID], 0);
412
+ }
413
+
414
+ /**
415
+ * To broadcast the content of all the queues to the audience.
416
+ */
417
+ export function displayQueues() {
418
+ let file = [];
419
+ let contenuDeLaFile = [];
420
+
421
+ let messageLog = { date: "" };
422
+ for (let i = 0; i < filesDattente.length; i++) {
423
+ if (filesDattente[i].length > 0) {
424
+
425
+ /* messageLog.source = "controleDAW.mjs";
426
+ messageLog.type = "Etat de la file d'attente";
427
+ messageLog.longeurFileAttente = filesDattente[i].length;
428
+ messageLog.file = filesDattente[i];
429
+ logInfoDAW(messageLog);*/
430
+
431
+ contenuDeLaFile = [];
432
+ for (let j = 0; j < filesDattente[i].length; j++) {
433
+ contenuDeLaFile.push([filesDattente[i][j][CD_PSEUDO_ID], filesDattente[i][j][CD_NOM_ID]]);
434
+ }
435
+ if (debug) console.log(" controleDAW: displayQueues: File:", i, "--", contenuDeLaFile);
436
+ file.push([i, filesDattente[i].length, contenuDeLaFile]);
437
+ }
438
+ }
439
+
440
+ serv.broadcast(JSON.stringify({ type: "etatDeLaFileAttente", value: file }));
441
+ // Pour les musiciens
442
+ serv.broadcast(JSON.stringify({ type: "lesFilesDattente", value: filesDattente }));
443
+ }
444
+
445
+ // Fonction appelée de façon régulière avec Td = Intervalle d'appel dans HOP > Tqa = Temps de quantization dans Ableton
446
+ let compteurTest = 0; // Pour controler la transmission depuis les clients, on le compte.
447
+ let laClef;
448
+ let leSignal;
449
+ let emptyQueueSignal;
450
+ let timerDivisionLocal;
451
+
452
+ /**
453
+ * Play the queues according to timerDivision which defines the speed of the tick
454
+ * filesDattente and filesDattenteJouables are global variables.
455
+ * This function is called every pulse generated by the synchro either MIDI or
456
+ * worker. It means every quarter note. TimerDivision is the number of pulse used
457
+ * to decrement the "compteurDattente" array of counters of waiting times.
458
+ * The principle is:
459
+ * - check the compteurDAttente
460
+ * - if it is 0 we take the next clip (take it and supress it from the list)
461
+ * - send the command to the DAW or Module
462
+ * - load the new value of compteurDattente for the clip sent
463
+ * - decrement all the compteurDAttente
464
+ *
465
+ * In the FIFO we only have the clips to be played.
466
+ *
467
+ * @param {number} timerDivision
468
+ */
469
+ /**
470
+ * Version améliorée de playAndShiftEventDAW qui gère mieux la synchronisation
471
+ */
472
+ export function playAndShiftEventDAW(timerDivision) {
473
+ let commandeDAW;
474
+ let messageLog = { date: "" };
475
+
476
+ // Contournement d'un pb de timerDivision parfois undefined sans raison apparente
477
+ if (timerDivision !== undefined) {
478
+ timerDivisionLocal = timerDivision;
479
+ }
480
+
481
+ if (debug) console.log(" controleDAW : playAndShiftEventDAW: timerDivisionLocal: ", timerDivisionLocal);
482
+ if (debug) console.log(" controleDAW : playAndShiftEventDAW: filesDattente: ", filesDattente);
483
+ if (filesDattente === undefined) return; // Protection
484
+
485
+ for (let i = 0; i < filesDattente.length; i++) { // Pour chaque file d'attente
486
+ // Mécanisme de pause d'une file d'attente
487
+ if (filesDattenteJouables[i] !== undefined) {
488
+ if (filesDattenteJouables[i] === false) {
489
+ continue;
490
+ }
491
+ }
492
+ if (debug) console.log("--- controleDAW.mjs:FIFO", i, " length:", filesDattente[i].length);
493
+
494
+ if (debug) console.log("---0 controleDAW.mjs:compteursDattente:", i, ":", compteursDattente[i]);
495
+ // file d'attente = [bus(0), channel(1), note(2), velocity(3), wsid(4),
496
+ // pseudo(5), dureeClip(6), nom(7), signal(8), type(9), IP(10), bufnum(11), level(12), typeVert(13)]
497
+ // Si la file n'est pas vide
498
+ if (filesDattente[i] !== undefined) {
499
+ if (filesDattente[i].length !== 0) {
500
+ if (debug) console.log("--- controleDAW.mjs:FIFO:", i, ":", filesDattente[i][0][CD_NOM_ID]);
501
+ if (debug) console.log("--- controleDAW.mjs:FIFO:", i, ":", filesDattente[i]);
502
+ if (debug) console.log("\n---0 controleDAW.mjs:FIFO:", i, " Durée d'attente du clip:", compteursDattente[i], " timer:", timerDivision);
503
+
504
+ // Si l'attente est à 0 on joue le clip suivant
505
+ if (compteursDattente[i] === 0) {
506
+
507
+ //Passe au clip suivant
508
+ commandeDAW = filesDattente[i].shift(); // On prend l'évenement en tête de la file "i"
509
+
510
+ if (commandeDAW === undefined) continue;
511
+
512
+ if (debug) console.log("---1 controleDAW.mjs:commande:", commandeDAW[CD_NOM_ID], "compteursDattente[i]:", compteursDattente[i]);
513
+
514
+ // On peut envoyer l'évènement à DAW
515
+ if (debug) console.log("--- controleDAW.mjs : playAndShiftEventDAW : COMMANDE DAW A JOUER:", commandeDAW[CD_NOM_ID]);
516
+
517
+ // Log pour analyse a posteriori
518
+ messageLog.source = "controleDAW.mjs";
519
+ messageLog.type = "COMMANDE DAW ENVOYEE";
520
+ messageLog.note = commandeDAW[CD_NOTE_ID];
521
+ messageLog.instrumentNo = i;
522
+ messageLog.pseudo = commandeDAW[CD_PSEUDO_ID];
523
+ messageLog.id = commandeDAW[CD_WS_ID];
524
+ messageLog.nomSon = commandeDAW[CD_NOM_ID];
525
+ logInfoDAW(messageLog);
526
+
527
+ compteurTest++;
528
+
529
+ // Joue le clip, mais pas si la note n'est pas négative et que l'on est avec des musiciens.
530
+ // si Note < 0 on est dans le cas d'un pattern d'info pour les musiciens.
531
+ // On fait rien pour la DAW.
532
+ // !! à vérifier.
533
+ if (commandeDAW[CD_NOTE_ID] >= 0) {
534
+
535
+ // Pour jouer les buffers sur Raspberries
536
+ if (par.useRaspberries !== undefined) {
537
+ // On teste chaque pattern avant playOSCRasp(message, value, port, IPaddress, level, durée)
538
+ if (debug) console.log("controleDAW.mjs: playAndShiftEventDAW:", par.playBufferMessage,
539
+ commandeDAW[CD_BUF_ID], par.raspOSCPort, commandeDAW[CD_IP_ID], commandeDAW[CD_LEVEL_ID]);
540
+
541
+ if (par.useRaspberries && !isNaN(commandeDAW[CD_BUF_ID])) {
542
+ oscMidi.playOSCRasp(par.playBufferMessage,
543
+ commandeDAW[CD_BUF_ID], par.raspOSCPort, commandeDAW[CD_IP_ID],
544
+ commandeDAW[CD_LEVEL_ID], commandeDAW[CD_DUREE_ID]);
545
+ } else {
546
+ oscMidi.sendNoteOn(commandeDAW[CD_BUS_ID], commandeDAW[CD_CHANNEL_ID],
547
+ commandeDAW[CD_NOTE_ID], commandeDAW[CD_VEL_ID]);
548
+ if (debug) console.log("--- controleDAW.mjs : playAndShiftEventDAW : COMMANDE ENVOYEE:", commandeDAW[CD_NOM_ID], commandeDAW[CD_NOTE_ID]);
549
+ }
550
+ } else {
551
+ oscMidi.sendNoteOn(commandeDAW[CD_BUS_ID], commandeDAW[CD_CHANNEL_ID], commandeDAW[CD_NOTE_ID], commandeDAW[CD_VEL_ID]);
552
+ if (debug) console.log("--- controleDAW.mjs : playAndShiftEventDAW : COMMANDE ENVOYEE 2:", commandeDAW[CD_NOM_ID], commandeDAW[CD_NOTE_ID]);
553
+ }
554
+ }
555
+ if (commandeDAW[CD_DUREE_ID] % timerDivisionLocal !== 0) {
556
+ console.log("WARN: controleDAW.mjs: playAndShiftEventDAW: pattern",
557
+ commandeDAW[CD_NOM_ID], " a une durée: ", commandeDAW[CD_DUREE_ID], "non multiple de timer division:", timerDivisionLocal);
558
+ }
559
+ if (debug) console.log("---2 controleDAW.mjs:sendNoteOn:", commandeDAW[CD_NOM_ID], " de durée: ", commandeDAW[CD_DUREE_ID], "avec timerDivisionLocal:", timerDivisionLocal);
560
+
561
+ // Via OSC on perd les accents, donc on passe pas une WebSocket
562
+ if (debug) console.log("playAndShiftEventDAW: ", compteurTest, ": ", commandeDAW[CD_NOM_ID], ":", commandeDAW[CD_PSEUDO_ID]);
563
+
564
+ // Pour affichage avec un programme Processing
565
+ // oscMidi.sendProcessingDisplayNames( "demandeDeSonParPseudo", commandeDAW[7] );
566
+
567
+ // La ligne automatePossibleMachine.react(commandeDAW[8]) est activé selon reactOnPlay
568
+ // Si reactOnPlay existe à true on envoie le signal, qui correspond au lancement d'un pattern, au moment de le jouer
569
+ // Au niveau timing cela signifie que l'on prend en compte une activation au moment où elle est jouée
570
+ // et pas au moment où elle est demandée.
571
+ // L'autre scénario est dans websocketserver où on envoie le signal au moment de la demande
572
+ // Ce sont deux scénarios différents. Celui-ci peut poser des pb de performance si les react s'enchainent
573
+ // quand on joue la file d'attente.
574
+
575
+ // Pour associer le nom du pattern au signal de groupe
576
+ laClef = Object.keys(commandeDAW[CD_SIG_ID]);
577
+ leSignal = JSON.parse('{"' + laClef[0] + '":"' + commandeDAW[CD_NOM_ID] + '"}');
578
+
579
+ if (debug) console.log("controleDAW:playAndShiftEventDAW: laclef:", laClef, ", leSignal: ", leSignal, commandeDAW[CD_SIG_ID]);
580
+
581
+ if (par.reactOnPlay !== undefined) {
582
+ if (par.reactOnPlay) {
583
+ if (debug) console.log("controleDAW: playAndShiftEventDAW: reactOnPlay: ", par.reactOnPlay, leSignal);
584
+ automatePossibleMachine.react(leSignal);
585
+ }
586
+ }
587
+
588
+ // Pour avertir le browser du demandeur du son et les musiciens s'il y en a.
589
+ serv.broadcast(JSON.stringify({ type: "infoPlayDAW", value: commandeDAW }));
590
+ if (debug) console.log("controleDAW:playAndShiftEventDAW: broadcast commandeDAW", commandeDAW);
591
+
592
+ //Met à jour l'attente quand on a joué le pattern.
593
+ compteursDattente[i] = commandeDAW[CD_DUREE_ID]; // On recharge le compteur d'attente pour le pattern en cours, en comptant le tick en cours
594
+
595
+ //Gestion d'une erreur possible dans la programmation de l'orchestration
596
+ //si des patterns ont des durées < timeDivision, donc inférieures au tick.
597
+ // Ce cas n'est plus traité à cause de la ligne 508, mais le pb reste
598
+ if (compteursDattente[i] < 0) {
599
+ console.log("WARN: Problème sur Pattern", commandeDAW[CD_NOM_ID], ", sa durée est inférieure au timerDivision");
600
+ compteursDattente[i] = 0;
601
+ }
602
+ if (debug) console.log("---3 controleDAW.mjs:compteursDattente:", i, ":", compteursDattente[i]);
603
+
604
+ } else {
605
+ if (compteursDattente[i] > 0) {
606
+ if (debug) console.log("---4 controleDAW.mjs:compteursDattente:", compteursDattente[i]);
607
+ }
608
+ }
609
+ } else {
610
+ emptyQueueSignal = JSON.parse('{"emptyQueueSignal":"' + i + '"}');
611
+ if (automatePossibleMachine !== undefined) {
612
+ automatePossibleMachine.react(emptyQueueSignal);
613
+ if (debug) console.log("controleDAW.mjs: playAndShiftEventDAW:", emptyQueueSignal);
614
+ }
615
+ }
616
+ }
617
+ } // Fin du for
618
+
619
+ // -- Mettre à jour les attentes
620
+ // On décrémente les compteurs d'attente de la durée du tick
621
+ // Dans la file d'attente la durée du pattern devient le temps d'attente en fonction de la durée du tick.
622
+ // C'est une façon de gérer des patterns plus longs que le tick.
623
+ for (let i = 0; i < compteursDattente.length; i++) {
624
+ if (avecMusicien && decalageFIFOavecMusicien < timerDivisionLocal) {
625
+ console.log("ERR: Le décalage pour musicien doit être un multiple de timerDivision !");
626
+ }
627
+ if (compteursDattente[i] >= timerDivisionLocal) {
628
+ compteursDattente[i] -= timerDivisionLocal;
629
+ }
630
+ }
631
+ if (debug) console.log("--- controleDAW.mjs:FIFO:Durée d'attente des clips:", compteursDattente, " timer:", timerDivisionLocal);
632
+ return;
633
+ }
634
+
635
+ /**
636
+ * Empty the queues. No parameters.
637
+ */
638
+ export function cleanQueues() {
639
+ let messageLog = { date: "" };
640
+ if (debug) console.log("controleDAW.mjs : cleanQueues", filesDattenteJouables, compteursDattente, filesDattente);
641
+
642
+ for (let i = 0; i < filesDattente.length; i++) {
643
+ filesDattenteJouables[i] = true;
644
+ compteursDattente[i] = 0;
645
+ filesDattente[i] = [];
646
+ }
647
+ messageLog.source = "controleDAW.mjs";
648
+ messageLog.type = "VIDAGE FILES ATTENTES";
649
+ logInfoDAW(messageLog);
650
+ if (debug) console.log("controleDAW: cleanQueues");
651
+ serv.broadcast(JSON.stringify({ type: "etatDeLaFileAttente", value: filesDattente }));
652
+ }
653
+
654
+ /**
655
+ * Empty a specific queue.
656
+ * @param {number} instrument
657
+ * @returns {void}
658
+ */
659
+ export function cleanQueue(instrument) {
660
+ let messageLog = { date: "" };
661
+
662
+ if (instrument === 255) {
663
+ cleanQueues();
664
+ return;
665
+ }
666
+
667
+ if (filesDattente[instrument] === undefined) {
668
+ console.log("ERR: controleDAW.mjs: cleanQueue d'un instrument inexistant: ", instrument);
669
+ return;
670
+ }
671
+
672
+ filesDattente[instrument] = [];
673
+ compteursDattente[instrument] = 0;
674
+
675
+ if (debug) console.log("controleDAW: cleanqueue: instr,file,compteur:",
676
+ instrument, filesDattente[instrument], compteursDattente[instrument]);
677
+
678
+ messageLog.source = "controleDAW.js";
679
+ messageLog.type = "VIDAGE FILE ATTENTE " + instrument;
680
+ logInfoDAW(messageLog);
681
+ if (debug) console.log("controleDAW: cleanQueue: ", instrument);
682
+
683
+ // Avec l'instrument concerné
684
+ serv.broadcast(JSON.stringify({ type: "cleanQueues", instrument }));
685
+ }
686
+
687
+ /**
688
+ * Pause all the queues. Can be used during a game for example in order
689
+ * to let the audience do some action.
690
+ */
691
+ export function pauseQueues() {
692
+ let messageLog = { date: "" };
693
+ if (debug) console.log("controleDAW.mjs : pauseQueues", filesDattenteJouables, compteursDattente, filesDattente);
694
+
695
+ filesDattenteJouables.fill(false);
696
+ messageLog.source = "controleDAW.mjs";
697
+ messageLog.type = "PAUSE DES FILES ATTENTES";
698
+ logInfoDAW(messageLog);
699
+ if (debug1) console.log("controleDAW: pauseQueues");
700
+ serv.broadcast(JSON.stringify({ type: "pauseQueues", instrument: 255 }));
701
+ }
702
+
703
+ /**
704
+ * For restarting all the queues after a pauseQueues()
705
+ */
706
+ export function resumeQueues() {
707
+ let messageLog = { date: "" };
708
+ if (debug) console.log("controleDAW.mjs : pauseQueues", filesDattenteJouables, compteursDattente, filesDattente);
709
+
710
+ filesDattenteJouables.fill(true);
711
+ messageLog.source = "controleDAW.mjs";
712
+ messageLog.type = "REPRISE DES FILES ATTENTES";
713
+ logInfoDAW(messageLog);
714
+ if (debug1) console.log("controleDAW: resumeQueues");
715
+ // 255 => tous les instruments
716
+ serv.broadcast(JSON.stringify({ type: "resumeQueues", instrument: 255 }));
717
+ }
718
+
719
+ /**
720
+ * Pause a specific queue. Can be used for a game to keep some music playing
721
+ * when the audience has some actions to do.
722
+ * @param {number} instrument
723
+ */
724
+ export function pauseQueue(instrument) {
725
+ let messageLog = { date: "" };
726
+ filesDattenteJouables[instrument] = false;
727
+ messageLog.source = "controleDAW.mjs";
728
+ messageLog.type = "PAUSE FILE ATTENTE " + instrument;
729
+ logInfoDAW(messageLog);
730
+ if (debug1) console.log("controleDAW: pauseQueue: ", instrument);
731
+ serv.broadcast(JSON.stringify({ type: "pauseOneQueue", instrument }));
732
+ }
733
+
734
+ /**
735
+ * Restart a specific queue after pauseQueue(instrument).
736
+ * @param {number} instrument
737
+ */
738
+ export function resumeQueue(instrument) {
739
+ let messageLog = { date: "" };
740
+ filesDattenteJouables[instrument] = true;
741
+ messageLog.source = "controleDAW.mjs";
742
+ messageLog.type = "REPRISE FILE ATTENTE " + instrument;
743
+ logInfoDAW(messageLog);
744
+ if (debug1) console.log("controleDAW: resumeQueue: ", instrument);
745
+ serv.broadcast(JSON.stringify({ type: "resumeOneQueue", instrument }));
746
+ }
747
+
748
+ // ================= Visualisation de la table des commandes ===============
749
+
750
+ /**
751
+ * To get the number of people connected to Skini
752
+ * @returns {number} number of people connected
753
+ */
754
+ export function nbeDeSpectateursConnectes() {
755
+ return tableDesCommandes.filter(cmd => cmd[2] !== 0).length;
756
+ }
757
+
758
+ /**
759
+ * To get the complete list of all active patterns (or clips)
760
+ * for a group of clients.
761
+ * @param {number} groupeDeClients
762
+ * @param {array} matriceDesPossibles
763
+ * @returns {array} array of clips as in tableDesCommandes
764
+ */
765
+ export function getAllClips(groupeDeClients, matriceDesPossibles) {
766
+ // Protection
767
+ if (matriceDesPossibles === undefined) return -1
768
+ if (groupeDeClients === -1) return -1;
769
+
770
+ // Vérification de l'existence du groupe dans la matrice
771
+ if (!matriceDesPossibles[groupeDeClients]) {
772
+ if (debug) console.log("WARN:controleDAW :getAllClips:cannot get groupeDeClients:", groupeDeClients, "from matriceDesPossibles");
773
+ return -1;
774
+ }
775
+
776
+ // Récupération des groupes actifs une seule fois
777
+ const groupesActifs = matriceDesPossibles[groupeDeClients]
778
+ .map((estActif, index) => estActif ? index : -1)
779
+ .filter(index => index !== -1);
780
+
781
+ // Si aucun groupe n'est actif, retourner un tableau vide
782
+ if (groupesActifs.length === 0) {
783
+ return [];
784
+ }
785
+
786
+ // Filtrage direct des commandes avec les groupes actifs
787
+ return tableDesCommandes.filter(commande =>
788
+ commande[GROUP_ID] !== undefined &&
789
+ groupesActifs.includes(commande[GROUP_ID])
790
+ );
791
+ }
792
+
793
+ //================== Algorithmes de sélection V2 ===============================
794
+ export function getListClips(niv) {
795
+ var clipsNiveau1 = new Array();
796
+ var clipsNiveau2 = new Array();
797
+ var clipsNiveau3 = new Array();
798
+
799
+ //if (debug) console.log("controleDAW.mjs: getListClips: niveaux", niv);
800
+
801
+ if (niv[0] == 0) return tableDesCommandes;
802
+
803
+ // Remplir avec le niveau 1
804
+ var tableLength = tableDesCommandes.length;
805
+ for (var i = 0; i < tableLength; i++) {
806
+ if (tableDesCommandes[i][6] === niv[0]) { // Niveau1
807
+ clipsNiveau1.push(tableDesCommandes[i]);
808
+ }
809
+ }
810
+
811
+ //if (debug) console.log("controleDAW.mjs: getListClips: niveau1", clipsNiveau1 );
812
+
813
+ if (niv[1] == 0) return clipsNiveau1; // Si pas de niveau2 on renvoie le niveau1
814
+
815
+ // Filtrer le niveau 2
816
+ var tableLength = clipsNiveau1.length;
817
+ for (var i = 0; i < tableLength; i++) {
818
+ if (clipsNiveau1[i][7] === niv[1]) { // Niveau2
819
+ clipsNiveau2.push(clipsNiveau1[i]);
820
+ }
821
+ }
822
+
823
+ //if (debug) console.log("controleDAW.mjs: getListClips: niveau2", clipsNiveau2 );
824
+
825
+ if (niv[2] == 0) return clipsNiveau2; // Si pas de niveau3 on renvoie le niveau2
826
+
827
+ // Filtrer le niveau 3
828
+ var tableLength = clipsNiveau2.length;
829
+ for (var i = 0; i < tableLength; i++) {
830
+ if (clipsNiveau2[i][8] === niv[2]) { // Niveau3
831
+ clipsNiveau3.push(clipsNiveau2[i]);
832
+ }
833
+ }
834
+
835
+ //if (debug) console.log("controleDAW.mjs: getListClips: niveau3", clipsNiveau3 );
836
+
837
+ return clipsNiveau3;
838
+ }
839
+
840
+ //************** Réorganisation des Fifos **************
841
+ // Le mécanisme est décrit dans la doc utilisateur de Skini.
842
+ //
843
+ /**
844
+ * Insert a pattern between two types of patterns.
845
+ * Starts from the end of the FIFO.
846
+ */
847
+ function putPatternBetween(fifo, avant, apres, pattern) {
848
+ // Recherche de la position où insérer le pattern
849
+ const insertIndex = fifo.findIndex((item, i) =>
850
+ i > 0 &&
851
+ item[CD_TYPE_ID] === apres &&
852
+ fifo[i - 1][CD_TYPE_ID] === avant
853
+ );
854
+
855
+ // Si une position valide est trouvée, insérer le pattern
856
+ if (insertIndex !== -1) {
857
+ if (debug) {
858
+ console.log("---- putPatternBetween: On met le pattern:", pattern[CD_NOM_ID],
859
+ "de type:", pattern[CD_TYPE_ID], "en", insertIndex,
860
+ " (entre types", apres, " et ", avant, ")");
861
+ }
862
+ fifo.splice(insertIndex, 0, pattern);
863
+ return true;
864
+ }
865
+ return false;
866
+ }
867
+
868
+ /**
869
+ * Insert a pattern before a type of pattern.
870
+ * Starts from the end of the FIFO.
871
+ */
872
+ function putPatternBefore(fifo, apres, pattern) {
873
+ if (debug1) console.log("---- putPatternBefore");
874
+
875
+ // Recherche de la dernière occurrence du type "apres"
876
+ for (let i = fifo.length - 1; i >= 0; i--) {
877
+ if (fifo[i][CD_TYPE_ID] === apres) {
878
+ if (debug1) {
879
+ console.log("---- putPatternBefore: On met le pattern:", pattern[CD_NOM_ID],
880
+ "de type:", pattern[CD_TYPE_ID], "en", i, " (avant: ", apres, ")");
881
+ }
882
+ fifo.splice(i, 0, pattern);
883
+ return true;
884
+ }
885
+ }
886
+ return false;
887
+ }
888
+
889
+ /**
890
+ * Reorder a queue according to the pattern types DMFN. (Début, Milieu, Fin, Neutre)
891
+ *
892
+ * Note : The simulator offer the possibility to do that in a more
893
+ * powerfull way with the list of types.
894
+ *
895
+ * @param {Array} fifo - of the "queue" or "instrument"
896
+ * @param {Array} pattern
897
+ */
898
+ function ordonneFifo(fifo, pattern) {
899
+ // On ordonne la fifo en partant de l'indice haut qui correspond au denier pattern entré
900
+ // ceci évite de trop perturber le timing d'éntrée des patterns dans la fifo.
901
+ // pattern en fifo => [bus, channel, note, velocity, wsid, pseudo, dureeClip, nom, signal, typePattern]
902
+ if (debug1) console.log("---- ordonneFifo", pattern[CD_SIG_ID], pattern[CD_TYPE_ID]);
903
+
904
+ if (fifo.length === 0 || pattern[CD_TYPE_ID] === typeNeutre) {
905
+ if (debug1) console.log("---- ordonneFifo: Fifo vide ou pattern N");
906
+ fifo.push(pattern);
907
+ return;
908
+ }
909
+
910
+ if (fifo.length === 0 && pattern[CD_TYPE_ID] === typeMilieu) {
911
+ if (debug1) console.log("---- ordonneFifo: M tout seul");
912
+ fifo.push(pattern);
913
+ return;
914
+ }
915
+
916
+ switch (pattern[CD_TYPE_ID]) {
917
+ case typeDebut:
918
+ if (fifo.length > 1) { // Au moins 2 elements
919
+ if (putPatternBetween(fifo, typeFin, typeFin, pattern)) { return; }
920
+ if (putPatternBetween(fifo, typeFin, typeMilieu, pattern)) { return; }
921
+ if (putPatternBetween(fifo, typeMilieu, typeFin, pattern)) { return; }
922
+ if (putPatternBetween(fifo, typeMilieu, typeMilieu, pattern)) { return; }
923
+ fifo.push(pattern);
924
+ return;
925
+ } else { // Un seul élément
926
+ if (fifo[0][CD_TYPE_ID] === typeFin || fifo[0][CD_TYPE_ID] === typeMilieu) {
927
+ if (debug1) console.log("---- ordonneFifo: Permuttons F ou M et D");
928
+ fifo.splice(0, 0, pattern);
929
+ return;
930
+ } else { // ça fait 2 débuts de suite
931
+ fifo.push(pattern);
932
+ return;
933
+ }
934
+ }
935
+
936
+ case typeMilieu:
937
+ if (fifo.length > 1) {
938
+ if (putPatternBetween(fifo, typeDebut, typeFin, pattern)) { return; }
939
+ if (putPatternBetween(fifo, typeMilieu, typeFin, pattern)) { return; }
940
+ if (putPatternBetween(fifo, typeFin, typeFin, pattern)) { return; }
941
+ } else { // Il n'y a qu'un élément dans la Fifo.
942
+ if (fifo[0][CD_TYPE_ID] === typeFin) { // Si c'est une fin
943
+ if (debug1) console.log("---- ordonneFifo: Permuttons F et M");
944
+ fifo.splice(0, 0, pattern); // Insère un élément en début de fifo
945
+ return;
946
+ }
947
+ else { // C'est qu'on a un debut avant, mais on pourra avoir un début apres ou un autre milieu
948
+ if (debug1) console.log("---- ordonneFifo: Un pattern Milieu seul dans la fifo");
949
+ fifo.splice(0, 0, pattern); // Insère un élément en début de fifo
950
+ return;
951
+ }
952
+ }
953
+
954
+ case typeFin:
955
+ if (fifo.length > 1) {
956
+ if (putPatternBetween(fifo, typeDebut, typeDebut, pattern)) { return; }
957
+ if (putPatternBetween(fifo, typeMilieu, typeMilieu, pattern)) { return; }
958
+ if (fifo[fifo.length - 1][CD_TYPE_ID] === typeFin) { // le dernier élément est déjà une fin F
959
+ if (putPatternBetween(fifo, typeDebut, typeMilieu, pattern)) { return; }
960
+ }
961
+ } else {
962
+ // ça fait une fin toute seule
963
+ fifo.push(pattern);
964
+ return;
965
+ }
966
+
967
+ case typeMauvais:
968
+ // On ne fait rien dans ce cas.
969
+ break;
970
+
971
+ default: if (debug1) console.log("---- ordonneFifo:Pattern de type inconnu");
972
+ }
973
+ }
974
+
975
+
976
+ /**
977
+ * Algorithms for reorganizing the FIFO according to the vertical types.
978
+ * (Vertical type === 0) => no constraint
979
+ *
980
+ */
981
+
982
+ let EMPTY_CLIP = [0, 0, 0, 0, 0, "", 4, "", "", 0, "", "", "", 0];
983
+
984
+ function isEmptyClip(clip) {
985
+ // Un clip est vide seulement si NOTE_ID est 0 ET qu'il n'y a pas de contenu significatif
986
+ // Un clip avec TYPE_V_ID = 0 n'est PAS considéré comme vide car c'est un type valide
987
+ return clip[CD_NOTE_ID] === 0 &&
988
+ clip[CD_PSEUDO_ID] === "" &&
989
+ clip[CD_NOM_ID] === "" &&
990
+ clip[CD_SIG_ID] === "";
991
+ }
992
+
993
+ function ensureLength(arr, length) {
994
+ while (arr.length < length) {
995
+ arr.push([...EMPTY_CLIP]);
996
+ }
997
+ }
998
+
999
+ /************************************************************************************* */
1000
+ /**
1001
+ * Improved version of ordonneVerticalFIFO that strictly enforces compatibility rules.
1002
+ * A clip can only be added at an index level if:
1003
+ * 1. It has the same type as clips at the same index on other FIFOs, OR
1004
+ * 2. It has type 0 (compatible with all), OR
1005
+ * 3. There are only empty clips at the same index on other FIFOs, OR
1006
+ * 4. There are no clips at that index in other FIFOs
1007
+ */
1008
+ function ordonneVerticalFIFO(instruments, targetInstrumentIndex, newClip) {
1009
+ if (debug1) console.log("controleDAW: ordonneVerticalFIFO:", newClip[CD_NOTE_ID], newClip[CD_NOM_ID]);
1010
+
1011
+ const newId = newClip[CD_NOTE_ID];
1012
+ const newType = newClip[CD_TYPE_V_ID];
1013
+ const targetInst = instruments[targetInstrumentIndex];
1014
+
1015
+ // Type 0 est compatible avec tout, ajouter à la fin
1016
+ if (newType === 0) {
1017
+ targetInst.push([...newClip]);
1018
+ console.log(`✅ Clip [${newId}:${newType}] ajouté à la fin (type 0 - compatible avec tout)`);
1019
+ return;
1020
+ }
1021
+
1022
+ // Trouver la longueur maximale actuelle
1023
+ const maxLength = Math.max(...instruments.map(inst => inst.length), 0);
1024
+
1025
+ // Chercher un index compatible
1026
+ for (let index = 0; index <= maxLength; index++) {
1027
+ let canInsertAtThisIndex = true;
1028
+ let hasCompatibleClip = false;
1029
+
1030
+ // Vérifier la compatibilité avec tous les autres instruments à cet index
1031
+ for (let instIdx = 0; instIdx < instruments.length; instIdx++) {
1032
+ if (instIdx === targetInstrumentIndex) continue;
1033
+
1034
+ const otherInst = instruments[instIdx];
1035
+ const otherClip = otherInst[index];
1036
+
1037
+ if (!otherClip) {
1038
+ continue; // Pas de clip à cet index - compatible
1039
+ }
1040
+
1041
+ if (isEmptyClip(otherClip)) {
1042
+ continue; // Clip vide - compatible
1043
+ }
1044
+
1045
+ const otherType = otherClip[CD_TYPE_V_ID];
1046
+
1047
+ if (otherType === 0) {
1048
+ hasCompatibleClip = true;
1049
+ continue; // Type 0 - compatible avec tout
1050
+ }
1051
+
1052
+ if (otherType === newType) {
1053
+ hasCompatibleClip = true;
1054
+ continue; // Même type - compatible
1055
+ }
1056
+
1057
+ // Types différents non-zéro - incompatible
1058
+ canInsertAtThisIndex = false;
1059
+ break;
1060
+ }
1061
+
1062
+ if (canInsertAtThisIndex) {
1063
+ // S'assurer que toutes les files ont la même longueur jusqu'à cet index
1064
+ synchronizeAllQueuesLength(instruments, index + 1, newClip[CD_DUREE_ID]);
1065
+
1066
+ // Vérifier si la position cible est disponible
1067
+ const targetClip = targetInst[index];
1068
+ if (!targetClip || isEmptyClip(targetClip)) {
1069
+ targetInst[index] = [...newClip];
1070
+ console.log(`✅ Clip [${newId}:${newType}] inséré à l'index ${index} de l'instrument ${targetInstrumentIndex}`);
1071
+ return;
1072
+ }
1073
+ }
1074
+ }
1075
+
1076
+ // Si aucun index compatible trouvé, ajouter à la fin
1077
+ targetInst.push([...newClip]);
1078
+ console.log(`➕ Clip [${newId}:${newType}] ajouté à la fin (fallback) de l'instrument ${targetInstrumentIndex}`);
1079
+ }
1080
+
1081
+ /**
1082
+ * Synchronise la longueur de toutes les files d'attente en ajoutant des clips vides
1083
+ * avec une durée cohérente
1084
+ */
1085
+ function synchronizeAllQueuesLength(instruments, targetLength, clipDuration) {
1086
+ for (let i = 0; i < instruments.length; i++) {
1087
+ const inst = instruments[i];
1088
+ while (inst.length < targetLength) {
1089
+ // Créer un clip vide avec la durée appropriée
1090
+ const emptyClip = [
1091
+ 0, // CD_BUS_ID
1092
+ 0, // CD_CHANNEL_ID
1093
+ 0, // CD_NOTE_ID (0 = clip vide)
1094
+ 0, // CD_VEL_ID
1095
+ 0, // CD_WS_ID
1096
+ "", // CD_PSEUDO_ID
1097
+ clipDuration, // CD_DUREE_ID - durée cohérente
1098
+ "", // CD_NOM_ID
1099
+ "", // CD_SIG_ID
1100
+ 0, // CD_TYPE_ID
1101
+ "", // CD_IP_ID
1102
+ 0, // CD_BUF_ID
1103
+ 0, // CD_LEVEL_ID
1104
+ 0 // CD_TYPE_V_ID
1105
+ ];
1106
+ inst.push(emptyClip);
1107
+ }
1108
+ }
1109
+ }
1110
+
1111
+ // === AFFICHAGE LISIBLE ===
1112
+ function printInstruments(instruments) {
1113
+ const maxLength = Math.max(...instruments.map(inst => inst.length), 0);
1114
+ const header = ['Index'].concat(instruments.map((_, i) => `Instr.${i}`));
1115
+ const table = [];
1116
+
1117
+ for (let i = 0; i < maxLength; i++) {
1118
+ const row = [i];
1119
+ for (let j = 0; j < instruments.length; j++) {
1120
+ const clip = instruments[j][i];
1121
+ let str;
1122
+ if (!clip) {
1123
+ str = ' *'; // Pas de clip du tout
1124
+ } else if (isEmptyClip(clip)) {
1125
+ str = ' [ ]'; // Clip vide
1126
+ } else {
1127
+ str = `[${clip[CD_NOTE_ID]}:${clip[CD_TYPE_V_ID]}]`; // Clip avec contenu
1128
+ }
1129
+ row.push(str);
1130
+ }
1131
+ table.push(row);
1132
+ }
1133
+
1134
+ // Affichage formaté
1135
+ const colWidths = header.map((_, i) =>
1136
+ Math.max(...table.map(row => String(row[i]).length), header[i].length)
1137
+ );
1138
+
1139
+ const formatRow = row =>
1140
+ row.map((cell, i) => String(cell).padEnd(colWidths[i])).join(' | ');
1141
+
1142
+ console.log('\n🎼 État des instruments :');
1143
+ console.log(formatRow(header));
1144
+ console.log('-'.repeat(colWidths.reduce((a, b) => a + b + 3, 0)));
1145
+ for (const row of table) {
1146
+ console.log(formatRow(row));
1147
+ }
1148
+ console.log('\n');
1149
+ }