@marmooo/midy 0.3.5 → 0.3.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marmooo/midy",
3
- "version": "0.3.5",
3
+ "version": "0.3.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",
@@ -22,7 +22,7 @@
22
22
  "test": "node test_runner.js"
23
23
  },
24
24
  "dependencies": {
25
- "@marmooo/soundfont-parser": "^0.1.2",
25
+ "@marmooo/soundfont-parser": "^0.1.3",
26
26
  "midi-file": "^1.2.4"
27
27
  },
28
28
  "devDependencies": {
@@ -30,25 +30,26 @@ export class MidyGM1 {
30
30
  isPaused: boolean;
31
31
  isStopping: boolean;
32
32
  isSeeking: boolean;
33
+ playPromise: any;
33
34
  timeline: any[];
34
- instruments: any[];
35
35
  notePromises: any[];
36
+ instruments: Set<any>;
36
37
  exclusiveClassNotes: any[];
37
38
  audioContext: any;
38
39
  masterVolume: any;
39
40
  scheduler: any;
40
41
  schedulerBuffer: any;
41
42
  voiceParamsHandlers: {
42
- modLfoToPitch: (channel: any, note: any, _prevValue: any, scheduleTime: any) => void;
43
- vibLfoToPitch: (_channel: any, _note: any, _prevValue: any, _scheduleTime: any) => void;
44
- modLfoToFilterFc: (channel: any, note: any, _prevValue: any, scheduleTime: any) => void;
45
- modLfoToVolume: (channel: any, note: any, _prevValue: any, scheduleTime: any) => void;
46
- chorusEffectsSend: (_channel: any, _note: any, _prevValue: any, _scheduleTime: any) => void;
47
- reverbEffectsSend: (_channel: any, _note: any, _prevValue: any, _scheduleTime: any) => void;
48
- delayModLFO: (_channel: any, note: any, _prevValue: any, scheduleTime: any) => void;
49
- freqModLFO: (_channel: any, note: any, _prevValue: any, scheduleTime: any) => void;
50
- delayVibLFO: (_channel: any, _note: any, _prevValue: any, _scheduleTime: any) => void;
51
- freqVibLFO: (_channel: any, _note: any, _prevValue: any, _scheduleTime: any) => void;
43
+ modLfoToPitch: (channel: any, note: any, scheduleTime: any) => void;
44
+ vibLfoToPitch: (_channel: any, _note: any, _scheduleTime: any) => void;
45
+ modLfoToFilterFc: (channel: any, note: any, scheduleTime: any) => void;
46
+ modLfoToVolume: (channel: any, note: any, scheduleTime: any) => void;
47
+ chorusEffectsSend: (_channel: any, _note: any, _scheduleTime: any) => void;
48
+ reverbEffectsSend: (_channel: any, _note: any, _scheduleTime: any) => void;
49
+ delayModLFO: (_channel: any, note: any, scheduleTime: any) => void;
50
+ freqModLFO: (_channel: any, note: any, scheduleTime: any) => void;
51
+ delayVibLFO: (_channel: any, _note: any, _scheduleTime: any) => void;
52
+ freqVibLFO: (_channel: any, _note: any, _scheduleTime: any) => void;
52
53
  };
53
54
  controlChangeHandlers: any[];
54
55
  channels: any[];
@@ -58,7 +59,7 @@ export class MidyGM1 {
58
59
  loadSoundFont(input: any): Promise<void>;
59
60
  loadMIDI(input: any): Promise<void>;
60
61
  cacheVoiceIds(): void;
61
- getVoiceId(channel: any, noteNumber: any, velocity: any): string | undefined;
62
+ getVoiceId(channel: any, noteNumber: any, velocity: any): any;
62
63
  createChannelAudioNodes(audioContext: any): {
63
64
  gainL: any;
64
65
  gainR: any;
@@ -69,7 +70,9 @@ export class MidyGM1 {
69
70
  createBufferSource(voiceParams: any, audioBuffer: any): any;
70
71
  scheduleTimelineEvents(t: any, resumeTime: any, queueIndex: any): Promise<any>;
71
72
  getQueueIndex(second: any): number;
72
- playNotes(): Promise<any>;
73
+ resetAllStates(): void;
74
+ updateStates(queueIndex: any, nextQueueIndex: any): void;
75
+ playNotes(): Promise<void>;
73
76
  ticksToSecond(ticks: any, secondsPerBeat: any): number;
74
77
  secondToTicks(second: any, secondsPerBeat: any): number;
75
78
  extractMidiData(midi: any): {
@@ -80,8 +83,8 @@ export class MidyGM1 {
80
83
  stopChannelNotes(channelNumber: any, velocity: any, force: any, scheduleTime: any): Promise<any[]>;
81
84
  stopNotes(velocity: any, force: any, scheduleTime: any): Promise<any[]>;
82
85
  start(): Promise<void>;
83
- stop(): void;
84
- pause(): void;
86
+ stop(): Promise<void>;
87
+ pause(): Promise<void>;
85
88
  resume(): Promise<void>;
86
89
  seekTo(second: any): void;
87
90
  calcTotalTime(): number;
@@ -122,16 +125,16 @@ export class MidyGM1 {
122
125
  setDelayModLFO(note: any, scheduleTime: any): void;
123
126
  setFreqModLFO(note: any, scheduleTime: any): void;
124
127
  createVoiceParamsHandlers(): {
125
- modLfoToPitch: (channel: any, note: any, _prevValue: any, scheduleTime: any) => void;
126
- vibLfoToPitch: (_channel: any, _note: any, _prevValue: any, _scheduleTime: any) => void;
127
- modLfoToFilterFc: (channel: any, note: any, _prevValue: any, scheduleTime: any) => void;
128
- modLfoToVolume: (channel: any, note: any, _prevValue: any, scheduleTime: any) => void;
129
- chorusEffectsSend: (_channel: any, _note: any, _prevValue: any, _scheduleTime: any) => void;
130
- reverbEffectsSend: (_channel: any, _note: any, _prevValue: any, _scheduleTime: any) => void;
131
- delayModLFO: (_channel: any, note: any, _prevValue: any, scheduleTime: any) => void;
132
- freqModLFO: (_channel: any, note: any, _prevValue: any, scheduleTime: any) => void;
133
- delayVibLFO: (_channel: any, _note: any, _prevValue: any, _scheduleTime: any) => void;
134
- freqVibLFO: (_channel: any, _note: any, _prevValue: any, _scheduleTime: any) => void;
128
+ modLfoToPitch: (channel: any, note: any, scheduleTime: any) => void;
129
+ vibLfoToPitch: (_channel: any, _note: any, _scheduleTime: any) => void;
130
+ modLfoToFilterFc: (channel: any, note: any, scheduleTime: any) => void;
131
+ modLfoToVolume: (channel: any, note: any, scheduleTime: any) => void;
132
+ chorusEffectsSend: (_channel: any, _note: any, _scheduleTime: any) => void;
133
+ reverbEffectsSend: (_channel: any, _note: any, _scheduleTime: any) => void;
134
+ delayModLFO: (_channel: any, note: any, scheduleTime: any) => void;
135
+ freqModLFO: (_channel: any, note: any, scheduleTime: any) => void;
136
+ delayVibLFO: (_channel: any, _note: any, _scheduleTime: any) => void;
137
+ freqVibLFO: (_channel: any, _note: any, _scheduleTime: any) => void;
135
138
  };
136
139
  getControllerState(channel: any, noteNumber: any, velocity: any): Float32Array<any>;
137
140
  applyVoiceParams(channel: any, controllerType: any, scheduleTime: any): void;
@@ -162,7 +165,7 @@ export class MidyGM1 {
162
165
  handleCoarseTuningRPN(channelNumber: any, scheduleTime: any): void;
163
166
  setCoarseTuning(channelNumber: any, value: any, scheduleTime: any): void;
164
167
  allSoundOff(channelNumber: any, _value: any, scheduleTime: any): Promise<any[]>;
165
- resetAllStates(channelNumber: any): void;
168
+ resetChannelStates(channelNumber: any): void;
166
169
  resetAllControllers(channelNumber: any, _value: any, scheduleTime: any): void;
167
170
  allNotesOff(channelNumber: any, _value: any, scheduleTime: any): Promise<any[]>;
168
171
  handleUniversalNonRealTimeExclusiveMessage(data: any, scheduleTime: any): void;
@@ -1 +1 @@
1
- {"version":3,"file":"midy-GM1.d.ts","sourceRoot":"","sources":["../src/midy-GM1.js"],"names":[],"mappings":"AA4FA;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,4BAAyB;IACzB,0BAAuB;IACvB,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,6BAA+D;IAC/D,gBAAiD;IAMnD,4BAMC;IAED,mCASC;IAED,2DAYC;IAED,yCAmBC;IAED,oCASC;IAED,sBAoCC;IAED,6EAcC;IAED;;;;MAeC;IAED,yCAaC;IAED,kDAUC;IAED,4DASC;IAED,+EAkDC;IAED,mCAOC;IAED,0BAgEC;IAED,uDAEC;IAED,wDAEC;IAED;;;MAgEC;IAED,kGAeC;IAED,mGAeC;IAED,wEAMC;IAED,uBAMC;IAED,aAGC;IAED,cAKC;IAED,wBAIC;IAED,0BAKC;IAED,wBAOC;IAED,sBAGC;IAED,yDAQC;IAED,yEASC;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,6FAyBC;IAED,oGAuCC;IAED,0EAiBC;IAED,kGAmCC;IAED,6FASC;IAED,gCASC;IAED,iEAoBC;IAED,qGAeC;IAED,6CAUC;IAED,qDAUC;IAED,qFASC;IAED,sFAeC;IAED,oGA2BC;IAED,mFAGC;IAED,wFAGC;IAED,sEAUC;IAED,mEAQC;IAED,wDAKC;IAED,sDAOC;IAED,mDAMC;IAED,kDAKC;IAED;;;;;;;;;;;MA2BC;IAED,oFAMC;IAED,6EAgCC;IAED,qCAeC;IAED,+FAWC;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,yCAqBC;IAGD,8EAgCC;IAED,gFAGC;IAED,+EAgBC;IAED,qCAWC;IAED,4EAaC;IAED,4DAGC;IAED,sDASC;IAED,gDAYC;IAGD,6DAgBC;CACF;AA//CD;IAWE,0FAMC;IAhBD,cAAW;IACX,gBAAe;IACf,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":"AA4FA;IAyBE;;;;;;;;;;;MAWE;IAEF,+BAcC;IAnDD,aAAa;IACb,oBAAiB;IACjB,qBAAmB;IACnB,kBAAc;IACd,0BAAwB;IACxB,kBAAc;IACd,mBAAiB;IACjB,kBAAc;IACd,mBAAe;IACf,kBAAgB;IAChB,sBAA2C;IAC3C,4BAAyB;IACzB,0BAAuB;IACvB,mBAAkB;IAClB,mBAAkB;IAClB,kBAAiB;IACjB,oBAAmB;IACnB,mBAAkB;IAClB,iBAAY;IACZ,gBAAc;IACd,oBAAkB;IAClB,sBAAwB;IACxB,2BAAqC;IAgBnC,kBAAgC;IAChC,kBAA8C;IAC9C,eAAwD;IACxD,qBAGE;IACF;;;;;;;;;;;MAA2D;IAC3D,6BAA+D;IAC/D,gBAAiD;IAMnD,4BAMC;IAED,mCASC;IAED,2DAYC;IAED,yCAmBC;IAED,oCASC;IAED,sBAoCC;IAED,8DAcC;IAED;;;;MAeC;IAED,yCAaC;IAED,kDAUC;IAED,4DASC;IAED,+EAkDC;IAED,mCAOC;IAED,uBAQC;IAED,yDA2BC;IAED,2BA+CC;IAED,uDAEC;IAED,wDAEC;IAED;;;MAgEC;IAED,kGAeC;IAED,mGAeC;IAED,wEAMC;IAED,uBAMC;IAED,sBAKC;IAED,uBAQC;IAED,wBAKC;IAED,0BAKC;IAED,wBAOC;IAED,sBAGC;IAED,yDAQC;IAED,yEASC;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,6FAyBC;IAED,oGAuCC;IAED,0EAiBC;IAED,kGAmCC;IAED,6FASC;IAED,gCASC;IAED,iEAoBC;IAED,qGAeC;IAED,6CAUC;IAED,qDAUC;IAED,qFASC;IAED,sFAeC;IAED,oGA2BC;IAED,mFAGC;IAED,wFAGC;IAED,sEAUC;IAED,mEAWC;IAED,wDAKC;IAED,sDAOC;IAED,mDAMC;IAED,kDAKC;IAED;;;;;;;;;;;MAiCC;IAED,oFAMC;IAED,6EA2BC;IAED,qCAeC;IAED,+FAWC;IAED,wDASC;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,iEAMC;IAED,uEAQC;IAED,mEAKC;IAED,yEAQC;IAED,gFAGC;IAED,6CAqBC;IAGD,8EAgCC;IAED,gFAGC;IAED,+EAgBC;IAED,qCAWC;IAED,4EAaC;IAED,4DAGC;IAED,sDASC;IAED,gDAYC;IAGD,6DAgBC;CACF;AAhiDD;IAWE,0FAMC;IAhBD,cAAW;IACX,gBAAe;IACf,kBAAa;IACb,gBAAW;IACX,iBAAY;IACZ,wBAAmB;IACnB,iBAAY;IACZ,mBAAc;IACd,qBAAgB;IAGd,gBAA4B;IAC5B,cAAwB;IACxB,eAA0B;IAC1B,WAAkB;IAClB,iBAA8B;CAEjC"}
@@ -247,13 +247,13 @@ class MidyGM1 {
247
247
  writable: true,
248
248
  value: false
249
249
  });
250
- Object.defineProperty(this, "timeline", {
250
+ Object.defineProperty(this, "playPromise", {
251
251
  enumerable: true,
252
252
  configurable: true,
253
253
  writable: true,
254
- value: []
254
+ value: void 0
255
255
  });
256
- Object.defineProperty(this, "instruments", {
256
+ Object.defineProperty(this, "timeline", {
257
257
  enumerable: true,
258
258
  configurable: true,
259
259
  writable: true,
@@ -265,6 +265,12 @@ class MidyGM1 {
265
265
  writable: true,
266
266
  value: []
267
267
  });
268
+ Object.defineProperty(this, "instruments", {
269
+ enumerable: true,
270
+ configurable: true,
271
+ writable: true,
272
+ value: new Set()
273
+ });
268
274
  Object.defineProperty(this, "exclusiveClassNotes", {
269
275
  enumerable: true,
270
276
  configurable: true,
@@ -385,7 +391,7 @@ class MidyGM1 {
385
391
  const soundFont = this.soundFonts[soundFontIndex];
386
392
  const voice = soundFont.getVoice(bankNumber, channel.programNumber, noteNumber, velocity);
387
393
  const { instrument, sampleID } = voice.generators;
388
- return `${soundFontIndex}:${instrument}:${sampleID}`;
394
+ return soundFontIndex * (2 ** 32) + (instrument << 16) + sampleID;
389
395
  }
390
396
  createChannelAudioNodes(audioContext) {
391
397
  const { gainLeft, gainRight } = this.panToGain(defaultControllerState.pan.defaultValue);
@@ -473,69 +479,80 @@ class MidyGM1 {
473
479
  }
474
480
  return 0;
475
481
  }
476
- playNotes() {
477
- return new Promise((resolve) => {
478
- this.isPlaying = true;
479
- this.isPaused = false;
480
- this.startTime = this.audioContext.currentTime;
481
- let queueIndex = this.getQueueIndex(this.resumeTime);
482
- let resumeTime = this.resumeTime - this.startTime;
482
+ resetAllStates() {
483
+ this.exclusiveClassNotes.fill(undefined);
484
+ this.drumExclusiveClassNotes.fill(undefined);
485
+ this.voiceCache.clear();
486
+ for (let i = 0; i < this.channels.length; i++) {
487
+ this.channels[i].scheduledNotes = [];
488
+ this.resetChannelStates(i);
489
+ }
490
+ }
491
+ updateStates(queueIndex, nextQueueIndex) {
492
+ if (nextQueueIndex < queueIndex)
493
+ queueIndex = 0;
494
+ for (let i = queueIndex; i < nextQueueIndex; i++) {
495
+ const event = this.timeline[i];
496
+ switch (event.type) {
497
+ case "controller":
498
+ this.setControlChange(event.channel, event.controllerType, event.value, 0);
499
+ break;
500
+ case "programChange":
501
+ this.setProgramChange(event.channel, event.programNumber, 0);
502
+ break;
503
+ case "pitchBend":
504
+ this.setPitchBend(event.channel, event.value + 8192, 0);
505
+ break;
506
+ case "sysEx":
507
+ this.handleSysEx(event.data, 0);
508
+ }
509
+ }
510
+ }
511
+ async playNotes() {
512
+ if (this.audioContext.state === "suspended") {
513
+ await this.audioContext.resume();
514
+ }
515
+ this.isPlaying = true;
516
+ this.isPaused = false;
517
+ this.startTime = this.audioContext.currentTime;
518
+ let queueIndex = this.getQueueIndex(this.resumeTime);
519
+ let resumeTime = this.resumeTime - this.startTime;
520
+ let finished = false;
521
+ this.notePromises = [];
522
+ while (queueIndex < this.timeline.length) {
523
+ const now = this.audioContext.currentTime;
524
+ const t = now + resumeTime;
525
+ queueIndex = await this.scheduleTimelineEvents(t, resumeTime, queueIndex);
526
+ if (this.isPausing) {
527
+ await this.stopNotes(0, true, now);
528
+ await this.audioContext.suspend();
529
+ this.notePromises = [];
530
+ break;
531
+ }
532
+ else if (this.isStopping) {
533
+ await this.stopNotes(0, true, now);
534
+ await this.audioContext.suspend();
535
+ finished = true;
536
+ break;
537
+ }
538
+ else if (this.isSeeking) {
539
+ await this.stopNotes(0, true, now);
540
+ this.startTime = this.audioContext.currentTime;
541
+ const nextQueueIndex = this.getQueueIndex(this.resumeTime);
542
+ this.updateStates(queueIndex, nextQueueIndex);
543
+ queueIndex = nextQueueIndex;
544
+ resumeTime = this.resumeTime - this.startTime;
545
+ this.isSeeking = false;
546
+ continue;
547
+ }
548
+ const waitTime = now + this.noteCheckInterval;
549
+ await this.scheduleTask(() => { }, waitTime);
550
+ }
551
+ if (finished) {
483
552
  this.notePromises = [];
484
- const schedulePlayback = async () => {
485
- if (queueIndex >= this.timeline.length) {
486
- await Promise.all(this.notePromises);
487
- this.notePromises = [];
488
- this.exclusiveClassNotes.fill(undefined);
489
- this.voiceCache.clear();
490
- for (let i = 0; i < this.channels.length; i++) {
491
- this.channels[i].scheduledNotes = [];
492
- this.resetAllStates(i);
493
- }
494
- resolve();
495
- return;
496
- }
497
- const now = this.audioContext.currentTime;
498
- const t = now + resumeTime;
499
- queueIndex = await this.scheduleTimelineEvents(t, resumeTime, queueIndex);
500
- if (this.isPausing) {
501
- await this.stopNotes(0, true, now);
502
- this.notePromises = [];
503
- this.isPausing = false;
504
- this.isPaused = true;
505
- resolve();
506
- return;
507
- }
508
- else if (this.isStopping) {
509
- await this.stopNotes(0, true, now);
510
- this.notePromises = [];
511
- this.exclusiveClassNotes.fill(undefined);
512
- this.voiceCache.clear();
513
- for (let i = 0; i < this.channels.length; i++) {
514
- this.channels[i].scheduledNotes = [];
515
- this.resetAllStates(i);
516
- }
517
- this.isStopping = false;
518
- this.isPaused = false;
519
- resolve();
520
- return;
521
- }
522
- else if (this.isSeeking) {
523
- this.stopNotes(0, true, now);
524
- this.exclusiveClassNotes.fill(undefined);
525
- this.startTime = this.audioContext.currentTime;
526
- queueIndex = this.getQueueIndex(this.resumeTime);
527
- resumeTime = this.resumeTime - this.startTime;
528
- this.isSeeking = false;
529
- await schedulePlayback();
530
- }
531
- else {
532
- const waitTime = now + this.noteCheckInterval;
533
- await this.scheduleTask(() => { }, waitTime);
534
- await schedulePlayback();
535
- }
536
- };
537
- schedulePlayback();
538
- });
553
+ this.resetAllStates();
554
+ }
555
+ this.isPlaying = false;
539
556
  }
540
557
  ticksToSecond(ticks, secondsPerBeat) {
541
558
  return ticks * secondsPerBeat / this.ticksPerBeat;
@@ -636,26 +653,32 @@ class MidyGM1 {
636
653
  this.resumeTime = 0;
637
654
  if (this.voiceCounter.size === 0)
638
655
  this.cacheVoiceIds();
639
- await this.playNotes();
640
- this.isPlaying = false;
656
+ this.playPromise = this.playNotes();
657
+ await this.playPromise;
641
658
  }
642
- stop() {
659
+ async stop() {
643
660
  if (!this.isPlaying)
644
661
  return;
645
662
  this.isStopping = true;
663
+ await this.playPromise;
664
+ this.isStopping = false;
646
665
  }
647
- pause() {
666
+ async pause() {
648
667
  if (!this.isPlaying || this.isPaused)
649
668
  return;
650
669
  const now = this.audioContext.currentTime;
651
670
  this.resumeTime += now - this.startTime - this.startDelay;
652
671
  this.isPausing = true;
672
+ await this.playPromise;
673
+ this.isPausing = false;
674
+ this.isPaused = true;
653
675
  }
654
676
  async resume() {
655
677
  if (!this.isPaused)
656
678
  return;
657
- await this.playNotes();
658
- this.isPlaying = false;
679
+ this.playPromise = this.playNotes();
680
+ await this.playPromise;
681
+ this.isPaused = false;
659
682
  }
660
683
  seekTo(second) {
661
684
  this.resumeTime = second;
@@ -1018,13 +1041,17 @@ class MidyGM1 {
1018
1041
  this.applyVoiceParams(channel, 14, scheduleTime);
1019
1042
  }
1020
1043
  setModLfoToPitch(channel, note, scheduleTime) {
1021
- const modLfoToPitch = note.voiceParams.modLfoToPitch;
1022
- const baseDepth = Math.abs(modLfoToPitch) +
1023
- channel.state.modulationDepth;
1024
- const modulationDepth = baseDepth * Math.sign(modLfoToPitch);
1025
- note.modulationDepth.gain
1026
- .cancelScheduledValues(scheduleTime)
1027
- .setValueAtTime(modulationDepth, scheduleTime);
1044
+ if (note.modulationDepth) {
1045
+ const modLfoToPitch = note.voiceParams.modLfoToPitch;
1046
+ const baseDepth = Math.abs(modLfoToPitch) + channel.state.modulationDepth;
1047
+ const modulationDepth = baseDepth * Math.sign(modLfoToPitch);
1048
+ note.modulationDepth.gain
1049
+ .cancelScheduledValues(scheduleTime)
1050
+ .setValueAtTime(modulationDepth, scheduleTime);
1051
+ }
1052
+ else {
1053
+ this.startModulation(channel, note, scheduleTime);
1054
+ }
1028
1055
  }
1029
1056
  setModLfoToFilterFc(note, scheduleTime) {
1030
1057
  const modLfoToFilterFc = note.voiceParams.modLfoToFilterFc;
@@ -1056,28 +1083,36 @@ class MidyGM1 {
1056
1083
  }
1057
1084
  createVoiceParamsHandlers() {
1058
1085
  return {
1059
- modLfoToPitch: (channel, note, _prevValue, scheduleTime) => {
1086
+ modLfoToPitch: (channel, note, scheduleTime) => {
1060
1087
  if (0 < channel.state.modulationDepth) {
1061
1088
  this.setModLfoToPitch(channel, note, scheduleTime);
1062
1089
  }
1063
1090
  },
1064
- vibLfoToPitch: (_channel, _note, _prevValue, _scheduleTime) => { },
1065
- modLfoToFilterFc: (channel, note, _prevValue, scheduleTime) => {
1091
+ vibLfoToPitch: (_channel, _note, _scheduleTime) => { },
1092
+ modLfoToFilterFc: (channel, note, scheduleTime) => {
1066
1093
  if (0 < channel.state.modulationDepth) {
1067
1094
  this.setModLfoToFilterFc(note, scheduleTime);
1068
1095
  }
1069
1096
  },
1070
- modLfoToVolume: (channel, note, _prevValue, scheduleTime) => {
1097
+ modLfoToVolume: (channel, note, scheduleTime) => {
1071
1098
  if (0 < channel.state.modulationDepth) {
1072
1099
  this.setModLfoToVolume(note, scheduleTime);
1073
1100
  }
1074
1101
  },
1075
- chorusEffectsSend: (_channel, _note, _prevValue, _scheduleTime) => { },
1076
- reverbEffectsSend: (_channel, _note, _prevValue, _scheduleTime) => { },
1077
- delayModLFO: (_channel, note, _prevValue, scheduleTime) => this.setDelayModLFO(note, scheduleTime),
1078
- freqModLFO: (_channel, note, _prevValue, scheduleTime) => this.setFreqModLFO(note, scheduleTime),
1079
- delayVibLFO: (_channel, _note, _prevValue, _scheduleTime) => { },
1080
- freqVibLFO: (_channel, _note, _prevValue, _scheduleTime) => { },
1102
+ chorusEffectsSend: (_channel, _note, _scheduleTime) => { },
1103
+ reverbEffectsSend: (_channel, _note, _scheduleTime) => { },
1104
+ delayModLFO: (_channel, note, scheduleTime) => {
1105
+ if (0 < channel.state.modulationDepth) {
1106
+ this.setDelayModLFO(note, scheduleTime);
1107
+ }
1108
+ },
1109
+ freqModLFO: (_channel, note, scheduleTime) => {
1110
+ if (0 < channel.state.modulationDepth) {
1111
+ this.setFreqModLFO(note, scheduleTime);
1112
+ }
1113
+ },
1114
+ delayVibLFO: (_channel, _note, _scheduleTime) => { },
1115
+ freqVibLFO: (_channel, _note, _scheduleTime) => { },
1081
1116
  };
1082
1117
  }
1083
1118
  getControllerState(channel, noteNumber, velocity) {
@@ -1100,7 +1135,7 @@ class MidyGM1 {
1100
1135
  continue;
1101
1136
  note.voiceParams[key] = value;
1102
1137
  if (key in this.voiceParamsHandlers) {
1103
- this.voiceParamsHandlers[key](channel, note, prevValue, scheduleTime);
1138
+ this.voiceParamsHandlers[key](channel, note, scheduleTime);
1104
1139
  }
1105
1140
  else {
1106
1141
  if (volumeEnvelopeKeySet.has(key))
@@ -1153,7 +1188,6 @@ class MidyGM1 {
1153
1188
  note.modulationDepth.gain.setValueAtTime(depth, scheduleTime);
1154
1189
  }
1155
1190
  else {
1156
- this.setPitchEnvelope(note, scheduleTime);
1157
1191
  this.startModulation(channel, note, scheduleTime);
1158
1192
  }
1159
1193
  });
@@ -1272,8 +1306,8 @@ class MidyGM1 {
1272
1306
  }
1273
1307
  handlePitchBendRangeRPN(channelNumber, scheduleTime) {
1274
1308
  const channel = this.channels[channelNumber];
1275
- this.limitData(channel, 0, 127, 0, 99);
1276
- const pitchBendRange = channel.dataMSB + channel.dataLSB / 100;
1309
+ this.limitData(channel, 0, 127, 0, 127);
1310
+ const pitchBendRange = (channel.dataMSB + channel.dataLSB / 128) * 100;
1277
1311
  this.setPitchBendRange(channelNumber, pitchBendRange, scheduleTime);
1278
1312
  }
1279
1313
  setPitchBendRange(channelNumber, value, scheduleTime) {
@@ -1281,7 +1315,7 @@ class MidyGM1 {
1281
1315
  scheduleTime ??= this.audioContext.currentTime;
1282
1316
  const state = channel.state;
1283
1317
  const prev = state.pitchWheelSensitivity;
1284
- const next = value / 128;
1318
+ const next = value / 12800;
1285
1319
  state.pitchWheelSensitivity = next;
1286
1320
  channel.detune += (state.pitchWheel * 2 - 1) * (next - prev) * 12800;
1287
1321
  this.updateChannelDetune(channel, scheduleTime);
@@ -1290,14 +1324,15 @@ class MidyGM1 {
1290
1324
  handleFineTuningRPN(channelNumber, scheduleTime) {
1291
1325
  const channel = this.channels[channelNumber];
1292
1326
  this.limitData(channel, 0, 127, 0, 127);
1293
- const fineTuning = channel.dataMSB * 128 + channel.dataLSB;
1327
+ const value = channel.dataMSB * 128 + channel.dataLSB;
1328
+ const fineTuning = (value - 8192) / 8192 * 100;
1294
1329
  this.setFineTuning(channelNumber, fineTuning, scheduleTime);
1295
1330
  }
1296
1331
  setFineTuning(channelNumber, value, scheduleTime) {
1297
1332
  const channel = this.channels[channelNumber];
1298
1333
  scheduleTime ??= this.audioContext.currentTime;
1299
1334
  const prev = channel.fineTuning;
1300
- const next = (value - 8192) / 8.192; // cent
1335
+ const next = value;
1301
1336
  channel.fineTuning = next;
1302
1337
  channel.detune += next - prev;
1303
1338
  this.updateChannelDetune(channel, scheduleTime);
@@ -1305,14 +1340,14 @@ class MidyGM1 {
1305
1340
  handleCoarseTuningRPN(channelNumber, scheduleTime) {
1306
1341
  const channel = this.channels[channelNumber];
1307
1342
  this.limitDataMSB(channel, 0, 127);
1308
- const coarseTuning = channel.dataMSB;
1343
+ const coarseTuning = (channel.dataMSB - 64) * 100;
1309
1344
  this.setCoarseTuning(channelNumber, coarseTuning, scheduleTime);
1310
1345
  }
1311
1346
  setCoarseTuning(channelNumber, value, scheduleTime) {
1312
1347
  const channel = this.channels[channelNumber];
1313
1348
  scheduleTime ??= this.audioContext.currentTime;
1314
1349
  const prev = channel.coarseTuning;
1315
- const next = (value - 64) * 100; // cent
1350
+ const next = value;
1316
1351
  channel.coarseTuning = next;
1317
1352
  channel.detune += next - prev;
1318
1353
  this.updateChannelDetune(channel, scheduleTime);
@@ -1321,7 +1356,7 @@ class MidyGM1 {
1321
1356
  scheduleTime ??= this.audioContext.currentTime;
1322
1357
  return this.stopActiveNotes(channelNumber, 0, true, scheduleTime);
1323
1358
  }
1324
- resetAllStates(channelNumber) {
1359
+ resetChannelStates(channelNumber) {
1325
1360
  const scheduleTime = this.audioContext.currentTime;
1326
1361
  const channel = this.channels[channelNumber];
1327
1362
  const state = channel.state;
@@ -1475,7 +1510,7 @@ Object.defineProperty(MidyGM1, "channelSettings", {
1475
1510
  rpnMSB: 127,
1476
1511
  rpnLSB: 127,
1477
1512
  modulationDepthRange: 50, // cent
1478
- fineTuning: 0, // cb
1479
- coarseTuning: 0, // cb
1513
+ fineTuning: 0, // cent
1514
+ coarseTuning: 0, // cent
1480
1515
  }
1481
1516
  });