@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"}
@@ -1,5 +1,5 @@
1
1
  import { parseMidi } from "./deps/cdn.jsdelivr.net/npm/midi-file@1.2.4/+esm.js";
2
- import { parse, SoundFont, } from "./deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.2/+esm.js";
2
+ import { parse, SoundFont, } from "./deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.4/+esm.js";
3
3
  class Note {
4
4
  constructor(noteNumber, velocity, startTime, instrumentKey) {
5
5
  Object.defineProperty(this, "bufferSource", {
@@ -8,25 +8,31 @@ class Note {
8
8
  writable: true,
9
9
  value: void 0
10
10
  });
11
- Object.defineProperty(this, "gainNode", {
11
+ Object.defineProperty(this, "filterNode", {
12
12
  enumerable: true,
13
13
  configurable: true,
14
14
  writable: true,
15
15
  value: void 0
16
16
  });
17
- Object.defineProperty(this, "filterNode", {
17
+ Object.defineProperty(this, "volumeNode", {
18
+ enumerable: true,
19
+ configurable: true,
20
+ writable: true,
21
+ value: void 0
22
+ });
23
+ Object.defineProperty(this, "volumeDepth", {
18
24
  enumerable: true,
19
25
  configurable: true,
20
26
  writable: true,
21
27
  value: void 0
22
28
  });
23
- Object.defineProperty(this, "modLFO", {
29
+ Object.defineProperty(this, "modulationLFO", {
24
30
  enumerable: true,
25
31
  configurable: true,
26
32
  writable: true,
27
33
  value: void 0
28
34
  });
29
- Object.defineProperty(this, "modLFOGain", {
35
+ Object.defineProperty(this, "modulationDepth", {
30
36
  enumerable: true,
31
37
  configurable: true,
32
38
  writable: true,
@@ -144,8 +150,8 @@ export class MidyGMLite {
144
150
  });
145
151
  this.audioContext = audioContext;
146
152
  this.masterGain = new GainNode(audioContext);
147
- this.masterGain.connect(audioContext.destination);
148
153
  this.channels = this.createChannels(audioContext);
154
+ this.masterGain.connect(audioContext.destination);
149
155
  this.GM1SystemOn();
150
156
  }
151
157
  initSoundFontTable() {
@@ -189,6 +195,7 @@ export class MidyGMLite {
189
195
  const merger = new ChannelMergerNode(audioContext, { numberOfInputs: 2 });
190
196
  gainL.connect(merger, 0, 0);
191
197
  gainR.connect(merger, 0, 1);
198
+ merger.connect(this.masterGain);
192
199
  return {
193
200
  gainL,
194
201
  gainR,
@@ -312,7 +319,7 @@ export class MidyGMLite {
312
319
  const t = this.audioContext.currentTime + offset;
313
320
  queueIndex = await this.scheduleTimelineEvents(t, offset, queueIndex);
314
321
  if (this.isPausing) {
315
- await this.stopNotes();
322
+ await this.stopNotes(0, true);
316
323
  this.notePromises = [];
317
324
  resolve();
318
325
  this.isPausing = false;
@@ -320,7 +327,7 @@ export class MidyGMLite {
320
327
  return;
321
328
  }
322
329
  else if (this.isStopping) {
323
- await this.stopNotes();
330
+ await this.stopNotes(0, true);
324
331
  this.notePromises = [];
325
332
  resolve();
326
333
  this.isStopping = false;
@@ -328,7 +335,7 @@ export class MidyGMLite {
328
335
  return;
329
336
  }
330
337
  else if (this.isSeeking) {
331
- this.stopNotes();
338
+ this.stopNotes(0, true);
332
339
  this.startTime = this.audioContext.currentTime;
333
340
  queueIndex = this.getQueueIndex(this.resumeTime);
334
341
  offset = this.resumeTime - this.startTime;
@@ -409,21 +416,24 @@ export class MidyGMLite {
409
416
  }
410
417
  return { instruments, timeline };
411
418
  }
412
- stopNotes() {
419
+ async stopChannelNotes(channelNumber, velocity, stopPedal) {
413
420
  const now = this.audioContext.currentTime;
414
- const velocity = 0;
415
- const stopPedal = true;
416
- this.channels.forEach((channel, channelNumber) => {
417
- channel.scheduledNotes.forEach((scheduledNotes) => {
418
- scheduledNotes.forEach((scheduledNote) => {
419
- if (scheduledNote) {
420
- const promise = this.scheduleNoteRelease(channelNumber, scheduledNote.noteNumber, velocity, now, stopPedal);
421
- this.notePromises.push(promise);
422
- }
423
- });
421
+ const channel = this.channels[channelNumber];
422
+ channel.scheduledNotes.forEach((noteList) => {
423
+ noteList.forEach((note) => {
424
+ if (note) {
425
+ const promise = this.scheduleNoteRelease(channelNumber, note.noteNumber, velocity, now, stopPedal);
426
+ this.notePromises.push(promise);
427
+ }
424
428
  });
425
- channel.scheduledNotes.clear();
426
429
  });
430
+ channel.scheduledNotes.clear();
431
+ await Promise.all(this.notePromises);
432
+ }
433
+ stopNotes(velocity, stopPedal) {
434
+ for (let i = 0; i < this.channels.length; i++) {
435
+ this.stopChannelNotes(i, velocity, stopPedal);
436
+ }
427
437
  return Promise.all(this.notePromises);
428
438
  }
429
439
  async start() {
@@ -491,10 +501,6 @@ export class MidyGMLite {
491
501
  }
492
502
  return noteList[0];
493
503
  }
494
- connectEffects(channel, gainNode) {
495
- gainNode.connect(channel.merger);
496
- merger.connect(this.masterGain);
497
- }
498
504
  cbToRatio(cb) {
499
505
  return Math.pow(10, cb / 200);
500
506
  }
@@ -508,42 +514,56 @@ export class MidyGMLite {
508
514
  return instrumentKey.playbackRate(noteNumber) *
509
515
  Math.pow(2, semitoneOffset / 12);
510
516
  }
511
- setVolumeEnvelope(channel, note) {
512
- const { instrumentKey, startTime, velocity } = note;
513
- note.gainNode = new GainNode(this.audioContext, { gain: 0 });
514
- let volume = (velocity / 127) * channel.volume * channel.expression;
515
- if (volume === 0)
516
- volume = 1e-6; // exponentialRampToValueAtTime() requires a non-zero value
517
- const attackVolume = this.cbToRatio(-instrumentKey.initialAttenuation) *
518
- volume;
517
+ setVolumeEnvelope(note) {
518
+ const { instrumentKey, startTime } = note;
519
+ note.volumeNode = new GainNode(this.audioContext, { gain: 0 });
520
+ const attackVolume = this.cbToRatio(-instrumentKey.initialAttenuation);
519
521
  const sustainVolume = attackVolume * (1 - instrumentKey.volSustain);
520
522
  const volDelay = startTime + instrumentKey.volDelay;
521
523
  const volAttack = volDelay + instrumentKey.volAttack;
522
524
  const volHold = volAttack + instrumentKey.volHold;
523
525
  const volDecay = volHold + instrumentKey.volDecay;
524
- note.gainNode.gain
526
+ note.volumeNode.gain
525
527
  .setValueAtTime(1e-6, volDelay) // exponentialRampToValueAtTime() requires a non-zero value
526
528
  .exponentialRampToValueAtTime(attackVolume, volAttack)
527
529
  .setValueAtTime(attackVolume, volHold)
528
530
  .linearRampToValueAtTime(sustainVolume, volDecay);
529
531
  }
530
- setFilterEnvelope(channel, note) {
531
- const { instrumentKey, startTime, noteNumber } = note;
532
+ setPitch(note, semitoneOffset) {
533
+ const { instrumentKey, noteNumber, startTime } = note;
534
+ const modEnvToPitch = instrumentKey.modEnvToPitch / 100;
535
+ note.bufferSource.playbackRate.value = this.calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset);
536
+ if (modEnvToPitch === 0)
537
+ return;
538
+ const basePitch = note.bufferSource.playbackRate.value;
539
+ const peekPitch = this.calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset + modEnvToPitch);
540
+ const modDelay = startTime + instrumentKey.modDelay;
541
+ const modAttack = modDelay + instrumentKey.modAttack;
542
+ const modHold = modAttack + instrumentKey.modHold;
543
+ const modDecay = modHold + instrumentKey.modDecay;
544
+ note.bufferSource.playbackRate.value
545
+ .setValueAtTime(basePitch, modDelay)
546
+ .exponentialRampToValueAtTime(peekPitch, modAttack)
547
+ .setValueAtTime(peekPitch, modHold)
548
+ .linearRampToValueAtTime(basePitch, modDecay);
549
+ }
550
+ setFilterNode(channel, note) {
551
+ const { instrumentKey, noteNumber, startTime } = note;
532
552
  const softPedalFactor = 1 -
533
553
  (0.1 + (noteNumber / 127) * 0.2) * channel.softPedal;
534
554
  const maxFreq = this.audioContext.sampleRate / 2;
535
555
  const baseFreq = this.centToHz(instrumentKey.initialFilterFc) *
536
556
  softPedalFactor;
537
557
  const peekFreq = this.centToHz(instrumentKey.initialFilterFc + instrumentKey.modEnvToFilterFc) * softPedalFactor;
538
- const sustainFreq = (baseFreq +
539
- (peekFreq - baseFreq) * (1 - instrumentKey.modSustain)) * softPedalFactor;
558
+ const sustainFreq = baseFreq +
559
+ (peekFreq - baseFreq) * (1 - instrumentKey.modSustain);
560
+ const adjustedBaseFreq = Math.min(maxFreq, baseFreq);
561
+ const adjustedPeekFreq = Math.min(maxFreq, peekFreq);
562
+ const adjustedSustainFreq = Math.min(maxFreq, sustainFreq);
540
563
  const modDelay = startTime + instrumentKey.modDelay;
541
564
  const modAttack = modDelay + instrumentKey.modAttack;
542
565
  const modHold = modAttack + instrumentKey.modHold;
543
566
  const modDecay = modHold + instrumentKey.modDecay;
544
- const adjustedBaseFreq = Math.min(maxFreq, baseFreq);
545
- const adjustedPeekFreq = Math.min(maxFreq, peekFreq);
546
- const adjustedSustainFreq = Math.min(maxFreq, sustainFreq);
547
567
  note.filterNode = new BiquadFilterNode(this.audioContext, {
548
568
  type: "lowpass",
549
569
  Q: instrumentKey.initialFilterQ / 10, // dB
@@ -554,35 +574,49 @@ export class MidyGMLite {
554
574
  .exponentialRampToValueAtTime(adjustedPeekFreq, modAttack)
555
575
  .setValueAtTime(adjustedPeekFreq, modHold)
556
576
  .linearRampToValueAtTime(adjustedSustainFreq, modDecay);
557
- note.bufferSource.detune.setValueAtTime(note.bufferSource.detune.value + instrumentKey.modEnvToPitch, modDelay);
558
577
  }
559
- startModulation(channel, note, time) {
578
+ startModulation(channel, note, startTime) {
560
579
  const { instrumentKey } = note;
561
- note.modLFOGain = new GainNode(this.audioContext, {
562
- gain: this.cbToRatio(instrumentKey.modLfoToVolume + channel.modulation),
563
- });
564
- note.modLFO = new OscillatorNode(this.audioContext, {
580
+ const { modLfoToPitch, modLfoToVolume } = instrumentKey;
581
+ note.modulationLFO = new OscillatorNode(this.audioContext, {
565
582
  frequency: this.centToHz(instrumentKey.freqModLFO),
566
583
  });
567
- note.modLFO.start(time);
568
- note.filterNode.frequency.setValueAtTime(note.filterNode.frequency.value + instrumentKey.modLfoToFilterFc, time);
569
- note.bufferSource.detune.setValueAtTime(note.bufferSource.detune.value + instrumentKey.modLfoToPitch, time);
570
- note.modLFO.connect(note.modLFOGain);
571
- note.modLFOGain.connect(note.bufferSource.detune);
584
+ note.filterDepth = new GainNode(this.audioContext, {
585
+ gain: instrumentKey.modLfoToFilterFc,
586
+ });
587
+ const modulationDepth = Math.abs(modLfoToPitch) + channel.modulationDepth;
588
+ const modulationDepthSign = (0 < modLfoToPitch) ? 1 : -1;
589
+ note.modulationDepth = new GainNode(this.audioContext, {
590
+ gain: modulationDepth * modulationDepthSign,
591
+ });
592
+ const volumeDepth = this.cbToRatio(Math.abs(modLfoToVolume)) - 1;
593
+ const volumeDepthSign = (0 < modLfoToVolume) ? 1 : -1;
594
+ note.volumeDepth = new GainNode(this.audioContext, {
595
+ gain: volumeDepth * volumeDepthSign,
596
+ });
597
+ note.modulationLFO.start(startTime + instrumentKey.delayModLFO);
598
+ note.modulationLFO.connect(note.filterDepth);
599
+ note.filterDepth.connect(note.filterNode.frequency);
600
+ note.modulationLFO.connect(note.modulationDepth);
601
+ note.modulationDepth.connect(note.bufferSource.detune);
602
+ note.modulationLFO.connect(note.volumeDepth);
603
+ note.volumeDepth.connect(note.volumeNode.gain);
572
604
  }
573
605
  async createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3) {
574
606
  const semitoneOffset = this.calcSemitoneOffset(channel);
575
607
  const note = new Note(noteNumber, velocity, startTime, instrumentKey);
576
608
  note.bufferSource = await this.createNoteBufferNode(instrumentKey, isSF3);
577
- note.bufferSource.playbackRate.value = this.calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset);
578
- this.setVolumeEnvelope(channel, note);
579
- this.setFilterEnvelope(channel, note);
580
- if (channel.modulation > 0) {
581
- const delayModLFO = startTime + instrumentKey.delayModLFO;
582
- this.startModulation(channel, note, delayModLFO);
609
+ this.setFilterNode(channel, note);
610
+ this.setVolumeEnvelope(note);
611
+ if (0 < channel.modulationDepth) {
612
+ this.setPitch(note, semitoneOffset);
613
+ this.startModulation(channel, note, startTime);
614
+ }
615
+ else {
616
+ note.bufferSource.playbackRate.value = this.calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset);
583
617
  }
584
618
  note.bufferSource.connect(note.filterNode);
585
- note.filterNode.connect(note.gainNode);
619
+ note.filterNode.connect(note.volumeNode);
586
620
  note.bufferSource.start(startTime, instrumentKey.start / instrumentKey.sampleRate);
587
621
  return note;
588
622
  }
@@ -598,7 +632,8 @@ export class MidyGMLite {
598
632
  if (!instrumentKey)
599
633
  return;
600
634
  const note = await this.createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3);
601
- this.connectEffects(channel, note.gainNode);
635
+ note.volumeNode.connect(channel.gainL);
636
+ note.volumeNode.connect(channel.gainR);
602
637
  const scheduledNotes = channel.scheduledNotes;
603
638
  if (scheduledNotes.has(noteNumber)) {
604
639
  scheduledNotes.get(noteNumber).push(note);
@@ -627,17 +662,14 @@ export class MidyGMLite {
627
662
  const velocityRate = (velocity + 127) / 127;
628
663
  const volEndTime = stopTime +
629
664
  note.instrumentKey.volRelease * velocityRate;
630
- note.gainNode.gain
665
+ note.volumeNode.gain
631
666
  .cancelScheduledValues(stopTime)
632
667
  .linearRampToValueAtTime(0, volEndTime);
633
- const maxFreq = this.audioContext.sampleRate / 2;
634
- const baseFreq = this.centToHz(note.instrumentKey.initialFilterFc);
635
- const adjustedBaseFreq = Math.min(maxFreq, baseFreq);
636
- const modEndTime = stopTime +
668
+ const modRelease = stopTime +
637
669
  note.instrumentKey.modRelease * velocityRate;
638
670
  note.filterNode.frequency
639
671
  .cancelScheduledValues(stopTime)
640
- .linearRampToValueAtTime(adjustedBaseFreq, modEndTime);
672
+ .linearRampToValueAtTime(0, modRelease);
641
673
  note.ending = true;
642
674
  this.scheduleTask(() => {
643
675
  note.bufferSource.loop = false;
@@ -646,15 +678,17 @@ export class MidyGMLite {
646
678
  note.bufferSource.onended = () => {
647
679
  scheduledNotes[i] = null;
648
680
  note.bufferSource.disconnect();
681
+ note.volumeNode.disconnect();
649
682
  note.filterNode.disconnect();
650
- note.gainNode.disconnect();
651
- if (note.modLFOGain)
652
- note.modLFOGain.disconnect();
653
- if (note.modLFO)
654
- note.modLFO.stop();
683
+ if (note.volumeDepth)
684
+ note.volumeDepth.disconnect();
685
+ if (note.modulationDepth)
686
+ note.modulationDepth.disconnect();
687
+ if (note.modulationLFO)
688
+ note.modulationLFO.stop();
655
689
  resolve();
656
690
  };
657
- bufferSource.stop(volEndTime);
691
+ note.bufferSource.stop(volEndTime);
658
692
  });
659
693
  }
660
694
  }
@@ -667,10 +701,10 @@ export class MidyGMLite {
667
701
  const channel = this.channels[channelNumber];
668
702
  const promises = [];
669
703
  channel.sustainPedal = false;
670
- channel.scheduledNotes.forEach((scheduledNotes) => {
671
- scheduledNotes.forEach((scheduledNote) => {
672
- if (scheduledNote) {
673
- const { noteNumber } = scheduledNote;
704
+ channel.scheduledNotes.forEach((noteList) => {
705
+ noteList.forEach((note) => {
706
+ if (note) {
707
+ const { noteNumber } = note;
674
708
  const promise = this.releaseNote(channelNumber, noteNumber, velocity);
675
709
  promises.push(promise);
676
710
  }
@@ -701,13 +735,13 @@ export class MidyGMLite {
701
735
  channel.program = program;
702
736
  }
703
737
  handlePitchBendMessage(channelNumber, lsb, msb) {
704
- const pitchBend = msb * 128 + lsb;
738
+ const pitchBend = msb * 128 + lsb - 8192;
705
739
  this.setPitchBend(channelNumber, pitchBend);
706
740
  }
707
741
  setPitchBend(channelNumber, pitchBend) {
708
742
  const channel = this.channels[channelNumber];
709
743
  const prevPitchBend = channel.pitchBend;
710
- channel.pitchBend = (pitchBend - 8192) / 8192;
744
+ channel.pitchBend = pitchBend / 8192;
711
745
  const detuneChange = (channel.pitchBend - prevPitchBend) *
712
746
  channel.pitchBendRange * 100;
713
747
  this.updateDetune(channel, detuneChange);
@@ -715,7 +749,7 @@ export class MidyGMLite {
715
749
  handleControlChange(channelNumber, controller, value) {
716
750
  switch (controller) {
717
751
  case 1:
718
- return this.setModulation(channelNumber, value);
752
+ return this.setModulationDepth(channelNumber, value);
719
753
  case 6:
720
754
  return this.dataEntryMSB(channelNumber, value);
721
755
  case 7:
@@ -746,18 +780,19 @@ export class MidyGMLite {
746
780
  const now = this.audioContext.currentTime;
747
781
  const activeNotes = this.getActiveNotes(channel, now);
748
782
  activeNotes.forEach((activeNote) => {
749
- if (activeNote.modLFO) {
750
- const { gainNode, instrumentKey } = activeNote;
751
- gainNode.gain.setValueAtTime(this.cbToRatio(instrumentKey.modLfoToVolume + channel.modulation), now);
783
+ if (activeNote.modulationDepth) {
784
+ activeNote.modulationDepth.gain.setValueAtTime(channel.modulationDepth, now);
752
785
  }
753
786
  else {
787
+ const semitoneOffset = this.calcSemitoneOffset(channel);
788
+ this.setPitch(activeNote, semitoneOffset);
754
789
  this.startModulation(channel, activeNote, now);
755
790
  }
756
791
  });
757
792
  }
758
- setModulation(channelNumber, modulation) {
793
+ setModulationDepth(channelNumber, modulation) {
759
794
  const channel = this.channels[channelNumber];
760
- channel.modulation = (modulation / 127) * channel.modulationDepthRange;
795
+ channel.modulationDepth = (modulation / 127) * channel.modulationDepthRange;
761
796
  this.updateModulation(channel);
762
797
  }
763
798
  setVolume(channelNumber, volume) {
@@ -851,37 +886,13 @@ export class MidyGMLite {
851
886
  this.updateDetune(channel, detuneChange);
852
887
  }
853
888
  allSoundOff(channelNumber) {
854
- const now = this.audioContext.currentTime;
855
- const channel = this.channels[channelNumber];
856
- const velocity = 0;
857
- const stopPedal = true;
858
- const promises = [];
859
- channel.scheduledNotes.forEach((noteList) => {
860
- const activeNote = this.getActiveNote(noteList, now);
861
- if (activeNote) {
862
- const notePromise = this.scheduleNoteRelease(channelNumber, noteNumber, velocity, now, stopPedal);
863
- promises.push(notePromise);
864
- }
865
- });
866
- return promises;
889
+ return this.stopChannelNotes(channelNumber, 0, true);
867
890
  }
868
891
  resetAllControllers(channelNumber) {
869
892
  Object.assign(this.channels[channelNumber], this.effectSettings);
870
893
  }
871
894
  allNotesOff(channelNumber) {
872
- const now = this.audioContext.currentTime;
873
- const channel = this.channels[channelNumber];
874
- const velocity = 0;
875
- const stopPedal = false;
876
- const promises = [];
877
- channel.scheduledNotes.forEach((noteList) => {
878
- const activeNote = this.getActiveNote(noteList, now);
879
- if (activeNote) {
880
- const notePromise = this.scheduleNoteRelease(channelNumber, noteNumber, velocity, now, stopPedal);
881
- promises.push(notePromise);
882
- }
883
- });
884
- return promises;
895
+ return this.stopChannelNotes(channelNumber, 0, false);
885
896
  }
886
897
  handleUniversalNonRealTimeExclusiveMessage(data) {
887
898
  switch (data[2]) {
@@ -974,7 +985,7 @@ Object.defineProperty(MidyGMLite, "channelSettings", {
974
985
  dataLSB: 0,
975
986
  program: 0,
976
987
  pitchBend: 0,
977
- modulationDepthRange: 0.5, // cb
988
+ modulationDepthRange: 50, // cent
978
989
  }
979
990
  });
980
991
  Object.defineProperty(MidyGMLite, "effectSettings", {
@@ -983,7 +994,7 @@ Object.defineProperty(MidyGMLite, "effectSettings", {
983
994
  writable: true,
984
995
  value: {
985
996
  expression: 1,
986
- modulation: 0,
997
+ modulationDepth: 0,
987
998
  sustainPedal: false,
988
999
  rpnMSB: 127,
989
1000
  rpnLSB: 127,