@marmooo/midy 0.1.1 → 0.1.3

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 (35) hide show
  1. package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.4/+esm.d.ts +153 -0
  2. package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.4/+esm.d.ts.map +1 -0
  3. package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/{soundfont-parser@0.0.2 → soundfont-parser@0.0.4}/+esm.js +73 -66
  4. package/esm/midy-GM1.d.ts +18 -14
  5. package/esm/midy-GM1.d.ts.map +1 -1
  6. package/esm/midy-GM1.js +133 -110
  7. package/esm/midy-GM2.d.ts +36 -30
  8. package/esm/midy-GM2.d.ts.map +1 -1
  9. package/esm/midy-GM2.js +190 -158
  10. package/esm/midy-GMLite.d.ts +16 -14
  11. package/esm/midy-GMLite.d.ts.map +1 -1
  12. package/esm/midy-GMLite.js +123 -112
  13. package/esm/midy.d.ts +33 -31
  14. package/esm/midy.d.ts.map +1 -1
  15. package/esm/midy.js +191 -185
  16. package/package.json +1 -1
  17. package/script/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.4/+esm.d.ts +153 -0
  18. package/script/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.4/+esm.d.ts.map +1 -0
  19. package/script/deps/cdn.jsdelivr.net/npm/@marmooo/{soundfont-parser@0.0.2 → soundfont-parser@0.0.4}/+esm.js +75 -68
  20. package/script/midy-GM1.d.ts +18 -14
  21. package/script/midy-GM1.d.ts.map +1 -1
  22. package/script/midy-GM1.js +133 -110
  23. package/script/midy-GM2.d.ts +36 -30
  24. package/script/midy-GM2.d.ts.map +1 -1
  25. package/script/midy-GM2.js +190 -158
  26. package/script/midy-GMLite.d.ts +16 -14
  27. package/script/midy-GMLite.d.ts.map +1 -1
  28. package/script/midy-GMLite.js +123 -112
  29. package/script/midy.d.ts +33 -31
  30. package/script/midy.d.ts.map +1 -1
  31. package/script/midy.js +191 -185
  32. package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.2/+esm.d.ts +0 -135
  33. package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.2/+esm.d.ts.map +0 -1
  34. package/script/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.2/+esm.d.ts +0 -135
  35. package/script/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.2/+esm.d.ts.map +0 -1
@@ -11,7 +11,7 @@ export class MidyGMLite {
11
11
  };
12
12
  static effectSettings: {
13
13
  expression: number;
14
- modulation: number;
14
+ modulationDepth: number;
15
15
  sustainPedal: boolean;
16
16
  rpnMSB: number;
17
17
  rpnLSB: number;
@@ -60,7 +60,8 @@ export class MidyGMLite {
60
60
  instruments: Set<any>;
61
61
  timeline: any[];
62
62
  };
63
- stopNotes(): Promise<any[]>;
63
+ stopChannelNotes(channelNumber: any, velocity: any, stopPedal: any): Promise<void>;
64
+ stopNotes(velocity: any, stopPedal: any): Promise<any[]>;
64
65
  start(): Promise<void>;
65
66
  stop(): void;
66
67
  pause(): void;
@@ -70,27 +71,27 @@ export class MidyGMLite {
70
71
  currentTime(): number;
71
72
  getActiveNotes(channel: any, time: any): Map<any, any>;
72
73
  getActiveNote(noteList: any, time: any): any;
73
- connectEffects(channel: any, gainNode: any): void;
74
74
  cbToRatio(cb: any): number;
75
75
  centToHz(cent: any): number;
76
76
  calcSemitoneOffset(channel: any): number;
77
77
  calcPlaybackRate(instrumentKey: any, noteNumber: any, semitoneOffset: any): number;
78
- setVolumeEnvelope(channel: any, note: any): void;
79
- setFilterEnvelope(channel: any, note: any): void;
80
- startModulation(channel: any, note: any, time: any): void;
78
+ setVolumeEnvelope(note: any): void;
79
+ setPitch(note: any, semitoneOffset: any): void;
80
+ setFilterNode(channel: any, note: any): void;
81
+ startModulation(channel: any, note: any, startTime: any): void;
81
82
  createNote(channel: any, instrumentKey: any, noteNumber: any, velocity: any, startTime: any, isSF3: any): Promise<Note>;
82
83
  scheduleNoteOn(channelNumber: any, noteNumber: any, velocity: any, startTime: any): Promise<void>;
83
84
  noteOn(channelNumber: any, noteNumber: any, velocity: any): Promise<void>;
84
85
  scheduleNoteRelease(channelNumber: any, noteNumber: any, velocity: any, stopTime: any, stopPedal?: boolean): Promise<any> | undefined;
85
86
  releaseNote(channelNumber: any, noteNumber: any, velocity: any): Promise<any> | undefined;
86
87
  releaseSustainPedal(channelNumber: any, halfVelocity: any): any[];
87
- handleMIDIMessage(statusByte: any, data1: any, data2: any): void | any[] | Promise<any>;
88
+ handleMIDIMessage(statusByte: any, data1: any, data2: any): void | Promise<any>;
88
89
  handleProgramChange(channelNumber: any, program: any): void;
89
90
  handlePitchBendMessage(channelNumber: any, lsb: any, msb: any): void;
90
91
  setPitchBend(channelNumber: any, pitchBend: any): void;
91
- handleControlChange(channelNumber: any, controller: any, value: any): void | any[];
92
+ handleControlChange(channelNumber: any, controller: any, value: any): void | Promise<void>;
92
93
  updateModulation(channel: any): void;
93
- setModulation(channelNumber: any, modulation: any): void;
94
+ setModulationDepth(channelNumber: any, modulation: any): void;
94
95
  setVolume(channelNumber: any, volume: any): void;
95
96
  panToGain(pan: any): {
96
97
  gainLeft: number;
@@ -108,9 +109,9 @@ export class MidyGMLite {
108
109
  updateDetune(channel: any, detuneChange: any): void;
109
110
  handlePitchBendRangeRPN(channelNumber: any): void;
110
111
  setPitchBendRange(channelNumber: any, pitchBendRange: any): void;
111
- allSoundOff(channelNumber: any): any[];
112
+ allSoundOff(channelNumber: any): Promise<void>;
112
113
  resetAllControllers(channelNumber: any): void;
113
- allNotesOff(channelNumber: any): any[];
114
+ allNotesOff(channelNumber: any): Promise<void>;
114
115
  handleUniversalNonRealTimeExclusiveMessage(data: any): void;
115
116
  GM1SystemOn(): void;
116
117
  handleUniversalRealTimeExclusiveMessage(data: any): void;
@@ -123,10 +124,11 @@ export class MidyGMLite {
123
124
  declare class Note {
124
125
  constructor(noteNumber: any, velocity: any, startTime: any, instrumentKey: any);
125
126
  bufferSource: any;
126
- gainNode: any;
127
127
  filterNode: any;
128
- modLFO: any;
129
- modLFOGain: any;
128
+ volumeNode: any;
129
+ volumeDepth: any;
130
+ modulationLFO: any;
131
+ modulationDepth: any;
130
132
  noteNumber: any;
131
133
  velocity: any;
132
134
  startTime: any;
@@ -1 +1 @@
1
- {"version":3,"file":"midy-GMLite.d.ts","sourceRoot":"","sources":["../src/midy-GMLite.js"],"names":[],"mappings":"AAqBA;IAmBE;;;;;;;;;MASE;IAEF;;;;;;;MAOE;IAEF,+BAMC;IA5CD,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;IAE5C,gBAAiD;IAInD,4BAMC;IAED,mCASC;IAED,gDAMC;IAED,sCASC;IAED;;;;MAcC;IAED,yCAUC;IAED,+DAyBC;IAED,mEAWC;IAED,qDAOC;IAED,2EA+CC;IAED,mCAOC;IAED,0BA+CC;IAED,uDAEC;IAED,wDAEC;IAED;;;MA8DC;IAED,4BAsBC;IAED,uBAKC;IAED,aAGC;IAED,cAKC;IAED,wBAIC;IAED,0BAKC;IAED,wBAOC;IAED,sBAGC;IAED,uDASC;IAED,6CAQC;IAED,kDAGC;IAED,2BAEC;IAED,4BAEC;IAED,yCAEC;IAED,mFAGC;IAED,iDAiBC;IAED,iDAiCC;IAED,0DAmBC;IAED,wHA6BC;IAED,kGA6BC;IAED,0EAGC;IAED,sIA8CC;IAED,0FAGC;IAED,kEAeC;IAED,wFAiBC;IAED,4DAGC;IAED,qEAGC;IAED,uDAOC;IAED,mFA+BC;IAED,qCAcC;IAED,yDAIC;IACD,iDAIC;IAED;;;MAMC;IAED,2CAIC;IAED,yDAIC;IAED,mDAGC;IAED,sCAUC;IAED,sDAMC;IAED,oCAYC;IAED,gDAEC;IAED,gDAEC;IAED,mDAGC;IAED,oDAUC;IAED,kDAKC;IAED,iEAOC;IAED,uCAoBC;IAED,8CAEC;IAED,uCAoBC;IAED,4DAgBC;IAED,oBAQC;IAED,yDAaC;IAED,yCAGC;IAED,mCAQC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF;AAn/BD;IAOE,gFAKC;IAXD,kBAAa;IACb,cAAS;IACT,gBAAW;IACX,YAAO;IACP,gBAAW;IAGT,gBAA4B;IAC5B,cAAwB;IACxB,eAA0B;IAC1B,mBAAkC;CAErC"}
1
+ {"version":3,"file":"midy-GMLite.d.ts","sourceRoot":"","sources":["../src/midy-GMLite.js"],"names":[],"mappings":"AAsBA;IAmBE;;;;;;;;;MASE;IAEF;;;;;;;MAOE;IAEF,+BAMC;IA5CD,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,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,mCAcC;IAED,+CAwBC;IAED,6CA6BC;IAED,+DA0BC;IAED,wHA8BC;IAED,kGA6BC;IAED,0EAGC;IAED,sIA4CC;IAED,0FAGC;IAED,kEAeC;IAED,gFAiBC;IAED,4DAGC;IAED,qEAGC;IAED,uDAOC;IAED,2FA+BC;IAED,qCAeC;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,oDAUC;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;AA1+BD;IAQE,gFAKC;IAZD,kBAAa;IACb,gBAAW;IACX,gBAAW;IACX,iBAAY;IACZ,mBAAc;IACd,qBAAgB;IAGd,gBAA4B;IAC5B,cAAwB;IACxB,eAA0B;IAC1B,mBAAkC;CAErC"}
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MidyGMLite = void 0;
4
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.2/+esm.js");
5
+ const _esm_js_2 = require("./deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.4/+esm.js");
6
6
  class Note {
7
7
  constructor(noteNumber, velocity, startTime, instrumentKey) {
8
8
  Object.defineProperty(this, "bufferSource", {
@@ -11,25 +11,31 @@ class Note {
11
11
  writable: true,
12
12
  value: void 0
13
13
  });
14
- Object.defineProperty(this, "gainNode", {
14
+ Object.defineProperty(this, "filterNode", {
15
15
  enumerable: true,
16
16
  configurable: true,
17
17
  writable: true,
18
18
  value: void 0
19
19
  });
20
- Object.defineProperty(this, "filterNode", {
20
+ Object.defineProperty(this, "volumeNode", {
21
+ enumerable: true,
22
+ configurable: true,
23
+ writable: true,
24
+ value: void 0
25
+ });
26
+ Object.defineProperty(this, "volumeDepth", {
21
27
  enumerable: true,
22
28
  configurable: true,
23
29
  writable: true,
24
30
  value: void 0
25
31
  });
26
- Object.defineProperty(this, "modLFO", {
32
+ Object.defineProperty(this, "modulationLFO", {
27
33
  enumerable: true,
28
34
  configurable: true,
29
35
  writable: true,
30
36
  value: void 0
31
37
  });
32
- Object.defineProperty(this, "modLFOGain", {
38
+ Object.defineProperty(this, "modulationDepth", {
33
39
  enumerable: true,
34
40
  configurable: true,
35
41
  writable: true,
@@ -147,8 +153,8 @@ class MidyGMLite {
147
153
  });
148
154
  this.audioContext = audioContext;
149
155
  this.masterGain = new GainNode(audioContext);
150
- this.masterGain.connect(audioContext.destination);
151
156
  this.channels = this.createChannels(audioContext);
157
+ this.masterGain.connect(audioContext.destination);
152
158
  this.GM1SystemOn();
153
159
  }
154
160
  initSoundFontTable() {
@@ -192,6 +198,7 @@ class MidyGMLite {
192
198
  const merger = new ChannelMergerNode(audioContext, { numberOfInputs: 2 });
193
199
  gainL.connect(merger, 0, 0);
194
200
  gainR.connect(merger, 0, 1);
201
+ merger.connect(this.masterGain);
195
202
  return {
196
203
  gainL,
197
204
  gainR,
@@ -315,7 +322,7 @@ class MidyGMLite {
315
322
  const t = this.audioContext.currentTime + offset;
316
323
  queueIndex = await this.scheduleTimelineEvents(t, offset, queueIndex);
317
324
  if (this.isPausing) {
318
- await this.stopNotes();
325
+ await this.stopNotes(0, true);
319
326
  this.notePromises = [];
320
327
  resolve();
321
328
  this.isPausing = false;
@@ -323,7 +330,7 @@ class MidyGMLite {
323
330
  return;
324
331
  }
325
332
  else if (this.isStopping) {
326
- await this.stopNotes();
333
+ await this.stopNotes(0, true);
327
334
  this.notePromises = [];
328
335
  resolve();
329
336
  this.isStopping = false;
@@ -331,7 +338,7 @@ class MidyGMLite {
331
338
  return;
332
339
  }
333
340
  else if (this.isSeeking) {
334
- this.stopNotes();
341
+ this.stopNotes(0, true);
335
342
  this.startTime = this.audioContext.currentTime;
336
343
  queueIndex = this.getQueueIndex(this.resumeTime);
337
344
  offset = this.resumeTime - this.startTime;
@@ -412,21 +419,24 @@ class MidyGMLite {
412
419
  }
413
420
  return { instruments, timeline };
414
421
  }
415
- stopNotes() {
422
+ async stopChannelNotes(channelNumber, velocity, stopPedal) {
416
423
  const now = this.audioContext.currentTime;
417
- const velocity = 0;
418
- const stopPedal = true;
419
- this.channels.forEach((channel, channelNumber) => {
420
- channel.scheduledNotes.forEach((scheduledNotes) => {
421
- scheduledNotes.forEach((scheduledNote) => {
422
- if (scheduledNote) {
423
- const promise = this.scheduleNoteRelease(channelNumber, scheduledNote.noteNumber, velocity, now, stopPedal);
424
- this.notePromises.push(promise);
425
- }
426
- });
424
+ const channel = this.channels[channelNumber];
425
+ channel.scheduledNotes.forEach((noteList) => {
426
+ noteList.forEach((note) => {
427
+ if (note) {
428
+ const promise = this.scheduleNoteRelease(channelNumber, note.noteNumber, velocity, now, stopPedal);
429
+ this.notePromises.push(promise);
430
+ }
427
431
  });
428
- channel.scheduledNotes.clear();
429
432
  });
433
+ channel.scheduledNotes.clear();
434
+ await Promise.all(this.notePromises);
435
+ }
436
+ stopNotes(velocity, stopPedal) {
437
+ for (let i = 0; i < this.channels.length; i++) {
438
+ this.stopChannelNotes(i, velocity, stopPedal);
439
+ }
430
440
  return Promise.all(this.notePromises);
431
441
  }
432
442
  async start() {
@@ -494,10 +504,6 @@ class MidyGMLite {
494
504
  }
495
505
  return noteList[0];
496
506
  }
497
- connectEffects(channel, gainNode) {
498
- gainNode.connect(channel.merger);
499
- merger.connect(this.masterGain);
500
- }
501
507
  cbToRatio(cb) {
502
508
  return Math.pow(10, cb / 200);
503
509
  }
@@ -511,42 +517,56 @@ class MidyGMLite {
511
517
  return instrumentKey.playbackRate(noteNumber) *
512
518
  Math.pow(2, semitoneOffset / 12);
513
519
  }
514
- setVolumeEnvelope(channel, note) {
515
- const { instrumentKey, startTime, velocity } = note;
516
- note.gainNode = new GainNode(this.audioContext, { gain: 0 });
517
- let volume = (velocity / 127) * channel.volume * channel.expression;
518
- if (volume === 0)
519
- volume = 1e-6; // exponentialRampToValueAtTime() requires a non-zero value
520
- const attackVolume = this.cbToRatio(-instrumentKey.initialAttenuation) *
521
- volume;
520
+ setVolumeEnvelope(note) {
521
+ const { instrumentKey, startTime } = note;
522
+ note.volumeNode = new GainNode(this.audioContext, { gain: 0 });
523
+ const attackVolume = this.cbToRatio(-instrumentKey.initialAttenuation);
522
524
  const sustainVolume = attackVolume * (1 - instrumentKey.volSustain);
523
525
  const volDelay = startTime + instrumentKey.volDelay;
524
526
  const volAttack = volDelay + instrumentKey.volAttack;
525
527
  const volHold = volAttack + instrumentKey.volHold;
526
528
  const volDecay = volHold + instrumentKey.volDecay;
527
- note.gainNode.gain
529
+ note.volumeNode.gain
528
530
  .setValueAtTime(1e-6, volDelay) // exponentialRampToValueAtTime() requires a non-zero value
529
531
  .exponentialRampToValueAtTime(attackVolume, volAttack)
530
532
  .setValueAtTime(attackVolume, volHold)
531
533
  .linearRampToValueAtTime(sustainVolume, volDecay);
532
534
  }
533
- setFilterEnvelope(channel, note) {
534
- const { instrumentKey, startTime, noteNumber } = note;
535
+ setPitch(note, semitoneOffset) {
536
+ const { instrumentKey, noteNumber, startTime } = note;
537
+ const modEnvToPitch = instrumentKey.modEnvToPitch / 100;
538
+ note.bufferSource.playbackRate.value = this.calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset);
539
+ if (modEnvToPitch === 0)
540
+ return;
541
+ const basePitch = note.bufferSource.playbackRate.value;
542
+ const peekPitch = this.calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset + modEnvToPitch);
543
+ const modDelay = startTime + instrumentKey.modDelay;
544
+ const modAttack = modDelay + instrumentKey.modAttack;
545
+ const modHold = modAttack + instrumentKey.modHold;
546
+ const modDecay = modHold + instrumentKey.modDecay;
547
+ note.bufferSource.playbackRate.value
548
+ .setValueAtTime(basePitch, modDelay)
549
+ .exponentialRampToValueAtTime(peekPitch, modAttack)
550
+ .setValueAtTime(peekPitch, modHold)
551
+ .linearRampToValueAtTime(basePitch, modDecay);
552
+ }
553
+ setFilterNode(channel, note) {
554
+ const { instrumentKey, noteNumber, startTime } = note;
535
555
  const softPedalFactor = 1 -
536
556
  (0.1 + (noteNumber / 127) * 0.2) * channel.softPedal;
537
557
  const maxFreq = this.audioContext.sampleRate / 2;
538
558
  const baseFreq = this.centToHz(instrumentKey.initialFilterFc) *
539
559
  softPedalFactor;
540
560
  const peekFreq = this.centToHz(instrumentKey.initialFilterFc + instrumentKey.modEnvToFilterFc) * softPedalFactor;
541
- const sustainFreq = (baseFreq +
542
- (peekFreq - baseFreq) * (1 - instrumentKey.modSustain)) * softPedalFactor;
561
+ const sustainFreq = baseFreq +
562
+ (peekFreq - baseFreq) * (1 - instrumentKey.modSustain);
563
+ const adjustedBaseFreq = Math.min(maxFreq, baseFreq);
564
+ const adjustedPeekFreq = Math.min(maxFreq, peekFreq);
565
+ const adjustedSustainFreq = Math.min(maxFreq, sustainFreq);
543
566
  const modDelay = startTime + instrumentKey.modDelay;
544
567
  const modAttack = modDelay + instrumentKey.modAttack;
545
568
  const modHold = modAttack + instrumentKey.modHold;
546
569
  const modDecay = modHold + instrumentKey.modDecay;
547
- const adjustedBaseFreq = Math.min(maxFreq, baseFreq);
548
- const adjustedPeekFreq = Math.min(maxFreq, peekFreq);
549
- const adjustedSustainFreq = Math.min(maxFreq, sustainFreq);
550
570
  note.filterNode = new BiquadFilterNode(this.audioContext, {
551
571
  type: "lowpass",
552
572
  Q: instrumentKey.initialFilterQ / 10, // dB
@@ -557,35 +577,49 @@ class MidyGMLite {
557
577
  .exponentialRampToValueAtTime(adjustedPeekFreq, modAttack)
558
578
  .setValueAtTime(adjustedPeekFreq, modHold)
559
579
  .linearRampToValueAtTime(adjustedSustainFreq, modDecay);
560
- note.bufferSource.detune.setValueAtTime(note.bufferSource.detune.value + instrumentKey.modEnvToPitch, modDelay);
561
580
  }
562
- startModulation(channel, note, time) {
581
+ startModulation(channel, note, startTime) {
563
582
  const { instrumentKey } = note;
564
- note.modLFOGain = new GainNode(this.audioContext, {
565
- gain: this.cbToRatio(instrumentKey.modLfoToVolume + channel.modulation),
566
- });
567
- note.modLFO = new OscillatorNode(this.audioContext, {
583
+ const { modLfoToPitch, modLfoToVolume } = instrumentKey;
584
+ note.modulationLFO = new OscillatorNode(this.audioContext, {
568
585
  frequency: this.centToHz(instrumentKey.freqModLFO),
569
586
  });
570
- note.modLFO.start(time);
571
- note.filterNode.frequency.setValueAtTime(note.filterNode.frequency.value + instrumentKey.modLfoToFilterFc, time);
572
- note.bufferSource.detune.setValueAtTime(note.bufferSource.detune.value + instrumentKey.modLfoToPitch, time);
573
- note.modLFO.connect(note.modLFOGain);
574
- note.modLFOGain.connect(note.bufferSource.detune);
587
+ note.filterDepth = new GainNode(this.audioContext, {
588
+ gain: instrumentKey.modLfoToFilterFc,
589
+ });
590
+ const modulationDepth = Math.abs(modLfoToPitch) + channel.modulationDepth;
591
+ const modulationDepthSign = (0 < modLfoToPitch) ? 1 : -1;
592
+ note.modulationDepth = new GainNode(this.audioContext, {
593
+ gain: modulationDepth * modulationDepthSign,
594
+ });
595
+ const volumeDepth = this.cbToRatio(Math.abs(modLfoToVolume)) - 1;
596
+ const volumeDepthSign = (0 < modLfoToVolume) ? 1 : -1;
597
+ note.volumeDepth = new GainNode(this.audioContext, {
598
+ gain: volumeDepth * volumeDepthSign,
599
+ });
600
+ note.modulationLFO.start(startTime + instrumentKey.delayModLFO);
601
+ note.modulationLFO.connect(note.filterDepth);
602
+ note.filterDepth.connect(note.filterNode.frequency);
603
+ note.modulationLFO.connect(note.modulationDepth);
604
+ note.modulationDepth.connect(note.bufferSource.detune);
605
+ note.modulationLFO.connect(note.volumeDepth);
606
+ note.volumeDepth.connect(note.volumeNode.gain);
575
607
  }
576
608
  async createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3) {
577
609
  const semitoneOffset = this.calcSemitoneOffset(channel);
578
610
  const note = new Note(noteNumber, velocity, startTime, instrumentKey);
579
611
  note.bufferSource = await this.createNoteBufferNode(instrumentKey, isSF3);
580
- note.bufferSource.playbackRate.value = this.calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset);
581
- this.setVolumeEnvelope(channel, note);
582
- this.setFilterEnvelope(channel, note);
583
- if (channel.modulation > 0) {
584
- const delayModLFO = startTime + instrumentKey.delayModLFO;
585
- this.startModulation(channel, note, delayModLFO);
612
+ this.setFilterNode(channel, note);
613
+ this.setVolumeEnvelope(note);
614
+ if (0 < channel.modulationDepth) {
615
+ this.setPitch(note, semitoneOffset);
616
+ this.startModulation(channel, note, startTime);
617
+ }
618
+ else {
619
+ note.bufferSource.playbackRate.value = this.calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset);
586
620
  }
587
621
  note.bufferSource.connect(note.filterNode);
588
- note.filterNode.connect(note.gainNode);
622
+ note.filterNode.connect(note.volumeNode);
589
623
  note.bufferSource.start(startTime, instrumentKey.start / instrumentKey.sampleRate);
590
624
  return note;
591
625
  }
@@ -601,7 +635,8 @@ class MidyGMLite {
601
635
  if (!instrumentKey)
602
636
  return;
603
637
  const note = await this.createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3);
604
- this.connectEffects(channel, note.gainNode);
638
+ note.volumeNode.connect(channel.gainL);
639
+ note.volumeNode.connect(channel.gainR);
605
640
  const scheduledNotes = channel.scheduledNotes;
606
641
  if (scheduledNotes.has(noteNumber)) {
607
642
  scheduledNotes.get(noteNumber).push(note);
@@ -630,17 +665,14 @@ class MidyGMLite {
630
665
  const velocityRate = (velocity + 127) / 127;
631
666
  const volEndTime = stopTime +
632
667
  note.instrumentKey.volRelease * velocityRate;
633
- note.gainNode.gain
668
+ note.volumeNode.gain
634
669
  .cancelScheduledValues(stopTime)
635
670
  .linearRampToValueAtTime(0, volEndTime);
636
- const maxFreq = this.audioContext.sampleRate / 2;
637
- const baseFreq = this.centToHz(note.instrumentKey.initialFilterFc);
638
- const adjustedBaseFreq = Math.min(maxFreq, baseFreq);
639
- const modEndTime = stopTime +
671
+ const modRelease = stopTime +
640
672
  note.instrumentKey.modRelease * velocityRate;
641
673
  note.filterNode.frequency
642
674
  .cancelScheduledValues(stopTime)
643
- .linearRampToValueAtTime(adjustedBaseFreq, modEndTime);
675
+ .linearRampToValueAtTime(0, modRelease);
644
676
  note.ending = true;
645
677
  this.scheduleTask(() => {
646
678
  note.bufferSource.loop = false;
@@ -649,15 +681,17 @@ class MidyGMLite {
649
681
  note.bufferSource.onended = () => {
650
682
  scheduledNotes[i] = null;
651
683
  note.bufferSource.disconnect();
684
+ note.volumeNode.disconnect();
652
685
  note.filterNode.disconnect();
653
- note.gainNode.disconnect();
654
- if (note.modLFOGain)
655
- note.modLFOGain.disconnect();
656
- if (note.modLFO)
657
- note.modLFO.stop();
686
+ if (note.volumeDepth)
687
+ note.volumeDepth.disconnect();
688
+ if (note.modulationDepth)
689
+ note.modulationDepth.disconnect();
690
+ if (note.modulationLFO)
691
+ note.modulationLFO.stop();
658
692
  resolve();
659
693
  };
660
- bufferSource.stop(volEndTime);
694
+ note.bufferSource.stop(volEndTime);
661
695
  });
662
696
  }
663
697
  }
@@ -670,10 +704,10 @@ class MidyGMLite {
670
704
  const channel = this.channels[channelNumber];
671
705
  const promises = [];
672
706
  channel.sustainPedal = false;
673
- channel.scheduledNotes.forEach((scheduledNotes) => {
674
- scheduledNotes.forEach((scheduledNote) => {
675
- if (scheduledNote) {
676
- const { noteNumber } = scheduledNote;
707
+ channel.scheduledNotes.forEach((noteList) => {
708
+ noteList.forEach((note) => {
709
+ if (note) {
710
+ const { noteNumber } = note;
677
711
  const promise = this.releaseNote(channelNumber, noteNumber, velocity);
678
712
  promises.push(promise);
679
713
  }
@@ -704,13 +738,13 @@ class MidyGMLite {
704
738
  channel.program = program;
705
739
  }
706
740
  handlePitchBendMessage(channelNumber, lsb, msb) {
707
- const pitchBend = msb * 128 + lsb;
741
+ const pitchBend = msb * 128 + lsb - 8192;
708
742
  this.setPitchBend(channelNumber, pitchBend);
709
743
  }
710
744
  setPitchBend(channelNumber, pitchBend) {
711
745
  const channel = this.channels[channelNumber];
712
746
  const prevPitchBend = channel.pitchBend;
713
- channel.pitchBend = (pitchBend - 8192) / 8192;
747
+ channel.pitchBend = pitchBend / 8192;
714
748
  const detuneChange = (channel.pitchBend - prevPitchBend) *
715
749
  channel.pitchBendRange * 100;
716
750
  this.updateDetune(channel, detuneChange);
@@ -718,7 +752,7 @@ class MidyGMLite {
718
752
  handleControlChange(channelNumber, controller, value) {
719
753
  switch (controller) {
720
754
  case 1:
721
- return this.setModulation(channelNumber, value);
755
+ return this.setModulationDepth(channelNumber, value);
722
756
  case 6:
723
757
  return this.dataEntryMSB(channelNumber, value);
724
758
  case 7:
@@ -749,18 +783,19 @@ class MidyGMLite {
749
783
  const now = this.audioContext.currentTime;
750
784
  const activeNotes = this.getActiveNotes(channel, now);
751
785
  activeNotes.forEach((activeNote) => {
752
- if (activeNote.modLFO) {
753
- const { gainNode, instrumentKey } = activeNote;
754
- gainNode.gain.setValueAtTime(this.cbToRatio(instrumentKey.modLfoToVolume + channel.modulation), now);
786
+ if (activeNote.modulationDepth) {
787
+ activeNote.modulationDepth.gain.setValueAtTime(channel.modulationDepth, now);
755
788
  }
756
789
  else {
790
+ const semitoneOffset = this.calcSemitoneOffset(channel);
791
+ this.setPitch(activeNote, semitoneOffset);
757
792
  this.startModulation(channel, activeNote, now);
758
793
  }
759
794
  });
760
795
  }
761
- setModulation(channelNumber, modulation) {
796
+ setModulationDepth(channelNumber, modulation) {
762
797
  const channel = this.channels[channelNumber];
763
- channel.modulation = (modulation / 127) * channel.modulationDepthRange;
798
+ channel.modulationDepth = (modulation / 127) * channel.modulationDepthRange;
764
799
  this.updateModulation(channel);
765
800
  }
766
801
  setVolume(channelNumber, volume) {
@@ -854,37 +889,13 @@ class MidyGMLite {
854
889
  this.updateDetune(channel, detuneChange);
855
890
  }
856
891
  allSoundOff(channelNumber) {
857
- const now = this.audioContext.currentTime;
858
- const channel = this.channels[channelNumber];
859
- const velocity = 0;
860
- const stopPedal = true;
861
- const promises = [];
862
- channel.scheduledNotes.forEach((noteList) => {
863
- const activeNote = this.getActiveNote(noteList, now);
864
- if (activeNote) {
865
- const notePromise = this.scheduleNoteRelease(channelNumber, noteNumber, velocity, now, stopPedal);
866
- promises.push(notePromise);
867
- }
868
- });
869
- return promises;
892
+ return this.stopChannelNotes(channelNumber, 0, true);
870
893
  }
871
894
  resetAllControllers(channelNumber) {
872
895
  Object.assign(this.channels[channelNumber], this.effectSettings);
873
896
  }
874
897
  allNotesOff(channelNumber) {
875
- const now = this.audioContext.currentTime;
876
- const channel = this.channels[channelNumber];
877
- const velocity = 0;
878
- const stopPedal = false;
879
- const promises = [];
880
- channel.scheduledNotes.forEach((noteList) => {
881
- const activeNote = this.getActiveNote(noteList, now);
882
- if (activeNote) {
883
- const notePromise = this.scheduleNoteRelease(channelNumber, noteNumber, velocity, now, stopPedal);
884
- promises.push(notePromise);
885
- }
886
- });
887
- return promises;
898
+ return this.stopChannelNotes(channelNumber, 0, false);
888
899
  }
889
900
  handleUniversalNonRealTimeExclusiveMessage(data) {
890
901
  switch (data[2]) {
@@ -978,7 +989,7 @@ Object.defineProperty(MidyGMLite, "channelSettings", {
978
989
  dataLSB: 0,
979
990
  program: 0,
980
991
  pitchBend: 0,
981
- modulationDepthRange: 0.5, // cb
992
+ modulationDepthRange: 50, // cent
982
993
  }
983
994
  });
984
995
  Object.defineProperty(MidyGMLite, "effectSettings", {
@@ -987,7 +998,7 @@ Object.defineProperty(MidyGMLite, "effectSettings", {
987
998
  writable: true,
988
999
  value: {
989
1000
  expression: 1,
990
- modulation: 0,
1001
+ modulationDepth: 0,
991
1002
  sustainPedal: false,
992
1003
  rpnMSB: 127,
993
1004
  rpnLSB: 127,