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.
- package/README.md +31 -0
- package/client/archive/bundle.js +11528 -0
- package/client/archive/golem.html +60 -0
- package/client/archive/golemClient.js +595 -0
- package/client/archive/golembundle.js +11634 -0
- package/client/archive/sequencer.html +43 -0
- package/client/archive/sequenceurClient.js +815 -0
- package/client/archive/stylegolem.css +158 -0
- package/client/archive/stylesequenceur.css +204 -0
- package/client/clientListe/Sortable-master/.circleci/config.yml +33 -0
- package/client/clientListe/Sortable-master/.editorconfig +15 -0
- package/client/clientListe/Sortable-master/.github/ISSUE_TEMPLATE/bug-report.md +73 -0
- package/client/clientListe/Sortable-master/.github/ISSUE_TEMPLATE/custom-template.md +48 -0
- package/client/clientListe/Sortable-master/.github/ISSUE_TEMPLATE/feature-request.md +41 -0
- package/client/clientListe/Sortable-master/.jshintrc +25 -0
- package/client/clientListe/Sortable-master/.testcaferc.json +7 -0
- package/client/clientListe/Sortable-master/CONTRIBUTING.md +26 -0
- package/client/clientListe/Sortable-master/LICENSE +21 -0
- package/client/clientListe/Sortable-master/README.md +813 -0
- package/client/clientListe/Sortable-master/Sortable.js +3709 -0
- package/client/clientListe/Sortable-master/Sortable.min.js +2 -0
- package/client/clientListe/Sortable-master/Sortable.min.old.js +2 -0
- package/client/clientListe/Sortable-master/Sortable.min.old.old.js +2 -0
- package/client/clientListe/Sortable-master/babel.config.js +27 -0
- package/client/clientListe/Sortable-master/bower.json +30 -0
- package/client/clientListe/Sortable-master/entry/entry-complete.js +8 -0
- package/client/clientListe/Sortable-master/entry/entry-core.js +19 -0
- package/client/clientListe/Sortable-master/entry/entry-defaults.js +19 -0
- package/client/clientListe/Sortable-master/index.html +460 -0
- package/client/clientListe/Sortable-master/modular/sortable.complete.esm.js +3701 -0
- package/client/clientListe/Sortable-master/modular/sortable.core.esm.js +3698 -0
- package/client/clientListe/Sortable-master/modular/sortable.esm.js +3699 -0
- package/client/clientListe/Sortable-master/package-lock.json +5704 -0
- package/client/clientListe/Sortable-master/package.json +56 -0
- package/client/clientListe/Sortable-master/plugins/AutoScroll/AutoScroll.js +270 -0
- package/client/clientListe/Sortable-master/plugins/AutoScroll/README.md +80 -0
- package/client/clientListe/Sortable-master/plugins/AutoScroll/index.js +1 -0
- package/client/clientListe/Sortable-master/plugins/MultiDrag/MultiDrag.js +617 -0
- package/client/clientListe/Sortable-master/plugins/MultiDrag/README.md +96 -0
- package/client/clientListe/Sortable-master/plugins/MultiDrag/index.js +1 -0
- package/client/clientListe/Sortable-master/plugins/OnSpill/OnSpill.js +79 -0
- package/client/clientListe/Sortable-master/plugins/OnSpill/README.md +60 -0
- package/client/clientListe/Sortable-master/plugins/OnSpill/index.js +1 -0
- package/client/clientListe/Sortable-master/plugins/README.md +178 -0
- package/client/clientListe/Sortable-master/plugins/Swap/README.md +55 -0
- package/client/clientListe/Sortable-master/plugins/Swap/Swap.js +90 -0
- package/client/clientListe/Sortable-master/plugins/Swap/index.js +1 -0
- package/client/clientListe/Sortable-master/scripts/banner.js +8 -0
- package/client/clientListe/Sortable-master/scripts/build.js +17 -0
- package/client/clientListe/Sortable-master/scripts/esm-build.js +28 -0
- package/client/clientListe/Sortable-master/scripts/minify.js +11 -0
- package/client/clientListe/Sortable-master/scripts/test-compat.js +30 -0
- package/client/clientListe/Sortable-master/scripts/test.js +21 -0
- package/client/clientListe/Sortable-master/scripts/umd-build.js +15 -0
- package/client/clientListe/Sortable-master/src/Animation.js +175 -0
- package/client/clientListe/Sortable-master/src/BrowserInfo.js +12 -0
- package/client/clientListe/Sortable-master/src/EventDispatcher.js +57 -0
- package/client/clientListe/Sortable-master/src/PluginManager.js +87 -0
- package/client/clientListe/Sortable-master/src/Sortable.js +1971 -0
- package/client/clientListe/Sortable-master/src/utils.js +556 -0
- package/client/clientListe/Sortable-master/st/app.js +224 -0
- package/client/clientListe/Sortable-master/st/iframe/frame.html +32 -0
- package/client/clientListe/Sortable-master/st/iframe/index.html +49 -0
- package/client/clientListe/Sortable-master/st/logo.png +0 -0
- package/client/clientListe/Sortable-master/st/og-image.png +0 -0
- package/client/clientListe/Sortable-master/st/prettify/prettify.css +1 -0
- package/client/clientListe/Sortable-master/st/prettify/prettify.js +46 -0
- package/client/clientListe/Sortable-master/st/prettify/run_prettify.js +64 -0
- package/client/clientListe/Sortable-master/st/saucelabs.svg +1 -0
- package/client/clientListe/Sortable-master/st/theme.css +254 -0
- package/client/clientListe/Sortable-master/tests/Sortable.compat.test.js +39 -0
- package/client/clientListe/Sortable-master/tests/Sortable.test.js +386 -0
- package/client/clientListe/Sortable-master/tests/dual-list.html +34 -0
- package/client/clientListe/Sortable-master/tests/empty-list.html +30 -0
- package/client/clientListe/Sortable-master/tests/filter.html +27 -0
- package/client/clientListe/Sortable-master/tests/handles.html +27 -0
- package/client/clientListe/Sortable-master/tests/nested.html +67 -0
- package/client/clientListe/Sortable-master/tests/single-list.html +25 -0
- package/client/clientListe/Sortable-master/tests/style.css +18 -0
- package/client/clientListe/clientListe.html +148 -0
- package/client/clientListe/clientListe.js +1508 -0
- package/client/clientListe/clientListebundle.js +13164 -0
- package/client/clientListe/images/poubelle.png +0 -0
- package/client/clientListe/images/start.png +0 -0
- package/client/clientListe/images/stop.png +0 -0
- package/client/clientListe/images/submit.png +0 -0
- package/client/clientListe/sortable-theme-bootstrap.css +90 -0
- package/client/configurateur/configReact.js +273 -0
- package/client/configurateur/configReactbundle.js +295 -0
- package/client/configurateur/configurateur.css +95 -0
- package/client/configurateur/configurateur.html +48 -0
- package/client/configurateur/lib/jexcel.css +755 -0
- package/client/configurateur/lib/jexcel.js +14970 -0
- package/client/configurateur/lib/jsuites.css +2801 -0
- package/client/configurateur/lib/jsuites.js +11822 -0
- package/client/configurateur/lib/react-dom.production.min.js +239 -0
- package/client/configurateur/lib/react.production.min.js +32 -0
- package/client/configurateur/src/configReact.js +247 -0
- package/client/controleur/clientcontroleur.js +536 -0
- package/client/controleur/clientcontroleur.test.js +282 -0
- package/client/controleur/controleur.html +51 -0
- package/client/controleur/controleurbundle.js +565 -0
- package/client/controleur/stylecontroleur.css +236 -0
- package/client/controleurHH/controleurHH.html +71 -0
- package/client/controleurHH/controleurHH.js +252 -0
- package/client/controleurHH/styles/index.css +320 -0
- package/client/controleurHH/styles/material.css +11552 -0
- package/client/parametrage/paramReact.js +473 -0
- package/client/parametrage/paramReactbundle.js +500 -0
- package/client/parametrage/parametrage.css +111 -0
- package/client/parametrage/parametrage.html +163 -0
- package/client/parametrage/src/paramReact.js +459 -0
- package/client/score/hash.js +83 -0
- package/client/score/p5.min.js +3 -0
- package/client/score/parto1.js +1171 -0
- package/client/score/parto1bundle.js +1181 -0
- package/client/score/processing.min.js +431 -0
- package/client/score/score.html +15 -0
- package/client/score/score.js +34 -0
- package/client/simulateurListe/simulateurFork.js +750 -0
- package/client/simulateurListe/simulateurFork.mjs +681 -0
- package/client/simulateurListe/simulateurForkSansReorg.js +569 -0
- package/client/simulateurListe/simulateurListe.js +628 -0
- package/myReact/archive/Nodeemitvaluedlocal1.hh.js +52 -0
- package/myReact/archive/abort-parNode.js +79 -0
- package/myReact/archive/abroNode.js +169 -0
- package/myReact/archive/abroNode2.js +80 -0
- package/myReact/archive/atom.compile.hh.js +51 -0
- package/myReact/archive/await-countNode.js +67 -0
- package/myReact/archive/await-nowvalNode.js +44 -0
- package/myReact/archive/callHH.js +96 -0
- package/myReact/archive/emit-if2.hh.compiled.js +113 -0
- package/myReact/archive/every1Node.js +35 -0
- package/myReact/archive/if-runNode.js +74 -0
- package/myReact/archive/if1Node.js +43 -0
- package/myReact/archive/makeawait.js +0 -0
- package/myReact/archive/myReact.old.js +684 -0
- package/myReact/archive/orchestration.js +281 -0
- package/myReact/archive/orchestration1.js +132 -0
- package/myReact/archive/orchestration1.xml +465 -0
- package/myReact/archive/orchestration2.js +161 -0
- package/myReact/archive/orchestrationHH.mano.js +280 -0
- package/myReact/archive/orchestrationHHTest.js +428 -0
- package/myReact/archive/orchestrationHHTest.xml +234 -0
- package/myReact/archive/orchestrationHHTestRun.js +104 -0
- package/myReact/archive/orchestrationHHTestRun.xml +34 -0
- package/myReact/archive/orchestrationTest0.js +178 -0
- package/myReact/archive/orchestrationTest1.js +181 -0
- package/myReact/archive/orchestrationTest2.js +281 -0
- package/myReact/archive/run3pointsNode.js +59 -0
- package/myReact/archive/runNode.js +123 -0
- package/myReact/archive/runNode2.js +91 -0
- package/myReact/archive/testAwait1.js +141 -0
- package/myReact/archive/testAwait1.xml +86 -0
- package/myReact/archive/testEvery1.js +122 -0
- package/myReact/archive/testEvery1.xml +79 -0
- package/myReact/archive/testHH1.js +135 -0
- package/myReact/archive/testHH1.xml +86 -0
- package/myReact/archive/testHH1revu.js +104 -0
- package/myReact/archive/testHH2.js +122 -0
- package/myReact/archive/testHH2.xml +79 -0
- package/myReact/archive/testHH3.js +130 -0
- package/myReact/archive/testHH3.xml +86 -0
- package/myReact/archive/testHHabort.js +121 -0
- package/myReact/archive/testHHabort.xml +83 -0
- package/myReact/archive/testMakeawait.js +202 -0
- package/myReact/archive/testRun1.js +168 -0
- package/myReact/archive/testRun1.xml +142 -0
- package/myReact/archive/titi.js +28 -0
- package/myReact/archive/titi.xml +110 -0
- package/myReact/archive/toto.js +73 -0
- package/myReact/archive/toto.xml +198 -0
- package/myReact/archive/trap-await-parallelNode.js +123 -0
- package/myReact/inutiles/hiver2022.xml +804 -0
- package/myReact/inutiles/hopeNode.xml +459 -0
- package/myReact/inutiles/mars2022.xml +871 -0
- package/myReact/inutiles/mystique1.xml +318 -0
- package/myReact/inutiles/mystiqueOSC.xml +277 -0
- package/myReact/inutiles/opus5Node.xml +1271 -0
- package/myReact/inutiles/opus5NodeLinux.xml +1241 -0
- package/myReact/inutiles/orchestrationHH.xml +541 -0
- package/myReact/inutiles/orchestrationHH2.xml +547 -0
- package/myReact/inutiles/testHH.xml +95 -0
- package/myReact/inutiles/trouveLaPercuTenor.xml +349 -0
- package/myReact/myReact.js +744 -0
- package/myReact/myReact.min.js +1 -0
- package/myReact/orchestrationHH.js +311 -0
- package/myReact/orchestrationHH.mjs +436 -0
- package/myReact/orchestrationHH.mjs.map +1 -0
- package/package.json +46 -0
- package/serveur/OSCandMidi.mjs +361 -0
- package/serveur/computeScore.mjs +415 -0
- package/serveur/controleDAW.mjs +1149 -0
- package/serveur/defaultSession.csv +2 -0
- package/serveur/defaultSkiniParametres.js +119 -0
- package/serveur/gameOSC.mjs +96 -0
- package/serveur/groupeClientsSons.mjs +1014 -0
- package/serveur/ipConfig.json +24 -0
- package/serveur/ipConfig127.json +19 -0
- package/serveur/ipConfig75.json +17 -0
- package/serveur/ipConfigBH.json +19 -0
- package/serveur/ipConfigLocal.json +19 -0
- package/serveur/midiConfig.json +26 -0
- package/serveur/midiConfigBH.json +26 -0
- package/serveur/midiConfigVoid.json +3 -0
- package/serveur/midimix.mjs +570 -0
- package/serveur/saveParam.mjs +159 -0
- package/serveur/skiniParametres.good.js +132 -0
- package/serveur/skiniParametres.js +106 -0
- package/serveur/utilsHHSkini.hh.js +64 -0
- package/serveur/utilsSkini.mjs +137 -0
- package/serveur/websocketServer.mjs +2052 -0
- package/serveur/workerInterfaceZ.mjs +327 -0
- package/serveur/workerSynchro.mjs +49 -0
- 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
|
+
}
|