@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"}
@@ -1,5 +1,43 @@
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.1/+esm.js";
2
+ import { parse, SoundFont, } from "./deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.2/+esm.js";
3
+ class Note {
4
+ constructor(noteNumber, velocity, startTime, instrumentKey) {
5
+ Object.defineProperty(this, "bufferSource", {
6
+ enumerable: true,
7
+ configurable: true,
8
+ writable: true,
9
+ value: void 0
10
+ });
11
+ Object.defineProperty(this, "gainNode", {
12
+ enumerable: true,
13
+ configurable: true,
14
+ writable: true,
15
+ value: void 0
16
+ });
17
+ Object.defineProperty(this, "filterNode", {
18
+ enumerable: true,
19
+ configurable: true,
20
+ writable: true,
21
+ value: void 0
22
+ });
23
+ Object.defineProperty(this, "modLFO", {
24
+ enumerable: true,
25
+ configurable: true,
26
+ writable: true,
27
+ value: void 0
28
+ });
29
+ Object.defineProperty(this, "modLFOGain", {
30
+ enumerable: true,
31
+ configurable: true,
32
+ writable: true,
33
+ value: void 0
34
+ });
35
+ this.noteNumber = noteNumber;
36
+ this.velocity = velocity;
37
+ this.startTime = startTime;
38
+ this.instrumentKey = instrumentKey;
39
+ }
40
+ }
3
41
  export class MidyGMLite {
4
42
  constructor(audioContext) {
5
43
  Object.defineProperty(this, "ticksPerBeat", {
@@ -151,14 +189,11 @@ export class MidyGMLite {
151
189
  const pannerNode = new StereoPannerNode(audioContext, {
152
190
  pan: MidyGMLite.channelSettings.pan,
153
191
  });
154
- const modulationEffect = this.createModulationEffect(audioContext);
155
- modulationEffect.lfo.start();
156
192
  pannerNode.connect(gainNode);
157
193
  gainNode.connect(this.masterGain);
158
194
  return {
159
195
  gainNode,
160
196
  pannerNode,
161
- modulationEffect,
162
197
  };
163
198
  }
164
199
  createChannels(audioContext) {
@@ -168,16 +203,15 @@ export class MidyGMLite {
168
203
  ...MidyGMLite.effectSettings,
169
204
  ...this.setChannelAudioNodes(audioContext),
170
205
  scheduledNotes: new Map(),
171
- sostenutoNotes: new Map(),
172
206
  };
173
207
  });
174
208
  return channels;
175
209
  }
176
- async createNoteBuffer(noteInfo, isSF3) {
177
- const sampleEnd = noteInfo.sample.length + noteInfo.end;
210
+ async createNoteBuffer(instrumentKey, isSF3) {
211
+ const sampleEnd = instrumentKey.sample.length + instrumentKey.end;
178
212
  if (isSF3) {
179
- const sample = new Uint8Array(noteInfo.sample.length);
180
- sample.set(noteInfo.sample);
213
+ const sample = new Uint8Array(instrumentKey.sample.length);
214
+ sample.set(instrumentKey.sample);
181
215
  const audioBuffer = await this.audioContext.decodeAudioData(sample.buffer);
182
216
  for (let channel = 0; channel < audioBuffer.numberOfChannels; channel++) {
183
217
  const channelData = audioBuffer.getChannelData(channel);
@@ -186,26 +220,27 @@ export class MidyGMLite {
186
220
  return audioBuffer;
187
221
  }
188
222
  else {
189
- const sample = noteInfo.sample.subarray(0, sampleEnd);
223
+ const sample = instrumentKey.sample.subarray(0, sampleEnd);
190
224
  const floatSample = this.convertToFloat32Array(sample);
191
225
  const audioBuffer = new AudioBuffer({
192
226
  numberOfChannels: 1,
193
227
  length: sample.length,
194
- sampleRate: noteInfo.sampleRate,
228
+ sampleRate: instrumentKey.sampleRate,
195
229
  });
196
230
  const channelData = audioBuffer.getChannelData(0);
197
231
  channelData.set(floatSample);
198
232
  return audioBuffer;
199
233
  }
200
234
  }
201
- async createNoteBufferNode(noteInfo, isSF3) {
235
+ async createNoteBufferNode(instrumentKey, isSF3) {
202
236
  const bufferSource = new AudioBufferSourceNode(this.audioContext);
203
- const audioBuffer = await this.createNoteBuffer(noteInfo, isSF3);
237
+ const audioBuffer = await this.createNoteBuffer(instrumentKey, isSF3);
204
238
  bufferSource.buffer = audioBuffer;
205
- bufferSource.loop = noteInfo.sampleModes % 2 !== 0;
239
+ bufferSource.loop = instrumentKey.sampleModes % 2 !== 0;
206
240
  if (bufferSource.loop) {
207
- bufferSource.loopStart = noteInfo.loopStart / noteInfo.sampleRate;
208
- bufferSource.loopEnd = noteInfo.loopEnd / noteInfo.sampleRate;
241
+ bufferSource.loopStart = instrumentKey.loopStart /
242
+ instrumentKey.sampleRate;
243
+ bufferSource.loopEnd = instrumentKey.loopEnd / instrumentKey.sampleRate;
209
244
  }
210
245
  return bufferSource;
211
246
  }
@@ -447,30 +482,26 @@ export class MidyGMLite {
447
482
  const now = this.audioContext.currentTime;
448
483
  return this.resumeTime + now - this.startTime - this.startDelay;
449
484
  }
450
- getActiveNotes(channel) {
485
+ getActiveNotes(channel, time) {
451
486
  const activeNotes = new Map();
452
- channel.scheduledNotes.forEach((scheduledNotes) => {
453
- const activeNote = this.getActiveChannelNotes(scheduledNotes);
487
+ channel.scheduledNotes.forEach((noteList) => {
488
+ const activeNote = this.getActiveNote(noteList, time);
454
489
  if (activeNote) {
455
490
  activeNotes.set(activeNote.noteNumber, activeNote);
456
491
  }
457
492
  });
458
493
  return activeNotes;
459
494
  }
460
- getActiveChannelNotes(scheduledNotes) {
461
- for (let i = 0; i < scheduledNotes; i++) {
462
- const scheduledNote = scheduledNotes[i];
463
- if (scheduledNote)
464
- return scheduledNote;
495
+ getActiveNote(noteList, time) {
496
+ for (let i = noteList.length - 1; i >= 0; i--) {
497
+ const note = noteList[i];
498
+ if (!note)
499
+ return;
500
+ if (time < note.startTime)
501
+ continue;
502
+ return (note.ending) ? null : note;
465
503
  }
466
- }
467
- createModulationEffect(audioContext) {
468
- const lfo = new OscillatorNode(audioContext, {
469
- frequency: 5,
470
- });
471
- return {
472
- lfo,
473
- };
504
+ return noteList[0];
474
505
  }
475
506
  connectNoteEffects(channel, gainNode) {
476
507
  gainNode.connect(channel.pannerNode);
@@ -482,73 +513,91 @@ export class MidyGMLite {
482
513
  return 8.176 * Math.pow(2, cent / 1200);
483
514
  }
484
515
  calcSemitoneOffset(channel) {
485
- return channel.pitchBend * channel.pitchBendRange + tuning;
516
+ return channel.pitchBend * channel.pitchBendRange;
486
517
  }
487
- calcPlaybackRate(noteInfo, noteNumber, semitoneOffset) {
488
- return noteInfo.playbackRate(noteNumber) * Math.pow(2, semitoneOffset / 12);
518
+ calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset) {
519
+ return instrumentKey.playbackRate(noteNumber) *
520
+ Math.pow(2, semitoneOffset / 12);
489
521
  }
490
- async createNoteAudioChain(channel, noteInfo, noteNumber, velocity, startTime, isSF3) {
491
- const bufferSource = await this.createNoteBufferNode(noteInfo, isSF3);
492
- const semitoneOffset = this.calcSemitoneOffset(channel);
493
- bufferSource.playbackRate.value = this.calcPlaybackRate(noteInfo, noteNumber, semitoneOffset);
494
- // volume envelope
495
- const gainNode = new GainNode(this.audioContext, {
522
+ setVolumeEnvelope(channel, note) {
523
+ const { instrumentKey, startTime, velocity } = note;
524
+ note.gainNode = new GainNode(this.audioContext, {
496
525
  gain: 0,
497
526
  });
498
527
  let volume = (velocity / 127) * channel.volume * channel.expression;
499
528
  if (volume === 0)
500
529
  volume = 1e-6; // exponentialRampToValueAtTime() requires a non-zero value
501
- const attackVolume = this.cbToRatio(-noteInfo.initialAttenuation) * volume;
502
- const sustainVolume = attackVolume * (1 - noteInfo.volSustain);
503
- const volDelay = startTime + noteInfo.volDelay;
504
- const volAttack = volDelay + noteInfo.volAttack;
505
- const volHold = volAttack + noteInfo.volHold;
506
- const volDecay = volHold + noteInfo.volDecay;
507
- gainNode.gain
530
+ const attackVolume = this.cbToRatio(-instrumentKey.initialAttenuation) *
531
+ volume;
532
+ const sustainVolume = attackVolume * (1 - instrumentKey.volSustain);
533
+ const volDelay = startTime + instrumentKey.volDelay;
534
+ const volAttack = volDelay + instrumentKey.volAttack;
535
+ const volHold = volAttack + instrumentKey.volHold;
536
+ const volDecay = volHold + instrumentKey.volDecay;
537
+ note.gainNode.gain
508
538
  .setValueAtTime(1e-6, volDelay) // exponentialRampToValueAtTime() requires a non-zero value
509
539
  .exponentialRampToValueAtTime(attackVolume, volAttack)
510
540
  .setValueAtTime(attackVolume, volHold)
511
541
  .linearRampToValueAtTime(sustainVolume, volDecay);
512
- // filter envelope
542
+ }
543
+ setFilterEnvelope(channel, note) {
544
+ const { instrumentKey, startTime, noteNumber } = note;
545
+ const softPedalFactor = 1 -
546
+ (0.1 + (noteNumber / 127) * 0.2) * channel.softPedal;
513
547
  const maxFreq = this.audioContext.sampleRate / 2;
514
- const baseFreq = this.centToHz(noteInfo.initialFilterFc);
515
- const peekFreq = this.centToHz(noteInfo.initialFilterFc + noteInfo.modEnvToFilterFc);
516
- const sustainFreq = baseFreq +
517
- (peekFreq - baseFreq) * (1 - noteInfo.modSustain);
548
+ const baseFreq = this.centToHz(instrumentKey.initialFilterFc) *
549
+ softPedalFactor;
550
+ const peekFreq = this.centToHz(instrumentKey.initialFilterFc + instrumentKey.modEnvToFilterFc) * softPedalFactor;
551
+ const sustainFreq = (baseFreq +
552
+ (peekFreq - baseFreq) * (1 - instrumentKey.modSustain)) * softPedalFactor;
553
+ const modDelay = startTime + instrumentKey.modDelay;
554
+ const modAttack = modDelay + instrumentKey.modAttack;
555
+ const modHold = modAttack + instrumentKey.modHold;
556
+ const modDecay = modHold + instrumentKey.modDecay;
518
557
  const adjustedBaseFreq = Math.min(maxFreq, baseFreq);
519
558
  const adjustedPeekFreq = Math.min(maxFreq, peekFreq);
520
559
  const adjustedSustainFreq = Math.min(maxFreq, sustainFreq);
521
- const filterNode = new BiquadFilterNode(this.audioContext, {
560
+ note.filterNode = new BiquadFilterNode(this.audioContext, {
522
561
  type: "lowpass",
523
- Q: noteInfo.initialFilterQ / 10, // dB
562
+ Q: instrumentKey.initialFilterQ / 10, // dB
524
563
  frequency: adjustedBaseFreq,
525
564
  });
526
- const modDelay = startTime + noteInfo.modDelay;
527
- const modAttack = modDelay + noteInfo.modAttack;
528
- const modHold = modAttack + noteInfo.modHold;
529
- const modDecay = modHold + noteInfo.modDecay;
530
- filterNode.frequency
565
+ note.filterNode.frequency
531
566
  .setValueAtTime(adjustedBaseFreq, modDelay)
532
567
  .exponentialRampToValueAtTime(adjustedPeekFreq, modAttack)
533
568
  .setValueAtTime(adjustedPeekFreq, modHold)
534
569
  .linearRampToValueAtTime(adjustedSustainFreq, modDecay);
535
- let lfoGain;
570
+ note.bufferSource.detune.setValueAtTime(note.bufferSource.detune.value + instrumentKey.modEnvToPitch, modDelay);
571
+ }
572
+ startModulation(channel, note, time) {
573
+ const { instrumentKey } = note;
574
+ note.modLFOGain = new GainNode(this.audioContext, {
575
+ gain: this.cbToRatio(instrumentKey.modLfoToVolume) * channel.modulation,
576
+ });
577
+ note.modLFO = new OscillatorNode(this.audioContext, {
578
+ frequency: this.centToHz(instrumentKey.freqModLFO),
579
+ });
580
+ note.modLFO.start(time);
581
+ note.filterNode.frequency.setValueAtTime(note.filterNode.frequency.value + instrumentKey.modLfoToFilterFc, time);
582
+ note.bufferSource.detune.setValueAtTime(note.bufferSource.detune.value + instrumentKey.modLfoToPitch, time);
583
+ note.modLFO.connect(note.modLFOGain);
584
+ note.modLFOGain.connect(note.bufferSource.detune);
585
+ }
586
+ async createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3) {
587
+ const semitoneOffset = this.calcSemitoneOffset(channel);
588
+ const note = new Note(noteNumber, velocity, startTime, instrumentKey);
589
+ note.bufferSource = await this.createNoteBufferNode(instrumentKey, isSF3);
590
+ note.bufferSource.playbackRate.value = this.calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset);
591
+ this.setVolumeEnvelope(channel, note);
592
+ this.setFilterEnvelope(channel, note);
536
593
  if (channel.modulation > 0) {
537
- const vibratoDelay = startTime + channel.vibratoDelay;
538
- const vibratoAttack = vibratoDelay + 0.1;
539
- lfoGain = new GainNode(this.audioContext, {
540
- gain: 0,
541
- });
542
- lfoGain.gain
543
- .setValueAtTime(1e-6, vibratoDelay) // exponentialRampToValueAtTime() requires a non-zero value
544
- .exponentialRampToValueAtTime(channel.modulation, vibratoAttack);
545
- channel.modulationEffect.lfo.connect(lfoGain);
546
- lfoGain.connect(bufferSource.detune);
594
+ const delayModLFO = startTime + instrumentKey.delayModLFO;
595
+ this.startModulation(channel, note, delayModLFO);
547
596
  }
548
- bufferSource.connect(filterNode);
549
- filterNode.connect(gainNode);
550
- bufferSource.start(startTime, noteInfo.start / noteInfo.sampleRate);
551
- return { bufferSource, gainNode, filterNode, lfoGain };
597
+ note.bufferSource.connect(note.filterNode);
598
+ note.filterNode.connect(note.gainNode);
599
+ note.bufferSource.start(startTime, instrumentKey.start / instrumentKey.sampleRate);
600
+ return note;
552
601
  }
553
602
  async scheduleNoteOn(channelNumber, noteNumber, velocity, startTime) {
554
603
  const channel = this.channels[channelNumber];
@@ -558,27 +607,17 @@ export class MidyGMLite {
558
607
  return;
559
608
  const soundFont = this.soundFonts[soundFontIndex];
560
609
  const isSF3 = soundFont.parsed.info.version.major === 3;
561
- const noteInfo = soundFont.getInstrumentKey(bankNumber, channel.program, noteNumber);
562
- if (!noteInfo)
610
+ const instrumentKey = soundFont.getInstrumentKey(bankNumber, channel.program, noteNumber);
611
+ if (!instrumentKey)
563
612
  return;
564
- const { bufferSource, gainNode, filterNode, lfoGain } = await this
565
- .createNoteAudioChain(channel, noteInfo, noteNumber, velocity, startTime, isSF3);
566
- this.connectNoteEffects(channel, gainNode);
613
+ const note = await this.createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3);
614
+ this.connectNoteEffects(channel, note.gainNode);
567
615
  const scheduledNotes = channel.scheduledNotes;
568
- const scheduledNote = {
569
- bufferSource,
570
- filterNode,
571
- gainNode,
572
- lfoGain,
573
- noteInfo,
574
- noteNumber,
575
- startTime,
576
- };
577
616
  if (scheduledNotes.has(noteNumber)) {
578
- scheduledNotes.get(noteNumber).push(scheduledNote);
617
+ scheduledNotes.get(noteNumber).push(note);
579
618
  }
580
619
  else {
581
- scheduledNotes.set(noteNumber, [scheduledNote]);
620
+ scheduledNotes.set(noteNumber, [note]);
582
621
  }
583
622
  }
584
623
  noteOn(channelNumber, noteNumber, velocity) {
@@ -598,15 +637,15 @@ export class MidyGMLite {
598
637
  continue;
599
638
  if (targetNote.ending)
600
639
  continue;
601
- const { bufferSource, filterNode, gainNode, lfoGain, noteInfo } = targetNote;
640
+ const { bufferSource, filterNode, gainNode, modLFO, modLFOGain, instrumentKey, } = targetNote;
602
641
  const velocityRate = (velocity + 127) / 127;
603
- const volEndTime = stopTime + noteInfo.volRelease * velocityRate;
642
+ const volEndTime = stopTime + instrumentKey.volRelease * velocityRate;
604
643
  gainNode.gain.cancelScheduledValues(stopTime);
605
644
  gainNode.gain.linearRampToValueAtTime(0, volEndTime);
606
645
  const maxFreq = this.audioContext.sampleRate / 2;
607
- const baseFreq = this.centToHz(noteInfo.initialFilterFc);
646
+ const baseFreq = this.centToHz(instrumentKey.initialFilterFc);
608
647
  const adjustedBaseFreq = Math.min(maxFreq, baseFreq);
609
- const modEndTime = stopTime + noteInfo.modRelease * velocityRate;
648
+ const modEndTime = stopTime + instrumentKey.modRelease * velocityRate;
610
649
  filterNode.frequency
611
650
  .cancelScheduledValues(stopTime)
612
651
  .linearRampToValueAtTime(adjustedBaseFreq, modEndTime);
@@ -620,8 +659,10 @@ export class MidyGMLite {
620
659
  bufferSource.disconnect(0);
621
660
  filterNode.disconnect(0);
622
661
  gainNode.disconnect(0);
623
- if (lfoGain)
624
- lfoGain.disconnect(0);
662
+ if (modLFOGain)
663
+ modLFOGain.disconnect(0);
664
+ if (modLFO)
665
+ modLFO.stop();
625
666
  resolve();
626
667
  };
627
668
  bufferSource.stop(volEndTime);
@@ -679,10 +720,10 @@ export class MidyGMLite {
679
720
  const channel = this.channels[channelNumber];
680
721
  channel.pitchBend = (pitchBend - 8192) / 8192;
681
722
  const semitoneOffset = this.calcSemitoneOffset(channel);
682
- const activeNotes = this.getActiveNotes(channel);
723
+ const activeNotes = this.getActiveNotes(channel, now);
683
724
  activeNotes.forEach((activeNote) => {
684
- const { bufferSource, noteInfo, noteNumber } = activeNote;
685
- const playbackRate = calcPlaybackRate(noteInfo, noteNumber, semitoneOffset);
725
+ const { bufferSource, instrumentKey, noteNumber } = activeNote;
726
+ const playbackRate = calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset);
686
727
  bufferSource.playbackRate
687
728
  .cancelScheduledValues(now)
688
729
  .setValueAtTime(playbackRate * pressure, now);
@@ -719,9 +760,20 @@ export class MidyGMLite {
719
760
  }
720
761
  }
721
762
  setModulation(channelNumber, modulation) {
763
+ const now = this.audioContext.currentTime;
722
764
  const channel = this.channels[channelNumber];
723
765
  channel.modulation = (modulation / 127) *
724
766
  (channel.modulationDepthRange * 100);
767
+ const activeNotes = this.getActiveNotes(channel, now);
768
+ activeNotes.forEach((activeNote) => {
769
+ if (activeNote.modLFO) {
770
+ activeNote.gainNode.gain.setValueAtTime(this.cbToRatio(activeNote.instrumentKey.modLfoToVolume) *
771
+ channel.modulation, now);
772
+ }
773
+ else {
774
+ this.startModulation(channel, activeNote, now);
775
+ }
776
+ });
725
777
  }
726
778
  setVolume(channelNumber, volume) {
727
779
  const channel = this.channels[channelNumber];
@@ -778,8 +830,8 @@ export class MidyGMLite {
778
830
  const velocity = 0;
779
831
  const stopPedal = true;
780
832
  const promises = [];
781
- channel.scheduledNotes.forEach((scheduledNotes) => {
782
- const activeNote = this.getActiveChannelNotes(scheduledNotes);
833
+ channel.scheduledNotes.forEach((noteList) => {
834
+ const activeNote = this.getActiveNote(noteList, now);
783
835
  if (activeNote) {
784
836
  const notePromise = this.scheduleNoteRelease(channelNumber, noteNumber, velocity, now, stopPedal);
785
837
  promises.push(notePromise);
@@ -796,8 +848,8 @@ export class MidyGMLite {
796
848
  const velocity = 0;
797
849
  const stopPedal = false;
798
850
  const promises = [];
799
- channel.scheduledNotes.forEach((scheduledNotes) => {
800
- const activeNote = this.getActiveChannelNotes(scheduledNotes);
851
+ channel.scheduledNotes.forEach((noteList) => {
852
+ const activeNote = this.getActiveNote(noteList, now);
801
853
  if (activeNote) {
802
854
  const notePromise = this.scheduleNoteRelease(channelNumber, noteNumber, velocity, now, stopPedal);
803
855
  promises.push(notePromise);
@@ -891,9 +943,6 @@ Object.defineProperty(MidyGMLite, "channelSettings", {
891
943
  value: {
892
944
  volume: 100 / 127,
893
945
  pan: 0,
894
- vibratoRate: 5,
895
- vibratoDepth: 0.5,
896
- vibratoDelay: 2.5,
897
946
  bank: 0,
898
947
  dataMSB: 0,
899
948
  dataLSB: 0,
package/esm/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
package/esm/midy.d.ts.map CHANGED
@@ -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"}