@marmooo/midy 0.0.4 → 0.0.5

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 (33) hide show
  1. package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/{soundfont-parser@0.0.1 → soundfont-parser@0.0.2}/+esm.d.ts +13 -6
  2. package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.2/+esm.d.ts.map +1 -0
  3. package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/{soundfont-parser@0.0.1 → soundfont-parser@0.0.2}/+esm.js +5 -5
  4. package/esm/midy-GM1.d.ts +24 -34
  5. package/esm/midy-GM1.d.ts.map +1 -1
  6. package/esm/midy-GM1.js +167 -106
  7. package/esm/midy-GM2.d.ts +123 -21
  8. package/esm/midy-GM2.d.ts.map +1 -1
  9. package/esm/midy-GM2.js +170 -116
  10. package/esm/midy-GMLite.d.ts +23 -35
  11. package/esm/midy-GMLite.d.ts.map +1 -1
  12. package/esm/midy-GMLite.js +156 -107
  13. package/esm/midy.d.ts +25 -23
  14. package/esm/midy.d.ts.map +1 -1
  15. package/esm/midy.js +191 -120
  16. package/package.json +1 -1
  17. package/script/deps/cdn.jsdelivr.net/npm/@marmooo/{soundfont-parser@0.0.1 → soundfont-parser@0.0.2}/+esm.d.ts +13 -6
  18. package/script/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.2/+esm.d.ts.map +1 -0
  19. package/script/deps/cdn.jsdelivr.net/npm/@marmooo/{soundfont-parser@0.0.1 → soundfont-parser@0.0.2}/+esm.js +5 -5
  20. package/script/midy-GM1.d.ts +24 -34
  21. package/script/midy-GM1.d.ts.map +1 -1
  22. package/script/midy-GM1.js +167 -106
  23. package/script/midy-GM2.d.ts +123 -21
  24. package/script/midy-GM2.d.ts.map +1 -1
  25. package/script/midy-GM2.js +170 -116
  26. package/script/midy-GMLite.d.ts +23 -35
  27. package/script/midy-GMLite.d.ts.map +1 -1
  28. package/script/midy-GMLite.js +156 -107
  29. package/script/midy.d.ts +25 -23
  30. package/script/midy.d.ts.map +1 -1
  31. package/script/midy.js +191 -120
  32. package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.1/+esm.d.ts.map +0 -1
  33. package/script/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.1/+esm.d.ts.map +0 -1
@@ -2,9 +2,6 @@ export class MidyGMLite {
2
2
  static channelSettings: {
3
3
  volume: number;
4
4
  pan: number;
5
- vibratoRate: number;
6
- vibratoDepth: number;
7
- vibratoDelay: number;
8
5
  bank: number;
9
6
  dataMSB: number;
10
7
  dataLSB: number;
@@ -42,12 +39,8 @@ export class MidyGMLite {
42
39
  masterGain: any;
43
40
  channels: {
44
41
  scheduledNotes: Map<any, any>;
45
- sostenutoNotes: Map<any, any>;
46
42
  gainNode: any;
47
43
  pannerNode: any;
48
- modulationEffect: {
49
- lfo: any;
50
- };
51
44
  expression: number;
52
45
  modulation: number;
53
46
  sustainPedal: boolean;
@@ -56,9 +49,6 @@ export class MidyGMLite {
56
49
  pitchBendRange: number;
57
50
  volume: number;
58
51
  pan: number;
59
- vibratoRate: number;
60
- vibratoDepth: number;
61
- vibratoDelay: number;
62
52
  bank: number;
63
53
  dataMSB: number;
64
54
  dataLSB: number;
@@ -73,18 +63,11 @@ export class MidyGMLite {
73
63
  setChannelAudioNodes(audioContext: any): {
74
64
  gainNode: any;
75
65
  pannerNode: any;
76
- modulationEffect: {
77
- lfo: any;
78
- };
79
66
  };
80
67
  createChannels(audioContext: any): {
81
68
  scheduledNotes: Map<any, any>;
82
- sostenutoNotes: Map<any, any>;
83
69
  gainNode: any;
84
70
  pannerNode: any;
85
- modulationEffect: {
86
- lfo: any;
87
- };
88
71
  expression: number;
89
72
  modulation: number;
90
73
  sustainPedal: boolean;
@@ -93,9 +76,6 @@ export class MidyGMLite {
93
76
  pitchBendRange: number;
94
77
  volume: number;
95
78
  pan: number;
96
- vibratoRate: number;
97
- vibratoDepth: number;
98
- vibratoDelay: number;
99
79
  bank: number;
100
80
  dataMSB: number;
101
81
  dataLSB: number;
@@ -103,8 +83,8 @@ export class MidyGMLite {
103
83
  pitchBend: number;
104
84
  modulationDepthRange: number;
105
85
  }[];
106
- createNoteBuffer(noteInfo: any, isSF3: any): Promise<any>;
107
- createNoteBufferNode(noteInfo: any, isSF3: any): Promise<any>;
86
+ createNoteBuffer(instrumentKey: any, isSF3: any): Promise<any>;
87
+ createNoteBufferNode(instrumentKey: any, isSF3: any): Promise<any>;
108
88
  convertToFloat32Array(uint8Array: any): Float32Array;
109
89
  scheduleTimelineEvents(t: any, offset: any, queueIndex: any): Promise<any>;
110
90
  getQueueIndex(second: any): number;
@@ -123,22 +103,17 @@ export class MidyGMLite {
123
103
  seekTo(second: any): void;
124
104
  calcTotalTime(): number;
125
105
  currentTime(): number;
126
- getActiveNotes(channel: any): Map<any, any>;
127
- getActiveChannelNotes(scheduledNotes: any): any;
128
- createModulationEffect(audioContext: any): {
129
- lfo: any;
130
- };
106
+ getActiveNotes(channel: any, time: any): Map<any, any>;
107
+ getActiveNote(noteList: any, time: any): any;
131
108
  connectNoteEffects(channel: any, gainNode: any): void;
132
109
  cbToRatio(cb: any): number;
133
110
  centToHz(cent: any): number;
134
- calcSemitoneOffset(channel: any): any;
135
- calcPlaybackRate(noteInfo: any, noteNumber: any, semitoneOffset: any): number;
136
- createNoteAudioChain(channel: any, noteInfo: any, noteNumber: any, velocity: any, startTime: any, isSF3: any): Promise<{
137
- bufferSource: any;
138
- gainNode: any;
139
- filterNode: any;
140
- lfoGain: any;
141
- }>;
111
+ calcSemitoneOffset(channel: any): number;
112
+ calcPlaybackRate(instrumentKey: any, noteNumber: any, semitoneOffset: any): number;
113
+ setVolumeEnvelope(channel: any, note: any): void;
114
+ setFilterEnvelope(channel: any, note: any): void;
115
+ startModulation(channel: any, note: any, time: any): void;
116
+ createNote(channel: any, instrumentKey: any, noteNumber: any, velocity: any, startTime: any, isSF3: any): Promise<Note>;
142
117
  scheduleNoteOn(channelNumber: any, noteNumber: any, velocity: any, startTime: any): Promise<void>;
143
118
  noteOn(channelNumber: any, noteNumber: any, velocity: any): Promise<void>;
144
119
  scheduleNoteRelease(channelNumber: any, noteNumber: any, velocity: any, stopTime: any, stopPedal?: boolean): Promise<any> | undefined;
@@ -170,4 +145,17 @@ export class MidyGMLite {
170
145
  handleSysEx(data: any): void;
171
146
  scheduleTask(callback: any, startTime: any): Promise<any>;
172
147
  }
148
+ declare class Note {
149
+ constructor(noteNumber: any, velocity: any, startTime: any, instrumentKey: any);
150
+ bufferSource: any;
151
+ gainNode: any;
152
+ filterNode: any;
153
+ modLFO: any;
154
+ modLFOGain: any;
155
+ noteNumber: any;
156
+ velocity: any;
157
+ startTime: any;
158
+ instrumentKey: any;
159
+ }
160
+ export {};
173
161
  //# sourceMappingURL=midy-GMLite.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"midy-GMLite.d.ts","sourceRoot":"","sources":["../src/midy-GMLite.js"],"names":[],"mappings":"AAMA;IAmBE;;;;;;;;;;;;MAYE;IAEF;;;;;;;MAOE;IAEF,+BAMC;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;IA0BhB,kBAAgC;IAChC,gBAA4C;IAE5C;;;;;;;;;;;;;;;;;;;;;;;;;QAAiD;IAInD,4BAMC;IAED,mCASC;IAED,gDAMC;IAED,sCASC;IAED;;;;;;MAgBC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;QAWC;IAED,0DAyBC;IAED,8DAUC;IAED,qDAOC;IAED,2EA+CC;IAED,mCAOC;IAED,0BA+CC;IAED,uDAEC;IAED,wDAEC;IAED;;;MAyEC;IAED,4BAsBC;IAED,uBAKC;IAED,aAGC;IAED,cAKC;IAED,wBAIC;IAED,0BAKC;IAED,wBAOC;IAED,sBAGC;IAED,4CASC;IAED,gDAKC;IAED;;MAOC;IAED,sDAEC;IAED,2BAEC;IAED,4BAEC;IAED,sCAEC;IAED,8EAEC;IAED;;;;;OA8EC;IAED,kGAuCC;IAED,0EAGC;IAED,sIA4CC;IAED,0FAGC;IAED,kEAeC;IAED,wFAiBC;IAED,4DAGC;IAED,qEAGC;IAED,0DAiBC;IAED,mFA+BC;IAED,yDAIC;IAED,iDAIC;IAED,2CAMC;IAED,yDAIC;IAED,sCAKC;IAED,sDAMC;IAED,gDAEC;IAED,gDAEC;IAED,+DAcC;IAED,uCAoBC;IAED,8CAEC;IAED,uCAoBC;IAED,4DAgBC;IAED,oBAQC;IAED,yDAaC;IAED,yCAGC;IAED,sCAQC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF"}
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;;;;;;;;;;;;;;;;;;QAAiD;IAInD,4BAMC;IAED,mCASC;IAED,gDAMC;IAED,sCASC;IAED;;;MAaC;IAED;;;;;;;;;;;;;;;;;;QAUC;IAED,+DAyBC;IAED,mEAWC;IAED,qDAOC;IAED,2EA+CC;IAED,mCAOC;IAED,0BA+CC;IAED,uDAEC;IAED,wDAEC;IAED;;;MAyEC;IAED,4BAsBC;IAED,uBAKC;IAED,aAGC;IAED,cAKC;IAED,wBAIC;IAED,0BAKC;IAED,wBAOC;IAED,sBAGC;IAED,uDASC;IAED,6CAQC;IAED,sDAEC;IAED,2BAEC;IAED,4BAEC;IAED,yCAEC;IAED,mFAGC;IAED,iDAmBC;IAED,iDAiCC;IAED,0DAmBC;IAED,wHA6BC;IAED,kGA6BC;IAED,0EAGC;IAED,sIAmDC;IAED,0FAGC;IAED,kEAeC;IAED,wFAiBC;IAED,4DAGC;IAED,qEAGC;IAED,0DAiBC;IAED,mFA+BC;IAED,yDAiBC;IAED,iDAIC;IAED,2CAMC;IAED,yDAIC;IAED,sCAKC;IAED,sDAMC;IAED,gDAEC;IAED,gDAEC;IAED,+DAcC;IAED,uCAoBC;IAED,8CAEC;IAED,uCAoBC;IAED,4DAgBC;IAED,oBAQC;IAED,yDAaC;IAED,yCAGC;IAED,sCAQC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF;AA59BD;IAOE,gFAKC;IAXD,kBAAa;IACb,cAAS;IACT,gBAAW;IACX,YAAO;IACP,gBAAW;IAGT,gBAA4B;IAC5B,cAAwB;IACxB,eAA0B;IAC1B,mBAAkC;CAErC"}
@@ -2,7 +2,45 @@
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.1/+esm.js");
5
+ const _esm_js_2 = require("./deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.2/+esm.js");
6
+ class Note {
7
+ constructor(noteNumber, velocity, startTime, instrumentKey) {
8
+ Object.defineProperty(this, "bufferSource", {
9
+ enumerable: true,
10
+ configurable: true,
11
+ writable: true,
12
+ value: void 0
13
+ });
14
+ Object.defineProperty(this, "gainNode", {
15
+ enumerable: true,
16
+ configurable: true,
17
+ writable: true,
18
+ value: void 0
19
+ });
20
+ Object.defineProperty(this, "filterNode", {
21
+ enumerable: true,
22
+ configurable: true,
23
+ writable: true,
24
+ value: void 0
25
+ });
26
+ Object.defineProperty(this, "modLFO", {
27
+ enumerable: true,
28
+ configurable: true,
29
+ writable: true,
30
+ value: void 0
31
+ });
32
+ Object.defineProperty(this, "modLFOGain", {
33
+ enumerable: true,
34
+ configurable: true,
35
+ writable: true,
36
+ value: void 0
37
+ });
38
+ this.noteNumber = noteNumber;
39
+ this.velocity = velocity;
40
+ this.startTime = startTime;
41
+ this.instrumentKey = instrumentKey;
42
+ }
43
+ }
6
44
  class MidyGMLite {
7
45
  constructor(audioContext) {
8
46
  Object.defineProperty(this, "ticksPerBeat", {
@@ -154,14 +192,11 @@ class MidyGMLite {
154
192
  const pannerNode = new StereoPannerNode(audioContext, {
155
193
  pan: MidyGMLite.channelSettings.pan,
156
194
  });
157
- const modulationEffect = this.createModulationEffect(audioContext);
158
- modulationEffect.lfo.start();
159
195
  pannerNode.connect(gainNode);
160
196
  gainNode.connect(this.masterGain);
161
197
  return {
162
198
  gainNode,
163
199
  pannerNode,
164
- modulationEffect,
165
200
  };
166
201
  }
167
202
  createChannels(audioContext) {
@@ -171,16 +206,15 @@ class MidyGMLite {
171
206
  ...MidyGMLite.effectSettings,
172
207
  ...this.setChannelAudioNodes(audioContext),
173
208
  scheduledNotes: new Map(),
174
- sostenutoNotes: new Map(),
175
209
  };
176
210
  });
177
211
  return channels;
178
212
  }
179
- async createNoteBuffer(noteInfo, isSF3) {
180
- const sampleEnd = noteInfo.sample.length + noteInfo.end;
213
+ async createNoteBuffer(instrumentKey, isSF3) {
214
+ const sampleEnd = instrumentKey.sample.length + instrumentKey.end;
181
215
  if (isSF3) {
182
- const sample = new Uint8Array(noteInfo.sample.length);
183
- sample.set(noteInfo.sample);
216
+ const sample = new Uint8Array(instrumentKey.sample.length);
217
+ sample.set(instrumentKey.sample);
184
218
  const audioBuffer = await this.audioContext.decodeAudioData(sample.buffer);
185
219
  for (let channel = 0; channel < audioBuffer.numberOfChannels; channel++) {
186
220
  const channelData = audioBuffer.getChannelData(channel);
@@ -189,26 +223,27 @@ class MidyGMLite {
189
223
  return audioBuffer;
190
224
  }
191
225
  else {
192
- const sample = noteInfo.sample.subarray(0, sampleEnd);
226
+ const sample = instrumentKey.sample.subarray(0, sampleEnd);
193
227
  const floatSample = this.convertToFloat32Array(sample);
194
228
  const audioBuffer = new AudioBuffer({
195
229
  numberOfChannels: 1,
196
230
  length: sample.length,
197
- sampleRate: noteInfo.sampleRate,
231
+ sampleRate: instrumentKey.sampleRate,
198
232
  });
199
233
  const channelData = audioBuffer.getChannelData(0);
200
234
  channelData.set(floatSample);
201
235
  return audioBuffer;
202
236
  }
203
237
  }
204
- async createNoteBufferNode(noteInfo, isSF3) {
238
+ async createNoteBufferNode(instrumentKey, isSF3) {
205
239
  const bufferSource = new AudioBufferSourceNode(this.audioContext);
206
- const audioBuffer = await this.createNoteBuffer(noteInfo, isSF3);
240
+ const audioBuffer = await this.createNoteBuffer(instrumentKey, isSF3);
207
241
  bufferSource.buffer = audioBuffer;
208
- bufferSource.loop = noteInfo.sampleModes % 2 !== 0;
242
+ bufferSource.loop = instrumentKey.sampleModes % 2 !== 0;
209
243
  if (bufferSource.loop) {
210
- bufferSource.loopStart = noteInfo.loopStart / noteInfo.sampleRate;
211
- bufferSource.loopEnd = noteInfo.loopEnd / noteInfo.sampleRate;
244
+ bufferSource.loopStart = instrumentKey.loopStart /
245
+ instrumentKey.sampleRate;
246
+ bufferSource.loopEnd = instrumentKey.loopEnd / instrumentKey.sampleRate;
212
247
  }
213
248
  return bufferSource;
214
249
  }
@@ -450,30 +485,26 @@ class MidyGMLite {
450
485
  const now = this.audioContext.currentTime;
451
486
  return this.resumeTime + now - this.startTime - this.startDelay;
452
487
  }
453
- getActiveNotes(channel) {
488
+ getActiveNotes(channel, time) {
454
489
  const activeNotes = new Map();
455
- channel.scheduledNotes.forEach((scheduledNotes) => {
456
- const activeNote = this.getActiveChannelNotes(scheduledNotes);
490
+ channel.scheduledNotes.forEach((noteList) => {
491
+ const activeNote = this.getActiveNote(noteList, time);
457
492
  if (activeNote) {
458
493
  activeNotes.set(activeNote.noteNumber, activeNote);
459
494
  }
460
495
  });
461
496
  return activeNotes;
462
497
  }
463
- getActiveChannelNotes(scheduledNotes) {
464
- for (let i = 0; i < scheduledNotes; i++) {
465
- const scheduledNote = scheduledNotes[i];
466
- if (scheduledNote)
467
- return scheduledNote;
498
+ getActiveNote(noteList, time) {
499
+ for (let i = noteList.length - 1; i >= 0; i--) {
500
+ const note = noteList[i];
501
+ if (!note)
502
+ return;
503
+ if (time < note.startTime)
504
+ continue;
505
+ return (note.ending) ? null : note;
468
506
  }
469
- }
470
- createModulationEffect(audioContext) {
471
- const lfo = new OscillatorNode(audioContext, {
472
- frequency: 5,
473
- });
474
- return {
475
- lfo,
476
- };
507
+ return noteList[0];
477
508
  }
478
509
  connectNoteEffects(channel, gainNode) {
479
510
  gainNode.connect(channel.pannerNode);
@@ -485,73 +516,91 @@ class MidyGMLite {
485
516
  return 8.176 * Math.pow(2, cent / 1200);
486
517
  }
487
518
  calcSemitoneOffset(channel) {
488
- return channel.pitchBend * channel.pitchBendRange + tuning;
519
+ return channel.pitchBend * channel.pitchBendRange;
489
520
  }
490
- calcPlaybackRate(noteInfo, noteNumber, semitoneOffset) {
491
- return noteInfo.playbackRate(noteNumber) * Math.pow(2, semitoneOffset / 12);
521
+ calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset) {
522
+ return instrumentKey.playbackRate(noteNumber) *
523
+ Math.pow(2, semitoneOffset / 12);
492
524
  }
493
- async createNoteAudioChain(channel, noteInfo, noteNumber, velocity, startTime, isSF3) {
494
- const bufferSource = await this.createNoteBufferNode(noteInfo, isSF3);
495
- const semitoneOffset = this.calcSemitoneOffset(channel);
496
- bufferSource.playbackRate.value = this.calcPlaybackRate(noteInfo, noteNumber, semitoneOffset);
497
- // volume envelope
498
- const gainNode = new GainNode(this.audioContext, {
525
+ setVolumeEnvelope(channel, note) {
526
+ const { instrumentKey, startTime, velocity } = note;
527
+ note.gainNode = new GainNode(this.audioContext, {
499
528
  gain: 0,
500
529
  });
501
530
  let volume = (velocity / 127) * channel.volume * channel.expression;
502
531
  if (volume === 0)
503
532
  volume = 1e-6; // exponentialRampToValueAtTime() requires a non-zero value
504
- const attackVolume = this.cbToRatio(-noteInfo.initialAttenuation) * volume;
505
- const sustainVolume = attackVolume * (1 - noteInfo.volSustain);
506
- const volDelay = startTime + noteInfo.volDelay;
507
- const volAttack = volDelay + noteInfo.volAttack;
508
- const volHold = volAttack + noteInfo.volHold;
509
- const volDecay = volHold + noteInfo.volDecay;
510
- gainNode.gain
533
+ const attackVolume = this.cbToRatio(-instrumentKey.initialAttenuation) *
534
+ volume;
535
+ const sustainVolume = attackVolume * (1 - instrumentKey.volSustain);
536
+ const volDelay = startTime + instrumentKey.volDelay;
537
+ const volAttack = volDelay + instrumentKey.volAttack;
538
+ const volHold = volAttack + instrumentKey.volHold;
539
+ const volDecay = volHold + instrumentKey.volDecay;
540
+ note.gainNode.gain
511
541
  .setValueAtTime(1e-6, volDelay) // exponentialRampToValueAtTime() requires a non-zero value
512
542
  .exponentialRampToValueAtTime(attackVolume, volAttack)
513
543
  .setValueAtTime(attackVolume, volHold)
514
544
  .linearRampToValueAtTime(sustainVolume, volDecay);
515
- // filter envelope
545
+ }
546
+ setFilterEnvelope(channel, note) {
547
+ const { instrumentKey, startTime, noteNumber } = note;
548
+ const softPedalFactor = 1 -
549
+ (0.1 + (noteNumber / 127) * 0.2) * channel.softPedal;
516
550
  const maxFreq = this.audioContext.sampleRate / 2;
517
- const baseFreq = this.centToHz(noteInfo.initialFilterFc);
518
- const peekFreq = this.centToHz(noteInfo.initialFilterFc + noteInfo.modEnvToFilterFc);
519
- const sustainFreq = baseFreq +
520
- (peekFreq - baseFreq) * (1 - noteInfo.modSustain);
551
+ const baseFreq = this.centToHz(instrumentKey.initialFilterFc) *
552
+ softPedalFactor;
553
+ const peekFreq = this.centToHz(instrumentKey.initialFilterFc + instrumentKey.modEnvToFilterFc) * softPedalFactor;
554
+ const sustainFreq = (baseFreq +
555
+ (peekFreq - baseFreq) * (1 - instrumentKey.modSustain)) * softPedalFactor;
556
+ const modDelay = startTime + instrumentKey.modDelay;
557
+ const modAttack = modDelay + instrumentKey.modAttack;
558
+ const modHold = modAttack + instrumentKey.modHold;
559
+ const modDecay = modHold + instrumentKey.modDecay;
521
560
  const adjustedBaseFreq = Math.min(maxFreq, baseFreq);
522
561
  const adjustedPeekFreq = Math.min(maxFreq, peekFreq);
523
562
  const adjustedSustainFreq = Math.min(maxFreq, sustainFreq);
524
- const filterNode = new BiquadFilterNode(this.audioContext, {
563
+ note.filterNode = new BiquadFilterNode(this.audioContext, {
525
564
  type: "lowpass",
526
- Q: noteInfo.initialFilterQ / 10, // dB
565
+ Q: instrumentKey.initialFilterQ / 10, // dB
527
566
  frequency: adjustedBaseFreq,
528
567
  });
529
- const modDelay = startTime + noteInfo.modDelay;
530
- const modAttack = modDelay + noteInfo.modAttack;
531
- const modHold = modAttack + noteInfo.modHold;
532
- const modDecay = modHold + noteInfo.modDecay;
533
- filterNode.frequency
568
+ note.filterNode.frequency
534
569
  .setValueAtTime(adjustedBaseFreq, modDelay)
535
570
  .exponentialRampToValueAtTime(adjustedPeekFreq, modAttack)
536
571
  .setValueAtTime(adjustedPeekFreq, modHold)
537
572
  .linearRampToValueAtTime(adjustedSustainFreq, modDecay);
538
- let lfoGain;
573
+ note.bufferSource.detune.setValueAtTime(note.bufferSource.detune.value + instrumentKey.modEnvToPitch, modDelay);
574
+ }
575
+ startModulation(channel, note, time) {
576
+ const { instrumentKey } = note;
577
+ note.modLFOGain = new GainNode(this.audioContext, {
578
+ gain: this.cbToRatio(instrumentKey.modLfoToVolume) * channel.modulation,
579
+ });
580
+ note.modLFO = new OscillatorNode(this.audioContext, {
581
+ frequency: this.centToHz(instrumentKey.freqModLFO),
582
+ });
583
+ note.modLFO.start(time);
584
+ note.filterNode.frequency.setValueAtTime(note.filterNode.frequency.value + instrumentKey.modLfoToFilterFc, time);
585
+ note.bufferSource.detune.setValueAtTime(note.bufferSource.detune.value + instrumentKey.modLfoToPitch, time);
586
+ note.modLFO.connect(note.modLFOGain);
587
+ note.modLFOGain.connect(note.bufferSource.detune);
588
+ }
589
+ async createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3) {
590
+ const semitoneOffset = this.calcSemitoneOffset(channel);
591
+ const note = new Note(noteNumber, velocity, startTime, instrumentKey);
592
+ note.bufferSource = await this.createNoteBufferNode(instrumentKey, isSF3);
593
+ note.bufferSource.playbackRate.value = this.calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset);
594
+ this.setVolumeEnvelope(channel, note);
595
+ this.setFilterEnvelope(channel, note);
539
596
  if (channel.modulation > 0) {
540
- const vibratoDelay = startTime + channel.vibratoDelay;
541
- const vibratoAttack = vibratoDelay + 0.1;
542
- lfoGain = new GainNode(this.audioContext, {
543
- gain: 0,
544
- });
545
- lfoGain.gain
546
- .setValueAtTime(1e-6, vibratoDelay) // exponentialRampToValueAtTime() requires a non-zero value
547
- .exponentialRampToValueAtTime(channel.modulation, vibratoAttack);
548
- channel.modulationEffect.lfo.connect(lfoGain);
549
- lfoGain.connect(bufferSource.detune);
597
+ const delayModLFO = startTime + instrumentKey.delayModLFO;
598
+ this.startModulation(channel, note, delayModLFO);
550
599
  }
551
- bufferSource.connect(filterNode);
552
- filterNode.connect(gainNode);
553
- bufferSource.start(startTime, noteInfo.start / noteInfo.sampleRate);
554
- return { bufferSource, gainNode, filterNode, lfoGain };
600
+ note.bufferSource.connect(note.filterNode);
601
+ note.filterNode.connect(note.gainNode);
602
+ note.bufferSource.start(startTime, instrumentKey.start / instrumentKey.sampleRate);
603
+ return note;
555
604
  }
556
605
  async scheduleNoteOn(channelNumber, noteNumber, velocity, startTime) {
557
606
  const channel = this.channels[channelNumber];
@@ -561,27 +610,17 @@ class MidyGMLite {
561
610
  return;
562
611
  const soundFont = this.soundFonts[soundFontIndex];
563
612
  const isSF3 = soundFont.parsed.info.version.major === 3;
564
- const noteInfo = soundFont.getInstrumentKey(bankNumber, channel.program, noteNumber);
565
- if (!noteInfo)
613
+ const instrumentKey = soundFont.getInstrumentKey(bankNumber, channel.program, noteNumber);
614
+ if (!instrumentKey)
566
615
  return;
567
- const { bufferSource, gainNode, filterNode, lfoGain } = await this
568
- .createNoteAudioChain(channel, noteInfo, noteNumber, velocity, startTime, isSF3);
569
- this.connectNoteEffects(channel, gainNode);
616
+ const note = await this.createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3);
617
+ this.connectNoteEffects(channel, note.gainNode);
570
618
  const scheduledNotes = channel.scheduledNotes;
571
- const scheduledNote = {
572
- bufferSource,
573
- filterNode,
574
- gainNode,
575
- lfoGain,
576
- noteInfo,
577
- noteNumber,
578
- startTime,
579
- };
580
619
  if (scheduledNotes.has(noteNumber)) {
581
- scheduledNotes.get(noteNumber).push(scheduledNote);
620
+ scheduledNotes.get(noteNumber).push(note);
582
621
  }
583
622
  else {
584
- scheduledNotes.set(noteNumber, [scheduledNote]);
623
+ scheduledNotes.set(noteNumber, [note]);
585
624
  }
586
625
  }
587
626
  noteOn(channelNumber, noteNumber, velocity) {
@@ -601,15 +640,15 @@ class MidyGMLite {
601
640
  continue;
602
641
  if (targetNote.ending)
603
642
  continue;
604
- const { bufferSource, filterNode, gainNode, lfoGain, noteInfo } = targetNote;
643
+ const { bufferSource, filterNode, gainNode, modLFO, modLFOGain, instrumentKey, } = targetNote;
605
644
  const velocityRate = (velocity + 127) / 127;
606
- const volEndTime = stopTime + noteInfo.volRelease * velocityRate;
645
+ const volEndTime = stopTime + instrumentKey.volRelease * velocityRate;
607
646
  gainNode.gain.cancelScheduledValues(stopTime);
608
647
  gainNode.gain.linearRampToValueAtTime(0, volEndTime);
609
648
  const maxFreq = this.audioContext.sampleRate / 2;
610
- const baseFreq = this.centToHz(noteInfo.initialFilterFc);
649
+ const baseFreq = this.centToHz(instrumentKey.initialFilterFc);
611
650
  const adjustedBaseFreq = Math.min(maxFreq, baseFreq);
612
- const modEndTime = stopTime + noteInfo.modRelease * velocityRate;
651
+ const modEndTime = stopTime + instrumentKey.modRelease * velocityRate;
613
652
  filterNode.frequency
614
653
  .cancelScheduledValues(stopTime)
615
654
  .linearRampToValueAtTime(adjustedBaseFreq, modEndTime);
@@ -623,8 +662,10 @@ class MidyGMLite {
623
662
  bufferSource.disconnect(0);
624
663
  filterNode.disconnect(0);
625
664
  gainNode.disconnect(0);
626
- if (lfoGain)
627
- lfoGain.disconnect(0);
665
+ if (modLFOGain)
666
+ modLFOGain.disconnect(0);
667
+ if (modLFO)
668
+ modLFO.stop();
628
669
  resolve();
629
670
  };
630
671
  bufferSource.stop(volEndTime);
@@ -682,10 +723,10 @@ class MidyGMLite {
682
723
  const channel = this.channels[channelNumber];
683
724
  channel.pitchBend = (pitchBend - 8192) / 8192;
684
725
  const semitoneOffset = this.calcSemitoneOffset(channel);
685
- const activeNotes = this.getActiveNotes(channel);
726
+ const activeNotes = this.getActiveNotes(channel, now);
686
727
  activeNotes.forEach((activeNote) => {
687
- const { bufferSource, noteInfo, noteNumber } = activeNote;
688
- const playbackRate = calcPlaybackRate(noteInfo, noteNumber, semitoneOffset);
728
+ const { bufferSource, instrumentKey, noteNumber } = activeNote;
729
+ const playbackRate = calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset);
689
730
  bufferSource.playbackRate
690
731
  .cancelScheduledValues(now)
691
732
  .setValueAtTime(playbackRate * pressure, now);
@@ -722,9 +763,20 @@ class MidyGMLite {
722
763
  }
723
764
  }
724
765
  setModulation(channelNumber, modulation) {
766
+ const now = this.audioContext.currentTime;
725
767
  const channel = this.channels[channelNumber];
726
768
  channel.modulation = (modulation / 127) *
727
769
  (channel.modulationDepthRange * 100);
770
+ const activeNotes = this.getActiveNotes(channel, now);
771
+ activeNotes.forEach((activeNote) => {
772
+ if (activeNote.modLFO) {
773
+ activeNote.gainNode.gain.setValueAtTime(this.cbToRatio(activeNote.instrumentKey.modLfoToVolume) *
774
+ channel.modulation, now);
775
+ }
776
+ else {
777
+ this.startModulation(channel, activeNote, now);
778
+ }
779
+ });
728
780
  }
729
781
  setVolume(channelNumber, volume) {
730
782
  const channel = this.channels[channelNumber];
@@ -781,8 +833,8 @@ class MidyGMLite {
781
833
  const velocity = 0;
782
834
  const stopPedal = true;
783
835
  const promises = [];
784
- channel.scheduledNotes.forEach((scheduledNotes) => {
785
- const activeNote = this.getActiveChannelNotes(scheduledNotes);
836
+ channel.scheduledNotes.forEach((noteList) => {
837
+ const activeNote = this.getActiveNote(noteList, now);
786
838
  if (activeNote) {
787
839
  const notePromise = this.scheduleNoteRelease(channelNumber, noteNumber, velocity, now, stopPedal);
788
840
  promises.push(notePromise);
@@ -799,8 +851,8 @@ class MidyGMLite {
799
851
  const velocity = 0;
800
852
  const stopPedal = false;
801
853
  const promises = [];
802
- channel.scheduledNotes.forEach((scheduledNotes) => {
803
- const activeNote = this.getActiveChannelNotes(scheduledNotes);
854
+ channel.scheduledNotes.forEach((noteList) => {
855
+ const activeNote = this.getActiveNote(noteList, now);
804
856
  if (activeNote) {
805
857
  const notePromise = this.scheduleNoteRelease(channelNumber, noteNumber, velocity, now, stopPedal);
806
858
  promises.push(notePromise);
@@ -895,9 +947,6 @@ Object.defineProperty(MidyGMLite, "channelSettings", {
895
947
  value: {
896
948
  volume: 100 / 127,
897
949
  pan: 0,
898
- vibratoRate: 5,
899
- vibratoDepth: 0.5,
900
- vibratoDelay: 2.5,
901
950
  bank: 0,
902
951
  dataMSB: 0,
903
952
  dataLSB: 0,
package/script/midy.d.ts CHANGED
@@ -86,9 +86,6 @@ export class Midy {
86
86
  };
87
87
  gainNode: any;
88
88
  pannerNode: any;
89
- modulationEffect: {
90
- lfo: any;
91
- };
92
89
  reverbEffect: {
93
90
  convolverNode: any;
94
91
  dryGain: any;
@@ -136,9 +133,6 @@ export class Midy {
136
133
  setChannelAudioNodes(audioContext: any): {
137
134
  gainNode: any;
138
135
  pannerNode: any;
139
- modulationEffect: {
140
- lfo: any;
141
- };
142
136
  reverbEffect: {
143
137
  convolverNode: any;
144
138
  dryGain: any;
@@ -172,9 +166,6 @@ export class Midy {
172
166
  };
173
167
  gainNode: any;
174
168
  pannerNode: any;
175
- modulationEffect: {
176
- lfo: any;
177
- };
178
169
  reverbEffect: {
179
170
  convolverNode: any;
180
171
  dryGain: any;
@@ -215,8 +206,8 @@ export class Midy {
215
206
  coarseTuning: number;
216
207
  modulationDepthRange: number;
217
208
  }[];
218
- createNoteBuffer(noteInfo: any, isSF3: any): Promise<any>;
219
- createNoteBufferNode(noteInfo: any, isSF3: any): Promise<any>;
209
+ createNoteBuffer(instrumentKey: any, isSF3: any): Promise<any>;
210
+ createNoteBufferNode(instrumentKey: any, isSF3: any): Promise<any>;
220
211
  convertToFloat32Array(uint8Array: any): Float32Array;
221
212
  scheduleTimelineEvents(t: any, offset: any, queueIndex: any): Promise<any>;
222
213
  getQueueIndex(second: any): number;
@@ -235,11 +226,8 @@ export class Midy {
235
226
  seekTo(second: any): void;
236
227
  calcTotalTime(): number;
237
228
  currentTime(): number;
238
- getActiveNotes(channel: any): Map<any, any>;
239
- getActiveChannelNotes(scheduledNotes: any): any;
240
- createModulationEffect(audioContext: any): {
241
- lfo: any;
242
- };
229
+ getActiveNotes(channel: any, time: any): Map<any, any>;
230
+ getActiveNote(noteList: any, time: any): any;
243
231
  createReverbEffect(audioContext: any, options?: {}): {
244
232
  convolverNode: any;
245
233
  dryGain: any;
@@ -255,13 +243,12 @@ export class Midy {
255
243
  cbToRatio(cb: any): number;
256
244
  centToHz(cent: any): number;
257
245
  calcSemitoneOffset(channel: any): any;
258
- calcPlaybackRate(noteInfo: any, noteNumber: any, semitoneOffset: any): number;
259
- createNoteAudioChain(channel: any, noteInfo: any, noteNumber: any, velocity: any, startTime: any, isSF3: any): Promise<{
260
- bufferSource: any;
261
- gainNode: any;
262
- filterNode: any;
263
- lfoGain: any;
264
- }>;
246
+ calcPlaybackRate(instrumentKey: any, noteNumber: any, semitoneOffset: any): number;
247
+ setVolumeEnvelope(channel: any, note: any): void;
248
+ setFilterEnvelope(channel: any, note: any): void;
249
+ startModulation(channel: any, note: any, time: any): void;
250
+ startVibrato(channel: any, note: any, time: any): void;
251
+ createNote(channel: any, instrumentKey: any, noteNumber: any, velocity: any, startTime: any, isSF3: any): Promise<Note>;
265
252
  calcBank(channel: any, channelNumber: any): any;
266
253
  scheduleNoteOn(channelNumber: any, noteNumber: any, velocity: any, startTime: any): Promise<void>;
267
254
  noteOn(channelNumber: any, noteNumber: any, velocity: any): Promise<void>;
@@ -319,4 +306,19 @@ export class Midy {
319
306
  handleSysEx(data: any): void;
320
307
  scheduleTask(callback: any, startTime: any): Promise<any>;
321
308
  }
309
+ declare class Note {
310
+ constructor(noteNumber: any, velocity: any, startTime: any, instrumentKey: any);
311
+ bufferSource: any;
312
+ gainNode: any;
313
+ filterNode: any;
314
+ modLFO: any;
315
+ modLFOGain: any;
316
+ vibLFO: any;
317
+ vibLFOGain: any;
318
+ noteNumber: any;
319
+ velocity: any;
320
+ startTime: any;
321
+ instrumentKey: any;
322
+ }
323
+ export {};
322
324
  //# sourceMappingURL=midy.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"midy.d.ts","sourceRoot":"","sources":["../src/midy.js"],"names":[],"mappings":"AAMA;IAwBE;;;;;;;;;;;;;;;;;;;;MAoBE;IAEF;;;;;;;;;;;MAWE;IAEF;;;;;;;MAOE;IAEF,+BAMC;IAzED,qBAAmB;IACnB,kBAAc;IACd,qBAAmB;IACnB,yBAAqB;IACrB,2BAAuB;IACvB,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;IA+ChB,kBAAgC;IAChC,gBAA4C;IAE5C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAAiD;IAInD,4BAMC;IAED,mCASC;IAED,gDAMC;IAED,sCASC;IAED;;;;;;;;;;;;;;;;;MAuBC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAiBC;IAED,0DAyBC;IAED,8DAUC;IAED,qDAOC;IAED,2EAyDC;IAED,mCAOC;IAED,0BA+CC;IAED,uDAEC;IAED,wDAEC;IAED;;;MA2GC;IAED,4BAsBC;IAED,uBAKC;IAED,aAGC;IAED,cAKC;IAED,wBAIC;IAED,0BAKC;IAED,wBAOC;IAED,sBAGC;IAED,4CASC;IAED,gDAKC;IAED;;MAOC;IAED;;;;MAoCC;IAED;;;;;MA2CC;IAED,sDA2BC;IAED,2BAEC;IAED,4BAEC;IAED,sCAKC;IAED,8EAEC;IAED;;;;;OAqFC;IAED,gDAQC;IAED,kGAgDC;IAED,0EAGC;IAED,sIA6CC;IAED,0FAGC;IAED,kEAeC;IAED,oEAYC;IAED,gEAqBC;IAED,sFAcC;IAED,4DAIC;IAED,+DAcC;IAED,qEAGC;IAED,0DAiBC;IAED,0EAkEC;IAED,+CAEC;IAED,yDAIC;IAED,iEAEC;IAED,iDAIC;IAED,2CAMC;IAED,yDAIC;IAED,+CAEC;IAED,sCAKC;IAED,sDAMC;IAED,oDAEC;IAED,iDASC;IAED,iDAIC;IAED,wDAUC;IAED,uDAGC;IAED,2DAOC;IAED,6DAOC;IAED,6DASC;IAED,4CAkBC;IAED,4CAkBC;IAED,gDAEC;IAED,gDAEC;IAGD,+DAuBC;IAED,uCAoBC;IAED,8CAEC;IAED,uCAoBC;IAED,gBAEC;IAED,eAEC;IAED,eAEC;IAED,eAEC;IAED,4DAmBC;IAED,oBAQC;IAED,oBAQC;IAED,yDAgDC;IAED,yCAGC;IAED,sCAQC;IAED,6CAGC;IAED,8CAMC;IAED,+CAGC;IAED,kDAMC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF"}
1
+ {"version":3,"file":"midy.d.ts","sourceRoot":"","sources":["../src/midy.js"],"names":[],"mappings":"AAuBA;IAwBE;;;;;;;;;;;;;;;;;;;;MAoBE;IAEF;;;;;;;;;;;MAWE;IAEF;;;;;;;MAOE;IAEF,+BAMC;IAzED,qBAAmB;IACnB,kBAAc;IACd,qBAAmB;IACnB,yBAAqB;IACrB,2BAAuB;IACvB,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;IA+ChB,kBAAgC;IAChC,gBAA4C;IAE5C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAAiD;IAInD,4BAMC;IAED,mCASC;IAED,gDAMC;IAED,sCASC;IAED;;;;;;;;;;;;;;MAoBC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAiBC;IAED,+DAyBC;IAED,mEAWC;IAED,qDAOC;IAED,2EAyDC;IAED,mCAOC;IAED,0BA+CC;IAED,uDAEC;IAED,wDAEC;IAED;;;MA2GC;IAED,4BAsBC;IAED,uBAKC;IAED,aAGC;IAED,cAKC;IAED,wBAIC;IAED,0BAKC;IAED,wBAOC;IAED,sBAGC;IAED,uDASC;IAED,6CAQC;IAED;;;;MAoCC;IAED;;;;;MA2CC;IAED,sDA2BC;IAED,2BAEC;IAED,4BAEC;IAED,sCAKC;IAED,mFAGC;IAED,iDAmBC;IAED,iDAiCC;IAED,0DAmBC;IAED,uDAcC;IAED,wHAqCC;IAED,gDAQC;IAED,kGAgCC;IAED,0EAGC;IAED,sIAwDC;IAED,0FAGC;IAED,kEAeC;IAED,oEAYC;IAED,gEAqBC;IAED,sFAcC;IAED,4DAIC;IAED,+DAcC;IAED,qEAGC;IAED,0DAiBC;IAED,0EAkEC;IAED,+CAEC;IAED,yDAiBC;IAED,iEAEC;IAED,iDAIC;IAED,2CAMC;IAED,yDAIC;IAED,+CAEC;IAED,sCAKC;IAED,sDAMC;IAED,oDAEC;IAED,iDASC;IAED,iDAIC;IAED,wDAWC;IAED,uDAGC;IAED,2DAGC;IAED,6DAGC;IAED,6DASC;IAED,4CAkBC;IAED,4CAkBC;IAED,gDAEC;IAED,gDAEC;IAGD,+DAuBC;IAED,uCAoBC;IAED,8CAEC;IAED,uCAoBC;IAED,gBAEC;IAED,eAEC;IAED,eAEC;IAED,eAEC;IAED,4DAmBC;IAED,oBAQC;IAED,oBAQC;IAED,yDAgDC;IAED,yCAGC;IAED,sCAQC;IAED,6CAGC;IAED,8CAMC;IAED,+CAGC;IAED,kDAMC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF;AAx/CD;IASE,gFAKC;IAbD,kBAAa;IACb,cAAS;IACT,gBAAW;IACX,YAAO;IACP,gBAAW;IACX,YAAO;IACP,gBAAW;IAGT,gBAA4B;IAC5B,cAAwB;IACxB,eAA0B;IAC1B,mBAAkC;CAErC"}