@marmooo/midy 0.2.2 → 0.2.3

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.
@@ -362,15 +362,15 @@ class MidyGM2 {
362
362
  });
363
363
  this.audioContext = audioContext;
364
364
  this.options = { ...this.defaultOptions, ...options };
365
- this.masterGain = new GainNode(audioContext);
365
+ this.masterVolume = new GainNode(audioContext);
366
366
  this.voiceParamsHandlers = this.createVoiceParamsHandlers();
367
367
  this.controlChangeHandlers = this.createControlChangeHandlers();
368
368
  this.channels = this.createChannels(audioContext);
369
369
  this.reverbEffect = this.options.reverbAlgorithm(audioContext);
370
370
  this.chorusEffect = this.createChorusEffect(audioContext);
371
- this.chorusEffect.output.connect(this.masterGain);
372
- this.reverbEffect.output.connect(this.masterGain);
373
- this.masterGain.connect(audioContext.destination);
371
+ this.chorusEffect.output.connect(this.masterVolume);
372
+ this.reverbEffect.output.connect(this.masterVolume);
373
+ this.masterVolume.connect(audioContext.destination);
374
374
  this.GM2SystemOn();
375
375
  }
376
376
  initSoundFontTable() {
@@ -416,7 +416,7 @@ class MidyGM2 {
416
416
  const merger = new ChannelMergerNode(audioContext, { numberOfInputs: 2 });
417
417
  gainL.connect(merger, 0, 0);
418
418
  gainR.connect(merger, 0, 1);
419
- merger.connect(this.masterGain);
419
+ merger.connect(this.masterVolume);
420
420
  return {
421
421
  gainL,
422
422
  gainR,
@@ -428,6 +428,7 @@ class MidyGM2 {
428
428
  return {
429
429
  ...this.constructor.channelSettings,
430
430
  state: new ControllerState(),
431
+ controlTable: this.initControlTable(),
431
432
  ...this.setChannelAudioNodes(audioContext),
432
433
  scheduledNotes: new Map(),
433
434
  sostenutoNotes: new Map(),
@@ -1350,12 +1351,13 @@ class MidyGM2 {
1350
1351
  const pressureDepth = (channel.pressureTable[0] - 64) / 37.5; // 2400 / 64;
1351
1352
  channel.detune += pressureDepth * (next - prev);
1352
1353
  }
1354
+ const table = channel.pressureTable;
1353
1355
  channel.scheduledNotes.forEach((noteList) => {
1354
1356
  for (let i = 0; i < noteList.length; i++) {
1355
1357
  const note = noteList[i];
1356
1358
  if (!note)
1357
1359
  continue;
1358
- this.setChannelPressure(channel, note);
1360
+ this.applyDestinationSettings(channel, note, table);
1359
1361
  }
1360
1362
  });
1361
1363
  // this.applyVoiceParams(channel, 13);
@@ -1364,11 +1366,13 @@ class MidyGM2 {
1364
1366
  if (channel.pressureTable[0] !== 64) {
1365
1367
  this.updateDetune(channel);
1366
1368
  }
1367
- if (channel.pressureTable[1] !== 64 && !note.portamento) {
1368
- this.setFilterEnvelope(channel, note);
1369
- }
1370
- if (channel.pressureTable[2] !== 64 && !note.portamento) {
1371
- this.setVolumeEnvelope(channel, note);
1369
+ if (!note.portamento) {
1370
+ if (channel.pressureTable[1] !== 64) {
1371
+ this.setFilterEnvelope(channel, note);
1372
+ }
1373
+ if (channel.pressureTable[2] !== 64) {
1374
+ this.setVolumeEnvelope(channel, note);
1375
+ }
1372
1376
  }
1373
1377
  if (channel.pressureTable[3] !== 0) {
1374
1378
  this.setModLfoToPitch(channel, note);
@@ -1521,7 +1525,7 @@ class MidyGM2 {
1521
1525
  this.setModLfoToFilterFc(channel, note);
1522
1526
  }
1523
1527
  },
1524
- modLfoToVolume: (channel, note) => {
1528
+ modLfoToVolume: (channel, note, _prevValue) => {
1525
1529
  if (0 < channel.state.modulationDepth) {
1526
1530
  this.setModLfoToVolume(channel, note);
1527
1531
  }
@@ -1537,12 +1541,12 @@ class MidyGM2 {
1537
1541
  delayVibLFO: (channel, note, prevValue) => {
1538
1542
  if (0 < channel.state.vibratoDepth) {
1539
1543
  const now = this.audioContext.currentTime;
1540
- const prevStartTime = note.startTime +
1541
- prevValue * channel.state.vibratoDelay * 2;
1544
+ const vibratoDelay = channel.state.vibratoDelay * 2;
1545
+ const prevStartTime = note.startTime + prevValue * vibratoDelay;
1542
1546
  if (now < prevStartTime)
1543
1547
  return;
1544
- const startTime = note.startTime +
1545
- value * channel.state.vibratoDelay * 2;
1548
+ const value = note.voiceParams.delayVibLFO;
1549
+ const startTime = note.startTime + value * vibratoDelay;
1546
1550
  note.vibratoLFO.stop(now);
1547
1551
  note.vibratoLFO.start(startTime);
1548
1552
  }
@@ -1550,9 +1554,10 @@ class MidyGM2 {
1550
1554
  freqVibLFO: (channel, note, _prevValue) => {
1551
1555
  if (0 < channel.state.vibratoDepth) {
1552
1556
  const now = this.audioContext.currentTime;
1557
+ const freqVibLFO = note.voiceParams.freqVibLFO;
1553
1558
  note.vibratoLFO.frequency
1554
1559
  .cancelScheduledValues(now)
1555
- .setValueAtTime(value * sate.vibratoRate, now);
1560
+ .setValueAtTime(freqVibLFO * channel.state.vibratoRate, now);
1556
1561
  }
1557
1562
  },
1558
1563
  };
@@ -1649,8 +1654,8 @@ class MidyGM2 {
1649
1654
  if (handler) {
1650
1655
  handler.call(this, channelNumber, value);
1651
1656
  const channel = this.channels[channelNumber];
1652
- const controller = 128 + controllerType;
1653
- this.applyVoiceParams(channel, controller);
1657
+ this.applyVoiceParams(channel, controller + 128);
1658
+ this.applyControlTable(channel, controllerType);
1654
1659
  }
1655
1660
  else {
1656
1661
  console.warn(`Unsupported Control change: controllerType=${controllerType} value=${value}`);
@@ -1661,13 +1666,14 @@ class MidyGM2 {
1661
1666
  }
1662
1667
  updateModulation(channel) {
1663
1668
  const now = this.audioContext.currentTime;
1669
+ const depth = channel.state.modulationDepth * channel.modulationDepthRange;
1664
1670
  channel.scheduledNotes.forEach((noteList) => {
1665
1671
  for (let i = 0; i < noteList.length; i++) {
1666
1672
  const note = noteList[i];
1667
1673
  if (!note)
1668
1674
  continue;
1669
1675
  if (note.modulationDepth) {
1670
- note.modulationDepth.gain.setValueAtTime(channel.state.modulationDepth, now);
1676
+ note.modulationDepth.gain.setValueAtTime(depth, now);
1671
1677
  }
1672
1678
  else {
1673
1679
  this.setPitchEnvelope(note);
@@ -1678,8 +1684,7 @@ class MidyGM2 {
1678
1684
  }
1679
1685
  setModulationDepth(channelNumber, modulation) {
1680
1686
  const channel = this.channels[channelNumber];
1681
- channel.state.modulationDepth = (modulation / 127) *
1682
- channel.modulationDepthRange;
1687
+ channel.state.modulationDepth = modulation / 127;
1683
1688
  this.updateModulation(channel);
1684
1689
  }
1685
1690
  setPortamentoTime(channelNumber, portamentoTime) {
@@ -1982,7 +1987,6 @@ class MidyGM2 {
1982
1987
  setModulationDepthRange(channelNumber, modulationDepthRange) {
1983
1988
  const channel = this.channels[channelNumber];
1984
1989
  channel.modulationDepthRange = modulationDepthRange;
1985
- channel.modulationDepth = (modulation / 127) * modulationDepthRange;
1986
1990
  this.updateModulation(channel);
1987
1991
  }
1988
1992
  allSoundOff(channelNumber) {
@@ -2098,9 +2102,8 @@ class MidyGM2 {
2098
2102
  switch (data[3]) {
2099
2103
  case 1: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca22.pdf
2100
2104
  return this.handleChannelPressureSysEx(data);
2101
- // case 3:
2102
- // // TODO
2103
- // return this.setControlChange();
2105
+ case 3: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca22.pdf
2106
+ return this.handleControlChangeSysEx(data);
2104
2107
  default:
2105
2108
  console.warn(`Unsupported Exclusive Message: ${data}`);
2106
2109
  }
@@ -2127,8 +2130,8 @@ class MidyGM2 {
2127
2130
  }
2128
2131
  else {
2129
2132
  const now = this.audioContext.currentTime;
2130
- this.masterGain.gain.cancelScheduledValues(now);
2131
- this.masterGain.gain.setValueAtTime(volume * volume, now);
2133
+ this.masterVolume.gain.cancelScheduledValues(now);
2134
+ this.masterVolume.gain.setValueAtTime(volume * volume, now);
2132
2135
  }
2133
2136
  }
2134
2137
  handleMasterFineTuningSysEx(data) {
@@ -2373,6 +2376,28 @@ class MidyGM2 {
2373
2376
  }
2374
2377
  }
2375
2378
  }
2379
+ applyDestinationSettings(channel, note, table) {
2380
+ if (table[0] !== 64) {
2381
+ this.updateDetune(channel);
2382
+ }
2383
+ if (!note.portamento) {
2384
+ if (table[1] !== 64) {
2385
+ this.setFilterEnvelope(channel, note);
2386
+ }
2387
+ if (table[2] !== 64) {
2388
+ this.setVolumeEnvelope(channel, note);
2389
+ }
2390
+ }
2391
+ if (table[3] !== 0) {
2392
+ this.setModLfoToPitch(channel, note);
2393
+ }
2394
+ if (table[4] !== 0) {
2395
+ this.setModLfoToFilterFc(channel, note);
2396
+ }
2397
+ if (table[5] !== 0) {
2398
+ this.setModLfoToVolume(channel, note);
2399
+ }
2400
+ }
2376
2401
  handleChannelPressureSysEx(data) {
2377
2402
  const channelNumber = data[4];
2378
2403
  const table = this.channels[channelNumber].pressureTable;
@@ -2382,6 +2407,40 @@ class MidyGM2 {
2382
2407
  table[pp] = rr;
2383
2408
  }
2384
2409
  }
2410
+ initControlTable() {
2411
+ const channelCount = 128;
2412
+ const slotSize = 6;
2413
+ const defaultValues = [64, 64, 64, 0, 0, 0];
2414
+ const table = new Uint8Array(channelCount * slotSize);
2415
+ for (let ch = 0; ch < channelCount; ch++) {
2416
+ const offset = ch * slotSize;
2417
+ table.set(defaultValues, offset);
2418
+ }
2419
+ return table;
2420
+ }
2421
+ applyControlTable(channel, controllerType) {
2422
+ const slotSize = 6;
2423
+ const offset = controllerType * slotSize;
2424
+ const table = channel.controlTable.subarray(offset, offset + slotSize);
2425
+ channel.scheduledNotes.forEach((noteList) => {
2426
+ for (let i = 0; i < noteList.length; i++) {
2427
+ const note = noteList[i];
2428
+ if (!note)
2429
+ continue;
2430
+ this.applyDestinationSettings(channel, note, table);
2431
+ }
2432
+ });
2433
+ }
2434
+ handleControlChangeSysEx(data) {
2435
+ const channelNumber = data[4];
2436
+ const controllerType = data[5];
2437
+ const table = this.channels[channelNumber].controlTable[controllerType];
2438
+ for (let i = 6; i < data.length - 1; i += 2) {
2439
+ const pp = data[i];
2440
+ const rr = data[i + 1];
2441
+ table[pp] = rr;
2442
+ }
2443
+ }
2385
2444
  getKeyBasedInstrumentControlValue(channel, keyNumber, controllerType) {
2386
2445
  const index = keyNumber * 128 + controllerType;
2387
2446
  const controlValue = channel.keyBasedInstrumentControlTable[index];
@@ -29,12 +29,12 @@ export class MidyGMLite {
29
29
  notePromises: any[];
30
30
  exclusiveClassMap: Map<any, any>;
31
31
  audioContext: any;
32
- masterGain: any;
32
+ masterVolume: any;
33
33
  voiceParamsHandlers: {
34
34
  modLfoToPitch: (channel: any, note: any, _prevValue: any) => void;
35
35
  vibLfoToPitch: (_channel: any, _note: any, _prevValue: any) => void;
36
36
  modLfoToFilterFc: (channel: any, note: any, _prevValue: any) => void;
37
- modLfoToVolume: (channel: any, note: any) => void;
37
+ modLfoToVolume: (channel: any, note: any, _prevValue: any) => void;
38
38
  chorusEffectsSend: (_channel: any, _note: any, _prevValue: any) => void;
39
39
  reverbEffectsSend: (_channel: any, _note: any, _prevValue: any) => void;
40
40
  delayModLFO: (_channel: any, note: any, _prevValue: any) => void;
@@ -120,7 +120,7 @@ export class MidyGMLite {
120
120
  modLfoToPitch: (channel: any, note: any, _prevValue: any) => void;
121
121
  vibLfoToPitch: (_channel: any, _note: any, _prevValue: any) => void;
122
122
  modLfoToFilterFc: (channel: any, note: any, _prevValue: any) => void;
123
- modLfoToVolume: (channel: any, note: any) => void;
123
+ modLfoToVolume: (channel: any, note: any, _prevValue: any) => void;
124
124
  chorusEffectsSend: (_channel: any, _note: any, _prevValue: any) => void;
125
125
  reverbEffectsSend: (_channel: any, _note: any, _prevValue: any) => void;
126
126
  delayModLFO: (_channel: any, note: any, _prevValue: any) => void;
@@ -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,gBAA4C;IAC5C;;;;;;;;;;;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,iCAWC;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,+EASC;IAED,qCAiBC;IAED,8DAKC;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,wEAOC;IAED,+CAEC;IAED,8CAqBC;IAED,+CAEC;IAED,4DAgBC;IAED,oBAMC;IAED,yDAaC;IAED,yCAGC;IAED,mCAQC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF;AArtCD;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":"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,iCAWC;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,wEAOC;IAED,+CAEC;IAED,8CAqBC;IAED,+CAEC;IAED,4DAgBC;IAED,oBAMC;IAED,yDAaC;IAED,yCAGC;IAED,mCAQC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF;AAptCD;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"}
@@ -224,11 +224,11 @@ class MidyGMLite {
224
224
  value: new Map()
225
225
  });
226
226
  this.audioContext = audioContext;
227
- this.masterGain = new GainNode(audioContext);
227
+ this.masterVolume = new GainNode(audioContext);
228
228
  this.voiceParamsHandlers = this.createVoiceParamsHandlers();
229
229
  this.controlChangeHandlers = this.createControlChangeHandlers();
230
230
  this.channels = this.createChannels(audioContext);
231
- this.masterGain.connect(audioContext.destination);
231
+ this.masterVolume.connect(audioContext.destination);
232
232
  this.GM1SystemOn();
233
233
  }
234
234
  initSoundFontTable() {
@@ -274,7 +274,7 @@ class MidyGMLite {
274
274
  const merger = new ChannelMergerNode(audioContext, { numberOfInputs: 2 });
275
275
  gainL.connect(merger, 0, 0);
276
276
  gainR.connect(merger, 0, 1);
277
- merger.connect(this.masterGain);
277
+ merger.connect(this.masterVolume);
278
278
  return {
279
279
  gainL,
280
280
  gainR,
@@ -916,7 +916,7 @@ class MidyGMLite {
916
916
  if (0 < channel.state.modulationDepth)
917
917
  this.setModLfoToFilterFc(note);
918
918
  },
919
- modLfoToVolume: (channel, note) => {
919
+ modLfoToVolume: (channel, note, _prevValue) => {
920
920
  if (0 < channel.state.modulationDepth)
921
921
  this.setModLfoToVolume(note);
922
922
  },
@@ -1002,6 +1002,8 @@ class MidyGMLite {
1002
1002
  const handler = this.controlChangeHandlers[controllerType];
1003
1003
  if (handler) {
1004
1004
  handler.call(this, channelNumber, value);
1005
+ const channel = this.channels[channelNumber];
1006
+ this.applyVoiceParams(channel, controller + 128);
1005
1007
  }
1006
1008
  else {
1007
1009
  console.warn(`Unsupported Control change: controllerType=${controllerType} value=${value}`);
@@ -1009,13 +1011,14 @@ class MidyGMLite {
1009
1011
  }
1010
1012
  updateModulation(channel) {
1011
1013
  const now = this.audioContext.currentTime;
1014
+ const depth = channel.state.modulationDepth * channel.modulationDepthRange;
1012
1015
  channel.scheduledNotes.forEach((noteList) => {
1013
1016
  for (let i = 0; i < noteList.length; i++) {
1014
1017
  const note = noteList[i];
1015
1018
  if (!note)
1016
1019
  continue;
1017
1020
  if (note.modulationDepth) {
1018
- note.modulationDepth.gain.setValueAtTime(channel.state.modulationDepth, now);
1021
+ note.modulationDepth.gain.setValueAtTime(depth, now);
1019
1022
  }
1020
1023
  else {
1021
1024
  this.setPitchEnvelope(note);
@@ -1026,8 +1029,7 @@ class MidyGMLite {
1026
1029
  }
1027
1030
  setModulationDepth(channelNumber, modulation) {
1028
1031
  const channel = this.channels[channelNumber];
1029
- channel.state.modulationDepth = (modulation / 127) *
1030
- channel.modulationDepthRange;
1032
+ channel.state.modulationDepth = modulation / 127;
1031
1033
  this.updateModulation(channel);
1032
1034
  }
1033
1035
  setVolume(channelNumber, volume) {
@@ -1203,8 +1205,8 @@ class MidyGMLite {
1203
1205
  }
1204
1206
  else {
1205
1207
  const now = this.audioContext.currentTime;
1206
- this.masterGain.gain.cancelScheduledValues(now);
1207
- this.masterGain.gain.setValueAtTime(volume * volume, now);
1208
+ this.masterVolume.gain.cancelScheduledValues(now);
1209
+ this.masterVolume.gain.setValueAtTime(volume * volume, now);
1208
1210
  }
1209
1211
  }
1210
1212
  handleExclusiveMessage(data) {
package/script/midy.d.ts CHANGED
@@ -77,12 +77,12 @@ export class Midy {
77
77
  output: any;
78
78
  };
79
79
  };
80
- masterGain: any;
80
+ masterVolume: any;
81
81
  voiceParamsHandlers: {
82
82
  modLfoToPitch: (channel: any, note: any, _prevValue: any) => void;
83
83
  vibLfoToPitch: (channel: any, note: any, _prevValue: any) => void;
84
84
  modLfoToFilterFc: (channel: any, note: any, _prevValue: any) => void;
85
- modLfoToVolume: (channel: any, note: any) => void;
85
+ modLfoToVolume: (channel: any, note: any, _prevValue: any) => void;
86
86
  chorusEffectsSend: (channel: any, note: any, prevValue: any) => void;
87
87
  reverbEffectsSend: (channel: any, note: any, prevValue: any) => void;
88
88
  delayModLFO: (_channel: any, note: any, _prevValue: any) => void;
@@ -224,7 +224,6 @@ export class Midy {
224
224
  handlePolyphonicKeyPressure(channelNumber: any, noteNumber: any, pressure: any): void;
225
225
  handleProgramChange(channelNumber: any, program: any): void;
226
226
  handleChannelPressure(channelNumber: any, value: any): void;
227
- setChannelPressure(channel: any, note: any): void;
228
227
  handlePitchBendMessage(channelNumber: any, lsb: any, msb: any): void;
229
228
  setPitchBend(channelNumber: any, value: any): void;
230
229
  setModLfoToPitch(channel: any, note: any): void;
@@ -239,7 +238,7 @@ export class Midy {
239
238
  modLfoToPitch: (channel: any, note: any, _prevValue: any) => void;
240
239
  vibLfoToPitch: (channel: any, note: any, _prevValue: any) => void;
241
240
  modLfoToFilterFc: (channel: any, note: any, _prevValue: any) => void;
242
- modLfoToVolume: (channel: any, note: any) => void;
241
+ modLfoToVolume: (channel: any, note: any, _prevValue: any) => void;
243
242
  chorusEffectsSend: (channel: any, note: any, prevValue: any) => void;
244
243
  reverbEffectsSend: (channel: any, note: any, prevValue: any) => void;
245
244
  delayModLFO: (_channel: any, note: any, _prevValue: any) => void;
@@ -369,7 +368,11 @@ export class Midy {
369
368
  getChorusSendToReverb(value: any): number;
370
369
  getChannelBitmap(data: any): any[];
371
370
  handleScaleOctaveTuning1ByteFormatSysEx(data: any): void;
371
+ applyDestinationSettings(channel: any, note: any, table: any): void;
372
372
  handleChannelPressureSysEx(data: any): void;
373
+ initControlTable(): Uint8Array<ArrayBuffer>;
374
+ applyControlTable(channel: any, controllerType: any): void;
375
+ handleControlChangeSysEx(data: any): void;
373
376
  getKeyBasedInstrumentControlValue(channel: any, keyNumber: any, controllerType: any): number;
374
377
  handleKeyBasedInstrumentControlSysEx(data: any): void;
375
378
  handleExclusiveMessage(data: 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":"AAiHA;IAmCE;;;;;;;;;;;;;;;;;MAiBE;IAEF;;;;;;;MAOE;IAgCF;;;;;OAaC;IAzGD,qBAAmB;IACnB,kBAAc;IACd,yBAAqB;IACrB,2BAAuB;IACvB;;;MAGE;IACF;;;;;;MAME;IACF,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;IAClB,iCAA8B;IA8B9B;;;;;MA4BE;IAGA,kBAAgC;IAChC;;;;;MAAqD;IACrD,kBAA8C;IAC9C;;;;;;;;;;;MAA2D;IAC3D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAA+D;IAC/D,gBAAiD;IACjD;;;MAA8D;IAC9D;;;;;;;;MAAyD;IAO3D,4BAMC;IAED,mCAWC;IAED,gDAMC;IAED,sCASC;IAED;;;;MAeC;IAED,yCAkBC;IAED,6DA2BC;IAED,iEAUC;IAED,2CAcC;IAED,2EA8DC;IAED,mCAOC;IAED,0BAkDC;IAED,uDAEC;IAED,wDAEC;IAED;;;MAoGC;IAED,+EAoBC;IAED,qDAKC;IAED,uBAKC;IAED,aAGC;IAED,cAKC;IAED,wBAIC;IAED,0BAKC;IAED,wBAOC;IAED,sBAGC;IAED,uDASC;IAED,6CAQC;IAED,kFAuBC;IAED;;;;MAWC;IAED,gFAUC;IAED,mFAYC;IAED,sGAcC;IAID;;;MA8BC;IAED;;;;;;;;MA0CC;IAED,2BAEC;IAED,8BAEC;IAED,8BAEC;IAED,4BAEC;IAED,qCAUC;IAED,6CAEC;IAED,iCAaC;IAED,wCAIC;IAED,gEAWC;IAED,iDAoBC;IAED,kCAqBC;IAED,6CAIC;IAED,gEAyBC;IAED,iDA2BC;IAED,+DAoBC;IAED,4DAcC;IAED,iIA6DC;IAED,gDAQC;IAED,mHA0DC;IAED,2FASC;IAED,qFAqCC;IAED,wJAwCC;IAED,qHAUC;IAED,kEAeC;IAED,oEAYC;IAED,gFAqBC;IAED,sFAeC;IAED,4DAIC;IAED,4DAkBC;IAED,qEAGC;IAED,mDASC;IAED,gDAWC;IAED,gDASC;IAED,mDAQC;IAED,iDAUC;IAED,oEA2BC;IAED,oEA2BC;IAED,gCAOC;IAED,+BAMC;IAED;;;;;;;;;;;MAoDC;IAED,oFAMC;IAED,0DAiDC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAqCC;IAED,+EAYC;IAED,+CAEC;IAED,qCAeC;IAED,8DAIC;IAED,iEAIC;IAED,sCAiBC;IAED,iDAKC;IAED;;;MAMC;IAED,mCAqBC;IAED,2CAKC;IAED,yDAIC;IAED,+CAEC;IAED,mDAGC;IAED,wCAWC;IAED,sDAKC;IAED,oDAEC;IAED,wDAUC;IAED,uDAGC;IAED,mEAaC;IAED,2DAGC;IAED,yDAYC;IAED,yDAUC;IAED,uDAUC;IAED,2DAWC;IAED,6DAGC;IAED,6DAGC;IAED,mEAmCC;IAED,mEAmCC;IAED,kFAeC;IAED,2DAMC;IAED,gDAyBC;IAGD,wCAEC;IAGD,wCAEC;IAED,gDAEC;IAED,gDAEC;IAED,mDAGC;IAED,kDAKC;IAED,wDASC;IAED,8CAKC;IAED,oDAOC;IAED,gDAKC;IAED,sDAOC;IAED,wDAKC;IAED,6EAIC;IAED,+CAEC;IAED,8CAyBC;IAED,+CAEC;IAED,gBAEC;IAED,eAEC;IAED,eAEC;IAED,eAEC;IAED,4DA4BC;IAED,oBASC;IAED,oBASC;IAED,yDA8CC;IAED,yCAGC;IAED,mCAQC;IAED,6CAGC;IAED,sCAMC;IAED,+CAGC;IAED,wCAMC;IAED,mDAeC;IAED,4CAOC;IAED,+BAKC;IAED,qDAiBC;IAED,gCAIC;IAED,kCAEC;IA6BD,4CAEC;IAED,4CAaC;IAED,+BAiBC;IAED,wFAKC;IAED,mCAKC;IAED,qCAEC;IAED,oCAOC;IAED,sCAEC;IAED,oCAUC;IAED,sCAEC;IAED,wCAuBC;IAED,0CAEC;IAED,mCAeC;IAED,yDAaC;IAED,oEAqBC;IAED,4CAQC;IAED,4CAUC;IAED,2DAWC;IAED,0CASC;IAED,6FAIC;IAED,sDAcC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF;AAvnFD;IAgBE,0FAMC;IArBD,kBAAa;IACb,gBAAW;IACX,wBAAmB;IACnB,gBAAW;IACX,WAAM;IACN,WAAM;IACN,iBAAY;IACZ,mBAAc;IACd,qBAAgB;IAChB,gBAAW;IACX,kBAAa;IACb,uBAAkB;IAClB,uBAAkB;IAClB,gBAAW;IAGT,gBAA4B;IAC5B,cAAwB;IACxB,eAA0B;IAC1B,WAAkB;IAClB,iBAA8B;CAEjC"}
package/script/midy.js CHANGED
@@ -362,15 +362,15 @@ class Midy {
362
362
  });
363
363
  this.audioContext = audioContext;
364
364
  this.options = { ...this.defaultOptions, ...options };
365
- this.masterGain = new GainNode(audioContext);
365
+ this.masterVolume = new GainNode(audioContext);
366
366
  this.voiceParamsHandlers = this.createVoiceParamsHandlers();
367
367
  this.controlChangeHandlers = this.createControlChangeHandlers();
368
368
  this.channels = this.createChannels(audioContext);
369
369
  this.reverbEffect = this.options.reverbAlgorithm(audioContext);
370
370
  this.chorusEffect = this.createChorusEffect(audioContext);
371
- this.chorusEffect.output.connect(this.masterGain);
372
- this.reverbEffect.output.connect(this.masterGain);
373
- this.masterGain.connect(audioContext.destination);
371
+ this.chorusEffect.output.connect(this.masterVolume);
372
+ this.reverbEffect.output.connect(this.masterVolume);
373
+ this.masterVolume.connect(audioContext.destination);
374
374
  this.GM2SystemOn();
375
375
  }
376
376
  initSoundFontTable() {
@@ -416,7 +416,7 @@ class Midy {
416
416
  const merger = new ChannelMergerNode(audioContext, { numberOfInputs: 2 });
417
417
  gainL.connect(merger, 0, 0);
418
418
  gainR.connect(merger, 0, 1);
419
- merger.connect(this.masterGain);
419
+ merger.connect(this.masterVolume);
420
420
  return {
421
421
  gainL,
422
422
  gainR,
@@ -428,6 +428,7 @@ class Midy {
428
428
  return {
429
429
  ...this.constructor.channelSettings,
430
430
  state: new ControllerState(),
431
+ controlTable: this.initControlTable(),
431
432
  ...this.setChannelAudioNodes(audioContext),
432
433
  scheduledNotes: new Map(),
433
434
  sostenutoNotes: new Map(),
@@ -1376,36 +1377,17 @@ class Midy {
1376
1377
  const pressureDepth = (channel.pressureTable[0] - 64) / 37.5; // 2400 / 64;
1377
1378
  channel.detune += pressureDepth * (next - prev);
1378
1379
  }
1380
+ const table = channel.pressureTable;
1379
1381
  channel.scheduledNotes.forEach((noteList) => {
1380
1382
  for (let i = 0; i < noteList.length; i++) {
1381
1383
  const note = noteList[i];
1382
1384
  if (!note)
1383
1385
  continue;
1384
- this.setChannelPressure(channel, note);
1386
+ this.applyDestinationSettings(channel, note, table);
1385
1387
  }
1386
1388
  });
1387
1389
  // this.applyVoiceParams(channel, 13);
1388
1390
  }
1389
- setChannelPressure(channel, note) {
1390
- if (channel.pressureTable[0] !== 64) {
1391
- this.updateDetune(channel);
1392
- }
1393
- if (channel.pressureTable[1] !== 64 && !note.portamento) {
1394
- this.setFilterEnvelope(channel, note);
1395
- }
1396
- if (channel.pressureTable[2] !== 64 && !note.portamento) {
1397
- this.setVolumeEnvelope(channel, note);
1398
- }
1399
- if (channel.pressureTable[3] !== 0) {
1400
- this.setModLfoToPitch(channel, note);
1401
- }
1402
- if (channel.pressureTable[4] !== 0) {
1403
- this.setModLfoToFilterFc(channel, note);
1404
- }
1405
- if (channel.pressureTable[5] !== 0) {
1406
- this.setModLfoToVolume(channel, note);
1407
- }
1408
- }
1409
1391
  handlePitchBendMessage(channelNumber, lsb, msb) {
1410
1392
  const pitchBend = msb * 128 + lsb;
1411
1393
  this.setPitchBend(channelNumber, pitchBend);
@@ -1547,7 +1529,7 @@ class Midy {
1547
1529
  this.setModLfoToFilterFc(channel, note);
1548
1530
  }
1549
1531
  },
1550
- modLfoToVolume: (channel, note) => {
1532
+ modLfoToVolume: (channel, note, _prevValue) => {
1551
1533
  if (0 < channel.state.modulationDepth) {
1552
1534
  this.setModLfoToVolume(channel, note);
1553
1535
  }
@@ -1563,12 +1545,12 @@ class Midy {
1563
1545
  delayVibLFO: (channel, note, prevValue) => {
1564
1546
  if (0 < channel.state.vibratoDepth) {
1565
1547
  const now = this.audioContext.currentTime;
1566
- const prevStartTime = note.startTime +
1567
- prevValue * channel.state.vibratoDelay * 2;
1548
+ const vibratoDelay = channel.state.vibratoDelay * 2;
1549
+ const prevStartTime = note.startTime + prevValue * vibratoDelay;
1568
1550
  if (now < prevStartTime)
1569
1551
  return;
1570
- const startTime = note.startTime +
1571
- value * channel.state.vibratoDelay * 2;
1552
+ const value = note.voiceParams.delayVibLFO;
1553
+ const startTime = note.startTime + value * vibratoDelay;
1572
1554
  note.vibratoLFO.stop(now);
1573
1555
  note.vibratoLFO.start(startTime);
1574
1556
  }
@@ -1576,9 +1558,10 @@ class Midy {
1576
1558
  freqVibLFO: (channel, note, _prevValue) => {
1577
1559
  if (0 < channel.state.vibratoDepth) {
1578
1560
  const now = this.audioContext.currentTime;
1561
+ const freqVibLFO = note.voiceParams.freqVibLFO;
1579
1562
  note.vibratoLFO.frequency
1580
1563
  .cancelScheduledValues(now)
1581
- .setValueAtTime(value * sate.vibratoRate, now);
1564
+ .setValueAtTime(freqVibLFO * channel.state.vibratoRate, now);
1582
1565
  }
1583
1566
  },
1584
1567
  };
@@ -1685,8 +1668,8 @@ class Midy {
1685
1668
  if (handler) {
1686
1669
  handler.call(this, channelNumber, value);
1687
1670
  const channel = this.channels[channelNumber];
1688
- const controller = 128 + controllerType;
1689
- this.applyVoiceParams(channel, controller);
1671
+ this.applyVoiceParams(channel, controllerType + 128);
1672
+ this.applyControlTable(channel, controllerType);
1690
1673
  }
1691
1674
  else {
1692
1675
  console.warn(`Unsupported Control change: controllerType=${controllerType} value=${value}`);
@@ -1697,13 +1680,14 @@ class Midy {
1697
1680
  }
1698
1681
  updateModulation(channel) {
1699
1682
  const now = this.audioContext.currentTime;
1683
+ const depth = channel.state.modulationDepth * channel.modulationDepthRange;
1700
1684
  channel.scheduledNotes.forEach((noteList) => {
1701
1685
  for (let i = 0; i < noteList.length; i++) {
1702
1686
  const note = noteList[i];
1703
1687
  if (!note)
1704
1688
  continue;
1705
1689
  if (note.modulationDepth) {
1706
- note.modulationDepth.gain.setValueAtTime(channel.state.modulationDepth, now);
1690
+ note.modulationDepth.gain.setValueAtTime(depth, now);
1707
1691
  }
1708
1692
  else {
1709
1693
  this.setPitchEnvelope(note);
@@ -1714,8 +1698,7 @@ class Midy {
1714
1698
  }
1715
1699
  setModulationDepth(channelNumber, modulation) {
1716
1700
  const channel = this.channels[channelNumber];
1717
- channel.state.modulationDepth = (modulation / 127) *
1718
- channel.modulationDepthRange;
1701
+ channel.state.modulationDepth = modulation / 127;
1719
1702
  this.updateModulation(channel);
1720
1703
  }
1721
1704
  setPortamentoTime(channelNumber, portamentoTime) {
@@ -2109,7 +2092,6 @@ class Midy {
2109
2092
  setModulationDepthRange(channelNumber, modulationDepthRange) {
2110
2093
  const channel = this.channels[channelNumber];
2111
2094
  channel.modulationDepthRange = modulationDepthRange;
2112
- channel.modulationDepth = (modulation / 127) * modulationDepthRange;
2113
2095
  this.updateModulation(channel);
2114
2096
  }
2115
2097
  allSoundOff(channelNumber) {
@@ -2234,9 +2216,8 @@ class Midy {
2234
2216
  switch (data[3]) {
2235
2217
  case 1: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca22.pdf
2236
2218
  return this.handleChannelPressureSysEx(data);
2237
- // case 3:
2238
- // // TODO
2239
- // return this.setControlChange();
2219
+ case 3: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca22.pdf
2220
+ return this.handleControlChangeSysEx(data);
2240
2221
  default:
2241
2222
  console.warn(`Unsupported Exclusive Message: ${data}`);
2242
2223
  }
@@ -2263,8 +2244,8 @@ class Midy {
2263
2244
  }
2264
2245
  else {
2265
2246
  const now = this.audioContext.currentTime;
2266
- this.masterGain.gain.cancelScheduledValues(now);
2267
- this.masterGain.gain.setValueAtTime(volume * volume, now);
2247
+ this.masterVolume.gain.cancelScheduledValues(now);
2248
+ this.masterVolume.gain.setValueAtTime(volume * volume, now);
2268
2249
  }
2269
2250
  }
2270
2251
  handleMasterFineTuningSysEx(data) {
@@ -2509,6 +2490,28 @@ class Midy {
2509
2490
  }
2510
2491
  }
2511
2492
  }
2493
+ applyDestinationSettings(channel, note, table) {
2494
+ if (table[0] !== 64) {
2495
+ this.updateDetune(channel);
2496
+ }
2497
+ if (!note.portamento) {
2498
+ if (table[1] !== 64) {
2499
+ this.setFilterEnvelope(channel, note);
2500
+ }
2501
+ if (table[2] !== 64) {
2502
+ this.setVolumeEnvelope(channel, note);
2503
+ }
2504
+ }
2505
+ if (table[3] !== 0) {
2506
+ this.setModLfoToPitch(channel, note);
2507
+ }
2508
+ if (table[4] !== 0) {
2509
+ this.setModLfoToFilterFc(channel, note);
2510
+ }
2511
+ if (table[5] !== 0) {
2512
+ this.setModLfoToVolume(channel, note);
2513
+ }
2514
+ }
2512
2515
  handleChannelPressureSysEx(data) {
2513
2516
  const channelNumber = data[4];
2514
2517
  const table = this.channels[channelNumber].pressureTable;
@@ -2518,6 +2521,40 @@ class Midy {
2518
2521
  table[pp] = rr;
2519
2522
  }
2520
2523
  }
2524
+ initControlTable() {
2525
+ const channelCount = 128;
2526
+ const slotSize = 6;
2527
+ const defaultValues = [64, 64, 64, 0, 0, 0];
2528
+ const table = new Uint8Array(channelCount * slotSize);
2529
+ for (let ch = 0; ch < channelCount; ch++) {
2530
+ const offset = ch * slotSize;
2531
+ table.set(defaultValues, offset);
2532
+ }
2533
+ return table;
2534
+ }
2535
+ applyControlTable(channel, controllerType) {
2536
+ const slotSize = 6;
2537
+ const offset = controllerType * slotSize;
2538
+ const table = channel.controlTable.subarray(offset, offset + slotSize);
2539
+ channel.scheduledNotes.forEach((noteList) => {
2540
+ for (let i = 0; i < noteList.length; i++) {
2541
+ const note = noteList[i];
2542
+ if (!note)
2543
+ continue;
2544
+ this.applyDestinationSettings(channel, note, table);
2545
+ }
2546
+ });
2547
+ }
2548
+ handleControlChangeSysEx(data) {
2549
+ const channelNumber = data[4];
2550
+ const controllerType = data[5];
2551
+ const table = this.channels[channelNumber].controlTable[controllerType];
2552
+ for (let i = 6; i < data.length - 1; i += 2) {
2553
+ const pp = data[i];
2554
+ const rr = data[i + 1];
2555
+ table[pp] = rr;
2556
+ }
2557
+ }
2521
2558
  getKeyBasedInstrumentControlValue(channel, keyNumber, controllerType) {
2522
2559
  const index = keyNumber * 128 + controllerType;
2523
2560
  const controlValue = channel.keyBasedInstrumentControlTable[index];