@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/esm/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;
package/esm/midy.d.ts.map CHANGED
@@ -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/esm/midy.js CHANGED
@@ -246,6 +246,12 @@ const volumeEnvelopeKeys = [
246
246
  const volumeEnvelopeKeySet = new Set(volumeEnvelopeKeys);
247
247
  export class Midy {
248
248
  constructor(audioContext, options = this.defaultOptions) {
249
+ Object.defineProperty(this, "mode", {
250
+ enumerable: true,
251
+ configurable: true,
252
+ writable: true,
253
+ value: "GM2"
254
+ });
249
255
  Object.defineProperty(this, "ticksPerBeat", {
250
256
  enumerable: true,
251
257
  configurable: true,
@@ -424,6 +430,11 @@ export class Midy {
424
430
  this.audioContext = audioContext;
425
431
  this.options = { ...this.defaultOptions, ...options };
426
432
  this.masterVolume = new GainNode(audioContext);
433
+ this.scheduler = new GainNode(audioContext, { gain: 0 });
434
+ this.schedulerBuffer = new AudioBuffer({
435
+ length: 1,
436
+ sampleRate: audioContext.sampleRate,
437
+ });
427
438
  this.voiceParamsHandlers = this.createVoiceParamsHandlers();
428
439
  this.controlChangeHandlers = this.createControlChangeHandlers();
429
440
  this.channels = this.createChannels(audioContext);
@@ -432,6 +443,7 @@ export class Midy {
432
443
  this.chorusEffect.output.connect(this.masterVolume);
433
444
  this.reverbEffect.output.connect(this.masterVolume);
434
445
  this.masterVolume.connect(audioContext.destination);
446
+ this.scheduler.connect(audioContext.destination);
435
447
  this.GM2SystemOn();
436
448
  }
437
449
  initSoundFontTable() {
@@ -531,10 +543,24 @@ export class Midy {
531
543
  return audioBuffer;
532
544
  }
533
545
  }
534
- createNoteBufferNode(audioBuffer, voiceParams) {
546
+ calcLoopMode(channel, note, voiceParams) {
547
+ if (channel.isDrum) {
548
+ const noteNumber = note.noteNumber;
549
+ if (noteNumber === 88 || 47 <= noteNumber && noteNumber <= 84) {
550
+ return true;
551
+ }
552
+ else {
553
+ return false;
554
+ }
555
+ }
556
+ else {
557
+ return voiceParams.sampleModes % 2 !== 0;
558
+ }
559
+ }
560
+ createBufferSource(channel, note, voiceParams, audioBuffer) {
535
561
  const bufferSource = new AudioBufferSourceNode(this.audioContext);
536
562
  bufferSource.buffer = audioBuffer;
537
- bufferSource.loop = voiceParams.sampleModes % 2 !== 0;
563
+ bufferSource.loop = this.calcLoopMode(channel, note, voiceParams);
538
564
  if (bufferSource.loop) {
539
565
  bufferSource.loopStart = voiceParams.loopStart / voiceParams.sampleRate;
540
566
  bufferSource.loopEnd = voiceParams.loopEnd / voiceParams.sampleRate;
@@ -1014,7 +1040,9 @@ export class Midy {
1014
1040
  return 8.176 * this.centToRate(cent);
1015
1041
  }
1016
1042
  calcChannelDetune(channel) {
1017
- const masterTuning = this.masterCoarseTuning + this.masterFineTuning;
1043
+ const masterTuning = channel.isDrum
1044
+ ? 0
1045
+ : this.masterCoarseTuning + this.masterFineTuning;
1018
1046
  const channelTuning = channel.coarseTuning + channel.fineTuning;
1019
1047
  const tuning = masterTuning + channelTuning;
1020
1048
  const pitchWheel = channel.state.pitchWheel * 2 - 1;
@@ -1041,9 +1069,8 @@ export class Midy {
1041
1069
  .setValueAtTime(detune, scheduleTime);
1042
1070
  }
1043
1071
  getPortamentoTime(channel) {
1044
- const factor = 5 * Math.log(10) / 127;
1045
- const time = channel.state.portamentoTime;
1046
- return Math.log(time) / factor;
1072
+ const factor = 5 * Math.log(10) * 127;
1073
+ return channel.state.portamentoTime * factor;
1047
1074
  }
1048
1075
  setPortamentoStartVolumeEnvelope(channel, note, scheduleTime) {
1049
1076
  const { voiceParams, startTime } = note;
@@ -1207,7 +1234,7 @@ export class Midy {
1207
1234
  const voiceParams = voice.getAllParams(controllerState);
1208
1235
  const note = new Note(noteNumber, velocity, startTime, voice, voiceParams);
1209
1236
  const audioBuffer = await this.getAudioBuffer(channel.program, noteNumber, velocity, voiceParams, isSF3);
1210
- note.bufferSource = this.createNoteBufferNode(audioBuffer, voiceParams);
1237
+ note.bufferSource = this.createBufferSource(channel, note, voiceParams, audioBuffer);
1211
1238
  note.volumeNode = new GainNode(this.audioContext);
1212
1239
  note.gainL = new GainNode(this.audioContext);
1213
1240
  note.gainR = new GainNode(this.audioContext);
@@ -1251,14 +1278,21 @@ export class Midy {
1251
1278
  note.bufferSource.start(startTime);
1252
1279
  return note;
1253
1280
  }
1254
- calcBank(channel, channelNumber) {
1255
- if (channel.bankMSB === 121) {
1256
- return 0;
1257
- }
1258
- if (channelNumber % 9 <= 1 && channel.bankMSB === 120) {
1259
- return 128;
1281
+ calcBank(channel) {
1282
+ switch (this.mode) {
1283
+ case "GM1":
1284
+ if (channel.isDrum)
1285
+ return 128;
1286
+ return 0;
1287
+ case "GM2":
1288
+ if (channel.bankMSB === 121)
1289
+ return 0;
1290
+ if (channel.isDrum)
1291
+ return 128;
1292
+ return channel.bank;
1293
+ default:
1294
+ return channel.bank;
1260
1295
  }
1261
- return channel.bank;
1262
1296
  }
1263
1297
  async scheduleNoteOn(channelNumber, noteNumber, velocity, startTime, portamento) {
1264
1298
  const channel = this.channels[channelNumber];
@@ -1282,7 +1316,7 @@ export class Midy {
1282
1316
  if (this.exclusiveClassMap.has(exclusiveClass)) {
1283
1317
  const prevEntry = this.exclusiveClassMap.get(exclusiveClass);
1284
1318
  const [prevNote, prevChannelNumber] = prevEntry;
1285
- if (!prevNote.ending) {
1319
+ if (prevNote && !prevNote.ending) {
1286
1320
  this.scheduleNoteOff(prevChannelNumber, prevNote.noteNumber, 0, // velocity,
1287
1321
  startTime, true, // force
1288
1322
  undefined);
@@ -1445,9 +1479,21 @@ export class Midy {
1445
1479
  const channel = this.channels[channelNumber];
1446
1480
  channel.bank = channel.bankMSB * 128 + channel.bankLSB;
1447
1481
  channel.program = program;
1482
+ if (this.mode === "GM2") {
1483
+ switch (channel.bankMSB) {
1484
+ case 120:
1485
+ channel.isDrum = true;
1486
+ break;
1487
+ case 121:
1488
+ channel.isDrum = false;
1489
+ break;
1490
+ }
1491
+ }
1448
1492
  }
1449
1493
  handleChannelPressure(channelNumber, value, scheduleTime) {
1450
1494
  const channel = this.channels[channelNumber];
1495
+ if (channel.isDrum)
1496
+ return;
1451
1497
  const prev = channel.state.channelPressure;
1452
1498
  const next = value / 127;
1453
1499
  channel.state.channelPressure = next;
@@ -1466,8 +1512,10 @@ export class Midy {
1466
1512
  this.setPitchBend(channelNumber, pitchBend, scheduleTime);
1467
1513
  }
1468
1514
  setPitchBend(channelNumber, value, scheduleTime) {
1469
- scheduleTime ??= this.audioContext.currentTime;
1470
1515
  const channel = this.channels[channelNumber];
1516
+ if (channel.isDrum)
1517
+ return;
1518
+ scheduleTime ??= this.audioContext.currentTime;
1471
1519
  const state = channel.state;
1472
1520
  const prev = state.pitchWheel * 2 - 1;
1473
1521
  const next = (value - 8192) / 8192;
@@ -1749,15 +1797,16 @@ export class Midy {
1749
1797
  });
1750
1798
  }
1751
1799
  setModulationDepth(channelNumber, modulation, scheduleTime) {
1752
- scheduleTime ??= this.audioContext.currentTime;
1753
1800
  const channel = this.channels[channelNumber];
1801
+ if (channel.isDrum)
1802
+ return;
1803
+ scheduleTime ??= this.audioContext.currentTime;
1754
1804
  channel.state.modulationDepth = modulation / 127;
1755
1805
  this.updateModulation(channel, scheduleTime);
1756
1806
  }
1757
1807
  setPortamentoTime(channelNumber, portamentoTime) {
1758
1808
  const channel = this.channels[channelNumber];
1759
- const factor = 5 * Math.log(10) / 127;
1760
- channel.state.portamentoTime = Math.exp(factor * portamentoTime);
1809
+ channel.state.portamentoTime = portamentoTime / 127;
1761
1810
  }
1762
1811
  setKeyBasedVolume(channel, scheduleTime) {
1763
1812
  this.processScheduledNotes(channel, (note) => {
@@ -1829,8 +1878,10 @@ export class Midy {
1829
1878
  .setValueAtTime(volume * gainRight, scheduleTime);
1830
1879
  }
1831
1880
  setSustainPedal(channelNumber, value, scheduleTime) {
1832
- scheduleTime ??= this.audioContext.currentTime;
1833
1881
  const channel = this.channels[channelNumber];
1882
+ if (channel.isDrum)
1883
+ return;
1884
+ scheduleTime ??= this.audioContext.currentTime;
1834
1885
  channel.state.sustainPedal = value / 127;
1835
1886
  if (64 <= value) {
1836
1887
  this.processScheduledNotes(channel, (note) => {
@@ -1842,11 +1893,16 @@ export class Midy {
1842
1893
  }
1843
1894
  }
1844
1895
  setPortamento(channelNumber, value) {
1845
- this.channels[channelNumber].state.portamento = value / 127;
1896
+ const channel = this.channels[channelNumber];
1897
+ if (channel.isDrum)
1898
+ return;
1899
+ channel.state.portamento = value / 127;
1846
1900
  }
1847
1901
  setSostenutoPedal(channelNumber, value, scheduleTime) {
1848
- scheduleTime ??= this.audioContext.currentTime;
1849
1902
  const channel = this.channels[channelNumber];
1903
+ if (channel.isDrum)
1904
+ return;
1905
+ scheduleTime ??= this.audioContext.currentTime;
1850
1906
  channel.state.sostenutoPedal = value / 127;
1851
1907
  if (64 <= value) {
1852
1908
  channel.sostenutoNotes = this.getActiveNotes(channel, scheduleTime);
@@ -1855,13 +1911,28 @@ export class Midy {
1855
1911
  this.releaseSostenutoPedal(channelNumber, value, scheduleTime);
1856
1912
  }
1857
1913
  }
1858
- setSoftPedal(channelNumber, softPedal, _scheduleTime) {
1914
+ setSoftPedal(channelNumber, softPedal, scheduleTime) {
1859
1915
  const channel = this.channels[channelNumber];
1916
+ if (channel.isDrum)
1917
+ return;
1918
+ scheduleTime ??= this.audioContext.currentTime;
1860
1919
  channel.state.softPedal = softPedal / 127;
1920
+ this.processScheduledNotes(channel, (note) => {
1921
+ if (note.portamento) {
1922
+ this.setPortamentoStartVolumeEnvelope(channel, note, scheduleTime);
1923
+ this.setPortamentoStartFilterEnvelope(channel, note, scheduleTime);
1924
+ }
1925
+ else {
1926
+ this.setVolumeEnvelope(channel, note, scheduleTime);
1927
+ this.setFilterEnvelope(channel, note, scheduleTime);
1928
+ }
1929
+ });
1861
1930
  }
1862
1931
  setFilterResonance(channelNumber, filterResonance, scheduleTime) {
1863
- scheduleTime ??= this.audioContext.currentTime;
1864
1932
  const channel = this.channels[channelNumber];
1933
+ if (channel.isDrum)
1934
+ return;
1935
+ scheduleTime ??= this.audioContext.currentTime;
1865
1936
  const state = channel.state;
1866
1937
  state.filterResonance = filterResonance / 64;
1867
1938
  this.processScheduledNotes(channel, (note) => {
@@ -1870,13 +1941,17 @@ export class Midy {
1870
1941
  });
1871
1942
  }
1872
1943
  setReleaseTime(channelNumber, releaseTime, _scheduleTime) {
1873
- scheduleTime ??= this.audioContext.currentTime;
1874
1944
  const channel = this.channels[channelNumber];
1945
+ if (channel.isDrum)
1946
+ return;
1947
+ scheduleTime ??= this.audioContext.currentTime;
1875
1948
  channel.state.releaseTime = releaseTime / 64;
1876
1949
  }
1877
1950
  setAttackTime(channelNumber, attackTime, scheduleTime) {
1878
- scheduleTime ??= this.audioContext.currentTime;
1879
1951
  const channel = this.channels[channelNumber];
1952
+ if (channel.isDrum)
1953
+ return;
1954
+ scheduleTime ??= this.audioContext.currentTime;
1880
1955
  channel.state.attackTime = attackTime / 64;
1881
1956
  this.processScheduledNotes(channel, (note) => {
1882
1957
  if (note.startTime < scheduleTime)
@@ -1885,8 +1960,10 @@ export class Midy {
1885
1960
  });
1886
1961
  }
1887
1962
  setBrightness(channelNumber, brightness, scheduleTime) {
1888
- scheduleTime ??= this.audioContext.currentTime;
1889
1963
  const channel = this.channels[channelNumber];
1964
+ if (channel.isDrum)
1965
+ return;
1966
+ scheduleTime ??= this.audioContext.currentTime;
1890
1967
  channel.state.brightness = brightness / 64;
1891
1968
  this.processScheduledNotes(channel, (note) => {
1892
1969
  if (note.portamento) {
@@ -1898,16 +1975,20 @@ export class Midy {
1898
1975
  });
1899
1976
  }
1900
1977
  setDecayTime(channelNumber, dacayTime, scheduleTime) {
1901
- scheduleTime ??= this.audioContext.currentTime;
1902
1978
  const channel = this.channels[channelNumber];
1979
+ if (channel.isDrum)
1980
+ return;
1981
+ scheduleTime ??= this.audioContext.currentTime;
1903
1982
  channel.state.decayTime = dacayTime / 64;
1904
1983
  this.processScheduledNotes(channel, (note) => {
1905
1984
  this.setVolumeEnvelope(channel, note, scheduleTime);
1906
1985
  });
1907
1986
  }
1908
1987
  setVibratoRate(channelNumber, vibratoRate, scheduleTime) {
1909
- scheduleTime ??= this.audioContext.currentTime;
1910
1988
  const channel = this.channels[channelNumber];
1989
+ if (channel.isDrum)
1990
+ return;
1991
+ scheduleTime ??= this.audioContext.currentTime;
1911
1992
  channel.state.vibratoRate = vibratoRate / 64;
1912
1993
  if (channel.vibratoDepth <= 0)
1913
1994
  return;
@@ -1916,8 +1997,10 @@ export class Midy {
1916
1997
  });
1917
1998
  }
1918
1999
  setVibratoDepth(channelNumber, vibratoDepth, scheduleTime) {
1919
- scheduleTime ??= this.audioContext.currentTime;
1920
2000
  const channel = this.channels[channelNumber];
2001
+ if (channel.isDrum)
2002
+ return;
2003
+ scheduleTime ??= this.audioContext.currentTime;
1921
2004
  const prev = channel.state.vibratoDepth;
1922
2005
  channel.state.vibratoDepth = vibratoDepth / 64;
1923
2006
  if (0 < prev) {
@@ -1932,8 +2015,10 @@ export class Midy {
1932
2015
  }
1933
2016
  }
1934
2017
  setVibratoDelay(channelNumber, vibratoDelay) {
1935
- scheduleTime ??= this.audioContext.currentTime;
1936
2018
  const channel = this.channels[channelNumber];
2019
+ if (channel.isDrum)
2020
+ return;
2021
+ scheduleTime ??= this.audioContext.currentTime;
1937
2022
  channel.state.vibratoDelay = vibratoDelay / 64;
1938
2023
  if (0 < channel.state.vibratoDepth) {
1939
2024
  this.processScheduledNotes(channel, (note) => {
@@ -2080,8 +2165,10 @@ export class Midy {
2080
2165
  this.setPitchBendRange(channelNumber, pitchBendRange, scheduleTime);
2081
2166
  }
2082
2167
  setPitchBendRange(channelNumber, value, scheduleTime) {
2083
- scheduleTime ??= this.audioContext.currentTime;
2084
2168
  const channel = this.channels[channelNumber];
2169
+ if (channel.isDrum)
2170
+ return;
2171
+ scheduleTime ??= this.audioContext.currentTime;
2085
2172
  const state = channel.state;
2086
2173
  const prev = state.pitchWheelSensitivity;
2087
2174
  const next = value / 128;
@@ -2097,8 +2184,10 @@ export class Midy {
2097
2184
  this.setFineTuning(channelNumber, fineTuning, scheduleTime);
2098
2185
  }
2099
2186
  setFineTuning(channelNumber, value, scheduleTime) {
2100
- scheduleTime ??= this.audioContext.currentTime;
2101
2187
  const channel = this.channels[channelNumber];
2188
+ if (channel.isDrum)
2189
+ return;
2190
+ scheduleTime ??= this.audioContext.currentTime;
2102
2191
  const prev = channel.fineTuning;
2103
2192
  const next = (value - 8192) / 8.192; // cent
2104
2193
  channel.fineTuning = next;
@@ -2112,8 +2201,10 @@ export class Midy {
2112
2201
  this.setCoarseTuning(channelNumber, coarseTuning, scheduleTime);
2113
2202
  }
2114
2203
  setCoarseTuning(channelNumber, value, scheduleTime) {
2115
- scheduleTime ??= this.audioContext.currentTime;
2116
2204
  const channel = this.channels[channelNumber];
2205
+ if (channel.isDrum)
2206
+ return;
2207
+ scheduleTime ??= this.audioContext.currentTime;
2117
2208
  const prev = channel.coarseTuning;
2118
2209
  const next = (value - 64) * 100; // cent
2119
2210
  channel.coarseTuning = next;
@@ -2127,8 +2218,10 @@ export class Midy {
2127
2218
  this.setModulationDepthRange(channelNumber, modulationDepthRange, scheduleTime);
2128
2219
  }
2129
2220
  setModulationDepthRange(channelNumber, modulationDepthRange, scheduleTime) {
2130
- scheduleTime ??= this.audioContext.currentTime;
2131
2221
  const channel = this.channels[channelNumber];
2222
+ if (channel.isDrum)
2223
+ return;
2224
+ scheduleTime ??= this.audioContext.currentTime;
2132
2225
  channel.modulationDepthRange = modulationDepthRange;
2133
2226
  this.updateModulation(channel, scheduleTime);
2134
2227
  }
@@ -2199,12 +2292,12 @@ export class Midy {
2199
2292
  case 9:
2200
2293
  switch (data[3]) {
2201
2294
  case 1:
2202
- this.GM1SystemOn();
2295
+ this.GM1SystemOn(scheduleTime);
2203
2296
  break;
2204
2297
  case 2: // GM System Off
2205
2298
  break;
2206
2299
  case 3:
2207
- this.GM2SystemOn();
2300
+ this.GM2SystemOn(scheduleTime);
2208
2301
  break;
2209
2302
  default:
2210
2303
  console.warn(`Unsupported Exclusive Message: ${data}`);
@@ -2214,25 +2307,35 @@ export class Midy {
2214
2307
  console.warn(`Unsupported Exclusive Message: ${data}`);
2215
2308
  }
2216
2309
  }
2217
- GM1SystemOn() {
2310
+ GM1SystemOn(scheduleTime) {
2311
+ scheduleTime ??= this.audioContext.currentTime;
2312
+ this.mode = "GM1";
2218
2313
  for (let i = 0; i < this.channels.length; i++) {
2314
+ this.allSoundOff(i, 0, scheduleTime);
2219
2315
  const channel = this.channels[i];
2220
2316
  channel.bankMSB = 0;
2221
2317
  channel.bankLSB = 0;
2222
2318
  channel.bank = 0;
2319
+ channel.isDrum = false;
2223
2320
  }
2224
2321
  this.channels[9].bankMSB = 1;
2225
2322
  this.channels[9].bank = 128;
2323
+ this.channels[9].isDrum = true;
2226
2324
  }
2227
- GM2SystemOn() {
2325
+ GM2SystemOn(scheduleTime) {
2326
+ scheduleTime ??= this.audioContext.currentTime;
2327
+ this.mode = "GM2";
2228
2328
  for (let i = 0; i < this.channels.length; i++) {
2329
+ this.allSoundOff(i, 0, scheduleTime);
2229
2330
  const channel = this.channels[i];
2230
2331
  channel.bankMSB = 121;
2231
2332
  channel.bankLSB = 0;
2232
2333
  channel.bank = 121 * 128;
2334
+ channel.isDrum = false;
2233
2335
  }
2234
2336
  this.channels[9].bankMSB = 120;
2235
2337
  this.channels[9].bank = 120 * 128;
2338
+ this.channels[9].isDrum = true;
2236
2339
  }
2237
2340
  handleUniversalRealTimeExclusiveMessage(data, scheduleTime) {
2238
2341
  switch (data[2]) {
@@ -2308,8 +2411,14 @@ export class Midy {
2308
2411
  const prev = this.masterFineTuning;
2309
2412
  const next = (value - 8192) / 8.192; // cent
2310
2413
  this.masterFineTuning = next;
2311
- channel.detune += next - prev;
2312
- this.updateChannelDetune(channel, scheduleTime);
2414
+ const detuneChange = next - prev;
2415
+ for (let i = 0; i < this.channels.length; i++) {
2416
+ const channel = this.channels[i];
2417
+ if (channel.isDrum)
2418
+ continue;
2419
+ channel.detune += detuneChange;
2420
+ this.updateChannelDetune(channel, scheduleTime);
2421
+ }
2313
2422
  }
2314
2423
  handleMasterCoarseTuningSysEx(data, scheduleTime) {
2315
2424
  const coarseTuning = data[4];
@@ -2319,8 +2428,14 @@ export class Midy {
2319
2428
  const prev = this.masterCoarseTuning;
2320
2429
  const next = (value - 64) * 100; // cent
2321
2430
  this.masterCoarseTuning = next;
2322
- channel.detune += next - prev;
2323
- this.updateChannelDetune(channel, scheduleTime);
2431
+ const detuneChange = next - prev;
2432
+ for (let i = 0; i < this.channels.length; i++) {
2433
+ const channel = this.channels[i];
2434
+ if (channel.isDrum)
2435
+ continue;
2436
+ channel.detune += detuneChange;
2437
+ this.updateChannelDetune(channel, scheduleTime);
2438
+ }
2324
2439
  }
2325
2440
  handleGlobalParameterControlSysEx(data, scheduleTime) {
2326
2441
  if (data[7] === 1) {
@@ -2532,6 +2647,8 @@ export class Midy {
2532
2647
  if (!channelBitmap[i])
2533
2648
  continue;
2534
2649
  const channel = this.channels[i];
2650
+ if (channel.isDrum)
2651
+ continue;
2535
2652
  for (let j = 0; j < 12; j++) {
2536
2653
  const centValue = data[j + 7] - 64;
2537
2654
  channel.scaleOctaveTuningTable[j] = centValue;
@@ -2550,6 +2667,8 @@ export class Midy {
2550
2667
  if (!channelBitmap[i])
2551
2668
  continue;
2552
2669
  const channel = this.channels[i];
2670
+ if (channel.isDrum)
2671
+ continue;
2553
2672
  for (let j = 0; j < 12; j++) {
2554
2673
  const index = 7 + j * 2;
2555
2674
  const msb = data[index] & 0x7F;
@@ -2620,7 +2739,10 @@ export class Midy {
2620
2739
  }
2621
2740
  handlePressureSysEx(data, tableName) {
2622
2741
  const channelNumber = data[4];
2623
- const table = this.channels[channelNumber][tableName];
2742
+ const channel = this.channels[channelNumber];
2743
+ if (channel.isDrum)
2744
+ return;
2745
+ const table = channel[tableName];
2624
2746
  for (let i = 5; i < data.length - 1; i += 2) {
2625
2747
  const pp = data[i];
2626
2748
  const rr = data[i + 1];
@@ -2648,8 +2770,11 @@ export class Midy {
2648
2770
  }
2649
2771
  handleControlChangeSysEx(data) {
2650
2772
  const channelNumber = data[4];
2773
+ const channel = this.channels[channelNumber];
2774
+ if (channel.isDrum)
2775
+ return;
2651
2776
  const controllerType = data[5];
2652
- const table = this.channels[channelNumber].controlTable[controllerType];
2777
+ const table = channel.controlTable[controllerType];
2653
2778
  for (let i = 6; i < data.length - 1; i += 2) {
2654
2779
  const pp = data[i];
2655
2780
  const rr = data[i + 1];
@@ -2663,8 +2788,11 @@ export class Midy {
2663
2788
  }
2664
2789
  handleKeyBasedInstrumentControlSysEx(data, scheduleTime) {
2665
2790
  const channelNumber = data[4];
2791
+ const channel = this.channels[channelNumber];
2792
+ if (channel.isDrum)
2793
+ return;
2666
2794
  const keyNumber = data[5];
2667
- const table = this.channels[channelNumber].keyBasedInstrumentControlTable;
2795
+ const table = channel.keyBasedInstrumentControlTable;
2668
2796
  for (let i = 6; i < data.length - 1; i += 2) {
2669
2797
  const controllerType = data[i];
2670
2798
  const value = data[i + 1];
@@ -2685,13 +2813,20 @@ export class Midy {
2685
2813
  }
2686
2814
  scheduleTask(callback, scheduleTime) {
2687
2815
  return new Promise((resolve) => {
2688
- const bufferSource = new AudioBufferSourceNode(this.audioContext);
2816
+ const bufferSource = new AudioBufferSourceNode(this.audioContext, {
2817
+ buffer: this.schedulerBuffer,
2818
+ });
2819
+ bufferSource.connect(this.scheduler);
2689
2820
  bufferSource.onended = () => {
2690
- callback();
2691
- resolve();
2821
+ try {
2822
+ callback();
2823
+ }
2824
+ finally {
2825
+ bufferSource.disconnect();
2826
+ resolve();
2827
+ }
2692
2828
  };
2693
2829
  bufferSource.start(scheduleTime);
2694
- bufferSource.stop(scheduleTime);
2695
2830
  });
2696
2831
  }
2697
2832
  }
@@ -2701,6 +2836,7 @@ Object.defineProperty(Midy, "channelSettings", {
2701
2836
  writable: true,
2702
2837
  value: {
2703
2838
  currentBufferSource: null,
2839
+ isDrum: false,
2704
2840
  detune: 0,
2705
2841
  program: 0,
2706
2842
  bank: 121 * 128,
@@ -2711,8 +2847,8 @@ Object.defineProperty(Midy, "channelSettings", {
2711
2847
  rpnMSB: 127,
2712
2848
  rpnLSB: 127,
2713
2849
  mono: false, // CC#124, CC#125
2850
+ modulationDepthRange: 50, // cent
2714
2851
  fineTuning: 0, // cb
2715
2852
  coarseTuning: 0, // cb
2716
- modulationDepthRange: 50, // cent
2717
2853
  }
2718
2854
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marmooo/midy",
3
- "version": "0.2.8",
3
+ "version": "0.2.9",
4
4
  "description": "A MIDI player/synthesizer written in JavaScript that supports GM-Lite/GM1 and SF2/SF3.",
5
5
  "repository": {
6
6
  "type": "git",