@marmooo/midy 0.1.5 → 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 +6 -5
  2. package/esm/midy-GM1.d.ts.map +1 -1
  3. package/esm/midy-GM1.js +97 -65
  4. package/esm/midy-GM2.d.ts +15 -9
  5. package/esm/midy-GM2.d.ts.map +1 -1
  6. package/esm/midy-GM2.js +316 -129
  7. package/esm/midy-GMLite.d.ts +6 -5
  8. package/esm/midy-GMLite.d.ts.map +1 -1
  9. package/esm/midy-GMLite.js +97 -65
  10. package/esm/midy.d.ts +15 -9
  11. package/esm/midy.d.ts.map +1 -1
  12. package/esm/midy.js +317 -134
  13. package/package.json +5 -1
  14. package/script/midy-GM1.d.ts +6 -5
  15. package/script/midy-GM1.d.ts.map +1 -1
  16. package/script/midy-GM1.js +100 -68
  17. package/script/midy-GM2.d.ts +15 -9
  18. package/script/midy-GM2.d.ts.map +1 -1
  19. package/script/midy-GM2.js +319 -132
  20. package/script/midy-GMLite.d.ts +6 -5
  21. package/script/midy-GMLite.d.ts.map +1 -1
  22. package/script/midy-GMLite.js +100 -68
  23. package/script/midy.d.ts +15 -9
  24. package/script/midy.d.ts.map +1 -1
  25. package/script/midy.js +320 -137
  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: {
@@ -64,7 +65,6 @@ export class MidyGMLite {
64
65
  createChannels(audioContext: any): any[];
65
66
  createNoteBuffer(instrumentKey: any, isSF3: any): Promise<any>;
66
67
  createNoteBufferNode(instrumentKey: any, isSF3: any): Promise<any>;
67
- convertToFloat32Array(uint8Array: any): Float32Array;
68
68
  scheduleTimelineEvents(t: any, offset: any, queueIndex: any): Promise<any>;
69
69
  getQueueIndex(second: any): number;
70
70
  playNotes(): Promise<any>;
@@ -74,8 +74,8 @@ export class MidyGMLite {
74
74
  instruments: Set<any>;
75
75
  timeline: any[];
76
76
  };
77
- stopChannelNotes(channelNumber: any, velocity: any, stopPedal: any): Promise<void>;
78
- stopNotes(velocity: any, stopPedal: any): Promise<any[]>;
77
+ stopChannelNotes(channelNumber: any, velocity: any, force: any): Promise<void>;
78
+ stopNotes(velocity: any, force: any): Promise<any[]>;
79
79
  start(): Promise<void>;
80
80
  stop(): void;
81
81
  pause(): void;
@@ -97,7 +97,8 @@ export class MidyGMLite {
97
97
  createNote(channel: any, instrumentKey: any, noteNumber: any, velocity: any, startTime: any, isSF3: any): Promise<Note>;
98
98
  scheduleNoteOn(channelNumber: any, noteNumber: any, velocity: any, startTime: any): Promise<void>;
99
99
  noteOn(channelNumber: any, noteNumber: any, velocity: any): Promise<void>;
100
- scheduleNoteRelease(channelNumber: any, noteNumber: any, _velocity: any, stopTime: any, stopPedal?: boolean): Promise<any> | undefined;
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;
101
102
  releaseNote(channelNumber: any, noteNumber: any, velocity: any): Promise<any> | undefined;
102
103
  releaseSustainPedal(channelNumber: any, halfVelocity: any): any[];
103
104
  handleMIDIMessage(statusByte: any, data1: any, data2: any): void | Promise<any>;
@@ -129,7 +130,7 @@ export class MidyGMLite {
129
130
  setPan(channelNumber: any, pan: any): void;
130
131
  setExpression(channelNumber: any, expression: any): void;
131
132
  dataEntryLSB(channelNumber: any, value: any): void;
132
- updateChannelGain(channel: any): void;
133
+ updateChannelVolume(channel: any): void;
133
134
  setSustainPedal(channelNumber: any, value: any): void;
134
135
  handleRPN(channelNumber: any): void;
135
136
  setRPNMSB(channelNumber: any, value: 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,mCASC;IAED,gDAMC;IAED,sCASC;IAED;;;;MAeC;IAED,yCAUC;IAED,+DAyBC;IAED,mEAWC;IAED,qDAOC;IAED,2EA+CC;IAED,mCAOC;IAED,0BA+CC;IAED,uDAEC;IAED,wDAEC;IAED;;;MA8DC;IAED,mFAmBC;IAED,yDAKC;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,wHAmCC;IAED,kGA8BC;IAED,0EAGC;IAED,uIA2CC;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,sCAUC;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,oBAQC;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,8 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MidyGMLite = void 0;
4
- const _esm_js_1 = require("./deps/cdn.jsdelivr.net/npm/midi-file@1.2.4/+esm.js");
5
- const _esm_js_2 = require("./deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.6/+esm.js");
4
+ const midi_file_1 = require("midi-file");
5
+ const soundfont_parser_1 = require("@marmooo/soundfont-parser");
6
6
  class Note {
7
7
  constructor(noteNumber, velocity, startTime, instrumentKey) {
8
8
  Object.defineProperty(this, "bufferSource", {
@@ -151,6 +151,12 @@ class MidyGMLite {
151
151
  writable: true,
152
152
  value: []
153
153
  });
154
+ Object.defineProperty(this, "exclusiveClassMap", {
155
+ enumerable: true,
156
+ configurable: true,
157
+ writable: true,
158
+ value: new Map()
159
+ });
154
160
  this.audioContext = audioContext;
155
161
  this.masterGain = new GainNode(audioContext);
156
162
  this.controlChangeHandlers = this.createControlChangeHandlers();
@@ -168,24 +174,26 @@ class MidyGMLite {
168
174
  addSoundFont(soundFont) {
169
175
  const index = this.soundFonts.length;
170
176
  this.soundFonts.push(soundFont);
171
- soundFont.parsed.presetHeaders.forEach((presetHeader) => {
177
+ const presetHeaders = soundFont.parsed.presetHeaders;
178
+ for (let i = 0; i < presetHeaders.length; i++) {
179
+ const presetHeader = presetHeaders[i];
172
180
  if (!presetHeader.presetName.startsWith("\u0000")) { // TODO: Only SF3 generated by PolyPone?
173
181
  const banks = this.soundFontTable[presetHeader.preset];
174
182
  banks.set(presetHeader.bank, index);
175
183
  }
176
- });
184
+ }
177
185
  }
178
186
  async loadSoundFont(soundFontUrl) {
179
187
  const response = await fetch(soundFontUrl);
180
188
  const arrayBuffer = await response.arrayBuffer();
181
- const parsed = (0, _esm_js_2.parse)(new Uint8Array(arrayBuffer));
182
- const soundFont = new _esm_js_2.SoundFont(parsed);
189
+ const parsed = (0, soundfont_parser_1.parse)(new Uint8Array(arrayBuffer));
190
+ const soundFont = new soundfont_parser_1.SoundFont(parsed);
183
191
  this.addSoundFont(soundFont);
184
192
  }
185
193
  async loadMIDI(midiUrl) {
186
194
  const response = await fetch(midiUrl);
187
195
  const arrayBuffer = await response.arrayBuffer();
188
- const midi = (0, _esm_js_1.parseMidi)(new Uint8Array(arrayBuffer));
196
+ const midi = (0, midi_file_1.parseMidi)(new Uint8Array(arrayBuffer));
189
197
  this.ticksPerBeat = midi.header.ticksPerBeat;
190
198
  const midiData = this.extractMidiData(midi);
191
199
  this.instruments = midiData.instruments;
@@ -218,27 +226,31 @@ class MidyGMLite {
218
226
  return channels;
219
227
  }
220
228
  async createNoteBuffer(instrumentKey, isSF3) {
229
+ const sampleStart = instrumentKey.start;
221
230
  const sampleEnd = instrumentKey.sample.length + instrumentKey.end;
222
231
  if (isSF3) {
223
- const sample = new Uint8Array(instrumentKey.sample.length);
224
- sample.set(instrumentKey.sample);
225
- const audioBuffer = await this.audioContext.decodeAudioData(sample.buffer);
226
- for (let channel = 0; channel < audioBuffer.numberOfChannels; channel++) {
227
- const channelData = audioBuffer.getChannelData(channel);
228
- channelData.set(channelData.subarray(0, sampleEnd));
229
- }
232
+ const sample = instrumentKey.sample;
233
+ const start = sample.byteOffset + sampleStart;
234
+ const end = sample.byteOffset + sampleEnd;
235
+ const buffer = sample.buffer.slice(start, end);
236
+ const audioBuffer = await this.audioContext.decodeAudioData(buffer);
230
237
  return audioBuffer;
231
238
  }
232
239
  else {
233
- const sample = instrumentKey.sample.subarray(0, sampleEnd);
234
- const floatSample = this.convertToFloat32Array(sample);
240
+ const sample = instrumentKey.sample;
241
+ const start = sample.byteOffset + sampleStart;
242
+ const end = sample.byteOffset + sampleEnd;
243
+ const buffer = sample.buffer.slice(start, end);
235
244
  const audioBuffer = new AudioBuffer({
236
245
  numberOfChannels: 1,
237
246
  length: sample.length,
238
247
  sampleRate: instrumentKey.sampleRate,
239
248
  });
240
249
  const channelData = audioBuffer.getChannelData(0);
241
- channelData.set(floatSample);
250
+ const int16Array = new Int16Array(buffer);
251
+ for (let i = 0; i < int16Array.length; i++) {
252
+ channelData[i] = int16Array[i] / 32768;
253
+ }
242
254
  return audioBuffer;
243
255
  }
244
256
  }
@@ -254,14 +266,6 @@ class MidyGMLite {
254
266
  }
255
267
  return bufferSource;
256
268
  }
257
- convertToFloat32Array(uint8Array) {
258
- const int16Array = new Int16Array(uint8Array.buffer);
259
- const float32Array = new Float32Array(int16Array.length);
260
- for (let i = 0; i < int16Array.length; i++) {
261
- float32Array[i] = int16Array[i] / 32768;
262
- }
263
- return float32Array;
264
- }
265
269
  async scheduleTimelineEvents(t, offset, queueIndex) {
266
270
  while (queueIndex < this.timeline.length) {
267
271
  const event = this.timeline[queueIndex];
@@ -317,6 +321,7 @@ class MidyGMLite {
317
321
  if (queueIndex >= this.timeline.length) {
318
322
  await Promise.all(this.notePromises);
319
323
  this.notePromises = [];
324
+ this.exclusiveClassMap.clear();
320
325
  resolve();
321
326
  return;
322
327
  }
@@ -332,6 +337,7 @@ class MidyGMLite {
332
337
  }
333
338
  else if (this.isStopping) {
334
339
  await this.stopNotes(0, true);
340
+ this.exclusiveClassMap.clear();
335
341
  this.notePromises = [];
336
342
  resolve();
337
343
  this.isStopping = false;
@@ -340,6 +346,7 @@ class MidyGMLite {
340
346
  }
341
347
  else if (this.isSeeking) {
342
348
  this.stopNotes(0, true);
349
+ this.exclusiveClassMap.clear();
343
350
  this.startTime = this.audioContext.currentTime;
344
351
  queueIndex = this.getQueueIndex(this.resumeTime);
345
352
  offset = this.resumeTime - this.startTime;
@@ -372,9 +379,11 @@ class MidyGMLite {
372
379
  bank: this.channels[i].bank,
373
380
  };
374
381
  }
375
- midi.tracks.forEach((track) => {
382
+ for (let i = 0; i < midi.tracks.length; i++) {
383
+ const track = midi.tracks[i];
376
384
  let currentTicks = 0;
377
- track.forEach((event) => {
385
+ for (let j = 0; j < track.length; j++) {
386
+ const event = track[j];
378
387
  currentTicks += event.deltaTime;
379
388
  event.ticks = currentTicks;
380
389
  switch (event.type) {
@@ -394,8 +403,8 @@ class MidyGMLite {
394
403
  }
395
404
  delete event.deltaTime;
396
405
  timeline.push(event);
397
- });
398
- });
406
+ }
407
+ }
399
408
  const priority = {
400
409
  controller: 0,
401
410
  sysEx: 1,
@@ -420,7 +429,7 @@ class MidyGMLite {
420
429
  }
421
430
  return { instruments, timeline };
422
431
  }
423
- async stopChannelNotes(channelNumber, velocity, stopPedal) {
432
+ async stopChannelNotes(channelNumber, velocity, force) {
424
433
  const now = this.audioContext.currentTime;
425
434
  const channel = this.channels[channelNumber];
426
435
  channel.scheduledNotes.forEach((noteList) => {
@@ -428,16 +437,16 @@ class MidyGMLite {
428
437
  const note = noteList[i];
429
438
  if (!note)
430
439
  continue;
431
- const promise = this.scheduleNoteRelease(channelNumber, note.noteNumber, velocity, now, stopPedal);
440
+ const promise = this.scheduleNoteRelease(channelNumber, note.noteNumber, velocity, now, force);
432
441
  this.notePromises.push(promise);
433
442
  }
434
443
  });
435
444
  channel.scheduledNotes.clear();
436
445
  await Promise.all(this.notePromises);
437
446
  }
438
- stopNotes(velocity, stopPedal) {
447
+ stopNotes(velocity, force) {
439
448
  for (let i = 0; i < this.channels.length; i++) {
440
- this.stopChannelNotes(i, velocity, stopPedal);
449
+ this.stopChannelNotes(i, velocity, force);
441
450
  }
442
451
  return Promise.all(this.notePromises);
443
452
  }
@@ -613,7 +622,7 @@ class MidyGMLite {
613
622
  note.volumeNode = new GainNode(this.audioContext);
614
623
  note.filterNode = new BiquadFilterNode(this.audioContext, {
615
624
  type: "lowpass",
616
- Q: instrumentKey.initialFilterQ / 10 * channel.filterResonance, // dB
625
+ Q: instrumentKey.initialFilterQ / 10, // dB
617
626
  });
618
627
  this.setVolumeEnvelope(note);
619
628
  this.setFilterEnvelope(note);
@@ -626,7 +635,7 @@ class MidyGMLite {
626
635
  }
627
636
  note.bufferSource.connect(note.filterNode);
628
637
  note.filterNode.connect(note.volumeNode);
629
- note.bufferSource.start(startTime, instrumentKey.start / instrumentKey.sampleRate);
638
+ note.bufferSource.start(startTime);
630
639
  return note;
631
640
  }
632
641
  async scheduleNoteOn(channelNumber, noteNumber, velocity, startTime) {
@@ -643,6 +652,19 @@ class MidyGMLite {
643
652
  const note = await this.createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3);
644
653
  note.volumeNode.connect(channel.gainL);
645
654
  note.volumeNode.connect(channel.gainR);
655
+ const exclusiveClass = instrumentKey.exclusiveClass;
656
+ if (exclusiveClass !== 0) {
657
+ if (this.exclusiveClassMap.has(exclusiveClass)) {
658
+ const prevEntry = this.exclusiveClassMap.get(exclusiveClass);
659
+ const [prevNote, prevChannelNumber] = prevEntry;
660
+ if (!prevNote.ending) {
661
+ this.scheduleNoteRelease(prevChannelNumber, prevNote.noteNumber, 0, // velocity,
662
+ startTime, undefined, // portamentoNoteNumber
663
+ true);
664
+ }
665
+ }
666
+ this.exclusiveClassMap.set(exclusiveClass, [note, channelNumber]);
667
+ }
646
668
  const scheduledNotes = channel.scheduledNotes;
647
669
  if (scheduledNotes.has(noteNumber)) {
648
670
  scheduledNotes.get(noteNumber).push(note);
@@ -655,9 +677,38 @@ class MidyGMLite {
655
677
  const now = this.audioContext.currentTime;
656
678
  return this.scheduleNoteOn(channelNumber, noteNumber, velocity, now);
657
679
  }
658
- scheduleNoteRelease(channelNumber, noteNumber, _velocity, stopTime, stopPedal = false) {
680
+ stopNote(endTime, stopTime, scheduledNotes, index) {
681
+ const note = scheduledNotes[index];
682
+ note.volumeNode.gain
683
+ .cancelScheduledValues(endTime)
684
+ .linearRampToValueAtTime(0, stopTime);
685
+ note.ending = true;
686
+ this.scheduleTask(() => {
687
+ note.bufferSource.loop = false;
688
+ }, stopTime);
689
+ return new Promise((resolve) => {
690
+ note.bufferSource.onended = () => {
691
+ scheduledNotes[index] = null;
692
+ note.bufferSource.disconnect();
693
+ note.volumeNode.disconnect();
694
+ note.filterNode.disconnect();
695
+ if (note.modulationDepth) {
696
+ note.volumeDepth.disconnect();
697
+ note.modulationDepth.disconnect();
698
+ note.modulationLFO.stop();
699
+ }
700
+ if (note.vibratoDepth) {
701
+ note.vibratoDepth.disconnect();
702
+ note.vibratoLFO.stop();
703
+ }
704
+ resolve();
705
+ };
706
+ note.bufferSource.stop(stopTime);
707
+ });
708
+ }
709
+ scheduleNoteRelease(channelNumber, noteNumber, _velocity, endTime, force) {
659
710
  const channel = this.channels[channelNumber];
660
- if (stopPedal && channel.sustainPedal)
711
+ if (!force && channel.sustainPedal)
661
712
  return;
662
713
  if (!channel.scheduledNotes.has(noteNumber))
663
714
  return;
@@ -668,33 +719,13 @@ class MidyGMLite {
668
719
  continue;
669
720
  if (note.ending)
670
721
  continue;
671
- const volEndTime = stopTime + note.instrumentKey.volRelease;
672
- note.volumeNode.gain
673
- .cancelScheduledValues(stopTime)
674
- .linearRampToValueAtTime(0, volEndTime);
675
- const modRelease = stopTime + note.instrumentKey.modRelease;
722
+ const volRelease = endTime + note.instrumentKey.volRelease;
723
+ const modRelease = endTime + note.instrumentKey.modRelease;
676
724
  note.filterNode.frequency
677
- .cancelScheduledValues(stopTime)
725
+ .cancelScheduledValues(endTime)
678
726
  .linearRampToValueAtTime(0, modRelease);
679
- note.ending = true;
680
- this.scheduleTask(() => {
681
- note.bufferSource.loop = false;
682
- }, stopTime);
683
- return new Promise((resolve) => {
684
- note.bufferSource.onended = () => {
685
- scheduledNotes[i] = null;
686
- note.bufferSource.disconnect();
687
- note.volumeNode.disconnect();
688
- note.filterNode.disconnect();
689
- if (note.modulationDepth) {
690
- note.volumeDepth.disconnect();
691
- note.modulationDepth.disconnect();
692
- note.modulationLFO.stop();
693
- }
694
- resolve();
695
- };
696
- note.bufferSource.stop(volEndTime);
697
- });
727
+ const stopTime = Math.min(volRelease, modRelease);
728
+ return this.stopNote(endTime, stopTime, scheduledNotes, i);
698
729
  }
699
730
  }
700
731
  releaseNote(channelNumber, noteNumber, velocity) {
@@ -803,7 +834,7 @@ class MidyGMLite {
803
834
  setVolume(channelNumber, volume) {
804
835
  const channel = this.channels[channelNumber];
805
836
  channel.volume = volume / 127;
806
- this.updateChannelGain(channel);
837
+ this.updateChannelVolume(channel);
807
838
  }
808
839
  panToGain(pan) {
809
840
  const theta = Math.PI / 2 * Math.max(0, pan - 1) / 126;
@@ -815,18 +846,18 @@ class MidyGMLite {
815
846
  setPan(channelNumber, pan) {
816
847
  const channel = this.channels[channelNumber];
817
848
  channel.pan = pan;
818
- this.updateChannelGain(channel);
849
+ this.updateChannelVolume(channel);
819
850
  }
820
851
  setExpression(channelNumber, expression) {
821
852
  const channel = this.channels[channelNumber];
822
853
  channel.expression = expression / 127;
823
- this.updateChannelGain(channel);
854
+ this.updateChannelVolume(channel);
824
855
  }
825
856
  dataEntryLSB(channelNumber, value) {
826
857
  this.channels[channelNumber].dataLSB = value;
827
858
  this.handleRPN(channelNumber);
828
859
  }
829
- updateChannelGain(channel) {
860
+ updateChannelVolume(channel) {
830
861
  const now = this.audioContext.currentTime;
831
862
  const volume = channel.volume * channel.expression;
832
863
  const { gainLeft, gainRight } = this.panToGain(channel.pan);
@@ -921,11 +952,12 @@ class MidyGMLite {
921
952
  }
922
953
  }
923
954
  GM1SystemOn() {
924
- this.channels.forEach((channel) => {
955
+ for (let i = 0; i < this.channels.length; i++) {
956
+ const channel = this.channels[i];
925
957
  channel.bankMSB = 0;
926
958
  channel.bankLSB = 0;
927
959
  channel.bank = 0;
928
- });
960
+ }
929
961
  this.channels[9].bankMSB = 1;
930
962
  this.channels[9].bank = 128;
931
963
  }
package/script/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;
@@ -159,7 +160,7 @@ export class Midy {
159
160
  createChannels(audioContext: any): any[];
160
161
  createNoteBuffer(instrumentKey: any, isSF3: any): Promise<any>;
161
162
  createNoteBufferNode(instrumentKey: any, isSF3: any): Promise<any>;
162
- convertToFloat32Array(uint8Array: any): Float32Array;
163
+ findPortamentoTarget(queueIndex: any): any;
163
164
  scheduleTimelineEvents(t: any, offset: any, queueIndex: any): Promise<any>;
164
165
  getQueueIndex(second: any): number;
165
166
  playNotes(): Promise<any>;
@@ -169,8 +170,8 @@ export class Midy {
169
170
  instruments: Set<any>;
170
171
  timeline: any[];
171
172
  };
172
- stopChannelNotes(channelNumber: any, velocity: any, stopPedal: any): Promise<void>;
173
- stopNotes(velocity: any, stopPedal: any): Promise<any[]>;
173
+ stopChannelNotes(channelNumber: any, velocity: any, force: any): Promise<void>;
174
+ stopNotes(velocity: any, force: any): Promise<any[]>;
174
175
  start(): Promise<void>;
175
176
  stop(): void;
176
177
  pause(): void;
@@ -206,18 +207,21 @@ export class Midy {
206
207
  centToHz(cent: any): number;
207
208
  calcSemitoneOffset(channel: any): any;
208
209
  calcPlaybackRate(instrumentKey: any, noteNumber: any, semitoneOffset: any): number;
210
+ setPortamentoStartVolumeEnvelope(channel: any, note: any): void;
209
211
  setVolumeEnvelope(channel: any, note: any): void;
210
212
  setPitch(note: any, semitoneOffset: any): void;
211
213
  clampCutoffFrequency(frequency: any): number;
214
+ setPortamentoStartFilterEnvelope(channel: any, note: any): void;
212
215
  setFilterEnvelope(channel: any, note: any): void;
213
216
  startModulation(channel: any, note: any, startTime: any): void;
214
217
  startVibrato(channel: any, note: any, startTime: any): void;
215
- createNote(channel: any, instrumentKey: any, noteNumber: any, velocity: any, startTime: any, isSF3: any): Promise<Note>;
218
+ createNote(channel: any, instrumentKey: any, noteNumber: any, velocity: any, startTime: any, portamento: any, isSF3: any): Promise<Note>;
216
219
  calcBank(channel: any, channelNumber: any): any;
217
- scheduleNoteOn(channelNumber: any, noteNumber: any, velocity: any, startTime: any): Promise<void>;
218
- noteOn(channelNumber: any, noteNumber: any, velocity: any): Promise<void>;
219
- scheduleNoteRelease(channelNumber: any, noteNumber: any, _velocity: any, stopTime: any, stopPedal?: boolean): Promise<any> | undefined;
220
- releaseNote(channelNumber: any, noteNumber: any, velocity: any): Promise<any> | undefined;
220
+ scheduleNoteOn(channelNumber: any, noteNumber: any, velocity: any, startTime: any, portamento: any): Promise<void>;
221
+ noteOn(channelNumber: any, noteNumber: any, velocity: any, portamento: any): Promise<void>;
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;
224
+ releaseNote(channelNumber: any, noteNumber: any, velocity: any, portamentoNoteNumber: any): Promise<any> | undefined;
221
225
  releaseSustainPedal(channelNumber: any, halfVelocity: any): any[];
222
226
  releaseSostenutoPedal(channelNumber: any, halfVelocity: any): any[];
223
227
  handleMIDIMessage(statusByte: any, data1: any, data2: any): void | Promise<any>;
@@ -276,7 +280,7 @@ export class Midy {
276
280
  setExpression(channelNumber: any, expression: any): void;
277
281
  setBankLSB(channelNumber: any, lsb: any): void;
278
282
  dataEntryLSB(channelNumber: any, value: any): void;
279
- updateChannelGain(channel: any): void;
283
+ updateChannelVolume(channel: any): void;
280
284
  setSustainPedal(channelNumber: any, value: any): void;
281
285
  setPortamento(channelNumber: any, value: any): void;
282
286
  setReverbSendLevel(channelNumber: any, reverbSendLevel: any): void;
@@ -357,6 +361,8 @@ declare class Note {
357
361
  modulationDepth: any;
358
362
  vibratoLFO: any;
359
363
  vibratoDepth: any;
364
+ reverbEffectsSend: any;
365
+ chorusEffectsSend: any;
360
366
  noteNumber: any;
361
367
  velocity: any;
362
368
  startTime: any;
@@ -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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"}