@marmooo/midy 0.2.9 → 0.3.1

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 CHANGED
@@ -9,7 +9,8 @@ This library provides several files depending on the implementation level.
9
9
  [en](https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/gml-v1.pdf),
10
10
  [ja](https://amei.or.jp/midistandardcommittee/Recommended_Practice/General_MIDI_Lite_v1.0_japanese.pdf))
11
11
  - midy-GM1.js: support minimal GM1 (ref:
12
- [en](https://archive.org/details/complete_midi_96-1-3/page/n1/mode/2up),
12
+ [en](https://midi.org/midi-1-0-detailed-specification),
13
+ [en full](https://archive.org/details/complete_midi_96-1-3/page/n1/mode/2up),
13
14
  [ja](https://amei.or.jp/midistandardcommittee/MIDI1.0.pdf))
14
15
  - midy-GM2.js: support minimal GM2 (ref:
15
16
  [en v1.2a](https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/GM2-v12a.pdf),
package/esm/midy-GM1.d.ts CHANGED
@@ -1,9 +1,7 @@
1
1
  export class MidyGM1 {
2
2
  static channelSettings: {
3
- currentBufferSource: null;
4
- isDrum: boolean;
5
3
  detune: number;
6
- program: number;
4
+ programNumber: number;
7
5
  bank: number;
8
6
  dataMSB: number;
9
7
  dataLSB: number;
@@ -15,6 +13,7 @@ export class MidyGM1 {
15
13
  };
16
14
  constructor(audioContext: any);
17
15
  mode: string;
16
+ numChannels: number;
18
17
  ticksPerBeat: number;
19
18
  totalTime: number;
20
19
  noteCheckInterval: number;
@@ -34,7 +33,7 @@ export class MidyGM1 {
34
33
  timeline: any[];
35
34
  instruments: any[];
36
35
  notePromises: any[];
37
- exclusiveClassMap: SparseMap;
36
+ exclusiveClassNotes: any[];
38
37
  audioContext: any;
39
38
  masterVolume: any;
40
39
  scheduler: any;
@@ -88,6 +87,7 @@ export class MidyGM1 {
88
87
  instruments: Set<any>;
89
88
  timeline: any[];
90
89
  };
90
+ stopActiveNotes(channelNumber: any, velocity: any, force: any, scheduleTime: any): Promise<any[]>;
91
91
  stopChannelNotes(channelNumber: any, velocity: any, force: any, scheduleTime: any): Promise<any[]>;
92
92
  stopNotes(velocity: any, force: any, scheduleTime: any): Promise<any[]>;
93
93
  start(): Promise<void>;
@@ -112,16 +112,19 @@ export class MidyGM1 {
112
112
  clampCutoffFrequency(frequency: any): number;
113
113
  setFilterEnvelope(note: any, scheduleTime: any): void;
114
114
  startModulation(channel: any, note: any, scheduleTime: any): void;
115
- getAudioBuffer(program: any, noteNumber: any, velocity: any, voiceParams: any, isSF3: any): Promise<any>;
115
+ getAudioBuffer(programNumber: any, noteNumber: any, velocity: any, voiceParams: any, isSF3: any): Promise<any>;
116
116
  createNote(channel: any, voice: any, noteNumber: any, velocity: any, startTime: any, isSF3: any): Promise<Note>;
117
+ handleExclusiveClass(note: any, channelNumber: any, startTime: any): void;
117
118
  scheduleNoteOn(channelNumber: any, noteNumber: any, velocity: any, startTime: any): Promise<void>;
118
119
  noteOn(channelNumber: any, noteNumber: any, velocity: any, scheduleTime: any): Promise<void>;
119
- stopNote(endTime: any, stopTime: any, scheduledNotes: any, index: any): Promise<any>;
120
+ disconnectNote(note: any): void;
121
+ stopNote(endTime: any, stopTime: any, noteList: any, index: any): Promise<any>;
122
+ findNoteOffTarget(noteList: any): any[] | undefined;
120
123
  scheduleNoteOff(channelNumber: any, noteNumber: any, _velocity: any, endTime: any, force: any): Promise<any> | undefined;
121
124
  noteOff(channelNumber: any, noteNumber: any, velocity: any, scheduleTime: any): Promise<any> | undefined;
122
125
  releaseSustainPedal(channelNumber: any, halfVelocity: any, scheduleTime: any): (Promise<any> | undefined)[];
123
126
  handleMIDIMessage(statusByte: any, data1: any, data2: any, scheduleTime: any): void | Promise<any>;
124
- handleProgramChange(channelNumber: any, program: any, _scheduleTime: any): void;
127
+ handleProgramChange(channelNumber: any, programNumber: any, _scheduleTime: any): void;
125
128
  handlePitchBendMessage(channelNumber: any, lsb: any, msb: any, scheduleTime: any): void;
126
129
  setPitchBend(channelNumber: any, value: any, scheduleTime: any): void;
127
130
  setModLfoToPitch(channel: any, note: any, scheduleTime: any): void;
@@ -183,6 +186,7 @@ export class MidyGM1 {
183
186
  handleCoarseTuningRPN(channelNumber: any, scheduleTime: any): void;
184
187
  setCoarseTuning(channelNumber: any, value: any, scheduleTime: any): void;
185
188
  allSoundOff(channelNumber: any, _value: any, scheduleTime: any): Promise<any[]>;
189
+ resetAllStates(channelNumber: any): void;
186
190
  resetAllControllers(channelNumber: any): void;
187
191
  allNotesOff(channelNumber: any, _value: any, scheduleTime: any): Promise<any[]>;
188
192
  handleUniversalNonRealTimeExclusiveMessage(data: any, scheduleTime: any): void;
@@ -1 +1 @@
1
- {"version":3,"file":"midy-GM1.d.ts","sourceRoot":"","sources":["../src/midy-GM1.js"],"names":[],"mappings":"AAiJA;IAuBE;;;;;;;;;;;;;MAaE;IAEF,+BAcC;IAnDD,aAAa;IACb,qBAAmB;IACnB,kBAAc;IACd,0BAAwB;IACxB,kBAAc;IACd,mBAAiB;IACjB,kBAAc;IACd,mBAAe;IACf,kBAAgB;IAChB,sBAA2C;IAC3C,kCAA+B;IAC/B,gCAA6B;IAC7B,mBAAkB;IAClB,mBAAkB;IAClB,kBAAiB;IACjB,oBAAmB;IACnB,mBAAkB;IAClB,gBAAc;IACd,mBAAiB;IACjB,oBAAkB;IAClB,6BAAuC;IAkBrC,kBAAgC;IAChC,kBAA8C;IAC9C,eAAwD;IACxD,qBAGE;IACF;;;;;;;;;;;MAA2D;IAC3D;;;;;;;;;;;;;MAA+D;IAC/D,gBAAiD;IAMnD,4BAMC;IAED,mCAWC;IAED,gDAMC;IAED,sCASC;IAED;;;;MAeC;IAED,yCAWC;IAED,6DA2BC;IAED,4DASC;IAED,2EAsDC;IAED,mCAOC;IAED,0BAoDC;IAED,uDAEC;IAED,wDAEC;IAED,6EAEC;IAED;;;MA4EC;IAED,mGAgBC;IAED,wEAMC;IAED,uBAKC;IAED,aAGC;IAED,cAKC;IAED,wBAIC;IAED,0BAKC;IAED,wBAOC;IAED,sBAGC;IAED,yDAQC;IAED,2DASC;IAED,qDAQC;IAED,2BAEC;IAED,8BAEC;IAED,8BAEC;IAED,4BAEC;IAED,qCAMC;IAED,2DAIC;IAED,+DAIC;IAED,sDAeC;IAED,qDAoBC;IAED,6CAIC;IAED,sDAsBC;IAED,kEAoBC;IAED,yGAgBC;IAED,gHAwCC;IAED,kGAkDC;IAED,6FAQC;IAED,qFAwBC;IAED,yHAuBC;IAED,yGASC;IAED,4GAeC;IAED,mGA2BC;IAED,gFAGC;IAED,wFAGC;IAED,sEAWC;IAED,mEAQC;IAED,wDAKC;IAED,sDAOC;IAED,mDAMC;IAED,kDAKC;IAED;;;;;;;;;;;MA2BC;IAED,oFAMC;IAED,6EA2CC;IAED;;;;;;;;;;;;;MAeC;IAED,kGAWC;IAED,wDAUC;IAED,iFAMC;IAED,oEAKC;IAED;;;MAMC;IAED,8DAKC;IAED,4EAKC;IAED,sEAGC;IAED,2DAUC;IAED,yEAYC;IAED,kFAeC;IAED,2DAMC;IAED,uDAkBC;IAED,gDAEC;IAED,gDAEC;IAED,sEAGC;IAED,qEAKC;IAED,2EAWC;IAED,iEAKC;IAED,uEASC;IAED,mEAKC;IAED,yEASC;IAED,gFAGC;IAED,8CAqBC;IAED,gFAGC;IAED,+EAgBC;IAED,qCAWC;IAED,4EAaC;IAED,4DAGC;IAED,sDASC;IAED,gDAYC;IAED,6DAgBC;CACF;AA97CD;IACE,uBAGC;IAFC,YAA2B;IAC3B,qBAAuB;IAGzB,gCAKC;IAED,mBAEC;IAED,0BAUC;IAED,uBAEC;IAED,mBAEC;IAED,cAMC;IASD,6BAKC;IAZD,qDAKC;CAQF;AAED;IASE,0FAMC;IAdD,kBAAa;IACb,gBAAW;IACX,iBAAY;IACZ,wBAAmB;IACnB,iBAAY;IACZ,mBAAc;IACd,qBAAgB;IAGd,gBAA4B;IAC5B,cAAwB;IACxB,eAA0B;IAC1B,WAAkB;IAClB,iBAA8B;CAEjC"}
1
+ {"version":3,"file":"midy-GM1.d.ts","sourceRoot":"","sources":["../src/midy-GM1.js"],"names":[],"mappings":"AAiJA;IAwBE;;;;;;;;;;;MAWE;IAEF,+BAcC;IAlDD,aAAa;IACb,oBAAiB;IACjB,qBAAmB;IACnB,kBAAc;IACd,0BAAwB;IACxB,kBAAc;IACd,mBAAiB;IACjB,kBAAc;IACd,mBAAe;IACf,kBAAgB;IAChB,sBAA2C;IAC3C,kCAA+B;IAC/B,gCAA6B;IAC7B,mBAAkB;IAClB,mBAAkB;IAClB,kBAAiB;IACjB,oBAAmB;IACnB,mBAAkB;IAClB,gBAAc;IACd,mBAAiB;IACjB,oBAAkB;IAClB,2BAAqC;IAgBnC,kBAAgC;IAChC,kBAA8C;IAC9C,eAAwD;IACxD,qBAGE;IACF;;;;;;;;;;;MAA2D;IAC3D;;;;;;;;;;;;;MAA+D;IAC/D,gBAAiD;IAMnD,4BAMC;IAED,mCAWC;IAED,gDAMC;IAED,sCASC;IAED;;;;MAeC;IAED,yCAaC;IAED,6DA2BC;IAED,4DASC;IAED,2EAsDC;IAED,mCAOC;IAED,0BAoDC;IAED,uDAEC;IAED,wDAEC;IAED,6EAEC;IAED;;;MA4EC;IAED,kGAiBC;IAED,mGAgBC;IAED,wEAMC;IAED,uBAKC;IAED,aAMC;IAED,cAKC;IAED,wBAIC;IAED,0BAKC;IAED,wBAOC;IAED,sBAGC;IAED,yDASC;IAED,2DASC;IAED,qDAQC;IAED,2BAEC;IAED,8BAEC;IAED,8BAEC;IAED,4BAEC;IAED,qCAMC;IAED,2DAIC;IAED,+DAIC;IAED,sDAeC;IAED,qDAoBC;IAED,6CAIC;IAED,sDAsBC;IAED,kEAoBC;IAED,+GA0BC;IAED,gHAwCC;IAED,0EAiBC;IAED,kGAsCC;IAED,6FAQC;IAED,gCASC;IAED,+EAiBC;IAED,oDAOC;IAED,yHAsBC;IAED,yGASC;IAED,4GAeC;IAED,mGA2BC;IAED,sFAGC;IAED,wFAGC;IAED,sEAUC;IAED,mEAQC;IAED,wDAKC;IAED,sDAOC;IAED,mDAMC;IAED,kDAKC;IAED;;;;;;;;;;;MA2BC;IAED,oFAMC;IAED,6EA2CC;IAED;;;;;;;;;;;;;MAeC;IAED,kGAWC;IAED,wDAUC;IAED,iFAKC;IAED,oEAKC;IAED;;;MAMC;IAED,8DAKC;IAED,4EAKC;IAED,sEAGC;IAED,2DAUC;IAED,yEAWC;IAED,kFAeC;IAED,2DAMC;IAED,uDAkBC;IAED,gDAEC;IAED,gDAEC;IAED,sEAGC;IAED,qEAKC;IAED,2EAUC;IAED,iEAKC;IAED,uEAQC;IAED,mEAKC;IAED,yEAQC;IAED,gFAGC;IAED,yCAUC;IAGD,8CAqBC;IAED,gFAGC;IAED,+EAgBC;IAED,qCAWC;IAED,4EAaC;IAED,4DAGC;IAED,sDASC;IAED,gDAYC;IAGD,6DAgBC;CACF;AA3/CD;IACE,uBAGC;IAFC,YAA2B;IAC3B,qBAAuB;IAGzB,gCAKC;IAED,mBAEC;IAED,0BAUC;IAED,uBAEC;IAED,mBAEC;IAED,cAMC;IASD,6BAKC;IAZD,qDAKC;CAQF;AAED;IASE,0FAMC;IAdD,kBAAa;IACb,gBAAW;IACX,iBAAY;IACZ,wBAAmB;IACnB,iBAAY;IACZ,mBAAc;IACd,qBAAgB;IAGd,gBAA4B;IAC5B,cAAwB;IACxB,eAA0B;IAC1B,WAAkB;IAClB,iBAA8B;CAEjC"}
package/esm/midy-GM1.js CHANGED
@@ -176,6 +176,12 @@ export class MidyGM1 {
176
176
  writable: true,
177
177
  value: "GM1"
178
178
  });
179
+ Object.defineProperty(this, "numChannels", {
180
+ enumerable: true,
181
+ configurable: true,
182
+ writable: true,
183
+ value: 16
184
+ });
179
185
  Object.defineProperty(this, "ticksPerBeat", {
180
186
  enumerable: true,
181
187
  configurable: true,
@@ -290,11 +296,11 @@ export class MidyGM1 {
290
296
  writable: true,
291
297
  value: []
292
298
  });
293
- Object.defineProperty(this, "exclusiveClassMap", {
299
+ Object.defineProperty(this, "exclusiveClassNotes", {
294
300
  enumerable: true,
295
301
  configurable: true,
296
302
  writable: true,
297
- value: new SparseMap(128)
303
+ value: new Array(128)
298
304
  });
299
305
  this.audioContext = audioContext;
300
306
  this.masterVolume = new GainNode(audioContext);
@@ -361,8 +367,10 @@ export class MidyGM1 {
361
367
  };
362
368
  }
363
369
  createChannels(audioContext) {
364
- const channels = Array.from({ length: 16 }, () => {
370
+ const channels = Array.from({ length: this.numChannels }, () => {
365
371
  return {
372
+ currentBufferSource: null,
373
+ isDrum: false,
366
374
  ...this.constructor.channelSettings,
367
375
  state: new ControllerState(),
368
376
  ...this.setChannelAudioNodes(audioContext),
@@ -419,7 +427,7 @@ export class MidyGM1 {
419
427
  const startTime = event.startTime + this.startDelay - offset;
420
428
  switch (event.type) {
421
429
  case "noteOn":
422
- if (event.velocity !== 0) {
430
+ if (0 < event.velocity) {
423
431
  await this.scheduleNoteOn(event.channel, event.noteNumber, event.velocity, startTime);
424
432
  break;
425
433
  }
@@ -467,7 +475,7 @@ export class MidyGM1 {
467
475
  if (queueIndex >= this.timeline.length) {
468
476
  await Promise.all(this.notePromises);
469
477
  this.notePromises = [];
470
- this.exclusiveClassMap.clear();
478
+ this.exclusiveClassNotes.fill(undefined);
471
479
  this.audioBufferCache.clear();
472
480
  resolve();
473
481
  return;
@@ -486,7 +494,7 @@ export class MidyGM1 {
486
494
  else if (this.isStopping) {
487
495
  await this.stopNotes(0, true, now);
488
496
  this.notePromises = [];
489
- this.exclusiveClassMap.clear();
497
+ this.exclusiveClassNotes.fill(undefined);
490
498
  this.audioBufferCache.clear();
491
499
  resolve();
492
500
  this.isStopping = false;
@@ -495,7 +503,7 @@ export class MidyGM1 {
495
503
  }
496
504
  else if (this.isSeeking) {
497
505
  this.stopNotes(0, true, now);
498
- this.exclusiveClassMap.clear();
506
+ this.exclusiveClassNotes.fill(undefined);
499
507
  this.startTime = this.audioContext.currentTime;
500
508
  queueIndex = this.getQueueIndex(this.resumeTime);
501
509
  offset = this.resumeTime - this.startTime;
@@ -523,7 +531,7 @@ export class MidyGM1 {
523
531
  extractMidiData(midi) {
524
532
  const instruments = new Set();
525
533
  const timeline = [];
526
- const tmpChannels = new Array(16);
534
+ const tmpChannels = new Array(this.channels.length);
527
535
  for (let i = 0; i < tmpChannels.length; i++) {
528
536
  tmpChannels[i] = {
529
537
  programNumber: -1,
@@ -586,6 +594,17 @@ export class MidyGM1 {
586
594
  }
587
595
  return { instruments, timeline };
588
596
  }
597
+ stopActiveNotes(channelNumber, velocity, force, scheduleTime) {
598
+ const channel = this.channels[channelNumber];
599
+ const promises = [];
600
+ const activeNotes = this.getActiveNotes(channel, scheduleTime);
601
+ activeNotes.forEach((note) => {
602
+ const promise = this.scheduleNoteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force, undefined);
603
+ this.notePromises.push(promise);
604
+ promises.push(promise);
605
+ });
606
+ return Promise.all(promises);
607
+ }
589
608
  stopChannelNotes(channelNumber, velocity, force, scheduleTime) {
590
609
  const channel = this.channels[channelNumber];
591
610
  const promises = [];
@@ -615,6 +634,9 @@ export class MidyGM1 {
615
634
  if (!this.isPlaying)
616
635
  return;
617
636
  this.isStopping = true;
637
+ for (let i = 0; i < this.channels.length; i++) {
638
+ this.resetAllStates(i);
639
+ }
618
640
  }
619
641
  pause() {
620
642
  if (!this.isPlaying || this.isPaused)
@@ -654,6 +676,8 @@ export class MidyGM1 {
654
676
  const note = noteList[i];
655
677
  if (!note)
656
678
  continue;
679
+ if (note.ending)
680
+ continue;
657
681
  callback(note);
658
682
  }
659
683
  });
@@ -792,8 +816,8 @@ export class MidyGM1 {
792
816
  note.modulationLFO.connect(note.volumeDepth);
793
817
  note.volumeDepth.connect(note.volumeEnvelopeNode.gain);
794
818
  }
795
- async getAudioBuffer(program, noteNumber, velocity, voiceParams, isSF3) {
796
- const audioBufferId = this.getAudioBufferId(program, noteNumber, velocity);
819
+ async getAudioBuffer(programNumber, noteNumber, velocity, voiceParams, isSF3) {
820
+ const audioBufferId = this.getAudioBufferId(programNumber, noteNumber, velocity);
797
821
  const cache = this.audioBufferCache.get(audioBufferId);
798
822
  if (cache) {
799
823
  cache.counter += 1;
@@ -816,7 +840,7 @@ export class MidyGM1 {
816
840
  const controllerState = this.getControllerState(channel, noteNumber, velocity);
817
841
  const voiceParams = voice.getAllParams(controllerState);
818
842
  const note = new Note(noteNumber, velocity, startTime, voice, voiceParams);
819
- const audioBuffer = await this.getAudioBuffer(channel.program, noteNumber, velocity, voiceParams, isSF3);
843
+ const audioBuffer = await this.getAudioBuffer(channel.programNumber, noteNumber, velocity, voiceParams, isSF3);
820
844
  note.bufferSource = this.createBufferSource(audioBuffer, voiceParams);
821
845
  note.volumeEnvelopeNode = new GainNode(this.audioContext);
822
846
  note.filterNode = new BiquadFilterNode(this.audioContext, {
@@ -834,14 +858,28 @@ export class MidyGM1 {
834
858
  note.bufferSource.start(startTime);
835
859
  return note;
836
860
  }
861
+ handleExclusiveClass(note, channelNumber, startTime) {
862
+ const exclusiveClass = note.voiceParams.exclusiveClass;
863
+ if (exclusiveClass === 0)
864
+ return;
865
+ const prev = this.exclusiveClassNotes[exclusiveClass];
866
+ if (prev) {
867
+ const [prevNote, prevChannelNumber] = prev;
868
+ if (prevNote && !prevNote.ending) {
869
+ this.scheduleNoteOff(prevChannelNumber, prevNote.noteNumber, 0, // velocity,
870
+ startTime, true);
871
+ }
872
+ }
873
+ this.exclusiveClassNotes[exclusiveClass] = [note, channelNumber];
874
+ }
837
875
  async scheduleNoteOn(channelNumber, noteNumber, velocity, startTime) {
838
876
  const channel = this.channels[channelNumber];
839
877
  const bankNumber = channel.bank;
840
- const soundFontIndex = this.soundFontTable[channel.program].get(bankNumber);
878
+ const soundFontIndex = this.soundFontTable[channel.programNumber].get(bankNumber);
841
879
  if (soundFontIndex === undefined)
842
880
  return;
843
881
  const soundFont = this.soundFonts[soundFontIndex];
844
- const voice = soundFont.getVoice(bankNumber, channel.program, noteNumber, velocity);
882
+ const voice = soundFont.getVoice(bankNumber, channel.programNumber, noteNumber, velocity);
845
883
  if (!voice)
846
884
  return;
847
885
  const isSF3 = soundFont.parsed.info.version.major === 3;
@@ -851,32 +889,33 @@ export class MidyGM1 {
851
889
  if (0.5 <= channel.state.sustainPedal) {
852
890
  channel.sustainNotes.push(note);
853
891
  }
854
- const exclusiveClass = note.voiceParams.exclusiveClass;
855
- if (exclusiveClass !== 0) {
856
- if (this.exclusiveClassMap.has(exclusiveClass)) {
857
- const prevEntry = this.exclusiveClassMap.get(exclusiveClass);
858
- const [prevNote, prevChannelNumber] = prevEntry;
859
- if (prevNote && !prevNote.ending) {
860
- this.scheduleNoteOff(prevChannelNumber, prevNote.noteNumber, 0, // velocity,
861
- startTime, true);
862
- }
863
- }
864
- this.exclusiveClassMap.set(exclusiveClass, [note, channelNumber]);
865
- }
892
+ this.handleExclusiveClass(note, channelNumber, startTime);
866
893
  const scheduledNotes = channel.scheduledNotes;
867
- if (scheduledNotes.has(noteNumber)) {
868
- scheduledNotes.get(noteNumber).push(note);
894
+ let noteList = scheduledNotes.get(noteNumber);
895
+ if (noteList) {
896
+ noteList.push(note);
869
897
  }
870
898
  else {
871
- scheduledNotes.set(noteNumber, [note]);
899
+ noteList = [note];
900
+ scheduledNotes.set(noteNumber, noteList);
872
901
  }
873
902
  }
874
903
  noteOn(channelNumber, noteNumber, velocity, scheduleTime) {
875
904
  scheduleTime ??= this.audioContext.currentTime;
876
905
  return this.scheduleNoteOn(channelNumber, noteNumber, velocity, scheduleTime);
877
906
  }
878
- stopNote(endTime, stopTime, scheduledNotes, index) {
879
- const note = scheduledNotes[index];
907
+ disconnectNote(note) {
908
+ note.bufferSource.disconnect();
909
+ note.filterNode.disconnect();
910
+ note.volumeEnvelopeNode.disconnect();
911
+ if (note.modulationDepth) {
912
+ note.volumeDepth.disconnect();
913
+ note.modulationDepth.disconnect();
914
+ note.modulationLFO.stop();
915
+ }
916
+ }
917
+ stopNote(endTime, stopTime, noteList, index) {
918
+ const note = noteList[index];
880
919
  note.volumeEnvelopeNode.gain
881
920
  .cancelScheduledValues(endTime)
882
921
  .linearRampToValueAtTime(0, stopTime);
@@ -886,41 +925,43 @@ export class MidyGM1 {
886
925
  }, stopTime);
887
926
  return new Promise((resolve) => {
888
927
  note.bufferSource.onended = () => {
889
- scheduledNotes[index] = null;
890
- note.bufferSource.disconnect();
891
- note.filterNode.disconnect();
892
- note.volumeEnvelopeNode.disconnect();
893
- if (note.modulationDepth) {
894
- note.volumeDepth.disconnect();
895
- note.modulationDepth.disconnect();
896
- note.modulationLFO.stop();
897
- }
928
+ noteList[index] = undefined;
929
+ this.disconnectNote(note);
898
930
  resolve();
899
931
  };
900
932
  note.bufferSource.stop(stopTime);
901
933
  });
902
934
  }
935
+ findNoteOffTarget(noteList) {
936
+ for (let i = 0; i < noteList.length; i++) {
937
+ const note = noteList[i];
938
+ if (!note)
939
+ continue;
940
+ if (note.ending)
941
+ continue;
942
+ return [note, i];
943
+ }
944
+ }
903
945
  scheduleNoteOff(channelNumber, noteNumber, _velocity, endTime, force) {
904
946
  const channel = this.channels[channelNumber];
905
947
  if (!force && 0.5 <= channel.state.sustainPedal)
906
948
  return;
907
949
  if (!channel.scheduledNotes.has(noteNumber))
908
950
  return;
909
- const scheduledNotes = channel.scheduledNotes.get(noteNumber);
910
- for (let i = 0; i < scheduledNotes.length; i++) {
911
- const note = scheduledNotes[i];
912
- if (!note)
913
- continue;
914
- if (note.ending)
915
- continue;
916
- const volRelease = endTime + note.voiceParams.volRelease;
917
- const modRelease = endTime + note.voiceParams.modRelease;
918
- note.filterNode.frequency
919
- .cancelScheduledValues(endTime)
920
- .linearRampToValueAtTime(0, modRelease);
921
- const stopTime = Math.min(volRelease, modRelease);
922
- return this.stopNote(endTime, stopTime, scheduledNotes, i);
923
- }
951
+ const noteList = channel.scheduledNotes.get(noteNumber);
952
+ if (!noteList)
953
+ return; // be careful with drum channel
954
+ const noteOffTarget = this.findNoteOffTarget(noteList, endTime);
955
+ if (!noteOffTarget)
956
+ return;
957
+ const [note, i] = noteOffTarget;
958
+ const volRelease = endTime + note.voiceParams.volRelease;
959
+ const modRelease = endTime + note.voiceParams.modRelease;
960
+ note.filterNode.frequency
961
+ .cancelScheduledValues(endTime)
962
+ .linearRampToValueAtTime(0, modRelease);
963
+ const stopTime = Math.min(volRelease, modRelease);
964
+ return this.stopNote(endTime, stopTime, noteList, i);
924
965
  }
925
966
  noteOff(channelNumber, noteNumber, velocity, scheduleTime) {
926
967
  scheduleTime ??= this.audioContext.currentTime;
@@ -955,9 +996,9 @@ export class MidyGM1 {
955
996
  console.warn(`Unsupported MIDI message: ${messageType.toString(16)}`);
956
997
  }
957
998
  }
958
- handleProgramChange(channelNumber, program, _scheduleTime) {
999
+ handleProgramChange(channelNumber, programNumber, _scheduleTime) {
959
1000
  const channel = this.channels[channelNumber];
960
- channel.program = program;
1001
+ channel.programNumber = programNumber;
961
1002
  }
962
1003
  handlePitchBendMessage(channelNumber, lsb, msb, scheduleTime) {
963
1004
  const pitchBend = msb * 128 + lsb;
@@ -965,8 +1006,6 @@ export class MidyGM1 {
965
1006
  }
966
1007
  setPitchBend(channelNumber, value, scheduleTime) {
967
1008
  const channel = this.channels[channelNumber];
968
- if (channel.isDrum)
969
- return;
970
1009
  scheduleTime ??= this.audioContext.currentTime;
971
1010
  const state = channel.state;
972
1011
  const prev = state.pitchWheel * 2 - 1;
@@ -1129,8 +1168,6 @@ export class MidyGM1 {
1129
1168
  }
1130
1169
  setModulationDepth(channelNumber, modulation, scheduleTime) {
1131
1170
  const channel = this.channels[channelNumber];
1132
- if (channel.isDrum)
1133
- return;
1134
1171
  scheduleTime ??= this.audioContext.currentTime;
1135
1172
  channel.state.modulationDepth = modulation / 127;
1136
1173
  this.updateModulation(channel, scheduleTime);
@@ -1177,8 +1214,6 @@ export class MidyGM1 {
1177
1214
  }
1178
1215
  setSustainPedal(channelNumber, value, scheduleTime) {
1179
1216
  const channel = this.channels[channelNumber];
1180
- if (channel.isDrum)
1181
- return;
1182
1217
  scheduleTime ??= this.audioContext.currentTime;
1183
1218
  channel.state.sustainPedal = value / 127;
1184
1219
  if (64 <= value) {
@@ -1251,8 +1286,6 @@ export class MidyGM1 {
1251
1286
  }
1252
1287
  setPitchBendRange(channelNumber, value, scheduleTime) {
1253
1288
  const channel = this.channels[channelNumber];
1254
- if (channel.isDrum)
1255
- return;
1256
1289
  scheduleTime ??= this.audioContext.currentTime;
1257
1290
  const state = channel.state;
1258
1291
  const prev = state.pitchWheelSensitivity;
@@ -1270,8 +1303,6 @@ export class MidyGM1 {
1270
1303
  }
1271
1304
  setFineTuning(channelNumber, value, scheduleTime) {
1272
1305
  const channel = this.channels[channelNumber];
1273
- if (channel.isDrum)
1274
- return;
1275
1306
  scheduleTime ??= this.audioContext.currentTime;
1276
1307
  const prev = channel.fineTuning;
1277
1308
  const next = (value - 8192) / 8.192; // cent
@@ -1287,8 +1318,6 @@ export class MidyGM1 {
1287
1318
  }
1288
1319
  setCoarseTuning(channelNumber, value, scheduleTime) {
1289
1320
  const channel = this.channels[channelNumber];
1290
- if (channel.isDrum)
1291
- return;
1292
1321
  scheduleTime ??= this.audioContext.currentTime;
1293
1322
  const prev = channel.coarseTuning;
1294
1323
  const next = (value - 64) * 100; // cent
@@ -1298,14 +1327,26 @@ export class MidyGM1 {
1298
1327
  }
1299
1328
  allSoundOff(channelNumber, _value, scheduleTime) {
1300
1329
  scheduleTime ??= this.audioContext.currentTime;
1301
- return this.stopChannelNotes(channelNumber, 0, true, scheduleTime);
1330
+ return this.stopActiveNotes(channelNumber, 0, true, scheduleTime);
1331
+ }
1332
+ resetAllStates(channelNumber) {
1333
+ const channel = this.channels[channelNumber];
1334
+ const state = channel.state;
1335
+ for (const type of Object.keys(defaultControllerState)) {
1336
+ state[type] = defaultControllerState[type].defaultValue;
1337
+ }
1338
+ for (const type of Object.keys(this.constructor.channelSettings)) {
1339
+ channel[type] = this.constructor.channelSettings[type];
1340
+ }
1341
+ this.mode = "GM1";
1302
1342
  }
1343
+ // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/rp15.pdf
1303
1344
  resetAllControllers(channelNumber) {
1304
1345
  const stateTypes = [
1346
+ "pitchWheel",
1305
1347
  "expression",
1306
1348
  "modulationDepth",
1307
1349
  "sustainPedal",
1308
- "pitchWheelSensitivity",
1309
1350
  ];
1310
1351
  const channel = this.channels[channelNumber];
1311
1352
  const state = channel.state;
@@ -1324,7 +1365,7 @@ export class MidyGM1 {
1324
1365
  }
1325
1366
  allNotesOff(channelNumber, _value, scheduleTime) {
1326
1367
  scheduleTime ??= this.audioContext.currentTime;
1327
- return this.stopChannelNotes(channelNumber, 0, false, scheduleTime);
1368
+ return this.stopActiveNotes(channelNumber, 0, false, scheduleTime);
1328
1369
  }
1329
1370
  handleUniversalNonRealTimeExclusiveMessage(data, scheduleTime) {
1330
1371
  switch (data[2]) {
@@ -1394,6 +1435,7 @@ export class MidyGM1 {
1394
1435
  console.warn(`Unsupported Exclusive Message: ${data}`);
1395
1436
  }
1396
1437
  }
1438
+ // https://github.com/marmooo/js-timer-benchmark
1397
1439
  scheduleTask(callback, scheduleTime) {
1398
1440
  return new Promise((resolve) => {
1399
1441
  const bufferSource = new AudioBufferSourceNode(this.audioContext, {
@@ -1418,10 +1460,8 @@ Object.defineProperty(MidyGM1, "channelSettings", {
1418
1460
  configurable: true,
1419
1461
  writable: true,
1420
1462
  value: {
1421
- currentBufferSource: null,
1422
- isDrum: false,
1423
1463
  detune: 0,
1424
- program: 0,
1464
+ programNumber: 0,
1425
1465
  bank: 0,
1426
1466
  dataMSB: 0,
1427
1467
  dataLSB: 0,
package/esm/midy-GM2.d.ts CHANGED
@@ -1,9 +1,7 @@
1
1
  export class MidyGM2 {
2
2
  static channelSettings: {
3
- currentBufferSource: null;
4
- isDrum: boolean;
5
3
  detune: number;
6
- program: number;
4
+ programNumber: number;
7
5
  bank: number;
8
6
  bankMSB: number;
9
7
  bankLSB: number;
@@ -23,8 +21,6 @@ export class MidyGM2 {
23
21
  };
24
22
  });
25
23
  mode: string;
26
- ticksPerBeat: number;
27
- totalTime: number;
28
24
  masterFineTuning: number;
29
25
  masterCoarseTuning: number;
30
26
  reverb: {
@@ -38,6 +34,9 @@ export class MidyGM2 {
38
34
  sendToReverb: number;
39
35
  delayTimes: any[];
40
36
  };
37
+ numChannels: number;
38
+ ticksPerBeat: number;
39
+ totalTime: number;
41
40
  noteCheckInterval: number;
42
41
  lookAhead: number;
43
42
  startDelay: number;
@@ -55,7 +54,8 @@ export class MidyGM2 {
55
54
  timeline: any[];
56
55
  instruments: any[];
57
56
  notePromises: any[];
58
- exclusiveClassMap: SparseMap;
57
+ exclusiveClassNotes: any[];
58
+ drumExclusiveClassNotes: any[];
59
59
  defaultOptions: {
60
60
  reverbAlgorithm: (audioContext: any) => {
61
61
  input: any;
@@ -135,8 +135,7 @@ export class MidyGM2 {
135
135
  };
136
136
  createChannels(audioContext: any): any[];
137
137
  createNoteBuffer(voiceParams: any, isSF3: any): Promise<any>;
138
- calcLoopMode(channel: any, note: any, voiceParams: any): boolean;
139
- createBufferSource(channel: any, note: any, voiceParams: any, audioBuffer: any): any;
138
+ createBufferSource(voiceParams: any, audioBuffer: any): any;
140
139
  findPortamentoTarget(queueIndex: any): any;
141
140
  scheduleTimelineEvents(t: any, offset: any, queueIndex: any): Promise<any>;
142
141
  getQueueIndex(second: any): number;
@@ -148,6 +147,7 @@ export class MidyGM2 {
148
147
  instruments: Set<any>;
149
148
  timeline: any[];
150
149
  };
150
+ stopActiveNotes(channelNumber: any, velocity: any, force: any, scheduleTime: any): Promise<any[]>;
151
151
  stopChannelNotes(channelNumber: any, velocity: any, force: any, scheduleTime: any): Promise<any[]>;
152
152
  stopNotes(velocity: any, force: any, scheduleTime: any): Promise<any[]>;
153
153
  start(): Promise<void>;
@@ -199,18 +199,23 @@ export class MidyGM2 {
199
199
  setFilterEnvelope(channel: any, note: any, scheduleTime: any): void;
200
200
  startModulation(channel: any, note: any, scheduleTime: any): void;
201
201
  startVibrato(channel: any, note: any, scheduleTime: any): void;
202
- getAudioBuffer(program: any, noteNumber: any, velocity: any, voiceParams: any, isSF3: any): Promise<any>;
202
+ getAudioBuffer(programNumber: any, noteNumber: any, velocity: any, voiceParams: any, isSF3: any): Promise<any>;
203
203
  createNote(channel: any, voice: any, noteNumber: any, velocity: any, startTime: any, portamento: any, isSF3: any): Promise<Note>;
204
204
  calcBank(channel: any): any;
205
+ handleExclusiveClass(note: any, channelNumber: any, startTime: any): void;
206
+ handleDrumExclusiveClass(note: any, channelNumber: any, startTime: any): void;
207
+ isDrumNoteOffException(channel: any, noteNumber: any): boolean;
205
208
  scheduleNoteOn(channelNumber: any, noteNumber: any, velocity: any, startTime: any, portamento: any): Promise<void>;
206
209
  noteOn(channelNumber: any, noteNumber: any, velocity: any, scheduleTime: any): Promise<void>;
207
- stopNote(endTime: any, stopTime: any, scheduledNotes: any, index: any): Promise<any>;
210
+ disconnectNote(note: any): void;
211
+ stopNote(endTime: any, stopTime: any, noteList: any, index: any): Promise<any>;
212
+ findNoteOffTarget(noteList: any): any[] | undefined;
208
213
  scheduleNoteOff(channelNumber: any, noteNumber: any, _velocity: any, endTime: any, force: any, portamentoNoteNumber: any): Promise<any> | undefined;
209
214
  noteOff(channelNumber: any, noteNumber: any, velocity: any, scheduleTime: any): Promise<any> | undefined;
210
215
  releaseSustainPedal(channelNumber: any, halfVelocity: any, scheduleTime: any): (Promise<any> | undefined)[];
211
216
  releaseSostenutoPedal(channelNumber: any, halfVelocity: any, scheduleTime: any): any[];
212
217
  handleMIDIMessage(statusByte: any, data1: any, data2: any, scheduleTime: any): void | Promise<any>;
213
- handleProgramChange(channelNumber: any, program: any, _scheduleTime: any): void;
218
+ handleProgramChange(channelNumber: any, programNumber: any, _scheduleTime: any): void;
214
219
  handleChannelPressure(channelNumber: any, value: any, scheduleTime: any): void;
215
220
  handlePitchBendMessage(channelNumber: any, lsb: any, msb: any, scheduleTime: any): void;
216
221
  setPitchBend(channelNumber: any, value: any, scheduleTime: any): void;
@@ -301,6 +306,7 @@ export class MidyGM2 {
301
306
  handleModulationDepthRangeRPN(channelNumber: any, scheduleTime: any): void;
302
307
  setModulationDepthRange(channelNumber: any, modulationDepthRange: any, scheduleTime: any): void;
303
308
  allSoundOff(channelNumber: any, _value: any, scheduleTime: any): Promise<any[]>;
309
+ resetAllStates(channelNumber: any): void;
304
310
  resetAllControllers(channelNumber: any): void;
305
311
  allNotesOff(channelNumber: any, _value: any, scheduleTime: any): Promise<any[]>;
306
312
  omniOff(channelNumber: any, value: any, scheduleTime: any): void;
@@ -1 +1 @@
1
- {"version":3,"file":"midy-GM2.d.ts","sourceRoot":"","sources":["../src/midy-GM2.js"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"midy-GM2.d.ts","sourceRoot":"","sources":["../src/midy-GM2.js"],"names":[],"mappings":"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"}