@marmooo/midy 0.1.3 → 0.1.4

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
@@ -4,6 +4,11 @@ export class Midy {
4
4
  volume: number;
5
5
  pan: number;
6
6
  portamentoTime: number;
7
+ filterResonance: number;
8
+ releaseTime: number;
9
+ attackTime: number;
10
+ brightness: number;
11
+ decayTime: number;
7
12
  reverbSendLevel: number;
8
13
  chorusSendLevel: number;
9
14
  vibratoRate: number;
@@ -165,16 +170,17 @@ export class Midy {
165
170
  centToHz(cent: any): number;
166
171
  calcSemitoneOffset(channel: any): any;
167
172
  calcPlaybackRate(instrumentKey: any, noteNumber: any, semitoneOffset: any): number;
168
- setVolumeEnvelope(note: any): void;
173
+ setVolumeEnvelope(channel: any, note: any): void;
169
174
  setPitch(note: any, semitoneOffset: any): void;
170
- setFilterNode(channel: any, note: any): void;
175
+ clampCutoffFrequency(frequency: any): number;
176
+ setFilterEnvelope(channel: any, note: any): void;
171
177
  startModulation(channel: any, note: any, startTime: any): void;
172
178
  startVibrato(channel: any, note: any, startTime: any): void;
173
179
  createNote(channel: any, instrumentKey: any, noteNumber: any, velocity: any, startTime: any, isSF3: any): Promise<Note>;
174
180
  calcBank(channel: any, channelNumber: any): any;
175
181
  scheduleNoteOn(channelNumber: any, noteNumber: any, velocity: any, startTime: any): Promise<void>;
176
182
  noteOn(channelNumber: any, noteNumber: any, velocity: any): Promise<void>;
177
- scheduleNoteRelease(channelNumber: any, noteNumber: any, velocity: any, stopTime: any, stopPedal?: boolean): Promise<any> | undefined;
183
+ scheduleNoteRelease(channelNumber: any, noteNumber: any, _velocity: any, stopTime: any, stopPedal?: boolean): Promise<any> | undefined;
178
184
  releaseNote(channelNumber: any, noteNumber: any, velocity: any): Promise<any> | undefined;
179
185
  releaseSustainPedal(channelNumber: any, halfVelocity: any): any[];
180
186
  releaseSostenutoPedal(channelNumber: any, halfVelocity: any): any[];
@@ -205,6 +211,11 @@ export class Midy {
205
211
  setChorusSendLevel(channelNumber: any, chorusSendLevel: any): void;
206
212
  setSostenutoPedal(channelNumber: any, value: any): void;
207
213
  setSoftPedal(channelNumber: any, softPedal: any): void;
214
+ setFilterResonance(channelNumber: any, filterResonance: any): void;
215
+ setReleaseTime(channelNumber: any, releaseTime: any): void;
216
+ setAttackTime(channelNumber: any, attackTime: any): void;
217
+ setBrightness(channelNumber: any, brightness: any): void;
218
+ setDecayTime(channelNumber: any, dacayTime: any): void;
208
219
  setVibratoRate(channelNumber: any, vibratoRate: any): void;
209
220
  setVibratoDepth(channelNumber: any, vibratoDepth: any): void;
210
221
  setVibratoDelay(channelNumber: any, vibratoDelay: 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
@@ -554,12 +554,13 @@ class Midy {
554
554
  const now = this.audioContext.currentTime;
555
555
  const channel = this.channels[channelNumber];
556
556
  channel.scheduledNotes.forEach((noteList) => {
557
- noteList.forEach((note) => {
558
- if (note) {
559
- const promise = this.scheduleNoteRelease(channelNumber, note.noteNumber, velocity, now, stopPedal);
560
- this.notePromises.push(promise);
561
- }
562
- });
557
+ for (let i = 0; i < noteList.length; i++) {
558
+ const note = noteList[i];
559
+ if (!note)
560
+ continue;
561
+ const promise = this.scheduleNoteRelease(channelNumber, note.noteNumber, velocity, now, stopPedal);
562
+ this.notePromises.push(promise);
563
+ }
563
564
  });
564
565
  channel.scheduledNotes.clear();
565
566
  await Promise.all(this.notePromises);
@@ -780,16 +781,17 @@ class Midy {
780
781
  return instrumentKey.playbackRate(noteNumber) *
781
782
  Math.pow(2, semitoneOffset / 12);
782
783
  }
783
- setVolumeEnvelope(note) {
784
+ setVolumeEnvelope(channel, note) {
784
785
  const { instrumentKey, startTime } = note;
785
- note.volumeNode = new GainNode(this.audioContext, { gain: 0 });
786
786
  const attackVolume = this.cbToRatio(-instrumentKey.initialAttenuation);
787
787
  const sustainVolume = attackVolume * (1 - instrumentKey.volSustain);
788
788
  const volDelay = startTime + instrumentKey.volDelay;
789
- const volAttack = volDelay + instrumentKey.volAttack;
789
+ const volAttack = volDelay + instrumentKey.volAttack * channel.attackTime;
790
790
  const volHold = volAttack + instrumentKey.volHold;
791
- const volDecay = volHold + instrumentKey.volDecay;
791
+ const volDecay = volHold + instrumentKey.volDecay * channel.decayTime;
792
792
  note.volumeNode.gain
793
+ .cancelScheduledValues(startTime)
794
+ .setValueAtTime(0, startTime)
793
795
  .setValueAtTime(1e-6, volDelay) // exponentialRampToValueAtTime() requires a non-zero value
794
796
  .exponentialRampToValueAtTime(attackVolume, volAttack)
795
797
  .setValueAtTime(attackVolume, volHold)
@@ -813,29 +815,30 @@ class Midy {
813
815
  .setValueAtTime(peekPitch, modHold)
814
816
  .linearRampToValueAtTime(basePitch, modDecay);
815
817
  }
816
- setFilterNode(channel, note) {
818
+ clampCutoffFrequency(frequency) {
819
+ const minFrequency = 20; // min Hz of initialFilterFc
820
+ const maxFrequency = 20000; // max Hz of initialFilterFc
821
+ return Math.max(minFrequency, Math.min(frequency, maxFrequency));
822
+ }
823
+ setFilterEnvelope(channel, note) {
817
824
  const { instrumentKey, noteNumber, startTime } = note;
818
825
  const softPedalFactor = 1 -
819
826
  (0.1 + (noteNumber / 127) * 0.2) * channel.softPedal;
820
- const maxFreq = this.audioContext.sampleRate / 2;
821
827
  const baseFreq = this.centToHz(instrumentKey.initialFilterFc) *
822
- softPedalFactor;
823
- const peekFreq = this.centToHz(instrumentKey.initialFilterFc + instrumentKey.modEnvToFilterFc) * softPedalFactor;
828
+ softPedalFactor * channel.brightness;
829
+ const peekFreq = this.centToHz(instrumentKey.initialFilterFc + instrumentKey.modEnvToFilterFc) * softPedalFactor * channel.brightness;
824
830
  const sustainFreq = baseFreq +
825
831
  (peekFreq - baseFreq) * (1 - instrumentKey.modSustain);
826
- const adjustedBaseFreq = Math.min(maxFreq, baseFreq);
827
- const adjustedPeekFreq = Math.min(maxFreq, peekFreq);
828
- const adjustedSustainFreq = Math.min(maxFreq, sustainFreq);
832
+ const adjustedBaseFreq = this.clampCutoffFrequency(baseFreq);
833
+ const adjustedPeekFreq = this.clampCutoffFrequency(peekFreq);
834
+ const adjustedSustainFreq = this.clampCutoffFrequency(sustainFreq);
829
835
  const modDelay = startTime + instrumentKey.modDelay;
830
836
  const modAttack = modDelay + instrumentKey.modAttack;
831
837
  const modHold = modAttack + instrumentKey.modHold;
832
838
  const modDecay = modHold + instrumentKey.modDecay;
833
- note.filterNode = new BiquadFilterNode(this.audioContext, {
834
- type: "lowpass",
835
- Q: instrumentKey.initialFilterQ / 10, // dB
836
- frequency: adjustedBaseFreq,
837
- });
838
839
  note.filterNode.frequency
840
+ .cancelScheduledValues(startTime)
841
+ .setValueAtTime(adjustedBaseFreq, startTime)
839
842
  .setValueAtTime(adjustedBaseFreq, modDelay)
840
843
  .exponentialRampToValueAtTime(adjustedPeekFreq, modAttack)
841
844
  .setValueAtTime(adjustedPeekFreq, modHold)
@@ -888,8 +891,13 @@ class Midy {
888
891
  const semitoneOffset = this.calcSemitoneOffset(channel);
889
892
  const note = new Note(noteNumber, velocity, startTime, instrumentKey);
890
893
  note.bufferSource = await this.createNoteBufferNode(instrumentKey, isSF3);
891
- this.setFilterNode(channel, note);
892
- this.setVolumeEnvelope(note);
894
+ note.volumeNode = new GainNode(this.audioContext);
895
+ note.filterNode = new BiquadFilterNode(this.audioContext, {
896
+ type: "lowpass",
897
+ Q: instrumentKey.initialFilterQ / 10 * channel.filterResonance, // dB
898
+ });
899
+ this.setVolumeEnvelope(channel, note);
900
+ this.setFilterEnvelope(channel, note);
893
901
  if (0 < channel.vibratoDepth) {
894
902
  this.startVibrato(channel, note, startTime);
895
903
  }
@@ -947,7 +955,7 @@ class Midy {
947
955
  const now = this.audioContext.currentTime;
948
956
  return this.scheduleNoteOn(channelNumber, noteNumber, velocity, now);
949
957
  }
950
- scheduleNoteRelease(channelNumber, noteNumber, velocity, stopTime, stopPedal = false) {
958
+ scheduleNoteRelease(channelNumber, noteNumber, _velocity, stopTime, stopPedal = false) {
951
959
  const channel = this.channels[channelNumber];
952
960
  if (stopPedal && channel.sustainPedal)
953
961
  return;
@@ -962,14 +970,12 @@ class Midy {
962
970
  continue;
963
971
  if (note.ending)
964
972
  continue;
965
- const velocityRate = (velocity + 127) / 127;
966
973
  const volEndTime = stopTime +
967
- note.instrumentKey.volRelease * velocityRate;
974
+ note.instrumentKey.volRelease * channel.releaseTime;
968
975
  note.volumeNode.gain
969
976
  .cancelScheduledValues(stopTime)
970
977
  .linearRampToValueAtTime(0, volEndTime);
971
- const modRelease = stopTime +
972
- note.instrumentKey.modRelease * velocityRate;
978
+ const modRelease = stopTime + note.instrumentKey.modRelease;
973
979
  note.filterNode.frequency
974
980
  .cancelScheduledValues(stopTime)
975
981
  .linearRampToValueAtTime(0, modRelease);
@@ -1009,13 +1015,14 @@ class Midy {
1009
1015
  const promises = [];
1010
1016
  channel.sustainPedal = false;
1011
1017
  channel.scheduledNotes.forEach((noteList) => {
1012
- noteList.forEach((note) => {
1013
- if (note) {
1014
- const { noteNumber } = note;
1015
- const promise = this.releaseNote(channelNumber, noteNumber, velocity);
1016
- promises.push(promise);
1017
- }
1018
- });
1018
+ for (let i = 0; i < noteList.length; i++) {
1019
+ const note = noteList[i];
1020
+ if (!note)
1021
+ continue;
1022
+ const { noteNumber } = note;
1023
+ const promise = this.releaseNote(channelNumber, noteNumber, velocity);
1024
+ promises.push(promise);
1025
+ }
1019
1026
  });
1020
1027
  return promises;
1021
1028
  }
@@ -1129,7 +1136,16 @@ class Midy {
1129
1136
  return this.setSostenutoPedal(channelNumber, value);
1130
1137
  case 67:
1131
1138
  return this.setSoftPedal(channelNumber, value);
1132
- // TODO: 71-75
1139
+ case 71:
1140
+ return this.setFilterResonance(channelNumber, value);
1141
+ case 72:
1142
+ return this.setReleaseTime(channelNumber, value);
1143
+ case 73:
1144
+ return this.setAttackTime(channelNumber, value);
1145
+ case 74:
1146
+ return this.setBrightness(channelNumber, value);
1147
+ case 75:
1148
+ return this.setDecayTime(channelNumber, value);
1133
1149
  case 76:
1134
1150
  return this.setVibratoRate(channelNumber, value);
1135
1151
  case 77:
@@ -1171,15 +1187,19 @@ class Midy {
1171
1187
  }
1172
1188
  updateModulation(channel) {
1173
1189
  const now = this.audioContext.currentTime;
1174
- const activeNotes = this.getActiveNotes(channel, now);
1175
- activeNotes.forEach((activeNote) => {
1176
- if (activeNote.modulationDepth) {
1177
- activeNote.modulationDepth.gain.setValueAtTime(channel.modulationDepth, now);
1178
- }
1179
- else {
1180
- const semitoneOffset = this.calcSemitoneOffset(channel);
1181
- this.setPitch(activeNote, semitoneOffset);
1182
- this.startModulation(channel, activeNote, now);
1190
+ channel.scheduledNotes.forEach((noteList) => {
1191
+ for (let i = 0; i < noteList.length; i++) {
1192
+ const note = noteList[i];
1193
+ if (!note)
1194
+ continue;
1195
+ if (note.modulationDepth) {
1196
+ note.modulationDepth.gain.setValueAtTime(channel.modulationDepth, now);
1197
+ }
1198
+ else {
1199
+ const semitoneOffset = this.calcSemitoneOffset(channel);
1200
+ this.setPitch(note, semitoneOffset);
1201
+ this.startModulation(channel, note, now);
1202
+ }
1183
1203
  }
1184
1204
  });
1185
1205
  }
@@ -1285,6 +1305,64 @@ class Midy {
1285
1305
  const channel = this.channels[channelNumber];
1286
1306
  channel.softPedal = softPedal / 127;
1287
1307
  }
1308
+ setFilterResonance(channelNumber, filterResonance) {
1309
+ const now = this.audioContext.currentTime;
1310
+ const channel = this.channels[channelNumber];
1311
+ channel.filterResonance = filterResonance / 64;
1312
+ channel.scheduledNotes.forEach((noteList) => {
1313
+ for (let i = 0; i < noteList.length; i++) {
1314
+ const note = noteList[i];
1315
+ if (!note)
1316
+ continue;
1317
+ const Q = note.instrumentKey.initialFilterQ / 10 *
1318
+ channel.filterResonance;
1319
+ note.filterNode.Q.setValueAtTime(Q, now);
1320
+ }
1321
+ });
1322
+ }
1323
+ setReleaseTime(channelNumber, releaseTime) {
1324
+ const channel = this.channels[channelNumber];
1325
+ channel.releaseTime = releaseTime / 64;
1326
+ }
1327
+ setAttackTime(channelNumber, attackTime) {
1328
+ const now = this.audioContext.currentTime;
1329
+ const channel = this.channels[channelNumber];
1330
+ channel.attackTime = attackTime / 64;
1331
+ channel.scheduledNotes.forEach((noteList) => {
1332
+ for (let i = 0; i < noteList.length; i++) {
1333
+ const note = noteList[i];
1334
+ if (!note)
1335
+ continue;
1336
+ if (note.startTime < now)
1337
+ continue;
1338
+ this.setVolumeEnvelope(channel, note);
1339
+ }
1340
+ });
1341
+ }
1342
+ setBrightness(channelNumber, brightness) {
1343
+ const channel = this.channels[channelNumber];
1344
+ channel.brightness = brightness / 64;
1345
+ channel.scheduledNotes.forEach((noteList) => {
1346
+ for (let i = 0; i < noteList.length; i++) {
1347
+ const note = noteList[i];
1348
+ if (!note)
1349
+ continue;
1350
+ this.setFilterEnvelope(channel, note);
1351
+ }
1352
+ });
1353
+ }
1354
+ setDecayTime(channelNumber, dacayTime) {
1355
+ const channel = this.channels[channelNumber];
1356
+ channel.decayTime = dacayTime / 64;
1357
+ channel.scheduledNotes.forEach((noteList) => {
1358
+ for (let i = 0; i < noteList.length; i++) {
1359
+ const note = noteList[i];
1360
+ if (!note)
1361
+ continue;
1362
+ this.setVolumeEnvelope(channel, note);
1363
+ }
1364
+ });
1365
+ }
1288
1366
  setVibratoRate(channelNumber, vibratoRate) {
1289
1367
  const channel = this.channels[channelNumber];
1290
1368
  channel.vibratoRate = vibratoRate / 64;
@@ -1365,13 +1443,17 @@ class Midy {
1365
1443
  }
1366
1444
  updateDetune(channel, detuneChange) {
1367
1445
  const now = this.audioContext.currentTime;
1368
- const activeNotes = this.getActiveNotes(channel, now);
1369
- activeNotes.forEach((activeNote) => {
1370
- const { bufferSource } = activeNote;
1371
- const detune = bufferSource.detune.value + detuneChange;
1372
- bufferSource.detune
1373
- .cancelScheduledValues(now)
1374
- .setValueAtTime(detune, now);
1446
+ channel.scheduledNotes.forEach((noteList) => {
1447
+ for (let i = 0; i < noteList.length; i++) {
1448
+ const note = noteList[i];
1449
+ if (!note)
1450
+ continue;
1451
+ const { bufferSource } = note;
1452
+ const detune = bufferSource.detune.value + detuneChange;
1453
+ bufferSource.detune
1454
+ .cancelScheduledValues(now)
1455
+ .setValueAtTime(detune, now);
1456
+ }
1375
1457
  });
1376
1458
  }
1377
1459
  handlePitchBendRangeRPN(channelNumber) {
@@ -1792,6 +1874,11 @@ Object.defineProperty(Midy, "channelSettings", {
1792
1874
  volume: 100 / 127,
1793
1875
  pan: 64,
1794
1876
  portamentoTime: 0,
1877
+ filterResonance: 1,
1878
+ releaseTime: 1,
1879
+ attackTime: 1,
1880
+ brightness: 1,
1881
+ decayTime: 1,
1795
1882
  reverbSendLevel: 0,
1796
1883
  chorusSendLevel: 0,
1797
1884
  vibratoRate: 1,