@marmooo/midy 0.2.8 → 0.2.9

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.
package/script/midy.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export class Midy {
2
2
  static channelSettings: {
3
3
  currentBufferSource: null;
4
+ isDrum: boolean;
4
5
  detune: number;
5
6
  program: number;
6
7
  bank: number;
@@ -11,9 +12,9 @@ export class Midy {
11
12
  rpnMSB: number;
12
13
  rpnLSB: number;
13
14
  mono: boolean;
15
+ modulationDepthRange: number;
14
16
  fineTuning: number;
15
17
  coarseTuning: number;
16
- modulationDepthRange: number;
17
18
  };
18
19
  constructor(audioContext: any, options?: {
19
20
  reverbAlgorithm: (audioContext: any) => {
@@ -21,6 +22,7 @@ export class Midy {
21
22
  output: any;
22
23
  };
23
24
  });
25
+ mode: string;
24
26
  ticksPerBeat: number;
25
27
  totalTime: number;
26
28
  masterFineTuning: number;
@@ -68,6 +70,8 @@ export class Midy {
68
70
  };
69
71
  };
70
72
  masterVolume: any;
73
+ scheduler: any;
74
+ schedulerBuffer: any;
71
75
  voiceParamsHandlers: {
72
76
  modLfoToPitch: (channel: any, note: any, _prevValue: any, scheduleTime: any) => void;
73
77
  vibLfoToPitch: (channel: any, note: any, _prevValue: any, scheduleTime: any) => void;
@@ -93,7 +97,7 @@ export class Midy {
93
97
  64: (channelNumber: any, value: any, scheduleTime: any) => void;
94
98
  65: (channelNumber: any, value: any) => void;
95
99
  66: (channelNumber: any, value: any, scheduleTime: any) => void;
96
- 67: (channelNumber: any, softPedal: any, _scheduleTime: any) => void;
100
+ 67: (channelNumber: any, softPedal: any, scheduleTime: any) => void;
97
101
  71: (channelNumber: any, filterResonance: any, scheduleTime: any) => void;
98
102
  72: (channelNumber: any, releaseTime: any, _scheduleTime: any) => void;
99
103
  73: (channelNumber: any, attackTime: any, scheduleTime: any) => void;
@@ -141,7 +145,8 @@ export class Midy {
141
145
  };
142
146
  createChannels(audioContext: any): any[];
143
147
  createNoteBuffer(voiceParams: any, isSF3: any): Promise<any>;
144
- createNoteBufferNode(audioBuffer: any, voiceParams: any): any;
148
+ calcLoopMode(channel: any, note: any, voiceParams: any): boolean;
149
+ createBufferSource(channel: any, note: any, voiceParams: any, audioBuffer: any): any;
145
150
  findPortamentoTarget(queueIndex: any): any;
146
151
  scheduleTimelineEvents(t: any, offset: any, queueIndex: any): Promise<any>;
147
152
  getQueueIndex(second: any): number;
@@ -206,7 +211,7 @@ export class Midy {
206
211
  startVibrato(channel: any, note: any, scheduleTime: any): void;
207
212
  getAudioBuffer(program: any, noteNumber: any, velocity: any, voiceParams: any, isSF3: any): Promise<any>;
208
213
  createNote(channel: any, voice: any, noteNumber: any, velocity: any, startTime: any, portamento: any, isSF3: any): Promise<Note>;
209
- calcBank(channel: any, channelNumber: any): any;
214
+ calcBank(channel: any): any;
210
215
  scheduleNoteOn(channelNumber: any, noteNumber: any, velocity: any, startTime: any, portamento: any): Promise<void>;
211
216
  noteOn(channelNumber: any, noteNumber: any, velocity: any, scheduleTime: any): Promise<void>;
212
217
  stopNote(endTime: any, stopTime: any, scheduledNotes: any, index: any): Promise<any>;
@@ -256,7 +261,7 @@ export class Midy {
256
261
  64: (channelNumber: any, value: any, scheduleTime: any) => void;
257
262
  65: (channelNumber: any, value: any) => void;
258
263
  66: (channelNumber: any, value: any, scheduleTime: any) => void;
259
- 67: (channelNumber: any, softPedal: any, _scheduleTime: any) => void;
264
+ 67: (channelNumber: any, softPedal: any, scheduleTime: any) => void;
260
265
  71: (channelNumber: any, filterResonance: any, scheduleTime: any) => void;
261
266
  72: (channelNumber: any, releaseTime: any, _scheduleTime: any) => void;
262
267
  73: (channelNumber: any, attackTime: any, scheduleTime: any) => void;
@@ -299,7 +304,7 @@ export class Midy {
299
304
  setSustainPedal(channelNumber: any, value: any, scheduleTime: any): void;
300
305
  setPortamento(channelNumber: any, value: any): void;
301
306
  setSostenutoPedal(channelNumber: any, value: any, scheduleTime: any): void;
302
- setSoftPedal(channelNumber: any, softPedal: any, _scheduleTime: any): void;
307
+ setSoftPedal(channelNumber: any, softPedal: any, scheduleTime: any): void;
303
308
  setFilterResonance(channelNumber: any, filterResonance: any, scheduleTime: any): void;
304
309
  setReleaseTime(channelNumber: any, releaseTime: any, _scheduleTime: any): void;
305
310
  setAttackTime(channelNumber: any, attackTime: any, scheduleTime: any): void;
@@ -334,8 +339,8 @@ export class Midy {
334
339
  monoOn(channelNumber: any, value: any, scheduleTime: any): void;
335
340
  polyOn(channelNumber: any, value: any, scheduleTime: any): void;
336
341
  handleUniversalNonRealTimeExclusiveMessage(data: any, scheduleTime: any): void;
337
- GM1SystemOn(): void;
338
- GM2SystemOn(): void;
342
+ GM1SystemOn(scheduleTime: any): void;
343
+ GM2SystemOn(scheduleTime: any): void;
339
344
  handleUniversalRealTimeExclusiveMessage(data: any, scheduleTime: any): void;
340
345
  handleMasterVolumeSysEx(data: any, scheduleTime: any): void;
341
346
  setMasterVolume(volume: any, scheduleTime: any): void;
@@ -1 +1 @@
1
- {"version":3,"file":"midy.d.ts","sourceRoot":"","sources":["../src/midy.js"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"midy.d.ts","sourceRoot":"","sources":["../src/midy.js"],"names":[],"mappings":"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"}
package/script/midy.js CHANGED
@@ -249,6 +249,12 @@ const volumeEnvelopeKeys = [
249
249
  const volumeEnvelopeKeySet = new Set(volumeEnvelopeKeys);
250
250
  class Midy {
251
251
  constructor(audioContext, options = this.defaultOptions) {
252
+ Object.defineProperty(this, "mode", {
253
+ enumerable: true,
254
+ configurable: true,
255
+ writable: true,
256
+ value: "GM2"
257
+ });
252
258
  Object.defineProperty(this, "ticksPerBeat", {
253
259
  enumerable: true,
254
260
  configurable: true,
@@ -427,6 +433,11 @@ class Midy {
427
433
  this.audioContext = audioContext;
428
434
  this.options = { ...this.defaultOptions, ...options };
429
435
  this.masterVolume = new GainNode(audioContext);
436
+ this.scheduler = new GainNode(audioContext, { gain: 0 });
437
+ this.schedulerBuffer = new AudioBuffer({
438
+ length: 1,
439
+ sampleRate: audioContext.sampleRate,
440
+ });
430
441
  this.voiceParamsHandlers = this.createVoiceParamsHandlers();
431
442
  this.controlChangeHandlers = this.createControlChangeHandlers();
432
443
  this.channels = this.createChannels(audioContext);
@@ -435,6 +446,7 @@ class Midy {
435
446
  this.chorusEffect.output.connect(this.masterVolume);
436
447
  this.reverbEffect.output.connect(this.masterVolume);
437
448
  this.masterVolume.connect(audioContext.destination);
449
+ this.scheduler.connect(audioContext.destination);
438
450
  this.GM2SystemOn();
439
451
  }
440
452
  initSoundFontTable() {
@@ -534,10 +546,24 @@ class Midy {
534
546
  return audioBuffer;
535
547
  }
536
548
  }
537
- createNoteBufferNode(audioBuffer, voiceParams) {
549
+ calcLoopMode(channel, note, voiceParams) {
550
+ if (channel.isDrum) {
551
+ const noteNumber = note.noteNumber;
552
+ if (noteNumber === 88 || 47 <= noteNumber && noteNumber <= 84) {
553
+ return true;
554
+ }
555
+ else {
556
+ return false;
557
+ }
558
+ }
559
+ else {
560
+ return voiceParams.sampleModes % 2 !== 0;
561
+ }
562
+ }
563
+ createBufferSource(channel, note, voiceParams, audioBuffer) {
538
564
  const bufferSource = new AudioBufferSourceNode(this.audioContext);
539
565
  bufferSource.buffer = audioBuffer;
540
- bufferSource.loop = voiceParams.sampleModes % 2 !== 0;
566
+ bufferSource.loop = this.calcLoopMode(channel, note, voiceParams);
541
567
  if (bufferSource.loop) {
542
568
  bufferSource.loopStart = voiceParams.loopStart / voiceParams.sampleRate;
543
569
  bufferSource.loopEnd = voiceParams.loopEnd / voiceParams.sampleRate;
@@ -1017,7 +1043,9 @@ class Midy {
1017
1043
  return 8.176 * this.centToRate(cent);
1018
1044
  }
1019
1045
  calcChannelDetune(channel) {
1020
- const masterTuning = this.masterCoarseTuning + this.masterFineTuning;
1046
+ const masterTuning = channel.isDrum
1047
+ ? 0
1048
+ : this.masterCoarseTuning + this.masterFineTuning;
1021
1049
  const channelTuning = channel.coarseTuning + channel.fineTuning;
1022
1050
  const tuning = masterTuning + channelTuning;
1023
1051
  const pitchWheel = channel.state.pitchWheel * 2 - 1;
@@ -1044,9 +1072,8 @@ class Midy {
1044
1072
  .setValueAtTime(detune, scheduleTime);
1045
1073
  }
1046
1074
  getPortamentoTime(channel) {
1047
- const factor = 5 * Math.log(10) / 127;
1048
- const time = channel.state.portamentoTime;
1049
- return Math.log(time) / factor;
1075
+ const factor = 5 * Math.log(10) * 127;
1076
+ return channel.state.portamentoTime * factor;
1050
1077
  }
1051
1078
  setPortamentoStartVolumeEnvelope(channel, note, scheduleTime) {
1052
1079
  const { voiceParams, startTime } = note;
@@ -1210,7 +1237,7 @@ class Midy {
1210
1237
  const voiceParams = voice.getAllParams(controllerState);
1211
1238
  const note = new Note(noteNumber, velocity, startTime, voice, voiceParams);
1212
1239
  const audioBuffer = await this.getAudioBuffer(channel.program, noteNumber, velocity, voiceParams, isSF3);
1213
- note.bufferSource = this.createNoteBufferNode(audioBuffer, voiceParams);
1240
+ note.bufferSource = this.createBufferSource(channel, note, voiceParams, audioBuffer);
1214
1241
  note.volumeNode = new GainNode(this.audioContext);
1215
1242
  note.gainL = new GainNode(this.audioContext);
1216
1243
  note.gainR = new GainNode(this.audioContext);
@@ -1254,14 +1281,21 @@ class Midy {
1254
1281
  note.bufferSource.start(startTime);
1255
1282
  return note;
1256
1283
  }
1257
- calcBank(channel, channelNumber) {
1258
- if (channel.bankMSB === 121) {
1259
- return 0;
1260
- }
1261
- if (channelNumber % 9 <= 1 && channel.bankMSB === 120) {
1262
- return 128;
1284
+ calcBank(channel) {
1285
+ switch (this.mode) {
1286
+ case "GM1":
1287
+ if (channel.isDrum)
1288
+ return 128;
1289
+ return 0;
1290
+ case "GM2":
1291
+ if (channel.bankMSB === 121)
1292
+ return 0;
1293
+ if (channel.isDrum)
1294
+ return 128;
1295
+ return channel.bank;
1296
+ default:
1297
+ return channel.bank;
1263
1298
  }
1264
- return channel.bank;
1265
1299
  }
1266
1300
  async scheduleNoteOn(channelNumber, noteNumber, velocity, startTime, portamento) {
1267
1301
  const channel = this.channels[channelNumber];
@@ -1285,7 +1319,7 @@ class Midy {
1285
1319
  if (this.exclusiveClassMap.has(exclusiveClass)) {
1286
1320
  const prevEntry = this.exclusiveClassMap.get(exclusiveClass);
1287
1321
  const [prevNote, prevChannelNumber] = prevEntry;
1288
- if (!prevNote.ending) {
1322
+ if (prevNote && !prevNote.ending) {
1289
1323
  this.scheduleNoteOff(prevChannelNumber, prevNote.noteNumber, 0, // velocity,
1290
1324
  startTime, true, // force
1291
1325
  undefined);
@@ -1448,9 +1482,21 @@ class Midy {
1448
1482
  const channel = this.channels[channelNumber];
1449
1483
  channel.bank = channel.bankMSB * 128 + channel.bankLSB;
1450
1484
  channel.program = program;
1485
+ if (this.mode === "GM2") {
1486
+ switch (channel.bankMSB) {
1487
+ case 120:
1488
+ channel.isDrum = true;
1489
+ break;
1490
+ case 121:
1491
+ channel.isDrum = false;
1492
+ break;
1493
+ }
1494
+ }
1451
1495
  }
1452
1496
  handleChannelPressure(channelNumber, value, scheduleTime) {
1453
1497
  const channel = this.channels[channelNumber];
1498
+ if (channel.isDrum)
1499
+ return;
1454
1500
  const prev = channel.state.channelPressure;
1455
1501
  const next = value / 127;
1456
1502
  channel.state.channelPressure = next;
@@ -1469,8 +1515,10 @@ class Midy {
1469
1515
  this.setPitchBend(channelNumber, pitchBend, scheduleTime);
1470
1516
  }
1471
1517
  setPitchBend(channelNumber, value, scheduleTime) {
1472
- scheduleTime ??= this.audioContext.currentTime;
1473
1518
  const channel = this.channels[channelNumber];
1519
+ if (channel.isDrum)
1520
+ return;
1521
+ scheduleTime ??= this.audioContext.currentTime;
1474
1522
  const state = channel.state;
1475
1523
  const prev = state.pitchWheel * 2 - 1;
1476
1524
  const next = (value - 8192) / 8192;
@@ -1752,15 +1800,16 @@ class Midy {
1752
1800
  });
1753
1801
  }
1754
1802
  setModulationDepth(channelNumber, modulation, scheduleTime) {
1755
- scheduleTime ??= this.audioContext.currentTime;
1756
1803
  const channel = this.channels[channelNumber];
1804
+ if (channel.isDrum)
1805
+ return;
1806
+ scheduleTime ??= this.audioContext.currentTime;
1757
1807
  channel.state.modulationDepth = modulation / 127;
1758
1808
  this.updateModulation(channel, scheduleTime);
1759
1809
  }
1760
1810
  setPortamentoTime(channelNumber, portamentoTime) {
1761
1811
  const channel = this.channels[channelNumber];
1762
- const factor = 5 * Math.log(10) / 127;
1763
- channel.state.portamentoTime = Math.exp(factor * portamentoTime);
1812
+ channel.state.portamentoTime = portamentoTime / 127;
1764
1813
  }
1765
1814
  setKeyBasedVolume(channel, scheduleTime) {
1766
1815
  this.processScheduledNotes(channel, (note) => {
@@ -1832,8 +1881,10 @@ class Midy {
1832
1881
  .setValueAtTime(volume * gainRight, scheduleTime);
1833
1882
  }
1834
1883
  setSustainPedal(channelNumber, value, scheduleTime) {
1835
- scheduleTime ??= this.audioContext.currentTime;
1836
1884
  const channel = this.channels[channelNumber];
1885
+ if (channel.isDrum)
1886
+ return;
1887
+ scheduleTime ??= this.audioContext.currentTime;
1837
1888
  channel.state.sustainPedal = value / 127;
1838
1889
  if (64 <= value) {
1839
1890
  this.processScheduledNotes(channel, (note) => {
@@ -1845,11 +1896,16 @@ class Midy {
1845
1896
  }
1846
1897
  }
1847
1898
  setPortamento(channelNumber, value) {
1848
- this.channels[channelNumber].state.portamento = value / 127;
1899
+ const channel = this.channels[channelNumber];
1900
+ if (channel.isDrum)
1901
+ return;
1902
+ channel.state.portamento = value / 127;
1849
1903
  }
1850
1904
  setSostenutoPedal(channelNumber, value, scheduleTime) {
1851
- scheduleTime ??= this.audioContext.currentTime;
1852
1905
  const channel = this.channels[channelNumber];
1906
+ if (channel.isDrum)
1907
+ return;
1908
+ scheduleTime ??= this.audioContext.currentTime;
1853
1909
  channel.state.sostenutoPedal = value / 127;
1854
1910
  if (64 <= value) {
1855
1911
  channel.sostenutoNotes = this.getActiveNotes(channel, scheduleTime);
@@ -1858,13 +1914,28 @@ class Midy {
1858
1914
  this.releaseSostenutoPedal(channelNumber, value, scheduleTime);
1859
1915
  }
1860
1916
  }
1861
- setSoftPedal(channelNumber, softPedal, _scheduleTime) {
1917
+ setSoftPedal(channelNumber, softPedal, scheduleTime) {
1862
1918
  const channel = this.channels[channelNumber];
1919
+ if (channel.isDrum)
1920
+ return;
1921
+ scheduleTime ??= this.audioContext.currentTime;
1863
1922
  channel.state.softPedal = softPedal / 127;
1923
+ this.processScheduledNotes(channel, (note) => {
1924
+ if (note.portamento) {
1925
+ this.setPortamentoStartVolumeEnvelope(channel, note, scheduleTime);
1926
+ this.setPortamentoStartFilterEnvelope(channel, note, scheduleTime);
1927
+ }
1928
+ else {
1929
+ this.setVolumeEnvelope(channel, note, scheduleTime);
1930
+ this.setFilterEnvelope(channel, note, scheduleTime);
1931
+ }
1932
+ });
1864
1933
  }
1865
1934
  setFilterResonance(channelNumber, filterResonance, scheduleTime) {
1866
- scheduleTime ??= this.audioContext.currentTime;
1867
1935
  const channel = this.channels[channelNumber];
1936
+ if (channel.isDrum)
1937
+ return;
1938
+ scheduleTime ??= this.audioContext.currentTime;
1868
1939
  const state = channel.state;
1869
1940
  state.filterResonance = filterResonance / 64;
1870
1941
  this.processScheduledNotes(channel, (note) => {
@@ -1873,13 +1944,17 @@ class Midy {
1873
1944
  });
1874
1945
  }
1875
1946
  setReleaseTime(channelNumber, releaseTime, _scheduleTime) {
1876
- scheduleTime ??= this.audioContext.currentTime;
1877
1947
  const channel = this.channels[channelNumber];
1948
+ if (channel.isDrum)
1949
+ return;
1950
+ scheduleTime ??= this.audioContext.currentTime;
1878
1951
  channel.state.releaseTime = releaseTime / 64;
1879
1952
  }
1880
1953
  setAttackTime(channelNumber, attackTime, scheduleTime) {
1881
- scheduleTime ??= this.audioContext.currentTime;
1882
1954
  const channel = this.channels[channelNumber];
1955
+ if (channel.isDrum)
1956
+ return;
1957
+ scheduleTime ??= this.audioContext.currentTime;
1883
1958
  channel.state.attackTime = attackTime / 64;
1884
1959
  this.processScheduledNotes(channel, (note) => {
1885
1960
  if (note.startTime < scheduleTime)
@@ -1888,8 +1963,10 @@ class Midy {
1888
1963
  });
1889
1964
  }
1890
1965
  setBrightness(channelNumber, brightness, scheduleTime) {
1891
- scheduleTime ??= this.audioContext.currentTime;
1892
1966
  const channel = this.channels[channelNumber];
1967
+ if (channel.isDrum)
1968
+ return;
1969
+ scheduleTime ??= this.audioContext.currentTime;
1893
1970
  channel.state.brightness = brightness / 64;
1894
1971
  this.processScheduledNotes(channel, (note) => {
1895
1972
  if (note.portamento) {
@@ -1901,16 +1978,20 @@ class Midy {
1901
1978
  });
1902
1979
  }
1903
1980
  setDecayTime(channelNumber, dacayTime, scheduleTime) {
1904
- scheduleTime ??= this.audioContext.currentTime;
1905
1981
  const channel = this.channels[channelNumber];
1982
+ if (channel.isDrum)
1983
+ return;
1984
+ scheduleTime ??= this.audioContext.currentTime;
1906
1985
  channel.state.decayTime = dacayTime / 64;
1907
1986
  this.processScheduledNotes(channel, (note) => {
1908
1987
  this.setVolumeEnvelope(channel, note, scheduleTime);
1909
1988
  });
1910
1989
  }
1911
1990
  setVibratoRate(channelNumber, vibratoRate, scheduleTime) {
1912
- scheduleTime ??= this.audioContext.currentTime;
1913
1991
  const channel = this.channels[channelNumber];
1992
+ if (channel.isDrum)
1993
+ return;
1994
+ scheduleTime ??= this.audioContext.currentTime;
1914
1995
  channel.state.vibratoRate = vibratoRate / 64;
1915
1996
  if (channel.vibratoDepth <= 0)
1916
1997
  return;
@@ -1919,8 +2000,10 @@ class Midy {
1919
2000
  });
1920
2001
  }
1921
2002
  setVibratoDepth(channelNumber, vibratoDepth, scheduleTime) {
1922
- scheduleTime ??= this.audioContext.currentTime;
1923
2003
  const channel = this.channels[channelNumber];
2004
+ if (channel.isDrum)
2005
+ return;
2006
+ scheduleTime ??= this.audioContext.currentTime;
1924
2007
  const prev = channel.state.vibratoDepth;
1925
2008
  channel.state.vibratoDepth = vibratoDepth / 64;
1926
2009
  if (0 < prev) {
@@ -1935,8 +2018,10 @@ class Midy {
1935
2018
  }
1936
2019
  }
1937
2020
  setVibratoDelay(channelNumber, vibratoDelay) {
1938
- scheduleTime ??= this.audioContext.currentTime;
1939
2021
  const channel = this.channels[channelNumber];
2022
+ if (channel.isDrum)
2023
+ return;
2024
+ scheduleTime ??= this.audioContext.currentTime;
1940
2025
  channel.state.vibratoDelay = vibratoDelay / 64;
1941
2026
  if (0 < channel.state.vibratoDepth) {
1942
2027
  this.processScheduledNotes(channel, (note) => {
@@ -2083,8 +2168,10 @@ class Midy {
2083
2168
  this.setPitchBendRange(channelNumber, pitchBendRange, scheduleTime);
2084
2169
  }
2085
2170
  setPitchBendRange(channelNumber, value, scheduleTime) {
2086
- scheduleTime ??= this.audioContext.currentTime;
2087
2171
  const channel = this.channels[channelNumber];
2172
+ if (channel.isDrum)
2173
+ return;
2174
+ scheduleTime ??= this.audioContext.currentTime;
2088
2175
  const state = channel.state;
2089
2176
  const prev = state.pitchWheelSensitivity;
2090
2177
  const next = value / 128;
@@ -2100,8 +2187,10 @@ class Midy {
2100
2187
  this.setFineTuning(channelNumber, fineTuning, scheduleTime);
2101
2188
  }
2102
2189
  setFineTuning(channelNumber, value, scheduleTime) {
2103
- scheduleTime ??= this.audioContext.currentTime;
2104
2190
  const channel = this.channels[channelNumber];
2191
+ if (channel.isDrum)
2192
+ return;
2193
+ scheduleTime ??= this.audioContext.currentTime;
2105
2194
  const prev = channel.fineTuning;
2106
2195
  const next = (value - 8192) / 8.192; // cent
2107
2196
  channel.fineTuning = next;
@@ -2115,8 +2204,10 @@ class Midy {
2115
2204
  this.setCoarseTuning(channelNumber, coarseTuning, scheduleTime);
2116
2205
  }
2117
2206
  setCoarseTuning(channelNumber, value, scheduleTime) {
2118
- scheduleTime ??= this.audioContext.currentTime;
2119
2207
  const channel = this.channels[channelNumber];
2208
+ if (channel.isDrum)
2209
+ return;
2210
+ scheduleTime ??= this.audioContext.currentTime;
2120
2211
  const prev = channel.coarseTuning;
2121
2212
  const next = (value - 64) * 100; // cent
2122
2213
  channel.coarseTuning = next;
@@ -2130,8 +2221,10 @@ class Midy {
2130
2221
  this.setModulationDepthRange(channelNumber, modulationDepthRange, scheduleTime);
2131
2222
  }
2132
2223
  setModulationDepthRange(channelNumber, modulationDepthRange, scheduleTime) {
2133
- scheduleTime ??= this.audioContext.currentTime;
2134
2224
  const channel = this.channels[channelNumber];
2225
+ if (channel.isDrum)
2226
+ return;
2227
+ scheduleTime ??= this.audioContext.currentTime;
2135
2228
  channel.modulationDepthRange = modulationDepthRange;
2136
2229
  this.updateModulation(channel, scheduleTime);
2137
2230
  }
@@ -2202,12 +2295,12 @@ class Midy {
2202
2295
  case 9:
2203
2296
  switch (data[3]) {
2204
2297
  case 1:
2205
- this.GM1SystemOn();
2298
+ this.GM1SystemOn(scheduleTime);
2206
2299
  break;
2207
2300
  case 2: // GM System Off
2208
2301
  break;
2209
2302
  case 3:
2210
- this.GM2SystemOn();
2303
+ this.GM2SystemOn(scheduleTime);
2211
2304
  break;
2212
2305
  default:
2213
2306
  console.warn(`Unsupported Exclusive Message: ${data}`);
@@ -2217,25 +2310,35 @@ class Midy {
2217
2310
  console.warn(`Unsupported Exclusive Message: ${data}`);
2218
2311
  }
2219
2312
  }
2220
- GM1SystemOn() {
2313
+ GM1SystemOn(scheduleTime) {
2314
+ scheduleTime ??= this.audioContext.currentTime;
2315
+ this.mode = "GM1";
2221
2316
  for (let i = 0; i < this.channels.length; i++) {
2317
+ this.allSoundOff(i, 0, scheduleTime);
2222
2318
  const channel = this.channels[i];
2223
2319
  channel.bankMSB = 0;
2224
2320
  channel.bankLSB = 0;
2225
2321
  channel.bank = 0;
2322
+ channel.isDrum = false;
2226
2323
  }
2227
2324
  this.channels[9].bankMSB = 1;
2228
2325
  this.channels[9].bank = 128;
2326
+ this.channels[9].isDrum = true;
2229
2327
  }
2230
- GM2SystemOn() {
2328
+ GM2SystemOn(scheduleTime) {
2329
+ scheduleTime ??= this.audioContext.currentTime;
2330
+ this.mode = "GM2";
2231
2331
  for (let i = 0; i < this.channels.length; i++) {
2332
+ this.allSoundOff(i, 0, scheduleTime);
2232
2333
  const channel = this.channels[i];
2233
2334
  channel.bankMSB = 121;
2234
2335
  channel.bankLSB = 0;
2235
2336
  channel.bank = 121 * 128;
2337
+ channel.isDrum = false;
2236
2338
  }
2237
2339
  this.channels[9].bankMSB = 120;
2238
2340
  this.channels[9].bank = 120 * 128;
2341
+ this.channels[9].isDrum = true;
2239
2342
  }
2240
2343
  handleUniversalRealTimeExclusiveMessage(data, scheduleTime) {
2241
2344
  switch (data[2]) {
@@ -2311,8 +2414,14 @@ class Midy {
2311
2414
  const prev = this.masterFineTuning;
2312
2415
  const next = (value - 8192) / 8.192; // cent
2313
2416
  this.masterFineTuning = next;
2314
- channel.detune += next - prev;
2315
- this.updateChannelDetune(channel, scheduleTime);
2417
+ const detuneChange = next - prev;
2418
+ for (let i = 0; i < this.channels.length; i++) {
2419
+ const channel = this.channels[i];
2420
+ if (channel.isDrum)
2421
+ continue;
2422
+ channel.detune += detuneChange;
2423
+ this.updateChannelDetune(channel, scheduleTime);
2424
+ }
2316
2425
  }
2317
2426
  handleMasterCoarseTuningSysEx(data, scheduleTime) {
2318
2427
  const coarseTuning = data[4];
@@ -2322,8 +2431,14 @@ class Midy {
2322
2431
  const prev = this.masterCoarseTuning;
2323
2432
  const next = (value - 64) * 100; // cent
2324
2433
  this.masterCoarseTuning = next;
2325
- channel.detune += next - prev;
2326
- this.updateChannelDetune(channel, scheduleTime);
2434
+ const detuneChange = next - prev;
2435
+ for (let i = 0; i < this.channels.length; i++) {
2436
+ const channel = this.channels[i];
2437
+ if (channel.isDrum)
2438
+ continue;
2439
+ channel.detune += detuneChange;
2440
+ this.updateChannelDetune(channel, scheduleTime);
2441
+ }
2327
2442
  }
2328
2443
  handleGlobalParameterControlSysEx(data, scheduleTime) {
2329
2444
  if (data[7] === 1) {
@@ -2535,6 +2650,8 @@ class Midy {
2535
2650
  if (!channelBitmap[i])
2536
2651
  continue;
2537
2652
  const channel = this.channels[i];
2653
+ if (channel.isDrum)
2654
+ continue;
2538
2655
  for (let j = 0; j < 12; j++) {
2539
2656
  const centValue = data[j + 7] - 64;
2540
2657
  channel.scaleOctaveTuningTable[j] = centValue;
@@ -2553,6 +2670,8 @@ class Midy {
2553
2670
  if (!channelBitmap[i])
2554
2671
  continue;
2555
2672
  const channel = this.channels[i];
2673
+ if (channel.isDrum)
2674
+ continue;
2556
2675
  for (let j = 0; j < 12; j++) {
2557
2676
  const index = 7 + j * 2;
2558
2677
  const msb = data[index] & 0x7F;
@@ -2623,7 +2742,10 @@ class Midy {
2623
2742
  }
2624
2743
  handlePressureSysEx(data, tableName) {
2625
2744
  const channelNumber = data[4];
2626
- const table = this.channels[channelNumber][tableName];
2745
+ const channel = this.channels[channelNumber];
2746
+ if (channel.isDrum)
2747
+ return;
2748
+ const table = channel[tableName];
2627
2749
  for (let i = 5; i < data.length - 1; i += 2) {
2628
2750
  const pp = data[i];
2629
2751
  const rr = data[i + 1];
@@ -2651,8 +2773,11 @@ class Midy {
2651
2773
  }
2652
2774
  handleControlChangeSysEx(data) {
2653
2775
  const channelNumber = data[4];
2776
+ const channel = this.channels[channelNumber];
2777
+ if (channel.isDrum)
2778
+ return;
2654
2779
  const controllerType = data[5];
2655
- const table = this.channels[channelNumber].controlTable[controllerType];
2780
+ const table = channel.controlTable[controllerType];
2656
2781
  for (let i = 6; i < data.length - 1; i += 2) {
2657
2782
  const pp = data[i];
2658
2783
  const rr = data[i + 1];
@@ -2666,8 +2791,11 @@ class Midy {
2666
2791
  }
2667
2792
  handleKeyBasedInstrumentControlSysEx(data, scheduleTime) {
2668
2793
  const channelNumber = data[4];
2794
+ const channel = this.channels[channelNumber];
2795
+ if (channel.isDrum)
2796
+ return;
2669
2797
  const keyNumber = data[5];
2670
- const table = this.channels[channelNumber].keyBasedInstrumentControlTable;
2798
+ const table = channel.keyBasedInstrumentControlTable;
2671
2799
  for (let i = 6; i < data.length - 1; i += 2) {
2672
2800
  const controllerType = data[i];
2673
2801
  const value = data[i + 1];
@@ -2688,13 +2816,20 @@ class Midy {
2688
2816
  }
2689
2817
  scheduleTask(callback, scheduleTime) {
2690
2818
  return new Promise((resolve) => {
2691
- const bufferSource = new AudioBufferSourceNode(this.audioContext);
2819
+ const bufferSource = new AudioBufferSourceNode(this.audioContext, {
2820
+ buffer: this.schedulerBuffer,
2821
+ });
2822
+ bufferSource.connect(this.scheduler);
2692
2823
  bufferSource.onended = () => {
2693
- callback();
2694
- resolve();
2824
+ try {
2825
+ callback();
2826
+ }
2827
+ finally {
2828
+ bufferSource.disconnect();
2829
+ resolve();
2830
+ }
2695
2831
  };
2696
2832
  bufferSource.start(scheduleTime);
2697
- bufferSource.stop(scheduleTime);
2698
2833
  });
2699
2834
  }
2700
2835
  }
@@ -2705,6 +2840,7 @@ Object.defineProperty(Midy, "channelSettings", {
2705
2840
  writable: true,
2706
2841
  value: {
2707
2842
  currentBufferSource: null,
2843
+ isDrum: false,
2708
2844
  detune: 0,
2709
2845
  program: 0,
2710
2846
  bank: 121 * 128,
@@ -2715,8 +2851,8 @@ Object.defineProperty(Midy, "channelSettings", {
2715
2851
  rpnMSB: 127,
2716
2852
  rpnLSB: 127,
2717
2853
  mono: false, // CC#124, CC#125
2854
+ modulationDepthRange: 50, // cent
2718
2855
  fineTuning: 0, // cb
2719
2856
  coarseTuning: 0, // cb
2720
- modulationDepthRange: 50, // cent
2721
2857
  }
2722
2858
  });