@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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marmooo/midy",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "A MIDI player/synthesizer written in JavaScript that supports GM-Lite/GM1 and SF2/SF3.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -21,6 +21,10 @@
21
21
  "scripts": {
22
22
  "test": "node test_runner.js"
23
23
  },
24
+ "dependencies": {
25
+ "@marmooo/soundfont-parser": "^0.0.7",
26
+ "midi-file": "^1.2.4"
27
+ },
24
28
  "devDependencies": {
25
29
  "@types/node": "^20.9.0",
26
30
  "picocolors": "^1.0.0"
@@ -37,6 +37,7 @@ export class MidyGM1 {
37
37
  timeline: any[];
38
38
  instruments: any[];
39
39
  notePromises: any[];
40
+ exclusiveClassMap: Map<any, any>;
40
41
  audioContext: any;
41
42
  masterGain: any;
42
43
  controlChangeHandlers: {
@@ -66,7 +67,6 @@ export class MidyGM1 {
66
67
  createChannels(audioContext: any): any[];
67
68
  createNoteBuffer(instrumentKey: any, isSF3: any): Promise<any>;
68
69
  createNoteBufferNode(instrumentKey: any, isSF3: any): Promise<any>;
69
- convertToFloat32Array(uint8Array: any): Float32Array;
70
70
  scheduleTimelineEvents(t: any, offset: any, queueIndex: any): Promise<any>;
71
71
  getQueueIndex(second: any): number;
72
72
  playNotes(): Promise<any>;
@@ -76,8 +76,8 @@ export class MidyGM1 {
76
76
  instruments: Set<any>;
77
77
  timeline: any[];
78
78
  };
79
- stopChannelNotes(channelNumber: any, velocity: any, stopPedal: any): Promise<void>;
80
- stopNotes(velocity: any, stopPedal: any): Promise<any[]>;
79
+ stopChannelNotes(channelNumber: any, velocity: any, force: any): Promise<void>;
80
+ stopNotes(velocity: any, force: any): Promise<any[]>;
81
81
  start(): Promise<void>;
82
82
  stop(): void;
83
83
  pause(): void;
@@ -99,7 +99,8 @@ export class MidyGM1 {
99
99
  createNote(channel: any, instrumentKey: any, noteNumber: any, velocity: any, startTime: any, isSF3: any): Promise<Note>;
100
100
  scheduleNoteOn(channelNumber: any, noteNumber: any, velocity: any, startTime: any): Promise<void>;
101
101
  noteOn(channelNumber: any, noteNumber: any, velocity: any): Promise<void>;
102
- scheduleNoteRelease(channelNumber: any, noteNumber: any, _velocity: any, stopTime: any, stopPedal?: boolean): Promise<any> | undefined;
102
+ stopNote(endTime: any, stopTime: any, scheduledNotes: any, index: any): Promise<any>;
103
+ scheduleNoteRelease(channelNumber: any, noteNumber: any, _velocity: any, endTime: any, force: any): Promise<any> | undefined;
103
104
  releaseNote(channelNumber: any, noteNumber: any, velocity: any): Promise<any> | undefined;
104
105
  releaseSustainPedal(channelNumber: any, halfVelocity: any): any[];
105
106
  handleMIDIMessage(statusByte: any, data1: any, data2: any): void | Promise<any>;
@@ -131,7 +132,7 @@ export class MidyGM1 {
131
132
  setPan(channelNumber: any, pan: any): void;
132
133
  setExpression(channelNumber: any, expression: any): void;
133
134
  dataEntryLSB(channelNumber: any, value: any): void;
134
- updateChannelGain(channel: any): void;
135
+ updateChannelVolume(channel: any): void;
135
136
  setSustainPedal(channelNumber: any, value: any): void;
136
137
  limitData(channel: any, minMSB: any, maxMSB: any, minLSB: any, maxLSB: any): void;
137
138
  limitDataMSB(channel: any, minMSB: any, maxMSB: any): void;
@@ -1 +1 @@
1
- {"version":3,"file":"midy-GM1.d.ts","sourceRoot":"","sources":["../src/midy-GM1.js"],"names":[],"mappings":"AAwBA;IAmBE;;;;;;;;;;;MAWE;IAEF;;;;;;;MAOE;IAEF,+BAOC;IA/CD,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;IAyBhB,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,sCAGC;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,kFAeC;IAED,2DAMC;IAED,oCAkBC;IAED,gDAEC;IAED,gDAEC;IAED,mDAGC;IAED,oDAaC;IAED,kDAKC;IAED,iEAOC;IAED,8CAKC;IAED,yDAMC;IAED,gDAKC;IAED,6DAMC;IAED,+CAEC;IAED,8CAEC;IAED,+CAEC;IAED,4DAgBC;IAED,oBAQC;IAED,yDAaC;IAED,yCAGC;IAED,mCAQC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF;AAnjCD;IAUE,gFAKC;IAdD,kBAAa;IACb,gBAAW;IACX,gBAAW;IACX,iBAAY;IACZ,mBAAc;IACd,qBAAgB;IAChB,gBAAW;IACX,kBAAa;IAGX,gBAA4B;IAC5B,cAAwB;IACxB,eAA0B;IAC1B,mBAAkC;CAErC"}
1
+ {"version":3,"file":"midy-GM1.d.ts","sourceRoot":"","sources":["../src/midy-GM1.js"],"names":[],"mappings":"AAqBA;IAoBE;;;;;;;;;;;MAWE;IAEF;;;;;;;MAOE;IAEF,+BAOC;IAhDD,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;IAyB5B,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,sCAGC;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,kFAeC;IAED,2DAMC;IAED,oCAkBC;IAED,gDAEC;IAED,gDAEC;IAED,mDAGC;IAED,oDAaC;IAED,kDAKC;IAED,iEAOC;IAED,8CAKC;IAED,yDAMC;IAED,gDAKC;IAED,6DAMC;IAED,+CAEC;IAED,8CAEC;IAED,+CAEC;IAED,4DAgBC;IAED,oBASC;IAED,yDAaC;IAED,yCAGC;IAED,mCAQC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF;AA9kCD;IAUE,gFAKC;IAdD,kBAAa;IACb,gBAAW;IACX,gBAAW;IACX,iBAAY;IACZ,mBAAc;IACd,qBAAgB;IAChB,gBAAW;IACX,kBAAa;IAGX,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.MidyGM1 = 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", {
@@ -163,6 +163,12 @@ class MidyGM1 {
163
163
  writable: true,
164
164
  value: []
165
165
  });
166
+ Object.defineProperty(this, "exclusiveClassMap", {
167
+ enumerable: true,
168
+ configurable: true,
169
+ writable: true,
170
+ value: new Map()
171
+ });
166
172
  this.audioContext = audioContext;
167
173
  this.masterGain = new GainNode(audioContext);
168
174
  this.controlChangeHandlers = this.createControlChangeHandlers();
@@ -180,24 +186,26 @@ class MidyGM1 {
180
186
  addSoundFont(soundFont) {
181
187
  const index = this.soundFonts.length;
182
188
  this.soundFonts.push(soundFont);
183
- soundFont.parsed.presetHeaders.forEach((presetHeader) => {
189
+ const presetHeaders = soundFont.parsed.presetHeaders;
190
+ for (let i = 0; i < presetHeaders.length; i++) {
191
+ const presetHeader = presetHeaders[i];
184
192
  if (!presetHeader.presetName.startsWith("\u0000")) { // TODO: Only SF3 generated by PolyPone?
185
193
  const banks = this.soundFontTable[presetHeader.preset];
186
194
  banks.set(presetHeader.bank, index);
187
195
  }
188
- });
196
+ }
189
197
  }
190
198
  async loadSoundFont(soundFontUrl) {
191
199
  const response = await fetch(soundFontUrl);
192
200
  const arrayBuffer = await response.arrayBuffer();
193
- const parsed = (0, _esm_js_2.parse)(new Uint8Array(arrayBuffer));
194
- const soundFont = new _esm_js_2.SoundFont(parsed);
201
+ const parsed = (0, soundfont_parser_1.parse)(new Uint8Array(arrayBuffer));
202
+ const soundFont = new soundfont_parser_1.SoundFont(parsed);
195
203
  this.addSoundFont(soundFont);
196
204
  }
197
205
  async loadMIDI(midiUrl) {
198
206
  const response = await fetch(midiUrl);
199
207
  const arrayBuffer = await response.arrayBuffer();
200
- const midi = (0, _esm_js_1.parseMidi)(new Uint8Array(arrayBuffer));
208
+ const midi = (0, midi_file_1.parseMidi)(new Uint8Array(arrayBuffer));
201
209
  this.ticksPerBeat = midi.header.ticksPerBeat;
202
210
  const midiData = this.extractMidiData(midi);
203
211
  this.instruments = midiData.instruments;
@@ -230,27 +238,31 @@ class MidyGM1 {
230
238
  return channels;
231
239
  }
232
240
  async createNoteBuffer(instrumentKey, isSF3) {
241
+ const sampleStart = instrumentKey.start;
233
242
  const sampleEnd = instrumentKey.sample.length + instrumentKey.end;
234
243
  if (isSF3) {
235
- const sample = new Uint8Array(instrumentKey.sample.length);
236
- sample.set(instrumentKey.sample);
237
- const audioBuffer = await this.audioContext.decodeAudioData(sample.buffer);
238
- for (let channel = 0; channel < audioBuffer.numberOfChannels; channel++) {
239
- const channelData = audioBuffer.getChannelData(channel);
240
- channelData.set(channelData.subarray(0, sampleEnd));
241
- }
244
+ const sample = instrumentKey.sample;
245
+ const start = sample.byteOffset + sampleStart;
246
+ const end = sample.byteOffset + sampleEnd;
247
+ const buffer = sample.buffer.slice(start, end);
248
+ const audioBuffer = await this.audioContext.decodeAudioData(buffer);
242
249
  return audioBuffer;
243
250
  }
244
251
  else {
245
- const sample = instrumentKey.sample.subarray(0, sampleEnd);
246
- const floatSample = this.convertToFloat32Array(sample);
252
+ const sample = instrumentKey.sample;
253
+ const start = sample.byteOffset + sampleStart;
254
+ const end = sample.byteOffset + sampleEnd;
255
+ const buffer = sample.buffer.slice(start, end);
247
256
  const audioBuffer = new AudioBuffer({
248
257
  numberOfChannels: 1,
249
258
  length: sample.length,
250
259
  sampleRate: instrumentKey.sampleRate,
251
260
  });
252
261
  const channelData = audioBuffer.getChannelData(0);
253
- channelData.set(floatSample);
262
+ const int16Array = new Int16Array(buffer);
263
+ for (let i = 0; i < int16Array.length; i++) {
264
+ channelData[i] = int16Array[i] / 32768;
265
+ }
254
266
  return audioBuffer;
255
267
  }
256
268
  }
@@ -266,14 +278,6 @@ class MidyGM1 {
266
278
  }
267
279
  return bufferSource;
268
280
  }
269
- convertToFloat32Array(uint8Array) {
270
- const int16Array = new Int16Array(uint8Array.buffer);
271
- const float32Array = new Float32Array(int16Array.length);
272
- for (let i = 0; i < int16Array.length; i++) {
273
- float32Array[i] = int16Array[i] / 32768;
274
- }
275
- return float32Array;
276
- }
277
281
  async scheduleTimelineEvents(t, offset, queueIndex) {
278
282
  while (queueIndex < this.timeline.length) {
279
283
  const event = this.timeline[queueIndex];
@@ -329,6 +333,7 @@ class MidyGM1 {
329
333
  if (queueIndex >= this.timeline.length) {
330
334
  await Promise.all(this.notePromises);
331
335
  this.notePromises = [];
336
+ this.exclusiveClassMap.clear();
332
337
  resolve();
333
338
  return;
334
339
  }
@@ -344,6 +349,7 @@ class MidyGM1 {
344
349
  }
345
350
  else if (this.isStopping) {
346
351
  await this.stopNotes(0, true);
352
+ this.exclusiveClassMap.clear();
347
353
  this.notePromises = [];
348
354
  resolve();
349
355
  this.isStopping = false;
@@ -352,6 +358,7 @@ class MidyGM1 {
352
358
  }
353
359
  else if (this.isSeeking) {
354
360
  this.stopNotes(0, true);
361
+ this.exclusiveClassMap.clear();
355
362
  this.startTime = this.audioContext.currentTime;
356
363
  queueIndex = this.getQueueIndex(this.resumeTime);
357
364
  offset = this.resumeTime - this.startTime;
@@ -384,9 +391,11 @@ class MidyGM1 {
384
391
  bank: this.channels[i].bank,
385
392
  };
386
393
  }
387
- midi.tracks.forEach((track) => {
394
+ for (let i = 0; i < midi.tracks.length; i++) {
395
+ const track = midi.tracks[i];
388
396
  let currentTicks = 0;
389
- track.forEach((event) => {
397
+ for (let j = 0; j < track.length; j++) {
398
+ const event = track[j];
390
399
  currentTicks += event.deltaTime;
391
400
  event.ticks = currentTicks;
392
401
  switch (event.type) {
@@ -406,8 +415,8 @@ class MidyGM1 {
406
415
  }
407
416
  delete event.deltaTime;
408
417
  timeline.push(event);
409
- });
410
- });
418
+ }
419
+ }
411
420
  const priority = {
412
421
  controller: 0,
413
422
  sysEx: 1,
@@ -432,7 +441,7 @@ class MidyGM1 {
432
441
  }
433
442
  return { instruments, timeline };
434
443
  }
435
- async stopChannelNotes(channelNumber, velocity, stopPedal) {
444
+ async stopChannelNotes(channelNumber, velocity, force) {
436
445
  const now = this.audioContext.currentTime;
437
446
  const channel = this.channels[channelNumber];
438
447
  channel.scheduledNotes.forEach((noteList) => {
@@ -440,16 +449,16 @@ class MidyGM1 {
440
449
  const note = noteList[i];
441
450
  if (!note)
442
451
  continue;
443
- const promise = this.scheduleNoteRelease(channelNumber, note.noteNumber, velocity, now, stopPedal);
452
+ const promise = this.scheduleNoteRelease(channelNumber, note.noteNumber, velocity, now, force);
444
453
  this.notePromises.push(promise);
445
454
  }
446
455
  });
447
456
  channel.scheduledNotes.clear();
448
457
  await Promise.all(this.notePromises);
449
458
  }
450
- stopNotes(velocity, stopPedal) {
459
+ stopNotes(velocity, force) {
451
460
  for (let i = 0; i < this.channels.length; i++) {
452
- this.stopChannelNotes(i, velocity, stopPedal);
461
+ this.stopChannelNotes(i, velocity, force);
453
462
  }
454
463
  return Promise.all(this.notePromises);
455
464
  }
@@ -626,7 +635,7 @@ class MidyGM1 {
626
635
  note.volumeNode = new GainNode(this.audioContext);
627
636
  note.filterNode = new BiquadFilterNode(this.audioContext, {
628
637
  type: "lowpass",
629
- Q: instrumentKey.initialFilterQ / 10 * channel.filterResonance, // dB
638
+ Q: instrumentKey.initialFilterQ / 10, // dB
630
639
  });
631
640
  this.setVolumeEnvelope(note);
632
641
  this.setFilterEnvelope(note);
@@ -639,7 +648,7 @@ class MidyGM1 {
639
648
  }
640
649
  note.bufferSource.connect(note.filterNode);
641
650
  note.filterNode.connect(note.volumeNode);
642
- note.bufferSource.start(startTime, instrumentKey.start / instrumentKey.sampleRate);
651
+ note.bufferSource.start(startTime);
643
652
  return note;
644
653
  }
645
654
  async scheduleNoteOn(channelNumber, noteNumber, velocity, startTime) {
@@ -656,6 +665,19 @@ class MidyGM1 {
656
665
  const note = await this.createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3);
657
666
  note.volumeNode.connect(channel.gainL);
658
667
  note.volumeNode.connect(channel.gainR);
668
+ const exclusiveClass = instrumentKey.exclusiveClass;
669
+ if (exclusiveClass !== 0) {
670
+ if (this.exclusiveClassMap.has(exclusiveClass)) {
671
+ const prevEntry = this.exclusiveClassMap.get(exclusiveClass);
672
+ const [prevNote, prevChannelNumber] = prevEntry;
673
+ if (!prevNote.ending) {
674
+ this.scheduleNoteRelease(prevChannelNumber, prevNote.noteNumber, 0, // velocity,
675
+ startTime, undefined, // portamentoNoteNumber
676
+ true);
677
+ }
678
+ }
679
+ this.exclusiveClassMap.set(exclusiveClass, [note, channelNumber]);
680
+ }
659
681
  const scheduledNotes = channel.scheduledNotes;
660
682
  if (scheduledNotes.has(noteNumber)) {
661
683
  scheduledNotes.get(noteNumber).push(note);
@@ -668,9 +690,38 @@ class MidyGM1 {
668
690
  const now = this.audioContext.currentTime;
669
691
  return this.scheduleNoteOn(channelNumber, noteNumber, velocity, now);
670
692
  }
671
- scheduleNoteRelease(channelNumber, noteNumber, _velocity, stopTime, stopPedal = false) {
693
+ stopNote(endTime, stopTime, scheduledNotes, index) {
694
+ const note = scheduledNotes[index];
695
+ note.volumeNode.gain
696
+ .cancelScheduledValues(endTime)
697
+ .linearRampToValueAtTime(0, stopTime);
698
+ note.ending = true;
699
+ this.scheduleTask(() => {
700
+ note.bufferSource.loop = false;
701
+ }, stopTime);
702
+ return new Promise((resolve) => {
703
+ note.bufferSource.onended = () => {
704
+ scheduledNotes[index] = null;
705
+ note.bufferSource.disconnect();
706
+ note.volumeNode.disconnect();
707
+ note.filterNode.disconnect();
708
+ if (note.modulationDepth) {
709
+ note.volumeDepth.disconnect();
710
+ note.modulationDepth.disconnect();
711
+ note.modulationLFO.stop();
712
+ }
713
+ if (note.vibratoDepth) {
714
+ note.vibratoDepth.disconnect();
715
+ note.vibratoLFO.stop();
716
+ }
717
+ resolve();
718
+ };
719
+ note.bufferSource.stop(stopTime);
720
+ });
721
+ }
722
+ scheduleNoteRelease(channelNumber, noteNumber, _velocity, endTime, force) {
672
723
  const channel = this.channels[channelNumber];
673
- if (stopPedal && channel.sustainPedal)
724
+ if (!force && channel.sustainPedal)
674
725
  return;
675
726
  if (!channel.scheduledNotes.has(noteNumber))
676
727
  return;
@@ -681,33 +732,13 @@ class MidyGM1 {
681
732
  continue;
682
733
  if (note.ending)
683
734
  continue;
684
- const volEndTime = stopTime + note.instrumentKey.volRelease;
685
- note.volumeNode.gain
686
- .cancelScheduledValues(stopTime)
687
- .linearRampToValueAtTime(0, volEndTime);
688
- const modRelease = stopTime + note.instrumentKey.modRelease;
735
+ const volRelease = endTime + note.instrumentKey.volRelease;
736
+ const modRelease = endTime + note.instrumentKey.modRelease;
689
737
  note.filterNode.frequency
690
- .cancelScheduledValues(stopTime)
738
+ .cancelScheduledValues(endTime)
691
739
  .linearRampToValueAtTime(0, modRelease);
692
- note.ending = true;
693
- this.scheduleTask(() => {
694
- note.bufferSource.loop = false;
695
- }, stopTime);
696
- return new Promise((resolve) => {
697
- note.bufferSource.onended = () => {
698
- scheduledNotes[i] = null;
699
- note.bufferSource.disconnect();
700
- note.volumeNode.disconnect();
701
- note.filterNode.disconnect();
702
- if (note.modulationDepth) {
703
- note.volumeDepth.disconnect();
704
- note.modulationDepth.disconnect();
705
- note.modulationLFO.stop();
706
- }
707
- resolve();
708
- };
709
- note.bufferSource.stop(volEndTime);
710
- });
740
+ const stopTime = Math.min(volRelease, modRelease);
741
+ return this.stopNote(endTime, stopTime, scheduledNotes, i);
711
742
  }
712
743
  }
713
744
  releaseNote(channelNumber, noteNumber, velocity) {
@@ -816,7 +847,7 @@ class MidyGM1 {
816
847
  setVolume(channelNumber, volume) {
817
848
  const channel = this.channels[channelNumber];
818
849
  channel.volume = volume / 127;
819
- this.updateChannelGain(channel);
850
+ this.updateChannelVolume(channel);
820
851
  }
821
852
  panToGain(pan) {
822
853
  const theta = Math.PI / 2 * Math.max(0, pan - 1) / 126;
@@ -828,18 +859,18 @@ class MidyGM1 {
828
859
  setPan(channelNumber, pan) {
829
860
  const channel = this.channels[channelNumber];
830
861
  channel.pan = pan;
831
- this.updateChannelGain(channel);
862
+ this.updateChannelVolume(channel);
832
863
  }
833
864
  setExpression(channelNumber, expression) {
834
865
  const channel = this.channels[channelNumber];
835
866
  channel.expression = expression / 127;
836
- this.updateChannelGain(channel);
867
+ this.updateChannelVolume(channel);
837
868
  }
838
869
  dataEntryLSB(channelNumber, value) {
839
870
  this.channels[channelNumber].dataLSB = value;
840
871
  this.handleRPN(channelNumber, 0);
841
872
  }
842
- updateChannelGain(channel) {
873
+ updateChannelVolume(channel) {
843
874
  const now = this.audioContext.currentTime;
844
875
  const volume = channel.volume * channel.expression;
845
876
  const { gainLeft, gainRight } = this.panToGain(channel.pan);
@@ -992,11 +1023,12 @@ class MidyGM1 {
992
1023
  }
993
1024
  }
994
1025
  GM1SystemOn() {
995
- this.channels.forEach((channel) => {
1026
+ for (let i = 0; i < this.channels.length; i++) {
1027
+ const channel = this.channels[i];
996
1028
  channel.bankMSB = 0;
997
1029
  channel.bankLSB = 0;
998
1030
  channel.bank = 0;
999
- });
1031
+ }
1000
1032
  this.channels[9].bankMSB = 1;
1001
1033
  this.channels[9].bank = 128;
1002
1034
  }
@@ -78,6 +78,7 @@ export class MidyGM2 {
78
78
  timeline: any[];
79
79
  instruments: any[];
80
80
  notePromises: any[];
81
+ exclusiveClassMap: Map<any, any>;
81
82
  defaultOptions: {
82
83
  reverbAlgorithm: (audioContext: any) => {
83
84
  input: any;
@@ -144,7 +145,7 @@ export class MidyGM2 {
144
145
  createChannels(audioContext: any): any[];
145
146
  createNoteBuffer(instrumentKey: any, isSF3: any): Promise<any>;
146
147
  createNoteBufferNode(instrumentKey: any, isSF3: any): Promise<any>;
147
- convertToFloat32Array(uint8Array: any): Float32Array;
148
+ findPortamentoTarget(queueIndex: any): any;
148
149
  scheduleTimelineEvents(t: any, offset: any, queueIndex: any): Promise<any>;
149
150
  getQueueIndex(second: any): number;
150
151
  playNotes(): Promise<any>;
@@ -154,8 +155,8 @@ export class MidyGM2 {
154
155
  instruments: Set<any>;
155
156
  timeline: any[];
156
157
  };
157
- stopChannelNotes(channelNumber: any, velocity: any, stopPedal: any): Promise<void>;
158
- stopNotes(velocity: any, stopPedal: any): Promise<any[]>;
158
+ stopChannelNotes(channelNumber: any, velocity: any, force: any): Promise<void>;
159
+ stopNotes(velocity: any, force: any): Promise<any[]>;
159
160
  start(): Promise<void>;
160
161
  stop(): void;
161
162
  pause(): void;
@@ -191,18 +192,21 @@ export class MidyGM2 {
191
192
  centToHz(cent: any): number;
192
193
  calcSemitoneOffset(channel: any): any;
193
194
  calcPlaybackRate(instrumentKey: any, noteNumber: any, semitoneOffset: any): number;
195
+ setPortamentoStartVolumeEnvelope(channel: any, note: any): void;
194
196
  setVolumeEnvelope(note: any): void;
195
197
  setPitch(note: any, semitoneOffset: any): void;
196
198
  clampCutoffFrequency(frequency: any): number;
199
+ setPortamentoStartFilterEnvelope(channel: any, note: any): void;
197
200
  setFilterEnvelope(channel: any, note: any): void;
198
201
  startModulation(channel: any, note: any, startTime: any): void;
199
202
  startVibrato(channel: any, note: any, startTime: any): void;
200
- createNote(channel: any, instrumentKey: any, noteNumber: any, velocity: any, startTime: any, isSF3: any): Promise<Note>;
203
+ createNote(channel: any, instrumentKey: any, noteNumber: any, velocity: any, startTime: any, portamento: any, isSF3: any): Promise<Note>;
201
204
  calcBank(channel: any, channelNumber: any): any;
202
- scheduleNoteOn(channelNumber: any, noteNumber: any, velocity: any, startTime: any): Promise<void>;
203
- noteOn(channelNumber: any, noteNumber: any, velocity: any): Promise<void>;
204
- scheduleNoteRelease(channelNumber: any, noteNumber: any, _velocity: any, stopTime: any, stopPedal?: boolean): Promise<any> | undefined;
205
- releaseNote(channelNumber: any, noteNumber: any, velocity: any): Promise<any> | undefined;
205
+ scheduleNoteOn(channelNumber: any, noteNumber: any, velocity: any, startTime: any, portamento: any): Promise<void>;
206
+ noteOn(channelNumber: any, noteNumber: any, velocity: any, portamento: any): Promise<void>;
207
+ stopNote(endTime: any, stopTime: any, scheduledNotes: any, index: any): Promise<any>;
208
+ scheduleNoteRelease(channelNumber: any, noteNumber: any, _velocity: any, endTime: any, portamentoNoteNumber: any, force: any): Promise<any> | undefined;
209
+ releaseNote(channelNumber: any, noteNumber: any, velocity: any, portamentoNoteNumber: any): Promise<any> | undefined;
206
210
  releaseSustainPedal(channelNumber: any, halfVelocity: any): any[];
207
211
  releaseSostenutoPedal(channelNumber: any, halfVelocity: any): any[];
208
212
  handleMIDIMessage(statusByte: any, data1: any, data2: any): void | Promise<any>;
@@ -250,7 +254,7 @@ export class MidyGM2 {
250
254
  setExpression(channelNumber: any, expression: any): void;
251
255
  setBankLSB(channelNumber: any, lsb: any): void;
252
256
  dataEntryLSB(channelNumber: any, value: any): void;
253
- updateChannelGain(channel: any): void;
257
+ updateChannelVolume(channel: any): void;
254
258
  setSustainPedal(channelNumber: any, value: any): void;
255
259
  setPortamento(channelNumber: any, value: any): void;
256
260
  setReverbSendLevel(channelNumber: any, reverbSendLevel: any): void;
@@ -321,6 +325,8 @@ declare class Note {
321
325
  modulationDepth: any;
322
326
  vibratoLFO: any;
323
327
  vibratoDepth: any;
328
+ reverbEffectsSend: any;
329
+ chorusEffectsSend: any;
324
330
  noteNumber: any;
325
331
  velocity: any;
326
332
  startTime: any;
@@ -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":"AAuBA;IAmCE;;;;;;;;;;;;;;;;;;;;MAoBE;IAEF;;;;;;;;;;;MAWE;IAEF;;;;;;;MAOE;IAgCF;;;;;OAYC;IAxHD,qBAAmB;IACnB,kBAAc;IACd,yBAAqB;IACrB,2BAAuB;IACvB;;;MAGE;IACF;;;;;;MAME;IACF,cAAa;IACb,cAAa;IACb,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;IA8C9B;;;;;MA4BE;IAGA,kBAAgC;IAChC;;;;;MAAqD;IACrD,gBAA4C;IAC5C;;;;;;;;;;;;;;;;;;;;;;;;;MAA+D;IAC/D,gBAAiD;IACjD;;;MAA8D;IAC9D;;;;;;;;MAAyD;IAO3D,4BAMC;IAED,mCAWC;IAED,gDAMC;IAED,sCASC;IAED;;;;MAeC;IAED,yCAcC;IAED,+DA2BC;IAED,mEAWC;IAED,2CAcC;IAED,2EAuDC;IAED,mCAOC;IAED,0BAkDC;IAED,uDAEC;IAED,wDAEC;IAED;;;MAoGC;IAED,+EAoBC;IAED,qDAKC;IAED,uBAKC;IAED,aAGC;IAED,cAKC;IAED,wBAIC;IAED,0BAKC;IAED,wBAOC;IAED,sBAGC;IAED,uDASC;IAED,6CAQC;IAED,kFAuBC;IAED;;;;MAWC;IAED,gFAUC;IAED,mFAYC;IAED,sGAcC;IAID;;;MA8BC;IAED;;;;;;;;MA0CC;IAED,2BAEC;IAED,4BAEC;IAED,sCAKC;IAED,mFAGC;IAED,gEAUC;IAED,mCAeC;IAED,+CAwBC;IAED,6CAIC;IAED,gEAoBC;IAED,iDAyBC;IAED,+DA0BC;IAED,4DAiBC;IAED,yIA6DC;IAED,gDAQC;IAED,mHA0DC;IAED,2FASC;IAED,qFAkCC;IAED,wJAqCC;IAED,qHAUC;IAED,kEAeC;IAED,oEAYC;IAED,gFAmBC;IAED,4DAIC;IAED,+DAcC;IAED,qEAGC;IAED,uDAOC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;MA2BC;IAED,2EASC;IAED,+CAEC;IAED,qCAkBC;IAED,8DAIC;IAED,iEAIC;IAED,iDAIC;IAED;;;MAMC;IAED,2CAIC;IAED,yDAIC;IAED,+CAEC;IAED,mDAGC;IAED,wCAUC;IAED,sDAMC;IAED,oDAEC;IAED,mEAyCC;IAED,mEAyCC;IAED,wDAWC;IAED,uDAGC;IAED,kFAeC;IAED,2DAMC;IAED,oCAqBC;IAED,gDAEC;IAED,gDAEC;IAED,mDAGC;IAED,oDAaC;IAED,kDAKC;IAED,iEAOC;IAED,8CAKC;IAED,yDAMC;IAED,gDAKC;IAED,6DAMC;IAED,wDAKC;IAED,6EAKC;IAED,+CAEC;IAED,8CAEC;IAED,+CAEC;IAED,gBAEC;IAED,eAEC;IAED,eAEC;IAED,eAEC;IAED,4DAmBC;IAED,oBASC;IAED,oBASC;IAED,yDAiDC;IAED,yCAGC;IAED,mCAQC;IAED,6CAGC;IAED,2CAMC;IAED,+CAGC;IAED,+CAMC;IAED,mDAeC;IAED,4CAOC;IAED,+BAKC;IAED,qDAiBC;IAED,gCAIC;IAED,kCAEC;IA6BD,4CAEC;IAED,4CAaC;IAED,+BAiBC;IAED,wFAKC;IAED,mCAKC;IAED,qCAEC;IAED,oCAOC;IAED,sCAEC;IAED,oCAUC;IAED,sCAEC;IAED,wCAuBC;IAED,0CAEC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF;AAn+DD;IAYE,gFAKC;IAhBD,kBAAa;IACb,gBAAW;IACX,gBAAW;IACX,iBAAY;IACZ,mBAAc;IACd,qBAAgB;IAChB,gBAAW;IACX,kBAAa;IACb,uBAAkB;IAClB,uBAAkB;IAGhB,gBAA4B;IAC5B,cAAwB;IACxB,eAA0B;IAC1B,mBAAkC;CAErC"}