@marmooo/midy 0.2.9 → 0.3.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/esm/midy-GM1.d.ts +8 -6
- package/esm/midy-GM1.d.ts.map +1 -1
- package/esm/midy-GM1.js +72 -55
- package/esm/midy-GM2.d.ts +14 -10
- package/esm/midy-GM2.d.ts.map +1 -1
- package/esm/midy-GM2.js +189 -87
- package/esm/midy-GMLite.d.ts +11 -8
- package/esm/midy-GMLite.d.ts.map +1 -1
- package/esm/midy-GMLite.js +124 -61
- package/esm/midy.d.ts +14 -10
- package/esm/midy.d.ts.map +1 -1
- package/esm/midy.js +190 -87
- package/package.json +1 -1
- package/script/midy-GM1.d.ts +8 -6
- package/script/midy-GM1.d.ts.map +1 -1
- package/script/midy-GM1.js +72 -55
- package/script/midy-GM2.d.ts +14 -10
- package/script/midy-GM2.d.ts.map +1 -1
- package/script/midy-GM2.js +189 -87
- package/script/midy-GMLite.d.ts +11 -8
- package/script/midy-GMLite.d.ts.map +1 -1
- package/script/midy-GMLite.js +124 -61
- package/script/midy.d.ts +14 -10
- package/script/midy.d.ts.map +1 -1
- package/script/midy.js +190 -87
package/script/midy.js
CHANGED
|
@@ -160,6 +160,39 @@ class Note {
|
|
|
160
160
|
this.voiceParams = voiceParams;
|
|
161
161
|
}
|
|
162
162
|
}
|
|
163
|
+
const drumExclusiveClassesByKit = new Array(57);
|
|
164
|
+
const drumExclusiveClassCount = 10;
|
|
165
|
+
const standardSet = new Uint8Array(128);
|
|
166
|
+
standardSet[42] = 1;
|
|
167
|
+
standardSet[44] = 1;
|
|
168
|
+
standardSet[46] = 1; // HH
|
|
169
|
+
standardSet[71] = 2;
|
|
170
|
+
standardSet[72] = 2; // Whistle
|
|
171
|
+
standardSet[73] = 3;
|
|
172
|
+
standardSet[74] = 3; // Guiro
|
|
173
|
+
standardSet[78] = 4;
|
|
174
|
+
standardSet[79] = 4; // Cuica
|
|
175
|
+
standardSet[80] = 5;
|
|
176
|
+
standardSet[81] = 5; // Triangle
|
|
177
|
+
standardSet[29] = 6;
|
|
178
|
+
standardSet[30] = 6; // Scratch
|
|
179
|
+
standardSet[86] = 7;
|
|
180
|
+
standardSet[87] = 7; // Surdo
|
|
181
|
+
drumExclusiveClassesByKit[0] = standardSet;
|
|
182
|
+
const analogSet = new Uint8Array(128);
|
|
183
|
+
analogSet[42] = 8;
|
|
184
|
+
analogSet[44] = 8;
|
|
185
|
+
analogSet[46] = 8; // CHH
|
|
186
|
+
drumExclusiveClassesByKit[25] = analogSet;
|
|
187
|
+
const orchestraSet = new Uint8Array(128);
|
|
188
|
+
orchestraSet[27] = 9;
|
|
189
|
+
orchestraSet[28] = 9;
|
|
190
|
+
orchestraSet[29] = 9; // HH
|
|
191
|
+
drumExclusiveClassesByKit[48] = orchestraSet;
|
|
192
|
+
const sfxSet = new Uint8Array(128);
|
|
193
|
+
sfxSet[41] = 10;
|
|
194
|
+
sfxSet[42] = 10; // Scratch
|
|
195
|
+
drumExclusiveClassesByKit[56] = sfxSet;
|
|
163
196
|
// normalized to 0-1 for use with the SF2 modulator model
|
|
164
197
|
const defaultControllerState = {
|
|
165
198
|
noteOnVelocity: { type: 2, defaultValue: 0 },
|
|
@@ -255,18 +288,6 @@ class Midy {
|
|
|
255
288
|
writable: true,
|
|
256
289
|
value: "GM2"
|
|
257
290
|
});
|
|
258
|
-
Object.defineProperty(this, "ticksPerBeat", {
|
|
259
|
-
enumerable: true,
|
|
260
|
-
configurable: true,
|
|
261
|
-
writable: true,
|
|
262
|
-
value: 120
|
|
263
|
-
});
|
|
264
|
-
Object.defineProperty(this, "totalTime", {
|
|
265
|
-
enumerable: true,
|
|
266
|
-
configurable: true,
|
|
267
|
-
writable: true,
|
|
268
|
-
value: 0
|
|
269
|
-
});
|
|
270
291
|
Object.defineProperty(this, "masterFineTuning", {
|
|
271
292
|
enumerable: true,
|
|
272
293
|
configurable: true,
|
|
@@ -300,6 +321,24 @@ class Midy {
|
|
|
300
321
|
delayTimes: this.generateDistributedArray(0.02, 2, 0.5),
|
|
301
322
|
}
|
|
302
323
|
});
|
|
324
|
+
Object.defineProperty(this, "numChannels", {
|
|
325
|
+
enumerable: true,
|
|
326
|
+
configurable: true,
|
|
327
|
+
writable: true,
|
|
328
|
+
value: 16
|
|
329
|
+
});
|
|
330
|
+
Object.defineProperty(this, "ticksPerBeat", {
|
|
331
|
+
enumerable: true,
|
|
332
|
+
configurable: true,
|
|
333
|
+
writable: true,
|
|
334
|
+
value: 120
|
|
335
|
+
});
|
|
336
|
+
Object.defineProperty(this, "totalTime", {
|
|
337
|
+
enumerable: true,
|
|
338
|
+
configurable: true,
|
|
339
|
+
writable: true,
|
|
340
|
+
value: 0
|
|
341
|
+
});
|
|
303
342
|
Object.defineProperty(this, "noteCheckInterval", {
|
|
304
343
|
enumerable: true,
|
|
305
344
|
configurable: true,
|
|
@@ -402,11 +441,17 @@ class Midy {
|
|
|
402
441
|
writable: true,
|
|
403
442
|
value: []
|
|
404
443
|
});
|
|
405
|
-
Object.defineProperty(this, "
|
|
444
|
+
Object.defineProperty(this, "exclusiveClassNotes", {
|
|
445
|
+
enumerable: true,
|
|
446
|
+
configurable: true,
|
|
447
|
+
writable: true,
|
|
448
|
+
value: new Array(128)
|
|
449
|
+
});
|
|
450
|
+
Object.defineProperty(this, "drumExclusiveClassNotes", {
|
|
406
451
|
enumerable: true,
|
|
407
452
|
configurable: true,
|
|
408
453
|
writable: true,
|
|
409
|
-
value: new
|
|
454
|
+
value: new Array(this.numChannels * drumExclusiveClassCount)
|
|
410
455
|
});
|
|
411
456
|
Object.defineProperty(this, "defaultOptions", {
|
|
412
457
|
enumerable: true,
|
|
@@ -500,8 +545,10 @@ class Midy {
|
|
|
500
545
|
};
|
|
501
546
|
}
|
|
502
547
|
createChannels(audioContext) {
|
|
503
|
-
const channels = Array.from({ length:
|
|
548
|
+
const channels = Array.from({ length: this.numChannels }, () => {
|
|
504
549
|
return {
|
|
550
|
+
currentBufferSource: null,
|
|
551
|
+
isDrum: false,
|
|
505
552
|
...this.constructor.channelSettings,
|
|
506
553
|
state: new ControllerState(),
|
|
507
554
|
controlTable: this.initControlTable(),
|
|
@@ -546,24 +593,10 @@ class Midy {
|
|
|
546
593
|
return audioBuffer;
|
|
547
594
|
}
|
|
548
595
|
}
|
|
549
|
-
|
|
550
|
-
if (channel.isDrum) {
|
|
551
|
-
const noteNumber = note.noteNumber;
|
|
552
|
-
if (noteNumber === 88 || 47 <= noteNumber && noteNumber <= 84) {
|
|
553
|
-
return true;
|
|
554
|
-
}
|
|
555
|
-
else {
|
|
556
|
-
return false;
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
else {
|
|
560
|
-
return voiceParams.sampleModes % 2 !== 0;
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
createBufferSource(channel, note, voiceParams, audioBuffer) {
|
|
596
|
+
createBufferSource(voiceParams, audioBuffer) {
|
|
564
597
|
const bufferSource = new AudioBufferSourceNode(this.audioContext);
|
|
565
598
|
bufferSource.buffer = audioBuffer;
|
|
566
|
-
bufferSource.loop =
|
|
599
|
+
bufferSource.loop = voiceParams.sampleModes % 2 !== 0;
|
|
567
600
|
if (bufferSource.loop) {
|
|
568
601
|
bufferSource.loopStart = voiceParams.loopStart / voiceParams.sampleRate;
|
|
569
602
|
bufferSource.loopEnd = voiceParams.loopEnd / voiceParams.sampleRate;
|
|
@@ -654,7 +687,8 @@ class Midy {
|
|
|
654
687
|
if (queueIndex >= this.timeline.length) {
|
|
655
688
|
await Promise.all(this.notePromises);
|
|
656
689
|
this.notePromises = [];
|
|
657
|
-
this.
|
|
690
|
+
this.exclusiveClassNotes.fill(undefined);
|
|
691
|
+
this.drumExclusiveClassNotes.fill(undefined);
|
|
658
692
|
this.audioBufferCache.clear();
|
|
659
693
|
resolve();
|
|
660
694
|
return;
|
|
@@ -673,7 +707,8 @@ class Midy {
|
|
|
673
707
|
else if (this.isStopping) {
|
|
674
708
|
await this.stopNotes(0, true, now);
|
|
675
709
|
this.notePromises = [];
|
|
676
|
-
this.
|
|
710
|
+
this.exclusiveClassNotes.fill(undefined);
|
|
711
|
+
this.drumExclusiveClassNotes.fill(undefined);
|
|
677
712
|
this.audioBufferCache.clear();
|
|
678
713
|
resolve();
|
|
679
714
|
this.isStopping = false;
|
|
@@ -682,7 +717,8 @@ class Midy {
|
|
|
682
717
|
}
|
|
683
718
|
else if (this.isSeeking) {
|
|
684
719
|
this.stopNotes(0, true, now);
|
|
685
|
-
this.
|
|
720
|
+
this.exclusiveClassNotes.fill(undefined);
|
|
721
|
+
this.drumExclusiveClassNotes.fill(undefined);
|
|
686
722
|
this.startTime = this.audioContext.currentTime;
|
|
687
723
|
queueIndex = this.getQueueIndex(this.resumeTime);
|
|
688
724
|
offset = this.resumeTime - this.startTime;
|
|
@@ -710,7 +746,7 @@ class Midy {
|
|
|
710
746
|
extractMidiData(midi) {
|
|
711
747
|
const instruments = new Set();
|
|
712
748
|
const timeline = [];
|
|
713
|
-
const tmpChannels = new Array(
|
|
749
|
+
const tmpChannels = new Array(this.channels.length);
|
|
714
750
|
for (let i = 0; i < tmpChannels.length; i++) {
|
|
715
751
|
tmpChannels[i] = {
|
|
716
752
|
programNumber: -1,
|
|
@@ -838,6 +874,9 @@ class Midy {
|
|
|
838
874
|
if (!this.isPlaying)
|
|
839
875
|
return;
|
|
840
876
|
this.isStopping = true;
|
|
877
|
+
for (let i = 0; i < this.channels.length; i++) {
|
|
878
|
+
this.resetAllStates(i);
|
|
879
|
+
}
|
|
841
880
|
}
|
|
842
881
|
pause() {
|
|
843
882
|
if (!this.isPlaying || this.isPaused)
|
|
@@ -1212,8 +1251,8 @@ class Midy {
|
|
|
1212
1251
|
note.vibratoLFO.connect(note.vibratoDepth);
|
|
1213
1252
|
note.vibratoDepth.connect(note.bufferSource.detune);
|
|
1214
1253
|
}
|
|
1215
|
-
async getAudioBuffer(
|
|
1216
|
-
const audioBufferId = this.getAudioBufferId(
|
|
1254
|
+
async getAudioBuffer(programNumber, noteNumber, velocity, voiceParams, isSF3) {
|
|
1255
|
+
const audioBufferId = this.getAudioBufferId(programNumber, noteNumber, velocity);
|
|
1217
1256
|
const cache = this.audioBufferCache.get(audioBufferId);
|
|
1218
1257
|
if (cache) {
|
|
1219
1258
|
cache.counter += 1;
|
|
@@ -1236,8 +1275,8 @@ class Midy {
|
|
|
1236
1275
|
const controllerState = this.getControllerState(channel, noteNumber, velocity);
|
|
1237
1276
|
const voiceParams = voice.getAllParams(controllerState);
|
|
1238
1277
|
const note = new Note(noteNumber, velocity, startTime, voice, voiceParams);
|
|
1239
|
-
const audioBuffer = await this.getAudioBuffer(channel.
|
|
1240
|
-
note.bufferSource = this.createBufferSource(
|
|
1278
|
+
const audioBuffer = await this.getAudioBuffer(channel.programNumber, noteNumber, velocity, voiceParams, isSF3);
|
|
1279
|
+
note.bufferSource = this.createBufferSource(voiceParams, audioBuffer);
|
|
1241
1280
|
note.volumeNode = new GainNode(this.audioContext);
|
|
1242
1281
|
note.gainL = new GainNode(this.audioContext);
|
|
1243
1282
|
note.gainR = new GainNode(this.audioContext);
|
|
@@ -1297,14 +1336,56 @@ class Midy {
|
|
|
1297
1336
|
return channel.bank;
|
|
1298
1337
|
}
|
|
1299
1338
|
}
|
|
1339
|
+
handleExclusiveClass(note, channelNumber, startTime) {
|
|
1340
|
+
const exclusiveClass = note.voiceParams.exclusiveClass;
|
|
1341
|
+
if (exclusiveClass === 0)
|
|
1342
|
+
return;
|
|
1343
|
+
const prev = this.exclusiveClassNotes[exclusiveClass];
|
|
1344
|
+
if (prev) {
|
|
1345
|
+
const [prevNote, prevChannelNumber] = prev;
|
|
1346
|
+
if (prevNote && !prevNote.ending) {
|
|
1347
|
+
this.scheduleNoteOff(prevChannelNumber, prevNote.noteNumber, 0, // velocity,
|
|
1348
|
+
startTime, true, // force
|
|
1349
|
+
undefined);
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
this.exclusiveClassNotes[exclusiveClass] = [note, channelNumber];
|
|
1353
|
+
}
|
|
1354
|
+
handleDrumExclusiveClass(note, channelNumber, startTime) {
|
|
1355
|
+
const channel = this.channels[channelNumber];
|
|
1356
|
+
if (!channel.isDrum)
|
|
1357
|
+
return;
|
|
1358
|
+
const kitTable = drumExclusiveClassesByKit[channel.programNumber];
|
|
1359
|
+
if (!kitTable)
|
|
1360
|
+
return;
|
|
1361
|
+
const drumExclusiveClass = kitTable[note.noteNumber];
|
|
1362
|
+
if (drumExclusiveClass === 0)
|
|
1363
|
+
return;
|
|
1364
|
+
const index = (drumExclusiveClass - 1) * this.channels.length +
|
|
1365
|
+
channelNumber;
|
|
1366
|
+
const prevNote = this.drumExclusiveClassNotes[index];
|
|
1367
|
+
if (prevNote && !prevNote.ending) {
|
|
1368
|
+
this.scheduleNoteOff(channelNumber, prevNote.noteNumber, 0, // velocity,
|
|
1369
|
+
startTime, true, // force
|
|
1370
|
+
undefined);
|
|
1371
|
+
}
|
|
1372
|
+
this.drumExclusiveClassNotes[index] = note;
|
|
1373
|
+
}
|
|
1374
|
+
isDrumNoteOffException(channel, noteNumber) {
|
|
1375
|
+
if (!channel.isDrum)
|
|
1376
|
+
return false;
|
|
1377
|
+
const programNumber = channel.programNumber;
|
|
1378
|
+
return (programNumber === 48 && noteNumber === 88) ||
|
|
1379
|
+
(programNumber === 56 && 47 <= noteNumber && noteNumber <= 84);
|
|
1380
|
+
}
|
|
1300
1381
|
async scheduleNoteOn(channelNumber, noteNumber, velocity, startTime, portamento) {
|
|
1301
1382
|
const channel = this.channels[channelNumber];
|
|
1302
1383
|
const bankNumber = this.calcBank(channel, channelNumber);
|
|
1303
|
-
const soundFontIndex = this.soundFontTable[channel.
|
|
1384
|
+
const soundFontIndex = this.soundFontTable[channel.programNumber].get(bankNumber);
|
|
1304
1385
|
if (soundFontIndex === undefined)
|
|
1305
1386
|
return;
|
|
1306
1387
|
const soundFont = this.soundFonts[soundFontIndex];
|
|
1307
|
-
const voice = soundFont.getVoice(bankNumber, channel.
|
|
1388
|
+
const voice = soundFont.getVoice(bankNumber, channel.programNumber, noteNumber, velocity);
|
|
1308
1389
|
if (!voice)
|
|
1309
1390
|
return;
|
|
1310
1391
|
const isSF3 = soundFont.parsed.info.version.major === 3;
|
|
@@ -1314,31 +1395,58 @@ class Midy {
|
|
|
1314
1395
|
if (0.5 <= channel.state.sustainPedal) {
|
|
1315
1396
|
channel.sustainNotes.push(note);
|
|
1316
1397
|
}
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
if (this.exclusiveClassMap.has(exclusiveClass)) {
|
|
1320
|
-
const prevEntry = this.exclusiveClassMap.get(exclusiveClass);
|
|
1321
|
-
const [prevNote, prevChannelNumber] = prevEntry;
|
|
1322
|
-
if (prevNote && !prevNote.ending) {
|
|
1323
|
-
this.scheduleNoteOff(prevChannelNumber, prevNote.noteNumber, 0, // velocity,
|
|
1324
|
-
startTime, true, // force
|
|
1325
|
-
undefined);
|
|
1326
|
-
}
|
|
1327
|
-
}
|
|
1328
|
-
this.exclusiveClassMap.set(exclusiveClass, [note, channelNumber]);
|
|
1329
|
-
}
|
|
1398
|
+
this.handleExclusiveClass(note, channelNumber, startTime);
|
|
1399
|
+
this.handleDrumExclusiveClass(note, channelNumber, startTime);
|
|
1330
1400
|
const scheduledNotes = channel.scheduledNotes;
|
|
1331
|
-
|
|
1332
|
-
|
|
1401
|
+
let notes = scheduledNotes.get(noteNumber);
|
|
1402
|
+
if (notes) {
|
|
1403
|
+
notes.push(note);
|
|
1333
1404
|
}
|
|
1334
1405
|
else {
|
|
1335
|
-
|
|
1406
|
+
notes = [note];
|
|
1407
|
+
scheduledNotes.set(noteNumber, notes);
|
|
1408
|
+
}
|
|
1409
|
+
if (this.isDrumNoteOffException(channel, noteNumber)) {
|
|
1410
|
+
const stopTime = startTime + note.bufferSource.buffer.duration;
|
|
1411
|
+
const index = notes.length - 1;
|
|
1412
|
+
const promise = new Promise((resolve) => {
|
|
1413
|
+
note.bufferSource.onended = () => {
|
|
1414
|
+
this.disconnectNote(note, scheduledNotes, index);
|
|
1415
|
+
resolve();
|
|
1416
|
+
};
|
|
1417
|
+
note.bufferSource.stop(stopTime);
|
|
1418
|
+
});
|
|
1419
|
+
this.notePromises.push(promise);
|
|
1336
1420
|
}
|
|
1337
1421
|
}
|
|
1338
1422
|
noteOn(channelNumber, noteNumber, velocity, scheduleTime) {
|
|
1339
1423
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1340
1424
|
return this.scheduleNoteOn(channelNumber, noteNumber, velocity, scheduleTime, false);
|
|
1341
1425
|
}
|
|
1426
|
+
disconnectNote(note, scheduledNotes, index) {
|
|
1427
|
+
scheduledNotes[index] = null;
|
|
1428
|
+
note.bufferSource.disconnect();
|
|
1429
|
+
note.filterNode.disconnect();
|
|
1430
|
+
note.volumeEnvelopeNode.disconnect();
|
|
1431
|
+
note.volumeNode.disconnect();
|
|
1432
|
+
note.gainL.disconnect();
|
|
1433
|
+
note.gainR.disconnect();
|
|
1434
|
+
if (note.modulationDepth) {
|
|
1435
|
+
note.volumeDepth.disconnect();
|
|
1436
|
+
note.modulationDepth.disconnect();
|
|
1437
|
+
note.modulationLFO.stop();
|
|
1438
|
+
}
|
|
1439
|
+
if (note.vibratoDepth) {
|
|
1440
|
+
note.vibratoDepth.disconnect();
|
|
1441
|
+
note.vibratoLFO.stop();
|
|
1442
|
+
}
|
|
1443
|
+
if (note.reverbEffectsSend) {
|
|
1444
|
+
note.reverbEffectsSend.disconnect();
|
|
1445
|
+
}
|
|
1446
|
+
if (note.chorusEffectsSend) {
|
|
1447
|
+
note.chorusEffectsSend.disconnect();
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1342
1450
|
stopNote(endTime, stopTime, scheduledNotes, index) {
|
|
1343
1451
|
const note = scheduledNotes[index];
|
|
1344
1452
|
note.volumeEnvelopeNode.gain
|
|
@@ -1350,28 +1458,7 @@ class Midy {
|
|
|
1350
1458
|
}, stopTime);
|
|
1351
1459
|
return new Promise((resolve) => {
|
|
1352
1460
|
note.bufferSource.onended = () => {
|
|
1353
|
-
scheduledNotes
|
|
1354
|
-
note.bufferSource.disconnect();
|
|
1355
|
-
note.filterNode.disconnect();
|
|
1356
|
-
note.volumeEnvelopeNode.disconnect();
|
|
1357
|
-
note.volumeNode.disconnect();
|
|
1358
|
-
note.gainL.disconnect();
|
|
1359
|
-
note.gainR.disconnect();
|
|
1360
|
-
if (note.modulationDepth) {
|
|
1361
|
-
note.volumeDepth.disconnect();
|
|
1362
|
-
note.modulationDepth.disconnect();
|
|
1363
|
-
note.modulationLFO.stop();
|
|
1364
|
-
}
|
|
1365
|
-
if (note.vibratoDepth) {
|
|
1366
|
-
note.vibratoDepth.disconnect();
|
|
1367
|
-
note.vibratoLFO.stop();
|
|
1368
|
-
}
|
|
1369
|
-
if (note.reverbEffectsSend) {
|
|
1370
|
-
note.reverbEffectsSend.disconnect();
|
|
1371
|
-
}
|
|
1372
|
-
if (note.chorusEffectsSend) {
|
|
1373
|
-
note.chorusEffectsSend.disconnect();
|
|
1374
|
-
}
|
|
1461
|
+
this.disconnectNote(note, scheduledNotes, index);
|
|
1375
1462
|
resolve();
|
|
1376
1463
|
};
|
|
1377
1464
|
note.bufferSource.stop(stopTime);
|
|
@@ -1379,6 +1466,8 @@ class Midy {
|
|
|
1379
1466
|
}
|
|
1380
1467
|
scheduleNoteOff(channelNumber, noteNumber, _velocity, endTime, force, portamentoNoteNumber) {
|
|
1381
1468
|
const channel = this.channels[channelNumber];
|
|
1469
|
+
if (this.isDrumNoteOffException(channel, noteNumber))
|
|
1470
|
+
return;
|
|
1382
1471
|
const state = channel.state;
|
|
1383
1472
|
if (!force) {
|
|
1384
1473
|
if (0.5 <= state.sustainPedal)
|
|
@@ -1478,10 +1567,10 @@ class Midy {
|
|
|
1478
1567
|
}
|
|
1479
1568
|
// this.applyVoiceParams(channel, 10);
|
|
1480
1569
|
}
|
|
1481
|
-
handleProgramChange(channelNumber,
|
|
1570
|
+
handleProgramChange(channelNumber, programNumber, _scheduleTime) {
|
|
1482
1571
|
const channel = this.channels[channelNumber];
|
|
1483
1572
|
channel.bank = channel.bankMSB * 128 + channel.bankLSB;
|
|
1484
|
-
channel.
|
|
1573
|
+
channel.programNumber = programNumber;
|
|
1485
1574
|
if (this.mode === "GM2") {
|
|
1486
1575
|
switch (channel.bankMSB) {
|
|
1487
1576
|
case 120:
|
|
@@ -2232,16 +2321,31 @@ class Midy {
|
|
|
2232
2321
|
scheduleTime ??= this.audioContext.currentTime;
|
|
2233
2322
|
return this.stopChannelNotes(channelNumber, 0, true, scheduleTime);
|
|
2234
2323
|
}
|
|
2324
|
+
resetAllStates(channelNumber) {
|
|
2325
|
+
const channel = this.channels[channelNumber];
|
|
2326
|
+
const state = channel.state;
|
|
2327
|
+
for (const type of Object.keys(defaultControllerState)) {
|
|
2328
|
+
state[type] = defaultControllerState[type].defaultValue;
|
|
2329
|
+
}
|
|
2330
|
+
for (const type of Object.keys(this.constructor.channelSettings)) {
|
|
2331
|
+
channel[type] = this.constructor.channelSettings[type];
|
|
2332
|
+
}
|
|
2333
|
+
this.mode = "GM2";
|
|
2334
|
+
this.masterFineTuning = 0; // cb
|
|
2335
|
+
this.masterCoarseTuning = 0; // cb
|
|
2336
|
+
}
|
|
2337
|
+
// https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/rp15.pdf
|
|
2235
2338
|
resetAllControllers(channelNumber) {
|
|
2236
2339
|
const stateTypes = [
|
|
2340
|
+
"polyphonicKeyPressure",
|
|
2341
|
+
"channelPressure",
|
|
2342
|
+
"pitchWheel",
|
|
2237
2343
|
"expression",
|
|
2238
2344
|
"modulationDepth",
|
|
2239
2345
|
"sustainPedal",
|
|
2240
2346
|
"portamento",
|
|
2241
2347
|
"sostenutoPedal",
|
|
2242
2348
|
"softPedal",
|
|
2243
|
-
"channelPressure",
|
|
2244
|
-
"pitchWheelSensitivity",
|
|
2245
2349
|
];
|
|
2246
2350
|
const channel = this.channels[channelNumber];
|
|
2247
2351
|
const state = channel.state;
|
|
@@ -2622,7 +2726,7 @@ class Midy {
|
|
|
2622
2726
|
return value * 0.00787;
|
|
2623
2727
|
}
|
|
2624
2728
|
getChannelBitmap(data) {
|
|
2625
|
-
const bitmap = new Array(
|
|
2729
|
+
const bitmap = new Array(this.channels.length).fill(false);
|
|
2626
2730
|
const ff = data[4] & 0b11;
|
|
2627
2731
|
const gg = data[5] & 0x7F;
|
|
2628
2732
|
const hh = data[6] & 0x7F;
|
|
@@ -2814,6 +2918,7 @@ class Midy {
|
|
|
2814
2918
|
console.warn(`Unsupported Exclusive Message: ${data}`);
|
|
2815
2919
|
}
|
|
2816
2920
|
}
|
|
2921
|
+
// https://github.com/marmooo/js-timer-benchmark
|
|
2817
2922
|
scheduleTask(callback, scheduleTime) {
|
|
2818
2923
|
return new Promise((resolve) => {
|
|
2819
2924
|
const bufferSource = new AudioBufferSourceNode(this.audioContext, {
|
|
@@ -2839,10 +2944,8 @@ Object.defineProperty(Midy, "channelSettings", {
|
|
|
2839
2944
|
configurable: true,
|
|
2840
2945
|
writable: true,
|
|
2841
2946
|
value: {
|
|
2842
|
-
currentBufferSource: null,
|
|
2843
|
-
isDrum: false,
|
|
2844
2947
|
detune: 0,
|
|
2845
|
-
|
|
2948
|
+
programNumber: 0,
|
|
2846
2949
|
bank: 121 * 128,
|
|
2847
2950
|
bankMSB: 121,
|
|
2848
2951
|
bankLSB: 0,
|