@marmooo/midy 0.1.5 → 0.1.6
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 +7 -7
- package/esm/midy-GM1.d.ts.map +1 -1
- package/esm/midy-GM1.js +62 -59
- package/esm/midy-GM2.d.ts +12 -9
- package/esm/midy-GM2.d.ts.map +1 -1
- package/esm/midy-GM2.js +156 -79
- package/esm/midy-GMLite.d.ts +7 -7
- package/esm/midy-GMLite.d.ts.map +1 -1
- package/esm/midy-GMLite.js +62 -59
- package/esm/midy.d.ts +12 -9
- package/esm/midy.d.ts.map +1 -1
- package/esm/midy.js +156 -83
- package/package.json +1 -1
- package/script/midy-GM1.d.ts +7 -7
- package/script/midy-GM1.d.ts.map +1 -1
- package/script/midy-GM1.js +62 -59
- package/script/midy-GM2.d.ts +12 -9
- package/script/midy-GM2.d.ts.map +1 -1
- package/script/midy-GM2.js +156 -79
- package/script/midy-GMLite.d.ts +7 -7
- package/script/midy-GMLite.d.ts.map +1 -1
- package/script/midy-GMLite.js +62 -59
- package/script/midy.d.ts +12 -9
- package/script/midy.d.ts.map +1 -1
- package/script/midy.js +156 -83
package/script/midy.js
CHANGED
|
@@ -252,12 +252,14 @@ class Midy {
|
|
|
252
252
|
addSoundFont(soundFont) {
|
|
253
253
|
const index = this.soundFonts.length;
|
|
254
254
|
this.soundFonts.push(soundFont);
|
|
255
|
-
soundFont.parsed.presetHeaders
|
|
255
|
+
const presetHeaders = soundFont.parsed.presetHeaders;
|
|
256
|
+
for (let i = 0; i < presetHeaders.length; i++) {
|
|
257
|
+
const presetHeader = presetHeaders[i];
|
|
256
258
|
if (!presetHeader.presetName.startsWith("\u0000")) { // TODO: Only SF3 generated by PolyPone?
|
|
257
259
|
const banks = this.soundFontTable[presetHeader.preset];
|
|
258
260
|
banks.set(presetHeader.bank, index);
|
|
259
261
|
}
|
|
260
|
-
}
|
|
262
|
+
}
|
|
261
263
|
}
|
|
262
264
|
async loadSoundFont(soundFontUrl) {
|
|
263
265
|
const response = await fetch(soundFontUrl);
|
|
@@ -309,27 +311,25 @@ class Midy {
|
|
|
309
311
|
return channels;
|
|
310
312
|
}
|
|
311
313
|
async createNoteBuffer(instrumentKey, isSF3) {
|
|
314
|
+
const sampleStart = instrumentKey.start;
|
|
312
315
|
const sampleEnd = instrumentKey.sample.length + instrumentKey.end;
|
|
313
316
|
if (isSF3) {
|
|
314
|
-
const sample =
|
|
315
|
-
sample.set(instrumentKey.sample);
|
|
317
|
+
const sample = instrumentKey.sample.slice(sampleStart, sampleEnd);
|
|
316
318
|
const audioBuffer = await this.audioContext.decodeAudioData(sample.buffer);
|
|
317
|
-
for (let channel = 0; channel < audioBuffer.numberOfChannels; channel++) {
|
|
318
|
-
const channelData = audioBuffer.getChannelData(channel);
|
|
319
|
-
channelData.set(channelData.subarray(0, sampleEnd));
|
|
320
|
-
}
|
|
321
319
|
return audioBuffer;
|
|
322
320
|
}
|
|
323
321
|
else {
|
|
324
|
-
const sample = instrumentKey.sample.subarray(
|
|
325
|
-
const floatSample = this.convertToFloat32Array(sample);
|
|
322
|
+
const sample = instrumentKey.sample.subarray(sampleStart, sampleEnd);
|
|
326
323
|
const audioBuffer = new AudioBuffer({
|
|
327
324
|
numberOfChannels: 1,
|
|
328
325
|
length: sample.length,
|
|
329
326
|
sampleRate: instrumentKey.sampleRate,
|
|
330
327
|
});
|
|
331
328
|
const channelData = audioBuffer.getChannelData(0);
|
|
332
|
-
|
|
329
|
+
const int16Array = new Int16Array(sample.buffer);
|
|
330
|
+
for (let i = 0; i < int16Array.length; i++) {
|
|
331
|
+
channelData[i] = int16Array[i] / 32768;
|
|
332
|
+
}
|
|
333
333
|
return audioBuffer;
|
|
334
334
|
}
|
|
335
335
|
}
|
|
@@ -345,13 +345,23 @@ class Midy {
|
|
|
345
345
|
}
|
|
346
346
|
return bufferSource;
|
|
347
347
|
}
|
|
348
|
-
|
|
349
|
-
const
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
348
|
+
findPortamentoTarget(queueIndex) {
|
|
349
|
+
const endEvent = this.timeline[queueIndex];
|
|
350
|
+
if (!this.channels[endEvent.channel].portamento)
|
|
351
|
+
return;
|
|
352
|
+
const endTime = endEvent.startTime;
|
|
353
|
+
let target;
|
|
354
|
+
while (++queueIndex < this.timeline.length) {
|
|
355
|
+
const event = this.timeline[queueIndex];
|
|
356
|
+
if (endTime !== event.startTime)
|
|
357
|
+
break;
|
|
358
|
+
if (event.type !== "noteOn")
|
|
359
|
+
continue;
|
|
360
|
+
if (!target || event.noteNumber < target.noteNumber) {
|
|
361
|
+
target = event;
|
|
362
|
+
}
|
|
353
363
|
}
|
|
354
|
-
return
|
|
364
|
+
return target;
|
|
355
365
|
}
|
|
356
366
|
async scheduleTimelineEvents(t, offset, queueIndex) {
|
|
357
367
|
while (queueIndex < this.timeline.length) {
|
|
@@ -361,12 +371,15 @@ class Midy {
|
|
|
361
371
|
switch (event.type) {
|
|
362
372
|
case "noteOn":
|
|
363
373
|
if (event.velocity !== 0) {
|
|
364
|
-
await this.scheduleNoteOn(event.channel, event.noteNumber, event.velocity, event.startTime + this.startDelay - offset);
|
|
374
|
+
await this.scheduleNoteOn(event.channel, event.noteNumber, event.velocity, event.startTime + this.startDelay - offset, event.portamento);
|
|
365
375
|
break;
|
|
366
376
|
}
|
|
367
377
|
/* falls through */
|
|
368
378
|
case "noteOff": {
|
|
369
|
-
const
|
|
379
|
+
const portamentoTarget = this.findPortamentoTarget(queueIndex);
|
|
380
|
+
if (portamentoTarget)
|
|
381
|
+
portamentoTarget.portamento = true;
|
|
382
|
+
const notePromise = this.scheduleNoteRelease(this.omni ? 0 : event.channel, event.noteNumber, event.velocity, event.startTime + this.startDelay - offset, portamentoTarget?.noteNumber, false);
|
|
370
383
|
if (notePromise) {
|
|
371
384
|
this.notePromises.push(notePromise);
|
|
372
385
|
}
|
|
@@ -470,9 +483,11 @@ class Midy {
|
|
|
470
483
|
bankLSB: this.channels[i].bankLSB,
|
|
471
484
|
};
|
|
472
485
|
}
|
|
473
|
-
midi.tracks.
|
|
486
|
+
for (let i = 0; i < midi.tracks.length; i++) {
|
|
487
|
+
const track = midi.tracks[i];
|
|
474
488
|
let currentTicks = 0;
|
|
475
|
-
track.
|
|
489
|
+
for (let j = 0; j < track.length; j++) {
|
|
490
|
+
const event = track[j];
|
|
476
491
|
currentTicks += event.deltaTime;
|
|
477
492
|
event.ticks = currentTicks;
|
|
478
493
|
switch (event.type) {
|
|
@@ -525,16 +540,18 @@ class Midy {
|
|
|
525
540
|
}
|
|
526
541
|
delete event.deltaTime;
|
|
527
542
|
timeline.push(event);
|
|
528
|
-
}
|
|
529
|
-
}
|
|
543
|
+
}
|
|
544
|
+
}
|
|
530
545
|
const priority = {
|
|
531
546
|
controller: 0,
|
|
532
547
|
sysEx: 1,
|
|
548
|
+
noteOff: 2, // for portamento
|
|
549
|
+
noteOn: 3,
|
|
533
550
|
};
|
|
534
551
|
timeline.sort((a, b) => {
|
|
535
552
|
if (a.ticks !== b.ticks)
|
|
536
553
|
return a.ticks - b.ticks;
|
|
537
|
-
return (priority[a.type] ||
|
|
554
|
+
return (priority[a.type] || 4) - (priority[b.type] || 4);
|
|
538
555
|
});
|
|
539
556
|
let prevTempoTime = 0;
|
|
540
557
|
let prevTempoTicks = 0;
|
|
@@ -551,7 +568,7 @@ class Midy {
|
|
|
551
568
|
}
|
|
552
569
|
return { instruments, timeline };
|
|
553
570
|
}
|
|
554
|
-
async stopChannelNotes(channelNumber, velocity,
|
|
571
|
+
async stopChannelNotes(channelNumber, velocity, force) {
|
|
555
572
|
const now = this.audioContext.currentTime;
|
|
556
573
|
const channel = this.channels[channelNumber];
|
|
557
574
|
channel.scheduledNotes.forEach((noteList) => {
|
|
@@ -559,16 +576,17 @@ class Midy {
|
|
|
559
576
|
const note = noteList[i];
|
|
560
577
|
if (!note)
|
|
561
578
|
continue;
|
|
562
|
-
const promise = this.scheduleNoteRelease(channelNumber, note.noteNumber, velocity, now,
|
|
579
|
+
const promise = this.scheduleNoteRelease(channelNumber, note.noteNumber, velocity, now, undefined, // portamentoNoteNumber
|
|
580
|
+
force);
|
|
563
581
|
this.notePromises.push(promise);
|
|
564
582
|
}
|
|
565
583
|
});
|
|
566
584
|
channel.scheduledNotes.clear();
|
|
567
585
|
await Promise.all(this.notePromises);
|
|
568
586
|
}
|
|
569
|
-
stopNotes(velocity,
|
|
587
|
+
stopNotes(velocity, force) {
|
|
570
588
|
for (let i = 0; i < this.channels.length; i++) {
|
|
571
|
-
this.stopChannelNotes(i, velocity,
|
|
589
|
+
this.stopChannelNotes(i, velocity, force);
|
|
572
590
|
}
|
|
573
591
|
return Promise.all(this.notePromises);
|
|
574
592
|
}
|
|
@@ -782,6 +800,17 @@ class Midy {
|
|
|
782
800
|
return instrumentKey.playbackRate(noteNumber) *
|
|
783
801
|
Math.pow(2, semitoneOffset / 12);
|
|
784
802
|
}
|
|
803
|
+
setPortamentoStartVolumeEnvelope(channel, note) {
|
|
804
|
+
const { instrumentKey, startTime } = note;
|
|
805
|
+
const attackVolume = this.cbToRatio(-instrumentKey.initialAttenuation);
|
|
806
|
+
const sustainVolume = attackVolume * (1 - instrumentKey.volSustain);
|
|
807
|
+
const volDelay = startTime + instrumentKey.volDelay;
|
|
808
|
+
const portamentoTime = volDelay + channel.portamentoTime;
|
|
809
|
+
note.volumeNode.gain
|
|
810
|
+
.cancelScheduledValues(startTime)
|
|
811
|
+
.setValueAtTime(0, volDelay)
|
|
812
|
+
.linearRampToValueAtTime(sustainVolume, portamentoTime);
|
|
813
|
+
}
|
|
785
814
|
setVolumeEnvelope(channel, note) {
|
|
786
815
|
const { instrumentKey, startTime } = note;
|
|
787
816
|
const attackVolume = this.cbToRatio(-instrumentKey.initialAttenuation);
|
|
@@ -821,6 +850,25 @@ class Midy {
|
|
|
821
850
|
const maxFrequency = 20000; // max Hz of initialFilterFc
|
|
822
851
|
return Math.max(minFrequency, Math.min(frequency, maxFrequency));
|
|
823
852
|
}
|
|
853
|
+
setPortamentoStartFilterEnvelope(channel, note) {
|
|
854
|
+
const { instrumentKey, noteNumber, startTime } = note;
|
|
855
|
+
const softPedalFactor = 1 -
|
|
856
|
+
(0.1 + (noteNumber / 127) * 0.2) * channel.softPedal;
|
|
857
|
+
const baseFreq = this.centToHz(instrumentKey.initialFilterFc) *
|
|
858
|
+
softPedalFactor * channel.brightness;
|
|
859
|
+
const peekFreq = this.centToHz(instrumentKey.initialFilterFc + instrumentKey.modEnvToFilterFc) * softPedalFactor * channel.brightness;
|
|
860
|
+
const sustainFreq = baseFreq +
|
|
861
|
+
(peekFreq - baseFreq) * (1 - instrumentKey.modSustain);
|
|
862
|
+
const adjustedBaseFreq = this.clampCutoffFrequency(baseFreq);
|
|
863
|
+
const adjustedSustainFreq = this.clampCutoffFrequency(sustainFreq);
|
|
864
|
+
const portamentoTime = startTime + channel.portamentoTime;
|
|
865
|
+
const modDelay = startTime + instrumentKey.modDelay;
|
|
866
|
+
note.filterNode.frequency
|
|
867
|
+
.cancelScheduledValues(startTime)
|
|
868
|
+
.setValueAtTime(adjustedBaseFreq, startTime)
|
|
869
|
+
.setValueAtTime(adjustedBaseFreq, modDelay)
|
|
870
|
+
.linearRampToValueAtTime(adjustedSustainFreq, portamentoTime);
|
|
871
|
+
}
|
|
824
872
|
setFilterEnvelope(channel, note) {
|
|
825
873
|
const { instrumentKey, noteNumber, startTime } = note;
|
|
826
874
|
const softPedalFactor = 1 -
|
|
@@ -888,7 +936,7 @@ class Midy {
|
|
|
888
936
|
note.vibratoLFO.connect(note.vibratoDepth);
|
|
889
937
|
note.vibratoDepth.connect(note.bufferSource.detune);
|
|
890
938
|
}
|
|
891
|
-
async createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3) {
|
|
939
|
+
async createNote(channel, instrumentKey, noteNumber, velocity, startTime, portamento, isSF3) {
|
|
892
940
|
const semitoneOffset = this.calcSemitoneOffset(channel);
|
|
893
941
|
const note = new Note(noteNumber, velocity, startTime, instrumentKey);
|
|
894
942
|
note.bufferSource = await this.createNoteBufferNode(instrumentKey, isSF3);
|
|
@@ -897,8 +945,14 @@ class Midy {
|
|
|
897
945
|
type: "lowpass",
|
|
898
946
|
Q: instrumentKey.initialFilterQ / 10 * channel.filterResonance, // dB
|
|
899
947
|
});
|
|
900
|
-
|
|
901
|
-
|
|
948
|
+
if (portamento) {
|
|
949
|
+
this.setPortamentoStartVolumeEnvelope(channel, note);
|
|
950
|
+
this.setPortamentoStartFilterEnvelope(channel, note);
|
|
951
|
+
}
|
|
952
|
+
else {
|
|
953
|
+
this.setVolumeEnvelope(channel, note);
|
|
954
|
+
this.setFilterEnvelope(channel, note);
|
|
955
|
+
}
|
|
902
956
|
if (0 < channel.vibratoDepth) {
|
|
903
957
|
this.startVibrato(channel, note, startTime);
|
|
904
958
|
}
|
|
@@ -915,7 +969,7 @@ class Midy {
|
|
|
915
969
|
}
|
|
916
970
|
note.bufferSource.connect(note.filterNode);
|
|
917
971
|
note.filterNode.connect(note.volumeNode);
|
|
918
|
-
note.bufferSource.start(startTime
|
|
972
|
+
note.bufferSource.start(startTime);
|
|
919
973
|
return note;
|
|
920
974
|
}
|
|
921
975
|
calcBank(channel, channelNumber) {
|
|
@@ -927,7 +981,7 @@ class Midy {
|
|
|
927
981
|
}
|
|
928
982
|
return channel.bank;
|
|
929
983
|
}
|
|
930
|
-
async scheduleNoteOn(channelNumber, noteNumber, velocity, startTime) {
|
|
984
|
+
async scheduleNoteOn(channelNumber, noteNumber, velocity, startTime, portamento) {
|
|
931
985
|
const channel = this.channels[channelNumber];
|
|
932
986
|
const bankNumber = this.calcBank(channel, channelNumber);
|
|
933
987
|
const soundFontIndex = this.soundFontTable[channel.program].get(bankNumber);
|
|
@@ -938,7 +992,7 @@ class Midy {
|
|
|
938
992
|
const instrumentKey = soundFont.getInstrumentKey(bankNumber, channel.program, noteNumber, velocity);
|
|
939
993
|
if (!instrumentKey)
|
|
940
994
|
return;
|
|
941
|
-
const note = await this.createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3);
|
|
995
|
+
const note = await this.createNote(channel, instrumentKey, noteNumber, velocity, startTime, portamento, isSF3);
|
|
942
996
|
note.volumeNode.connect(channel.gainL);
|
|
943
997
|
note.volumeNode.connect(channel.gainR);
|
|
944
998
|
if (channel.sostenutoPedal) {
|
|
@@ -952,13 +1006,42 @@ class Midy {
|
|
|
952
1006
|
scheduledNotes.set(noteNumber, [note]);
|
|
953
1007
|
}
|
|
954
1008
|
}
|
|
955
|
-
noteOn(channelNumber, noteNumber, velocity) {
|
|
1009
|
+
noteOn(channelNumber, noteNumber, velocity, portamento) {
|
|
956
1010
|
const now = this.audioContext.currentTime;
|
|
957
|
-
return this.scheduleNoteOn(channelNumber, noteNumber, velocity, now);
|
|
1011
|
+
return this.scheduleNoteOn(channelNumber, noteNumber, velocity, now, portamento);
|
|
958
1012
|
}
|
|
959
|
-
|
|
1013
|
+
stopNote(stopTime, endTime, scheduledNotes, index) {
|
|
1014
|
+
const note = scheduledNotes[index];
|
|
1015
|
+
note.volumeNode.gain
|
|
1016
|
+
.cancelScheduledValues(stopTime)
|
|
1017
|
+
.linearRampToValueAtTime(0, endTime);
|
|
1018
|
+
note.ending = true;
|
|
1019
|
+
this.scheduleTask(() => {
|
|
1020
|
+
note.bufferSource.loop = false;
|
|
1021
|
+
}, endTime);
|
|
1022
|
+
return new Promise((resolve) => {
|
|
1023
|
+
note.bufferSource.onended = () => {
|
|
1024
|
+
scheduledNotes[index] = null;
|
|
1025
|
+
note.bufferSource.disconnect();
|
|
1026
|
+
note.volumeNode.disconnect();
|
|
1027
|
+
note.filterNode.disconnect();
|
|
1028
|
+
if (note.modulationDepth) {
|
|
1029
|
+
note.volumeDepth.disconnect();
|
|
1030
|
+
note.modulationDepth.disconnect();
|
|
1031
|
+
note.modulationLFO.stop();
|
|
1032
|
+
}
|
|
1033
|
+
if (note.vibratoDepth) {
|
|
1034
|
+
note.vibratoDepth.disconnect();
|
|
1035
|
+
note.vibratoLFO.stop();
|
|
1036
|
+
}
|
|
1037
|
+
resolve();
|
|
1038
|
+
};
|
|
1039
|
+
note.bufferSource.stop(endTime);
|
|
1040
|
+
});
|
|
1041
|
+
}
|
|
1042
|
+
scheduleNoteRelease(channelNumber, noteNumber, _velocity, stopTime, portamentoNoteNumber, force) {
|
|
960
1043
|
const channel = this.channels[channelNumber];
|
|
961
|
-
if (
|
|
1044
|
+
if (!force) {
|
|
962
1045
|
if (channel.sustainPedal)
|
|
963
1046
|
return;
|
|
964
1047
|
if (channel.sostenutoNotes.has(noteNumber))
|
|
@@ -973,43 +1056,29 @@ class Midy {
|
|
|
973
1056
|
continue;
|
|
974
1057
|
if (note.ending)
|
|
975
1058
|
continue;
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
.
|
|
980
|
-
.
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
.
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
note.filterNode.disconnect();
|
|
995
|
-
if (note.modulationDepth) {
|
|
996
|
-
note.volumeDepth.disconnect();
|
|
997
|
-
note.modulationDepth.disconnect();
|
|
998
|
-
note.modulationLFO.stop();
|
|
999
|
-
}
|
|
1000
|
-
if (note.vibratoDepth) {
|
|
1001
|
-
note.vibratoDepth.disconnect();
|
|
1002
|
-
note.vibratoLFO.stop();
|
|
1003
|
-
}
|
|
1004
|
-
resolve();
|
|
1005
|
-
};
|
|
1006
|
-
note.bufferSource.stop(volEndTime);
|
|
1007
|
-
});
|
|
1059
|
+
if (portamentoNoteNumber === undefined) {
|
|
1060
|
+
const volEndTime = stopTime +
|
|
1061
|
+
note.instrumentKey.volRelease * channel.releaseTime;
|
|
1062
|
+
const modRelease = stopTime + note.instrumentKey.modRelease;
|
|
1063
|
+
note.filterNode.frequency
|
|
1064
|
+
.cancelScheduledValues(stopTime)
|
|
1065
|
+
.linearRampToValueAtTime(0, modRelease);
|
|
1066
|
+
return this.stopNote(stopTime, volEndTime, scheduledNotes, i);
|
|
1067
|
+
}
|
|
1068
|
+
else {
|
|
1069
|
+
const portamentoTime = stopTime + channel.portamentoTime;
|
|
1070
|
+
const detuneChange = (portamentoNoteNumber - noteNumber) * 100;
|
|
1071
|
+
const detune = note.bufferSource.detune.value + detuneChange;
|
|
1072
|
+
note.bufferSource.detune
|
|
1073
|
+
.cancelScheduledValues(stopTime)
|
|
1074
|
+
.linearRampToValueAtTime(detune, portamentoTime);
|
|
1075
|
+
return this.stopNote(stopTime, portamentoTime, scheduledNotes, i);
|
|
1076
|
+
}
|
|
1008
1077
|
}
|
|
1009
1078
|
}
|
|
1010
|
-
releaseNote(channelNumber, noteNumber, velocity) {
|
|
1079
|
+
releaseNote(channelNumber, noteNumber, velocity, portamentoNoteNumber) {
|
|
1011
1080
|
const now = this.audioContext.currentTime;
|
|
1012
|
-
return this.scheduleNoteRelease(channelNumber, noteNumber, velocity, now);
|
|
1081
|
+
return this.scheduleNoteRelease(channelNumber, noteNumber, velocity, now, portamentoNoteNumber, false);
|
|
1013
1082
|
}
|
|
1014
1083
|
releaseSustainPedal(channelNumber, halfVelocity) {
|
|
1015
1084
|
const velocity = halfVelocity * 2;
|
|
@@ -1184,12 +1253,14 @@ class Midy {
|
|
|
1184
1253
|
this.updateModulation(channel);
|
|
1185
1254
|
}
|
|
1186
1255
|
setPortamentoTime(channelNumber, portamentoTime) {
|
|
1187
|
-
this.channels[channelNumber]
|
|
1256
|
+
const channel = this.channels[channelNumber];
|
|
1257
|
+
const factor = 5 * Math.log(10) / 127;
|
|
1258
|
+
channel.portamentoTime = Math.exp(factor * portamentoTime);
|
|
1188
1259
|
}
|
|
1189
1260
|
setVolume(channelNumber, volume) {
|
|
1190
1261
|
const channel = this.channels[channelNumber];
|
|
1191
1262
|
channel.volume = volume / 127;
|
|
1192
|
-
this.
|
|
1263
|
+
this.updateChannelVolume(channel);
|
|
1193
1264
|
}
|
|
1194
1265
|
panToGain(pan) {
|
|
1195
1266
|
const theta = Math.PI / 2 * Math.max(0, pan - 1) / 126;
|
|
@@ -1201,12 +1272,12 @@ class Midy {
|
|
|
1201
1272
|
setPan(channelNumber, pan) {
|
|
1202
1273
|
const channel = this.channels[channelNumber];
|
|
1203
1274
|
channel.pan = pan;
|
|
1204
|
-
this.
|
|
1275
|
+
this.updateChannelVolume(channel);
|
|
1205
1276
|
}
|
|
1206
1277
|
setExpression(channelNumber, expression) {
|
|
1207
1278
|
const channel = this.channels[channelNumber];
|
|
1208
1279
|
channel.expression = expression / 127;
|
|
1209
|
-
this.
|
|
1280
|
+
this.updateChannelVolume(channel);
|
|
1210
1281
|
}
|
|
1211
1282
|
setBankLSB(channelNumber, lsb) {
|
|
1212
1283
|
this.channels[channelNumber].bankLSB = lsb;
|
|
@@ -1215,7 +1286,7 @@ class Midy {
|
|
|
1215
1286
|
this.channels[channelNumber].dataLSB = value;
|
|
1216
1287
|
this.handleRPN(channelNumber, 0);
|
|
1217
1288
|
}
|
|
1218
|
-
|
|
1289
|
+
updateChannelVolume(channel) {
|
|
1219
1290
|
const now = this.audioContext.currentTime;
|
|
1220
1291
|
const volume = channel.volume * channel.expression;
|
|
1221
1292
|
const { gainLeft, gainRight } = this.panToGain(channel.pan);
|
|
@@ -1233,12 +1304,12 @@ class Midy {
|
|
|
1233
1304
|
this.releaseSustainPedal(channelNumber, value);
|
|
1234
1305
|
}
|
|
1235
1306
|
}
|
|
1236
|
-
// TODO
|
|
1237
1307
|
setPortamento(channelNumber, value) {
|
|
1238
1308
|
this.channels[channelNumber].portamento = value >= 64;
|
|
1239
1309
|
}
|
|
1240
1310
|
setReverbSendLevel(channelNumber, reverbSendLevel) {
|
|
1241
1311
|
const channel = this.channels[channelNumber];
|
|
1312
|
+
const reverbEffect = this.reverbEffect;
|
|
1242
1313
|
if (0 < channel.reverbSendLevel) {
|
|
1243
1314
|
if (0 < reverbSendLevel) {
|
|
1244
1315
|
const now = this.audioContext.currentTime;
|
|
@@ -1557,20 +1628,22 @@ class Midy {
|
|
|
1557
1628
|
}
|
|
1558
1629
|
}
|
|
1559
1630
|
GM1SystemOn() {
|
|
1560
|
-
this.channels.
|
|
1631
|
+
for (let i = 0; i < this.channels.length; i++) {
|
|
1632
|
+
const channel = this.channels[i];
|
|
1561
1633
|
channel.bankMSB = 0;
|
|
1562
1634
|
channel.bankLSB = 0;
|
|
1563
1635
|
channel.bank = 0;
|
|
1564
|
-
}
|
|
1636
|
+
}
|
|
1565
1637
|
this.channels[9].bankMSB = 1;
|
|
1566
1638
|
this.channels[9].bank = 128;
|
|
1567
1639
|
}
|
|
1568
1640
|
GM2SystemOn() {
|
|
1569
|
-
this.channels.
|
|
1641
|
+
for (let i = 0; i < this.channels.length; i++) {
|
|
1642
|
+
const channel = this.channels[i];
|
|
1570
1643
|
channel.bankMSB = 121;
|
|
1571
1644
|
channel.bankLSB = 0;
|
|
1572
1645
|
channel.bank = 121 * 128;
|
|
1573
|
-
}
|
|
1646
|
+
}
|
|
1574
1647
|
this.channels[9].bankMSB = 120;
|
|
1575
1648
|
this.channels[9].bank = 120 * 128;
|
|
1576
1649
|
}
|
|
@@ -1880,7 +1953,7 @@ Object.defineProperty(Midy, "channelSettings", {
|
|
|
1880
1953
|
currentBufferSource: null,
|
|
1881
1954
|
volume: 100 / 127,
|
|
1882
1955
|
pan: 64,
|
|
1883
|
-
portamentoTime:
|
|
1956
|
+
portamentoTime: 1, // sec
|
|
1884
1957
|
filterResonance: 1,
|
|
1885
1958
|
releaseTime: 1,
|
|
1886
1959
|
attackTime: 1,
|