@marmooo/midy 0.2.4 → 0.2.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.
@@ -2,7 +2,7 @@ export class MidyGM2 {
2
2
  static channelSettings: {
3
3
  currentBufferSource: null;
4
4
  detune: number;
5
- scaleOctaveTuningTable: any[];
5
+ scaleOctaveTuningTable: Int8Array<ArrayBuffer>;
6
6
  channelPressureTable: Uint8Array<ArrayBuffer>;
7
7
  keyBasedInstrumentControlTable: Int8Array<ArrayBuffer>;
8
8
  program: number;
@@ -47,6 +47,8 @@ export class MidyGM2 {
47
47
  resumeTime: number;
48
48
  soundFonts: any[];
49
49
  soundFontTable: any[];
50
+ audioBufferCounter: Map<any, any>;
51
+ audioBufferCache: Map<any, any>;
50
52
  isPlaying: boolean;
51
53
  isPausing: boolean;
52
54
  isPaused: boolean;
@@ -55,7 +57,7 @@ export class MidyGM2 {
55
57
  timeline: any[];
56
58
  instruments: any[];
57
59
  notePromises: any[];
58
- exclusiveClassMap: Map<any, any>;
60
+ exclusiveClassMap: SparseMap;
59
61
  defaultOptions: {
60
62
  reverbAlgorithm: (audioContext: any) => {
61
63
  input: any;
@@ -133,13 +135,14 @@ export class MidyGM2 {
133
135
  };
134
136
  createChannels(audioContext: any): any[];
135
137
  createNoteBuffer(voiceParams: any, isSF3: any): Promise<any>;
136
- createNoteBufferNode(voiceParams: any, isSF3: any): Promise<any>;
138
+ createNoteBufferNode(audioBuffer: any, voiceParams: any): any;
137
139
  findPortamentoTarget(queueIndex: any): any;
138
140
  scheduleTimelineEvents(t: any, offset: any, queueIndex: any): Promise<any>;
139
141
  getQueueIndex(second: any): number;
140
142
  playNotes(): Promise<any>;
141
143
  ticksToSecond(ticks: any, secondsPerBeat: any): number;
142
144
  secondToTicks(second: any, secondsPerBeat: any): number;
145
+ getAudioBufferId(programNumber: any, noteNumber: any, velocity: any): string;
143
146
  extractMidiData(midi: any): {
144
147
  instruments: Set<any>;
145
148
  timeline: any[];
@@ -153,7 +156,7 @@ export class MidyGM2 {
153
156
  seekTo(second: any): void;
154
157
  calcTotalTime(): number;
155
158
  currentTime(): number;
156
- getActiveNotes(channel: any, time: any): Map<any, any>;
159
+ getActiveNotes(channel: any, time: any): SparseMap;
157
160
  getActiveNote(noteList: any, time: any): any;
158
161
  createConvolutionReverbImpulse(audioContext: any, decay: any, preDecay: any): any;
159
162
  createConvolutionReverb(audioContext: any, impulse: any): {
@@ -194,6 +197,7 @@ export class MidyGM2 {
194
197
  setFilterEnvelope(channel: any, note: any, pressure: any): void;
195
198
  startModulation(channel: any, note: any, startTime: any): void;
196
199
  startVibrato(channel: any, note: any, startTime: any): void;
200
+ getAudioBuffer(program: any, noteNumber: any, velocity: any, voiceParams: any, isSF3: any): Promise<any>;
197
201
  createNote(channel: any, voice: any, noteNumber: any, velocity: any, startTime: any, portamento: any, isSF3: any): Promise<Note>;
198
202
  calcBank(channel: any, channelNumber: any): any;
199
203
  scheduleNoteOn(channelNumber: any, noteNumber: any, velocity: any, startTime: any, portamento: any): Promise<void>;
@@ -330,7 +334,7 @@ export class MidyGM2 {
330
334
  setChorusSendToReverb(value: any): void;
331
335
  getChorusSendToReverb(value: any): number;
332
336
  getChannelBitmap(data: any): any[];
333
- handleScaleOctaveTuning1ByteFormatSysEx(data: any): void;
337
+ handleScaleOctaveTuning1ByteFormatSysEx(data: any, realtime: any): void;
334
338
  applyDestinationSettings(channel: any, note: any, table: any): void;
335
339
  handleChannelPressureSysEx(data: any, tableName: any): void;
336
340
  initControlTable(): Uint8Array<ArrayBuffer>;
@@ -342,6 +346,19 @@ export class MidyGM2 {
342
346
  handleSysEx(data: any): any;
343
347
  scheduleTask(callback: any, startTime: any): Promise<any>;
344
348
  }
349
+ declare class SparseMap {
350
+ constructor(size: any);
351
+ data: any[];
352
+ activeIndices: any[];
353
+ set(key: any, value: any): void;
354
+ get(key: any): any;
355
+ delete(key: any): boolean;
356
+ has(key: any): boolean;
357
+ get size(): number;
358
+ clear(): void;
359
+ forEach(callback: any): void;
360
+ [Symbol.iterator](): Generator<any[], void, unknown>;
361
+ }
345
362
  declare class Note {
346
363
  constructor(noteNumber: any, velocity: any, startTime: any, voice: any, voiceParams: any);
347
364
  bufferSource: any;
@@ -1 +1 @@
1
- {"version":3,"file":"midy-GM2.d.ts","sourceRoot":"","sources":["../src/midy-GM2.js"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"midy-GM2.d.ts","sourceRoot":"","sources":["../src/midy-GM2.js"],"names":[],"mappings":"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"}
@@ -3,6 +3,58 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MidyGM2 = void 0;
4
4
  const midi_file_1 = require("midi-file");
5
5
  const soundfont_parser_1 = require("@marmooo/soundfont-parser");
6
+ // 2-3 times faster than Map
7
+ class SparseMap {
8
+ constructor(size) {
9
+ this.data = new Array(size);
10
+ this.activeIndices = [];
11
+ }
12
+ set(key, value) {
13
+ if (this.data[key] === undefined) {
14
+ this.activeIndices.push(key);
15
+ }
16
+ this.data[key] = value;
17
+ }
18
+ get(key) {
19
+ return this.data[key];
20
+ }
21
+ delete(key) {
22
+ if (this.data[key] !== undefined) {
23
+ this.data[key] = undefined;
24
+ const index = this.activeIndices.indexOf(key);
25
+ if (index !== -1) {
26
+ this.activeIndices.splice(index, 1);
27
+ }
28
+ return true;
29
+ }
30
+ return false;
31
+ }
32
+ has(key) {
33
+ return this.data[key] !== undefined;
34
+ }
35
+ get size() {
36
+ return this.activeIndices.length;
37
+ }
38
+ clear() {
39
+ for (let i = 0; i < this.activeIndices.length; i++) {
40
+ const key = this.activeIndices[i];
41
+ this.data[key] = undefined;
42
+ }
43
+ this.activeIndices = [];
44
+ }
45
+ *[Symbol.iterator]() {
46
+ for (let i = 0; i < this.activeIndices.length; i++) {
47
+ const key = this.activeIndices[i];
48
+ yield [key, this.data[key]];
49
+ }
50
+ }
51
+ forEach(callback) {
52
+ for (let i = 0; i < this.activeIndices.length; i++) {
53
+ const key = this.activeIndices[i];
54
+ callback(this.data[key], key, this);
55
+ }
56
+ }
57
+ }
6
58
  class Note {
7
59
  constructor(noteNumber, velocity, startTime, voice, voiceParams) {
8
60
  Object.defineProperty(this, "bufferSource", {
@@ -283,6 +335,18 @@ class MidyGM2 {
283
335
  writable: true,
284
336
  value: this.initSoundFontTable()
285
337
  });
338
+ Object.defineProperty(this, "audioBufferCounter", {
339
+ enumerable: true,
340
+ configurable: true,
341
+ writable: true,
342
+ value: new Map()
343
+ });
344
+ Object.defineProperty(this, "audioBufferCache", {
345
+ enumerable: true,
346
+ configurable: true,
347
+ writable: true,
348
+ value: new Map()
349
+ });
286
350
  Object.defineProperty(this, "isPlaying", {
287
351
  enumerable: true,
288
352
  configurable: true,
@@ -335,7 +399,7 @@ class MidyGM2 {
335
399
  enumerable: true,
336
400
  configurable: true,
337
401
  writable: true,
338
- value: new Map()
402
+ value: new SparseMap(128)
339
403
  });
340
404
  Object.defineProperty(this, "defaultOptions", {
341
405
  enumerable: true,
@@ -375,7 +439,7 @@ class MidyGM2 {
375
439
  initSoundFontTable() {
376
440
  const table = new Array(128);
377
441
  for (let i = 0; i < 128; i++) {
378
- table[i] = new Map();
442
+ table[i] = new SparseMap(128);
379
443
  }
380
444
  return table;
381
445
  }
@@ -429,8 +493,8 @@ class MidyGM2 {
429
493
  state: new ControllerState(),
430
494
  controlTable: this.initControlTable(),
431
495
  ...this.setChannelAudioNodes(audioContext),
432
- scheduledNotes: new Map(),
433
- sostenutoNotes: new Map(),
496
+ scheduledNotes: new SparseMap(128),
497
+ sostenutoNotes: new SparseMap(128),
434
498
  };
435
499
  });
436
500
  return channels;
@@ -464,9 +528,8 @@ class MidyGM2 {
464
528
  return audioBuffer;
465
529
  }
466
530
  }
467
- async createNoteBufferNode(voiceParams, isSF3) {
531
+ createNoteBufferNode(audioBuffer, voiceParams) {
468
532
  const bufferSource = new AudioBufferSourceNode(this.audioContext);
469
- const audioBuffer = await this.createNoteBuffer(voiceParams, isSF3);
470
533
  bufferSource.buffer = audioBuffer;
471
534
  bufferSource.loop = voiceParams.sampleModes % 2 !== 0;
472
535
  if (bufferSource.loop) {
@@ -555,6 +618,7 @@ class MidyGM2 {
555
618
  await Promise.all(this.notePromises);
556
619
  this.notePromises = [];
557
620
  this.exclusiveClassMap.clear();
621
+ this.audioBufferCache.clear();
558
622
  resolve();
559
623
  return;
560
624
  }
@@ -570,8 +634,9 @@ class MidyGM2 {
570
634
  }
571
635
  else if (this.isStopping) {
572
636
  await this.stopNotes(0, true);
573
- this.exclusiveClassMap.clear();
574
637
  this.notePromises = [];
638
+ this.exclusiveClassMap.clear();
639
+ this.audioBufferCache.clear();
575
640
  resolve();
576
641
  this.isStopping = false;
577
642
  this.isPaused = false;
@@ -602,6 +667,9 @@ class MidyGM2 {
602
667
  secondToTicks(second, secondsPerBeat) {
603
668
  return second * this.ticksPerBeat / secondsPerBeat;
604
669
  }
670
+ getAudioBufferId(programNumber, noteNumber, velocity) {
671
+ return `${programNumber}:${noteNumber}:${velocity}`;
672
+ }
605
673
  extractMidiData(midi) {
606
674
  const instruments = new Set();
607
675
  const timeline = [];
@@ -623,6 +691,8 @@ class MidyGM2 {
623
691
  switch (event.type) {
624
692
  case "noteOn": {
625
693
  const channel = tmpChannels[event.channel];
694
+ const audioBufferId = this.getAudioBufferId(channel.programNumber, event.noteNumber, event.velocity);
695
+ this.audioBufferCounter.set(audioBufferId, (this.audioBufferCounter.get(audioBufferId) ?? 0) + 1);
626
696
  if (channel.programNumber < 0) {
627
697
  channel.programNumber = event.programNumber;
628
698
  switch (channel.bankMSB) {
@@ -672,6 +742,10 @@ class MidyGM2 {
672
742
  timeline.push(event);
673
743
  }
674
744
  }
745
+ for (const [audioBufferId, count] of this.audioBufferCounter) {
746
+ if (count === 1)
747
+ this.audioBufferCounter.delete(audioBufferId);
748
+ }
675
749
  const priority = {
676
750
  controller: 0,
677
751
  sysEx: 1,
@@ -765,7 +839,7 @@ class MidyGM2 {
765
839
  return this.resumeTime + now - this.startTime - this.startDelay;
766
840
  }
767
841
  getActiveNotes(channel, time) {
768
- const activeNotes = new Map();
842
+ const activeNotes = new SparseMap(128);
769
843
  channel.scheduledNotes.forEach((noteList) => {
770
844
  const activeNote = this.getActiveNote(noteList, time);
771
845
  if (activeNote) {
@@ -1100,12 +1174,31 @@ class MidyGM2 {
1100
1174
  note.vibratoLFO.connect(note.vibratoDepth);
1101
1175
  note.vibratoDepth.connect(note.bufferSource.detune);
1102
1176
  }
1177
+ async getAudioBuffer(program, noteNumber, velocity, voiceParams, isSF3) {
1178
+ const audioBufferId = this.getAudioBufferId(program, noteNumber, velocity);
1179
+ const cache = this.audioBufferCache.get(audioBufferId);
1180
+ if (cache) {
1181
+ cache.counter += 1;
1182
+ if (cache.maxCount <= cache.counter) {
1183
+ this.audioBufferCache.delete(audioBufferId);
1184
+ }
1185
+ return cache.audioBuffer;
1186
+ }
1187
+ else {
1188
+ const maxCount = this.audioBufferCounter.get(audioBufferId) ?? 0;
1189
+ const audioBuffer = await this.createNoteBuffer(voiceParams, isSF3);
1190
+ const cache = { audioBuffer, maxCount, counter: 1 };
1191
+ this.audioBufferCache.set(audioBufferId, cache);
1192
+ return audioBuffer;
1193
+ }
1194
+ }
1103
1195
  async createNote(channel, voice, noteNumber, velocity, startTime, portamento, isSF3) {
1104
1196
  const state = channel.state;
1105
1197
  const controllerState = this.getControllerState(channel, noteNumber, velocity);
1106
1198
  const voiceParams = voice.getAllParams(controllerState);
1107
1199
  const note = new Note(noteNumber, velocity, startTime, voice, voiceParams);
1108
- note.bufferSource = await this.createNoteBufferNode(voiceParams, isSF3);
1200
+ const audioBuffer = await this.getAudioBuffer(channel.program, noteNumber, velocity, voiceParams, isSF3);
1201
+ note.bufferSource = this.createNoteBufferNode(audioBuffer, voiceParams);
1109
1202
  note.volumeNode = new GainNode(this.audioContext);
1110
1203
  note.gainL = new GainNode(this.audioContext);
1111
1204
  note.gainR = new GainNode(this.audioContext);
@@ -1165,10 +1258,10 @@ class MidyGM2 {
1165
1258
  if (soundFontIndex === undefined)
1166
1259
  return;
1167
1260
  const soundFont = this.soundFonts[soundFontIndex];
1168
- const isSF3 = soundFont.parsed.info.version.major === 3;
1169
1261
  const voice = soundFont.getVoice(bankNumber, channel.program, noteNumber, velocity);
1170
1262
  if (!voice)
1171
1263
  return;
1264
+ const isSF3 = soundFont.parsed.info.version.major === 3;
1172
1265
  const note = await this.createNote(channel, voice, noteNumber, velocity, startTime, portamento, isSF3);
1173
1266
  note.gainL.connect(channel.gainL);
1174
1267
  note.gainR.connect(channel.gainR);
@@ -1337,6 +1430,7 @@ class MidyGM2 {
1337
1430
  channel.program = program;
1338
1431
  }
1339
1432
  handleChannelPressure(channelNumber, value) {
1433
+ const now = this.audioContext.currentTime;
1340
1434
  const channel = this.channels[channelNumber];
1341
1435
  const prev = channel.state.channelPressure;
1342
1436
  const next = value / 127;
@@ -1346,13 +1440,8 @@ class MidyGM2 {
1346
1440
  channel.detune += pressureDepth * (next - prev);
1347
1441
  }
1348
1442
  const table = channel.channelPressureTable;
1349
- channel.scheduledNotes.forEach((noteList) => {
1350
- for (let i = 0; i < noteList.length; i++) {
1351
- const note = noteList[i];
1352
- if (!note)
1353
- continue;
1354
- this.applyDestinationSettings(channel, note, table);
1355
- }
1443
+ this.getActiveNotes(channel, now).forEach((note) => {
1444
+ this.applyDestinationSettings(channel, note, table);
1356
1445
  });
1357
1446
  // this.applyVoiceParams(channel, 13);
1358
1447
  }
@@ -1753,8 +1842,7 @@ class MidyGM2 {
1753
1842
  channel.state.sostenutoPedal = value / 127;
1754
1843
  if (64 <= value) {
1755
1844
  const now = this.audioContext.currentTime;
1756
- const activeNotes = this.getActiveNotes(channel, now);
1757
- channel.sostenutoNotes = new Map(activeNotes);
1845
+ channel.sostenutoNotes = this.getActiveNotes(channel, now);
1758
1846
  }
1759
1847
  else {
1760
1848
  this.releaseSostenutoPedal(channelNumber, value);
@@ -2007,7 +2095,7 @@ class MidyGM2 {
2007
2095
  switch (data[3]) {
2008
2096
  case 8:
2009
2097
  // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca21.pdf
2010
- return this.handleScaleOctaveTuning1ByteFormatSysEx(data);
2098
+ return this.handleScaleOctaveTuning1ByteFormatSysEx(data, false);
2011
2099
  default:
2012
2100
  console.warn(`Unsupported Exclusive Message: ${data}`);
2013
2101
  }
@@ -2329,8 +2417,8 @@ class MidyGM2 {
2329
2417
  }
2330
2418
  return bitmap;
2331
2419
  }
2332
- handleScaleOctaveTuning1ByteFormatSysEx(data) {
2333
- if (data.length < 18) {
2420
+ handleScaleOctaveTuning1ByteFormatSysEx(data, realtime) {
2421
+ if (data.length < 19) {
2334
2422
  console.error("Data length is too short");
2335
2423
  return;
2336
2424
  }
@@ -2338,10 +2426,13 @@ class MidyGM2 {
2338
2426
  for (let i = 0; i < channelBitmap.length; i++) {
2339
2427
  if (!channelBitmap[i])
2340
2428
  continue;
2429
+ const channel = this.channels[i];
2341
2430
  for (let j = 0; j < 12; j++) {
2342
- const value = data[j + 7] - 64; // cent
2343
- this.channels[i].scaleOctaveTuningTable[j] = value;
2431
+ const centValue = data[j + 7] - 64;
2432
+ channel.scaleOctaveTuningTable[j] = centValue;
2344
2433
  }
2434
+ if (realtime)
2435
+ this.updateChannelDetune(channel);
2345
2436
  }
2346
2437
  }
2347
2438
  applyDestinationSettings(channel, note, table) {
@@ -2474,7 +2565,7 @@ Object.defineProperty(MidyGM2, "channelSettings", {
2474
2565
  value: {
2475
2566
  currentBufferSource: null,
2476
2567
  detune: 0,
2477
- scaleOctaveTuningTable: new Array(12).fill(0), // cent
2568
+ scaleOctaveTuningTable: new Int8Array(12), // [-64, 63] cent
2478
2569
  channelPressureTable: new Uint8Array([64, 64, 64, 0, 0, 0]),
2479
2570
  keyBasedInstrumentControlTable: new Int8Array(128 * 128), // [-64, 63]
2480
2571
  program: 0,
@@ -19,6 +19,8 @@ export class MidyGMLite {
19
19
  resumeTime: number;
20
20
  soundFonts: any[];
21
21
  soundFontTable: any[];
22
+ audioBufferCounter: Map<any, any>;
23
+ audioBufferCache: Map<any, any>;
22
24
  isPlaying: boolean;
23
25
  isPausing: boolean;
24
26
  isPaused: boolean;
@@ -27,7 +29,7 @@ export class MidyGMLite {
27
29
  timeline: any[];
28
30
  instruments: any[];
29
31
  notePromises: any[];
30
- exclusiveClassMap: Map<any, any>;
32
+ exclusiveClassMap: SparseMap;
31
33
  audioContext: any;
32
34
  masterVolume: any;
33
35
  voiceParamsHandlers: {
@@ -68,12 +70,13 @@ export class MidyGMLite {
68
70
  };
69
71
  createChannels(audioContext: any): any[];
70
72
  createNoteBuffer(voiceParams: any, isSF3: any): Promise<any>;
71
- createNoteBufferNode(voiceParams: any, isSF3: any): Promise<any>;
73
+ createNoteBufferNode(audioBuffer: any, voiceParams: any): any;
72
74
  scheduleTimelineEvents(t: any, offset: any, queueIndex: any): Promise<any>;
73
75
  getQueueIndex(second: any): number;
74
76
  playNotes(): Promise<any>;
75
77
  ticksToSecond(ticks: any, secondsPerBeat: any): number;
76
78
  secondToTicks(second: any, secondsPerBeat: any): number;
79
+ getAudioBufferId(programNumber: any, noteNumber: any, velocity: any): string;
77
80
  extractMidiData(midi: any): {
78
81
  instruments: Set<any>;
79
82
  timeline: any[];
@@ -87,7 +90,7 @@ export class MidyGMLite {
87
90
  seekTo(second: any): void;
88
91
  calcTotalTime(): number;
89
92
  currentTime(): number;
90
- getActiveNotes(channel: any, time: any): Map<any, any>;
93
+ getActiveNotes(channel: any, time: any): SparseMap;
91
94
  getActiveNote(noteList: any, time: any): any;
92
95
  cbToRatio(cb: any): number;
93
96
  rateToCent(rate: any): number;
@@ -101,6 +104,7 @@ export class MidyGMLite {
101
104
  clampCutoffFrequency(frequency: any): number;
102
105
  setFilterEnvelope(note: any): void;
103
106
  startModulation(channel: any, note: any, startTime: any): void;
107
+ getAudioBuffer(program: any, noteNumber: any, velocity: any, voiceParams: any, isSF3: any): Promise<any>;
104
108
  createNote(channel: any, voice: any, noteNumber: any, velocity: any, startTime: any, isSF3: any): Promise<Note>;
105
109
  scheduleNoteOn(channelNumber: any, noteNumber: any, velocity: any, startTime: any): Promise<void>;
106
110
  noteOn(channelNumber: any, noteNumber: any, velocity: any): Promise<void>;
@@ -177,6 +181,19 @@ export class MidyGMLite {
177
181
  handleSysEx(data: any): void;
178
182
  scheduleTask(callback: any, startTime: any): Promise<any>;
179
183
  }
184
+ declare class SparseMap {
185
+ constructor(size: any);
186
+ data: any[];
187
+ activeIndices: any[];
188
+ set(key: any, value: any): void;
189
+ get(key: any): any;
190
+ delete(key: any): boolean;
191
+ has(key: any): boolean;
192
+ get size(): number;
193
+ clear(): void;
194
+ forEach(callback: any): void;
195
+ [Symbol.iterator](): Generator<any[], void, unknown>;
196
+ }
180
197
  declare class Note {
181
198
  constructor(noteNumber: any, velocity: any, startTime: any, voice: any, voiceParams: any);
182
199
  bufferSource: any;
@@ -1 +1 @@
1
- {"version":3,"file":"midy-GMLite.d.ts","sourceRoot":"","sources":["../src/midy-GMLite.js"],"names":[],"mappings":"AAmFA;IAoBE;;;;;;;;;MASE;IAEF,+BAQC;IAtCD,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;IAClB,iCAA8B;IAc5B,kBAAgC;IAChC,kBAA8C;IAC9C;;;;;;;;;;;MAA2D;IAC3D;;;;;;;;;;;;;MAA+D;IAC/D,gBAAiD;IAKnD,4BAMC;IAED,mCAWC;IAED,gDAMC;IAED,sCASC;IAED;;;;MAeC;IAED,yCAUC;IAED,6DA2BC;IAED,iEAUC;IAED,2EA+CC;IAED,mCAOC;IAED,0BAkDC;IAED,uDAEC;IAED,wDAEC;IAED;;;MAgEC;IAED,+EAmBC;IAED,qDAKC;IAED,uBAKC;IAED,aAGC;IAED,cAKC;IAED,wBAIC;IAED,0BAKC;IAED,wBAOC;IAED,sBAGC;IAED,uDASC;IAED,6CAQC;IAED,2BAEC;IAED,8BAEC;IAED,8BAEC;IAED,4BAEC;IAED,wCAIC;IAED,wCAQC;IAED,4CAKC;IAED,mCAgBC;IAED,kCAqBC;IAED,6CAIC;IAED,mCAuBC;IAED,+DAoBC;IAED,gHA2BC;IAED,kGAgDC;IAED,0EAGC;IAED,qFAwBC;IAED,6HAuBC;IAED,0FAGC;IAED,kEAeC;IAED,gFAiBC;IAED,4DAGC;IAED,qEAGC;IAED,mDASC;IAED,gDASC;IAED,qCAMC;IAED,mCAQC;IAED,gCAOC;IAED,+BAMC;IAED;;;;;;;;;;;MAqBC;IAED,oFAMC;IAED,0DA6CC;IAED;;;;;;;;;;;;;MAeC;IAED,+EAWC;IAED,qCAeC;IAED,8DAIC;IACD,iDAIC;IAED;;;MAMC;IAED,2CAIC;IAED,yDAIC;IAED,mDAGC;IAED,wCAWC;IAED,sDAKC;IAED,kFAeC;IAED,oCAYC;IAED,gDAEC;IAED,gDAEC;IAED,mDAGC;IAED,kDAKC;IAED,wDASC;IAED,+CAEC;IAED,8CAqBC;IAED,+CAEC;IAED,4DAgBC;IAED,oBAMC;IAED,yDAaC;IAED,yCAGC;IAED,mCAQC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF;AA1tCD;IAQE,0FAMC;IAbD,kBAAa;IACb,gBAAW;IACX,wBAAmB;IACnB,iBAAY;IACZ,mBAAc;IACd,qBAAgB;IAGd,gBAA4B;IAC5B,cAAwB;IACxB,eAA0B;IAC1B,WAAkB;IAClB,iBAA8B;CAEjC"}
1
+ {"version":3,"file":"midy-GMLite.d.ts","sourceRoot":"","sources":["../src/midy-GMLite.js"],"names":[],"mappings":"AAgJA;IAsBE;;;;;;;;;MASE;IAEF,+BAQC;IAxCD,qBAAmB;IACnB,kBAAc;IACd,0BAAwB;IACxB,kBAAc;IACd,mBAAiB;IACjB,kBAAc;IACd,mBAAe;IACf,kBAAgB;IAChB,sBAA2C;IAC3C,kCAA+B;IAC/B,gCAA6B;IAC7B,mBAAkB;IAClB,mBAAkB;IAClB,kBAAiB;IACjB,oBAAmB;IACnB,mBAAkB;IAClB,gBAAc;IACd,mBAAiB;IACjB,oBAAkB;IAClB,6BAAuC;IAcrC,kBAAgC;IAChC,kBAA8C;IAC9C;;;;;;;;;;;MAA2D;IAC3D;;;;;;;;;;;;;MAA+D;IAC/D,gBAAiD;IAKnD,4BAMC;IAED,mCAWC;IAED,gDAMC;IAED,sCASC;IAED;;;;MAeC;IAED,yCAUC;IAED,6DA2BC;IAED,8DASC;IAED,2EA+CC;IAED,mCAOC;IAED,0BAoDC;IAED,uDAEC;IAED,wDAEC;IAED,6EAEC;IAED;;;MA4EC;IAED,+EAmBC;IAED,qDAKC;IAED,uBAKC;IAED,aAGC;IAED,cAKC;IAED,wBAIC;IAED,0BAKC;IAED,wBAOC;IAED,sBAGC;IAED,mDASC;IAED,6CAQC;IAED,2BAEC;IAED,8BAEC;IAED,8BAEC;IAED,4BAEC;IAED,wCAIC;IAED,wCAQC;IAED,4CAKC;IAED,mCAgBC;IAED,kCAqBC;IAED,6CAIC;IAED,mCAuBC;IAED,+DAoBC;IAED,yGAgBC;IAED,gHAuCC;IAED,kGAgDC;IAED,0EAGC;IAED,qFAwBC;IAED,6HAuBC;IAED,0FAGC;IAED,kEAeC;IAED,gFAiBC;IAED,4DAGC;IAED,qEAGC;IAED,mDASC;IAED,gDASC;IAED,qCAMC;IAED,mCAQC;IAED,gCAOC;IAED,+BAMC;IAED;;;;;;;;;;;MAqBC;IAED,oFAMC;IAED,0DA6CC;IAED;;;;;;;;;;;;;MAeC;IAED,+EAWC;IAED,qCAeC;IAED,8DAIC;IACD,iDAIC;IAED;;;MAMC;IAED,2CAIC;IAED,yDAIC;IAED,mDAGC;IAED,wCAWC;IAED,sDAKC;IAED,kFAeC;IAED,oCAYC;IAED,gDAEC;IAED,gDAEC;IAED,mDAGC;IAED,kDAKC;IAED,wDASC;IAED,+CAEC;IAED,8CAqBC;IAED,+CAEC;IAED,4DAgBC;IAED,oBAMC;IAED,yDAaC;IAED,yCAGC;IAED,mCAQC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF;AAv0CD;IACE,uBAGC;IAFC,YAA2B;IAC3B,qBAAuB;IAGzB,gCAKC;IAED,mBAEC;IAED,0BAUC;IAED,uBAEC;IAED,mBAEC;IAED,cAMC;IASD,6BAKC;IAZD,qDAKC;CAQF;AAED;IAQE,0FAMC;IAbD,kBAAa;IACb,gBAAW;IACX,wBAAmB;IACnB,iBAAY;IACZ,mBAAc;IACd,qBAAgB;IAGd,gBAA4B;IAC5B,cAAwB;IACxB,eAA0B;IAC1B,WAAkB;IAClB,iBAA8B;CAEjC"}