@marmooo/midy 0.1.6 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/esm/midy-GM1.d.ts +5 -4
  2. package/esm/midy-GM1.d.ts.map +1 -1
  3. package/esm/midy-GM1.js +45 -16
  4. package/esm/midy-GM2.d.ts +5 -2
  5. package/esm/midy-GM2.d.ts.map +1 -1
  6. package/esm/midy-GM2.js +176 -66
  7. package/esm/midy-GMLite.d.ts +5 -4
  8. package/esm/midy-GMLite.d.ts.map +1 -1
  9. package/esm/midy-GMLite.js +45 -16
  10. package/esm/midy.d.ts +5 -2
  11. package/esm/midy.d.ts.map +1 -1
  12. package/esm/midy.js +177 -67
  13. package/package.json +5 -1
  14. package/script/midy-GM1.d.ts +5 -4
  15. package/script/midy-GM1.d.ts.map +1 -1
  16. package/script/midy-GM1.js +48 -19
  17. package/script/midy-GM2.d.ts +5 -2
  18. package/script/midy-GM2.d.ts.map +1 -1
  19. package/script/midy-GM2.js +179 -69
  20. package/script/midy-GMLite.d.ts +5 -4
  21. package/script/midy-GMLite.d.ts.map +1 -1
  22. package/script/midy-GMLite.js +48 -19
  23. package/script/midy.d.ts +5 -2
  24. package/script/midy.d.ts.map +1 -1
  25. package/script/midy.js +180 -70
  26. package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.6/+esm.d.ts +0 -149
  27. package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.6/+esm.d.ts.map +0 -1
  28. package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.6/+esm.js +0 -180
  29. package/esm/deps/cdn.jsdelivr.net/npm/midi-file@1.2.4/+esm.d.ts +0 -84
  30. package/esm/deps/cdn.jsdelivr.net/npm/midi-file@1.2.4/+esm.d.ts.map +0 -1
  31. package/esm/deps/cdn.jsdelivr.net/npm/midi-file@1.2.4/+esm.js +0 -216
  32. package/script/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.6/+esm.d.ts +0 -149
  33. package/script/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.6/+esm.d.ts.map +0 -1
  34. package/script/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.6/+esm.js +0 -190
  35. package/script/deps/cdn.jsdelivr.net/npm/midi-file@1.2.4/+esm.d.ts +0 -84
  36. package/script/deps/cdn.jsdelivr.net/npm/midi-file@1.2.4/+esm.d.ts.map +0 -1
  37. package/script/deps/cdn.jsdelivr.net/npm/midi-file@1.2.4/+esm.js +0 -221
@@ -35,6 +35,7 @@ export class MidyGMLite {
35
35
  timeline: any[];
36
36
  instruments: any[];
37
37
  notePromises: any[];
38
+ exclusiveClassMap: Map<any, any>;
38
39
  audioContext: any;
39
40
  masterGain: any;
40
41
  controlChangeHandlers: {
@@ -96,11 +97,11 @@ export class MidyGMLite {
96
97
  createNote(channel: any, instrumentKey: any, noteNumber: any, velocity: any, startTime: any, isSF3: any): Promise<Note>;
97
98
  scheduleNoteOn(channelNumber: any, noteNumber: any, velocity: any, startTime: any): Promise<void>;
98
99
  noteOn(channelNumber: any, noteNumber: any, velocity: any): Promise<void>;
99
- stopNote(stopTime: any, endTime: any, scheduledNotes: any, index: any): Promise<any>;
100
- scheduleNoteRelease(channelNumber: any, noteNumber: any, _velocity: any, stopTime: any, force: any): void;
101
- releaseNote(channelNumber: any, noteNumber: any, velocity: any): void;
100
+ stopNote(endTime: any, stopTime: any, scheduledNotes: any, index: any): Promise<any>;
101
+ scheduleNoteRelease(channelNumber: any, noteNumber: any, _velocity: any, endTime: any, force: any): Promise<any> | undefined;
102
+ releaseNote(channelNumber: any, noteNumber: any, velocity: any): Promise<any> | undefined;
102
103
  releaseSustainPedal(channelNumber: any, halfVelocity: any): any[];
103
- handleMIDIMessage(statusByte: any, data1: any, data2: any): void | Promise<void>;
104
+ handleMIDIMessage(statusByte: any, data1: any, data2: any): void | Promise<any>;
104
105
  handleProgramChange(channelNumber: any, program: any): void;
105
106
  handlePitchBendMessage(channelNumber: any, lsb: any, msb: any): void;
106
107
  setPitchBend(channelNumber: any, pitchBend: any): void;
@@ -1 +1 @@
1
- {"version":3,"file":"midy-GMLite.d.ts","sourceRoot":"","sources":["../src/midy-GMLite.js"],"names":[],"mappings":"AAsBA;IAmBE;;;;;;;;;MASE;IAEF;;;;;;;MAOE;IAEF,+BAOC;IA7CD,qBAAmB;IACnB,kBAAc;IACd,0BAAwB;IACxB,kBAAc;IACd,mBAAiB;IACjB,kBAAc;IACd,mBAAe;IACf,kBAAgB;IAChB,sBAA2C;IAC3C,mBAAkB;IAClB,mBAAkB;IAClB,kBAAiB;IACjB,oBAAmB;IACnB,mBAAkB;IAClB,gBAAc;IACd,mBAAiB;IACjB,oBAAkB;IAuBhB,kBAAgC;IAChC,gBAA4C;IAC5C;;;;;;;;;;;;;MAA+D;IAC/D,gBAAiD;IAKnD,4BAMC;IAED,mCAWC;IAED,gDAMC;IAED,sCASC;IAED;;;;MAeC;IAED,yCAUC;IAED,+DAuBC;IAED,mEAWC;IAED,2EA+CC;IAED,mCAOC;IAED,0BA+CC;IAED,uDAEC;IAED,wDAEC;IAED;;;MAgEC;IAED,+EAmBC;IAED,qDAKC;IAED,uBAKC;IAED,aAGC;IAED,cAKC;IAED,wBAIC;IAED,0BAKC;IAED,wBAOC;IAED,sBAGC;IAED,uDASC;IAED,6CAQC;IAED,2BAEC;IAED,4BAEC;IAED,yCAEC;IAED,mFAGC;IAED,mCAeC;IAED,+CAwBC;IAED,6CAIC;IAED,mCAsBC;IAED,+DA0BC;IAED,wHAgCC;IAED,kGA8BC;IAED,0EAGC;IAED,qFA4BC;IAED,0GAsBC;IAED,sEAGC;IAED,kEAeC;IAED,iFAiBC;IAED,4DAGC;IAED,qEAGC;IAED,uDAOC;IAED;;;;;;;;;;;;;MAeC;IAED,2EASC;IAED,qCAkBC;IAED,8DAIC;IACD,iDAIC;IAED;;;MAMC;IAED,2CAIC;IAED,yDAIC;IAED,mDAGC;IAED,wCAUC;IAED,sDAMC;IAED,oCAYC;IAED,gDAEC;IAED,gDAEC;IAED,mDAGC;IAED,oDAaC;IAED,kDAKC;IAED,iEAOC;IAED,+CAEC;IAED,8CAEC;IAED,+CAEC;IAED,4DAgBC;IAED,oBASC;IAED,yDAaC;IAED,yCAGC;IAED,mCAQC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF;AAj/BD;IAQE,gFAKC;IAZD,kBAAa;IACb,gBAAW;IACX,gBAAW;IACX,iBAAY;IACZ,mBAAc;IACd,qBAAgB;IAGd,gBAA4B;IAC5B,cAAwB;IACxB,eAA0B;IAC1B,mBAAkC;CAErC"}
1
+ {"version":3,"file":"midy-GMLite.d.ts","sourceRoot":"","sources":["../src/midy-GMLite.js"],"names":[],"mappings":"AAmBA;IAoBE;;;;;;;;;MASE;IAEF;;;;;;;MAOE;IAEF,+BAOC;IA9CD,qBAAmB;IACnB,kBAAc;IACd,0BAAwB;IACxB,kBAAc;IACd,mBAAiB;IACjB,kBAAc;IACd,mBAAe;IACf,kBAAgB;IAChB,sBAA2C;IAC3C,mBAAkB;IAClB,mBAAkB;IAClB,kBAAiB;IACjB,oBAAmB;IACnB,mBAAkB;IAClB,gBAAc;IACd,mBAAiB;IACjB,oBAAkB;IAClB,iCAA8B;IAuB5B,kBAAgC;IAChC,gBAA4C;IAC5C;;;;;;;;;;;;;MAA+D;IAC/D,gBAAiD;IAKnD,4BAMC;IAED,mCAWC;IAED,gDAMC;IAED,sCASC;IAED;;;;MAeC;IAED,yCAUC;IAED,+DA2BC;IAED,mEAWC;IAED,2EA+CC;IAED,mCAOC;IAED,0BAkDC;IAED,uDAEC;IAED,wDAEC;IAED;;;MAgEC;IAED,+EAmBC;IAED,qDAKC;IAED,uBAKC;IAED,aAGC;IAED,cAKC;IAED,wBAIC;IAED,0BAKC;IAED,wBAOC;IAED,sBAGC;IAED,uDASC;IAED,6CAQC;IAED,2BAEC;IAED,4BAEC;IAED,yCAEC;IAED,mFAGC;IAED,mCAeC;IAED,+CAwBC;IAED,6CAIC;IAED,mCAsBC;IAED,+DA0BC;IAED,wHAgCC;IAED,kGAgDC;IAED,0EAGC;IAED,qFA4BC;IAED,6HAuBC;IAED,0FAGC;IAED,kEAeC;IAED,gFAiBC;IAED,4DAGC;IAED,qEAGC;IAED,uDAOC;IAED;;;;;;;;;;;;;MAeC;IAED,2EASC;IAED,qCAkBC;IAED,8DAIC;IACD,iDAIC;IAED;;;MAMC;IAED,2CAIC;IAED,yDAIC;IAED,mDAGC;IAED,wCAUC;IAED,sDAMC;IAED,oCAYC;IAED,gDAEC;IAED,gDAEC;IAED,mDAGC;IAED,oDAaC;IAED,kDAKC;IAED,iEAOC;IAED,+CAEC;IAED,8CAEC;IAED,+CAEC;IAED,4DAgBC;IAED,oBASC;IAED,yDAaC;IAED,yCAGC;IAED,mCAQC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF;AA5gCD;IAQE,gFAKC;IAZD,kBAAa;IACb,gBAAW;IACX,gBAAW;IACX,iBAAY;IACZ,mBAAc;IACd,qBAAgB;IAGd,gBAA4B;IAC5B,cAAwB;IACxB,eAA0B;IAC1B,mBAAkC;CAErC"}
@@ -1,5 +1,5 @@
1
- import { parseMidi } from "./deps/cdn.jsdelivr.net/npm/midi-file@1.2.4/+esm.js";
2
- import { parse, SoundFont, } from "./deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.6/+esm.js";
1
+ import { parseMidi } from "midi-file";
2
+ import { parse, SoundFont } from "@marmooo/soundfont-parser";
3
3
  class Note {
4
4
  constructor(noteNumber, velocity, startTime, instrumentKey) {
5
5
  Object.defineProperty(this, "bufferSource", {
@@ -148,6 +148,12 @@ export class MidyGMLite {
148
148
  writable: true,
149
149
  value: []
150
150
  });
151
+ Object.defineProperty(this, "exclusiveClassMap", {
152
+ enumerable: true,
153
+ configurable: true,
154
+ writable: true,
155
+ value: new Map()
156
+ });
151
157
  this.audioContext = audioContext;
152
158
  this.masterGain = new GainNode(audioContext);
153
159
  this.controlChangeHandlers = this.createControlChangeHandlers();
@@ -220,19 +226,25 @@ export class MidyGMLite {
220
226
  const sampleStart = instrumentKey.start;
221
227
  const sampleEnd = instrumentKey.sample.length + instrumentKey.end;
222
228
  if (isSF3) {
223
- const sample = instrumentKey.sample.slice(sampleStart, sampleEnd);
224
- const audioBuffer = await this.audioContext.decodeAudioData(sample.buffer);
229
+ const sample = instrumentKey.sample;
230
+ const start = sample.byteOffset + sampleStart;
231
+ const end = sample.byteOffset + sampleEnd;
232
+ const buffer = sample.buffer.slice(start, end);
233
+ const audioBuffer = await this.audioContext.decodeAudioData(buffer);
225
234
  return audioBuffer;
226
235
  }
227
236
  else {
228
- const sample = instrumentKey.sample.subarray(sampleStart, sampleEnd);
237
+ const sample = instrumentKey.sample;
238
+ const start = sample.byteOffset + sampleStart;
239
+ const end = sample.byteOffset + sampleEnd;
240
+ const buffer = sample.buffer.slice(start, end);
229
241
  const audioBuffer = new AudioBuffer({
230
242
  numberOfChannels: 1,
231
243
  length: sample.length,
232
244
  sampleRate: instrumentKey.sampleRate,
233
245
  });
234
246
  const channelData = audioBuffer.getChannelData(0);
235
- const int16Array = new Int16Array(sample.buffer);
247
+ const int16Array = new Int16Array(buffer);
236
248
  for (let i = 0; i < int16Array.length; i++) {
237
249
  channelData[i] = int16Array[i] / 32768;
238
250
  }
@@ -306,6 +318,7 @@ export class MidyGMLite {
306
318
  if (queueIndex >= this.timeline.length) {
307
319
  await Promise.all(this.notePromises);
308
320
  this.notePromises = [];
321
+ this.exclusiveClassMap.clear();
309
322
  resolve();
310
323
  return;
311
324
  }
@@ -321,6 +334,7 @@ export class MidyGMLite {
321
334
  }
322
335
  else if (this.isStopping) {
323
336
  await this.stopNotes(0, true);
337
+ this.exclusiveClassMap.clear();
324
338
  this.notePromises = [];
325
339
  resolve();
326
340
  this.isStopping = false;
@@ -329,6 +343,7 @@ export class MidyGMLite {
329
343
  }
330
344
  else if (this.isSeeking) {
331
345
  this.stopNotes(0, true);
346
+ this.exclusiveClassMap.clear();
332
347
  this.startTime = this.audioContext.currentTime;
333
348
  queueIndex = this.getQueueIndex(this.resumeTime);
334
349
  offset = this.resumeTime - this.startTime;
@@ -634,6 +649,19 @@ export class MidyGMLite {
634
649
  const note = await this.createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3);
635
650
  note.volumeNode.connect(channel.gainL);
636
651
  note.volumeNode.connect(channel.gainR);
652
+ const exclusiveClass = instrumentKey.exclusiveClass;
653
+ if (exclusiveClass !== 0) {
654
+ if (this.exclusiveClassMap.has(exclusiveClass)) {
655
+ const prevEntry = this.exclusiveClassMap.get(exclusiveClass);
656
+ const [prevNote, prevChannelNumber] = prevEntry;
657
+ if (!prevNote.ending) {
658
+ this.scheduleNoteRelease(prevChannelNumber, prevNote.noteNumber, 0, // velocity,
659
+ startTime, undefined, // portamentoNoteNumber
660
+ true);
661
+ }
662
+ }
663
+ this.exclusiveClassMap.set(exclusiveClass, [note, channelNumber]);
664
+ }
637
665
  const scheduledNotes = channel.scheduledNotes;
638
666
  if (scheduledNotes.has(noteNumber)) {
639
667
  scheduledNotes.get(noteNumber).push(note);
@@ -646,15 +674,15 @@ export class MidyGMLite {
646
674
  const now = this.audioContext.currentTime;
647
675
  return this.scheduleNoteOn(channelNumber, noteNumber, velocity, now);
648
676
  }
649
- stopNote(stopTime, endTime, scheduledNotes, index) {
677
+ stopNote(endTime, stopTime, scheduledNotes, index) {
650
678
  const note = scheduledNotes[index];
651
679
  note.volumeNode.gain
652
- .cancelScheduledValues(stopTime)
653
- .linearRampToValueAtTime(0, endTime);
680
+ .cancelScheduledValues(endTime)
681
+ .linearRampToValueAtTime(0, stopTime);
654
682
  note.ending = true;
655
683
  this.scheduleTask(() => {
656
684
  note.bufferSource.loop = false;
657
- }, endTime);
685
+ }, stopTime);
658
686
  return new Promise((resolve) => {
659
687
  note.bufferSource.onended = () => {
660
688
  scheduledNotes[index] = null;
@@ -672,10 +700,10 @@ export class MidyGMLite {
672
700
  }
673
701
  resolve();
674
702
  };
675
- note.bufferSource.stop(endTime);
703
+ note.bufferSource.stop(stopTime);
676
704
  });
677
705
  }
678
- scheduleNoteRelease(channelNumber, noteNumber, _velocity, stopTime, force) {
706
+ scheduleNoteRelease(channelNumber, noteNumber, _velocity, endTime, force) {
679
707
  const channel = this.channels[channelNumber];
680
708
  if (!force && channel.sustainPedal)
681
709
  return;
@@ -688,12 +716,13 @@ export class MidyGMLite {
688
716
  continue;
689
717
  if (note.ending)
690
718
  continue;
691
- const volEndTime = stopTime + note.instrumentKey.volRelease;
692
- const modRelease = stopTime + note.instrumentKey.modRelease;
719
+ const volRelease = endTime + note.instrumentKey.volRelease;
720
+ const modRelease = endTime + note.instrumentKey.modRelease;
693
721
  note.filterNode.frequency
694
- .cancelScheduledValues(stopTime)
722
+ .cancelScheduledValues(endTime)
695
723
  .linearRampToValueAtTime(0, modRelease);
696
- this.stopNote(stopTime, volEndTime, scheduledNotes, i);
724
+ const stopTime = Math.min(volRelease, modRelease);
725
+ return this.stopNote(endTime, stopTime, scheduledNotes, i);
697
726
  }
698
727
  }
699
728
  releaseNote(channelNumber, noteNumber, velocity) {
package/esm/midy.d.ts CHANGED
@@ -83,6 +83,7 @@ export class Midy {
83
83
  timeline: any[];
84
84
  instruments: any[];
85
85
  notePromises: any[];
86
+ exclusiveClassMap: Map<any, any>;
86
87
  defaultOptions: {
87
88
  reverbAlgorithm: (audioContext: any) => {
88
89
  input: any;
@@ -218,8 +219,8 @@ export class Midy {
218
219
  calcBank(channel: any, channelNumber: any): any;
219
220
  scheduleNoteOn(channelNumber: any, noteNumber: any, velocity: any, startTime: any, portamento: any): Promise<void>;
220
221
  noteOn(channelNumber: any, noteNumber: any, velocity: any, portamento: any): Promise<void>;
221
- stopNote(stopTime: any, endTime: any, scheduledNotes: any, index: any): Promise<any>;
222
- scheduleNoteRelease(channelNumber: any, noteNumber: any, _velocity: any, stopTime: any, portamentoNoteNumber: any, force: any): Promise<any> | undefined;
222
+ stopNote(endTime: any, stopTime: any, scheduledNotes: any, index: any): Promise<any>;
223
+ scheduleNoteRelease(channelNumber: any, noteNumber: any, _velocity: any, endTime: any, portamentoNoteNumber: any, force: any): Promise<any> | undefined;
223
224
  releaseNote(channelNumber: any, noteNumber: any, velocity: any, portamentoNoteNumber: any): Promise<any> | undefined;
224
225
  releaseSustainPedal(channelNumber: any, halfVelocity: any): any[];
225
226
  releaseSostenutoPedal(channelNumber: any, halfVelocity: any): any[];
@@ -360,6 +361,8 @@ declare class Note {
360
361
  modulationDepth: any;
361
362
  vibratoLFO: any;
362
363
  vibratoDepth: any;
364
+ reverbEffectsSend: any;
365
+ chorusEffectsSend: any;
363
366
  noteNumber: any;
364
367
  velocity: any;
365
368
  startTime: any;
package/esm/midy.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"midy.d.ts","sourceRoot":"","sources":["../src/midy.js"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"midy.d.ts","sourceRoot":"","sources":["../src/midy.js"],"names":[],"mappings":"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"}
package/esm/midy.js CHANGED
@@ -1,5 +1,5 @@
1
- import { parseMidi } from "./deps/cdn.jsdelivr.net/npm/midi-file@1.2.4/+esm.js";
2
- import { parse, SoundFont, } from "./deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.6/+esm.js";
1
+ import { parseMidi } from "midi-file";
2
+ import { parse, SoundFont } from "@marmooo/soundfont-parser";
3
3
  class Note {
4
4
  constructor(noteNumber, velocity, startTime, instrumentKey) {
5
5
  Object.defineProperty(this, "bufferSource", {
@@ -50,6 +50,18 @@ class Note {
50
50
  writable: true,
51
51
  value: void 0
52
52
  });
53
+ Object.defineProperty(this, "reverbEffectsSend", {
54
+ enumerable: true,
55
+ configurable: true,
56
+ writable: true,
57
+ value: void 0
58
+ });
59
+ Object.defineProperty(this, "chorusEffectsSend", {
60
+ enumerable: true,
61
+ configurable: true,
62
+ writable: true,
63
+ value: void 0
64
+ });
53
65
  this.noteNumber = noteNumber;
54
66
  this.velocity = velocity;
55
67
  this.startTime = startTime;
@@ -205,6 +217,12 @@ export class Midy {
205
217
  writable: true,
206
218
  value: []
207
219
  });
220
+ Object.defineProperty(this, "exclusiveClassMap", {
221
+ enumerable: true,
222
+ configurable: true,
223
+ writable: true,
224
+ value: new Map()
225
+ });
208
226
  Object.defineProperty(this, "defaultOptions", {
209
227
  enumerable: true,
210
228
  configurable: true,
@@ -311,19 +329,25 @@ export class Midy {
311
329
  const sampleStart = instrumentKey.start;
312
330
  const sampleEnd = instrumentKey.sample.length + instrumentKey.end;
313
331
  if (isSF3) {
314
- const sample = instrumentKey.sample.slice(sampleStart, sampleEnd);
315
- const audioBuffer = await this.audioContext.decodeAudioData(sample.buffer);
332
+ const sample = instrumentKey.sample;
333
+ const start = sample.byteOffset + sampleStart;
334
+ const end = sample.byteOffset + sampleEnd;
335
+ const buffer = sample.buffer.slice(start, end);
336
+ const audioBuffer = await this.audioContext.decodeAudioData(buffer);
316
337
  return audioBuffer;
317
338
  }
318
339
  else {
319
- const sample = instrumentKey.sample.subarray(sampleStart, sampleEnd);
340
+ const sample = instrumentKey.sample;
341
+ const start = sample.byteOffset + sampleStart;
342
+ const end = sample.byteOffset + sampleEnd;
343
+ const buffer = sample.buffer.slice(start, end);
320
344
  const audioBuffer = new AudioBuffer({
321
345
  numberOfChannels: 1,
322
346
  length: sample.length,
323
347
  sampleRate: instrumentKey.sampleRate,
324
348
  });
325
349
  const channelData = audioBuffer.getChannelData(0);
326
- const int16Array = new Int16Array(sample.buffer);
350
+ const int16Array = new Int16Array(buffer);
327
351
  for (let i = 0; i < int16Array.length; i++) {
328
352
  channelData[i] = int16Array[i] / 32768;
329
353
  }
@@ -424,6 +448,7 @@ export class Midy {
424
448
  if (queueIndex >= this.timeline.length) {
425
449
  await Promise.all(this.notePromises);
426
450
  this.notePromises = [];
451
+ this.exclusiveClassMap.clear();
427
452
  resolve();
428
453
  return;
429
454
  }
@@ -439,6 +464,7 @@ export class Midy {
439
464
  }
440
465
  else if (this.isStopping) {
441
466
  await this.stopNotes(0, true);
467
+ this.exclusiveClassMap.clear();
442
468
  this.notePromises = [];
443
469
  resolve();
444
470
  this.isStopping = false;
@@ -447,6 +473,7 @@ export class Midy {
447
473
  }
448
474
  else if (this.isSeeking) {
449
475
  this.stopNotes(0, true);
476
+ this.exclusiveClassMap.clear();
450
477
  this.startTime = this.audioContext.currentTime;
451
478
  queueIndex = this.getQueueIndex(this.resumeTime);
452
479
  offset = this.resumeTime - this.startTime;
@@ -675,14 +702,14 @@ export class Midy {
675
702
  return impulse;
676
703
  }
677
704
  createConvolutionReverb(audioContext, impulse) {
678
- const output = new GainNode(audioContext);
705
+ const input = new GainNode(audioContext);
679
706
  const convolverNode = new ConvolverNode(audioContext, {
680
707
  buffer: impulse,
681
708
  });
682
- convolverNode.connect(output);
709
+ input.connect(convolverNode);
683
710
  return {
684
- input: convolverNode,
685
- output,
711
+ input,
712
+ output: convolverNode,
686
713
  convolverNode,
687
714
  };
688
715
  }
@@ -724,7 +751,6 @@ export class Midy {
724
751
  // M.R.Schroeder, "Natural Sounding Artificial Reverberation", J.Audio Eng. Soc., vol.10, p.219, 1962
725
752
  createSchroederReverb(audioContext, combFeedbacks, combDelays, allpassFeedbacks, allpassDelays) {
726
753
  const input = new GainNode(audioContext);
727
- const output = new GainNode(audioContext);
728
754
  const mergerGain = new GainNode(audioContext);
729
755
  for (let i = 0; i < combDelays.length; i++) {
730
756
  const comb = this.createCombFilter(audioContext, input, combDelays[i], combFeedbacks[i]);
@@ -735,7 +761,7 @@ export class Midy {
735
761
  const allpass = this.createAllpassFilter(audioContext, (i === 0) ? mergerGain : allpasses.at(-1), allpassDelays[i], allpassFeedbacks[i]);
736
762
  allpasses.push(allpass);
737
763
  }
738
- allpasses.at(-1).connect(output);
764
+ const output = allpasses.at(-1);
739
765
  return { input, output };
740
766
  }
741
767
  createChorusEffect(audioContext) {
@@ -966,6 +992,20 @@ export class Midy {
966
992
  }
967
993
  note.bufferSource.connect(note.filterNode);
968
994
  note.filterNode.connect(note.volumeNode);
995
+ if (0 < channel.reverbSendLevel && 0 < instrumentKey.reverbEffectsSend) {
996
+ note.reverbEffectsSend = new GainNode(this.audioContext, {
997
+ gain: instrumentKey.reverbEffectsSend,
998
+ });
999
+ note.volumeNode.connect(note.reverbEffectsSend);
1000
+ note.reverbEffectsSend.connect(this.reverbEffect.input);
1001
+ }
1002
+ if (0 < channel.chorusSendLevel && 0 < instrumentKey.chorusEffectsSend) {
1003
+ note.chorusEffectsSend = new GainNode(this.audioContext, {
1004
+ gain: instrumentKey.chorusEffectsSend,
1005
+ });
1006
+ note.volumeNode.connect(note.chorusEffectsSend);
1007
+ note.chorusEffectsSend.connect(this.chorusEffect.input);
1008
+ }
969
1009
  note.bufferSource.start(startTime);
970
1010
  return note;
971
1011
  }
@@ -995,6 +1035,19 @@ export class Midy {
995
1035
  if (channel.sostenutoPedal) {
996
1036
  channel.sostenutoNotes.set(noteNumber, note);
997
1037
  }
1038
+ const exclusiveClass = instrumentKey.exclusiveClass;
1039
+ if (exclusiveClass !== 0) {
1040
+ if (this.exclusiveClassMap.has(exclusiveClass)) {
1041
+ const prevEntry = this.exclusiveClassMap.get(exclusiveClass);
1042
+ const [prevNote, prevChannelNumber] = prevEntry;
1043
+ if (!prevNote.ending) {
1044
+ this.scheduleNoteRelease(prevChannelNumber, prevNote.noteNumber, 0, // velocity,
1045
+ startTime, undefined, // portamentoNoteNumber
1046
+ true);
1047
+ }
1048
+ }
1049
+ this.exclusiveClassMap.set(exclusiveClass, [note, channelNumber]);
1050
+ }
998
1051
  const scheduledNotes = channel.scheduledNotes;
999
1052
  if (scheduledNotes.has(noteNumber)) {
1000
1053
  scheduledNotes.get(noteNumber).push(note);
@@ -1007,15 +1060,15 @@ export class Midy {
1007
1060
  const now = this.audioContext.currentTime;
1008
1061
  return this.scheduleNoteOn(channelNumber, noteNumber, velocity, now, portamento);
1009
1062
  }
1010
- stopNote(stopTime, endTime, scheduledNotes, index) {
1063
+ stopNote(endTime, stopTime, scheduledNotes, index) {
1011
1064
  const note = scheduledNotes[index];
1012
1065
  note.volumeNode.gain
1013
- .cancelScheduledValues(stopTime)
1014
- .linearRampToValueAtTime(0, endTime);
1066
+ .cancelScheduledValues(endTime)
1067
+ .linearRampToValueAtTime(0, stopTime);
1015
1068
  note.ending = true;
1016
1069
  this.scheduleTask(() => {
1017
1070
  note.bufferSource.loop = false;
1018
- }, endTime);
1071
+ }, stopTime);
1019
1072
  return new Promise((resolve) => {
1020
1073
  note.bufferSource.onended = () => {
1021
1074
  scheduledNotes[index] = null;
@@ -1031,12 +1084,18 @@ export class Midy {
1031
1084
  note.vibratoDepth.disconnect();
1032
1085
  note.vibratoLFO.stop();
1033
1086
  }
1087
+ if (note.reverbEffectsSend) {
1088
+ note.reverbEffectsSend.disconnect();
1089
+ }
1090
+ if (note.chorusEffectsSend) {
1091
+ note.chorusEffectsSend.disconnect();
1092
+ }
1034
1093
  resolve();
1035
1094
  };
1036
- note.bufferSource.stop(endTime);
1095
+ note.bufferSource.stop(stopTime);
1037
1096
  });
1038
1097
  }
1039
- scheduleNoteRelease(channelNumber, noteNumber, _velocity, stopTime, portamentoNoteNumber, force) {
1098
+ scheduleNoteRelease(channelNumber, noteNumber, _velocity, endTime, portamentoNoteNumber, force) {
1040
1099
  const channel = this.channels[channelNumber];
1041
1100
  if (!force) {
1042
1101
  if (channel.sustainPedal)
@@ -1054,22 +1113,23 @@ export class Midy {
1054
1113
  if (note.ending)
1055
1114
  continue;
1056
1115
  if (portamentoNoteNumber === undefined) {
1057
- const volEndTime = stopTime +
1116
+ const volRelease = endTime +
1058
1117
  note.instrumentKey.volRelease * channel.releaseTime;
1059
- const modRelease = stopTime + note.instrumentKey.modRelease;
1118
+ const modRelease = endTime + note.instrumentKey.modRelease;
1060
1119
  note.filterNode.frequency
1061
- .cancelScheduledValues(stopTime)
1120
+ .cancelScheduledValues(endTime)
1062
1121
  .linearRampToValueAtTime(0, modRelease);
1063
- return this.stopNote(stopTime, volEndTime, scheduledNotes, i);
1122
+ const stopTime = Math.min(volRelease, modRelease);
1123
+ return this.stopNote(endTime, stopTime, scheduledNotes, i);
1064
1124
  }
1065
1125
  else {
1066
- const portamentoTime = stopTime + channel.portamentoTime;
1126
+ const portamentoTime = endTime + channel.portamentoTime;
1067
1127
  const detuneChange = (portamentoNoteNumber - noteNumber) * 100;
1068
1128
  const detune = note.bufferSource.detune.value + detuneChange;
1069
1129
  note.bufferSource.detune
1070
- .cancelScheduledValues(stopTime)
1130
+ .cancelScheduledValues(endTime)
1071
1131
  .linearRampToValueAtTime(detune, portamentoTime);
1072
- return this.stopNote(stopTime, portamentoTime, scheduledNotes, i);
1132
+ return this.stopNote(endTime, portamentoTime, scheduledNotes, i);
1073
1133
  }
1074
1134
  }
1075
1135
  }
@@ -1116,7 +1176,7 @@ export class Midy {
1116
1176
  case 0x90:
1117
1177
  return this.noteOn(channelNumber, data1, data2);
1118
1178
  case 0xA0:
1119
- return; // this.handlePolyphonicKeyPressure(channelNumber, data1, data2);
1179
+ return this.handlePolyphonicKeyPressure(channelNumber, data1, data2);
1120
1180
  case 0xB0:
1121
1181
  return this.handleControlChange(channelNumber, data1, data2);
1122
1182
  case 0xC0:
@@ -1311,20 +1371,44 @@ export class Midy {
1311
1371
  if (0 < reverbSendLevel) {
1312
1372
  const now = this.audioContext.currentTime;
1313
1373
  channel.reverbSendLevel = reverbSendLevel / 127;
1314
- reverbEffect.output.gain.cancelScheduledValues(now);
1315
- reverbEffect.output.gain.setValueAtTime(channel.reverbSendLevel, now);
1374
+ reverbEffect.input.gain.cancelScheduledValues(now);
1375
+ reverbEffect.input.gain.setValueAtTime(channel.reverbSendLevel, now);
1316
1376
  }
1317
1377
  else {
1318
- channel.merger.disconnect(reverbEffect.input);
1378
+ channel.scheduledNotes.forEach((noteList) => {
1379
+ for (let i = 0; i < noteList.length; i++) {
1380
+ const note = noteList[i];
1381
+ if (!note)
1382
+ continue;
1383
+ if (note.instrumentKey.reverbEffectsSend <= 0)
1384
+ continue;
1385
+ note.reverbEffectsSend.disconnect();
1386
+ }
1387
+ });
1319
1388
  }
1320
1389
  }
1321
1390
  else {
1322
1391
  if (0 < reverbSendLevel) {
1323
- channel.merger.connect(reverbEffect.input);
1324
1392
  const now = this.audioContext.currentTime;
1393
+ channel.scheduledNotes.forEach((noteList) => {
1394
+ for (let i = 0; i < noteList.length; i++) {
1395
+ const note = noteList[i];
1396
+ if (!note)
1397
+ continue;
1398
+ if (note.instrumentKey.reverbEffectsSend <= 0)
1399
+ continue;
1400
+ if (!note.reverbEffectsSend) {
1401
+ note.reverbEffectsSend = new GainNode(this.audioContext, {
1402
+ gain: note.instrumentKey.reverbEffectsSend,
1403
+ });
1404
+ note.volumeNode.connect(note.reverbEffectsSend);
1405
+ }
1406
+ note.reverbEffectsSend.connect(reverbEffect.input);
1407
+ }
1408
+ });
1325
1409
  channel.reverbSendLevel = reverbSendLevel / 127;
1326
- reverbEffect.output.gain.cancelScheduledValues(now);
1327
- reverbEffect.output.gain.setValueAtTime(channel.reverbSendLevel, now);
1410
+ reverbEffect.input.gain.cancelScheduledValues(now);
1411
+ reverbEffect.input.gain.setValueAtTime(channel.reverbSendLevel, now);
1328
1412
  }
1329
1413
  }
1330
1414
  }
@@ -1335,20 +1419,44 @@ export class Midy {
1335
1419
  if (0 < chorusSendLevel) {
1336
1420
  const now = this.audioContext.currentTime;
1337
1421
  channel.chorusSendLevel = chorusSendLevel / 127;
1338
- chorusEffect.output.gain.cancelScheduledValues(now);
1339
- chorusEffect.output.gain.setValueAtTime(channel.chorusSendLevel, now);
1422
+ chorusEffect.input.gain.cancelScheduledValues(now);
1423
+ chorusEffect.input.gain.setValueAtTime(channel.chorusSendLevel, now);
1340
1424
  }
1341
1425
  else {
1342
- channel.merger.disconnect(chorusEffect.input);
1426
+ channel.scheduledNotes.forEach((noteList) => {
1427
+ for (let i = 0; i < noteList.length; i++) {
1428
+ const note = noteList[i];
1429
+ if (!note)
1430
+ continue;
1431
+ if (note.instrumentKey.chorusEffectsSend <= 0)
1432
+ continue;
1433
+ note.chorusEffectsSend.disconnect();
1434
+ }
1435
+ });
1343
1436
  }
1344
1437
  }
1345
1438
  else {
1346
1439
  if (0 < chorusSendLevel) {
1347
- channel.merger.connect(chorusEffect.input);
1348
1440
  const now = this.audioContext.currentTime;
1441
+ channel.scheduledNotes.forEach((noteList) => {
1442
+ for (let i = 0; i < noteList.length; i++) {
1443
+ const note = noteList[i];
1444
+ if (!note)
1445
+ continue;
1446
+ if (note.instrumentKey.chorusEffectsSend <= 0)
1447
+ continue;
1448
+ if (!note.chorusEffectsSend) {
1449
+ note.chorusEffectsSend = new GainNode(this.audioContext, {
1450
+ gain: note.instrumentKey.chorusEffectsSend,
1451
+ });
1452
+ note.volumeNode.connect(note.chorusEffectsSend);
1453
+ }
1454
+ note.chorusEffectsSend.connect(chorusEffect.input);
1455
+ }
1456
+ });
1349
1457
  channel.chorusSendLevel = chorusSendLevel / 127;
1350
- chorusEffect.output.gain.cancelScheduledValues(now);
1351
- chorusEffect.output.gain.setValueAtTime(channel.chorusSendLevel, now);
1458
+ chorusEffect.input.gain.cancelScheduledValues(now);
1459
+ chorusEffect.input.gain.setValueAtTime(channel.chorusSendLevel, now);
1352
1460
  }
1353
1461
  }
1354
1462
  }
@@ -1781,10 +1889,8 @@ export class Midy {
1781
1889
  }
1782
1890
  setReverbTime(value) {
1783
1891
  this.reverb.time = this.getReverbTime(value);
1784
- const { audioContext, channels, options } = this;
1785
- for (let i = 0; i < channels.length; i++) {
1786
- channels[i].reverbEffect = options.reverbAlgorithm(audioContext);
1787
- }
1892
+ const { audioContext, options } = this;
1893
+ this.reverbEffect = options.reverbAlgorithm(audioContext);
1788
1894
  }
1789
1895
  getReverbTime(value) {
1790
1896
  return Math.pow(Math.E, (value - 40) * 0.025);
@@ -1861,10 +1967,7 @@ export class Midy {
1861
1967
  const now = this.audioContext.currentTime;
1862
1968
  const modRate = this.getChorusModRate(value);
1863
1969
  this.chorus.modRate = modRate;
1864
- for (let i = 0; i < this.channels.length; i++) {
1865
- const lfo = this.channels[i].chorusEffect.lfo;
1866
- lfo.frequency.setValueAtTime(modRate, now);
1867
- }
1970
+ this.chorusEffect.lfo.frequency.setValueAtTime(modRate, now);
1868
1971
  }
1869
1972
  getChorusModRate(value) {
1870
1973
  return value * 0.122; // Hz
@@ -1873,12 +1976,9 @@ export class Midy {
1873
1976
  const now = this.audioContext.currentTime;
1874
1977
  const modDepth = this.getChorusModDepth(value);
1875
1978
  this.chorus.modDepth = modDepth;
1876
- for (let i = 0; i < this.channels.length; i++) {
1877
- const chorusEffect = this.channels[i].chorusEffect;
1878
- chorusEffect.lfoGain.gain
1879
- .cancelScheduledValues(now)
1880
- .setValueAtTime(modDepth / 2, now);
1881
- }
1979
+ this.chorusEffect.lfoGain.gain
1980
+ .cancelScheduledValues(now)
1981
+ .setValueAtTime(modDepth / 2, now);
1882
1982
  }
1883
1983
  getChorusModDepth(value) {
1884
1984
  return (value + 1) / 3200; // second
@@ -1887,14 +1987,11 @@ export class Midy {
1887
1987
  const now = this.audioContext.currentTime;
1888
1988
  const feedback = this.getChorusFeedback(value);
1889
1989
  this.chorus.feedback = feedback;
1890
- for (let i = 0; i < this.channels.length; i++) {
1891
- const chorusEffect = this.channels[i].chorusEffect;
1892
- for (let j = 0; j < chorusEffect.feedbackGains.length; j++) {
1893
- const feedbackGain = chorusEffect.feedbackGains[j];
1894
- feedbackGain.gain
1895
- .cancelScheduledValues(now)
1896
- .setValueAtTime(feedback, now);
1897
- }
1990
+ const chorusEffect = this.chorusEffect;
1991
+ for (let i = 0; i < chorusEffect.feedbackGains.length; i++) {
1992
+ chorusEffect.feedbackGains[i].gain
1993
+ .cancelScheduledValues(now)
1994
+ .setValueAtTime(feedback, now);
1898
1995
  }
1899
1996
  }
1900
1997
  getChorusFeedback(value) {
@@ -1902,15 +1999,28 @@ export class Midy {
1902
1999
  }
1903
2000
  setChorusSendToReverb(value) {
1904
2001
  const sendToReverb = this.getChorusSendToReverb(value);
1905
- if (0 < sendToReverb) {
1906
- const now = this.audioContext.currentTime;
2002
+ const sendGain = this.chorusEffect.sendGain;
2003
+ if (0 < this.chorus.sendToReverb) {
1907
2004
  this.chorus.sendToReverb = sendToReverb;
1908
- this.chorusEffect.sendGain.gain
1909
- .cancelScheduledValues(now)
1910
- .setValueAtTime(sendToReverb, now);
2005
+ if (0 < sendToReverb) {
2006
+ const now = this.audioContext.currentTime;
2007
+ sendGain.gain
2008
+ .cancelScheduledValues(now)
2009
+ .setValueAtTime(sendToReverb, now);
2010
+ }
2011
+ else {
2012
+ sendGain.disconnect();
2013
+ }
1911
2014
  }
1912
- else if (this.chorus.sendToReverb !== 0) {
1913
- this.chorusEffect.sendGain.disconnect(this.reverbEffect.input);
2015
+ else {
2016
+ this.chorus.sendToReverb = sendToReverb;
2017
+ if (0 < sendToReverb) {
2018
+ const now = this.audioContext.currentTime;
2019
+ sendGain.connect(this.reverbEffect.input);
2020
+ sendGain.gain
2021
+ .cancelScheduledValues(now)
2022
+ .setValueAtTime(sendToReverb, now);
2023
+ }
1914
2024
  }
1915
2025
  }
1916
2026
  getChorusSendToReverb(value) {