@marmooo/midy 0.1.5 → 0.1.7
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-GM1.d.ts +6 -5
- package/esm/midy-GM1.d.ts.map +1 -1
- package/esm/midy-GM1.js +97 -65
- package/esm/midy-GM2.d.ts +15 -9
- package/esm/midy-GM2.d.ts.map +1 -1
- package/esm/midy-GM2.js +316 -129
- package/esm/midy-GMLite.d.ts +6 -5
- package/esm/midy-GMLite.d.ts.map +1 -1
- package/esm/midy-GMLite.js +97 -65
- package/esm/midy.d.ts +15 -9
- package/esm/midy.d.ts.map +1 -1
- package/esm/midy.js +317 -134
- package/package.json +5 -1
- package/script/midy-GM1.d.ts +6 -5
- package/script/midy-GM1.d.ts.map +1 -1
- package/script/midy-GM1.js +100 -68
- package/script/midy-GM2.d.ts +15 -9
- package/script/midy-GM2.d.ts.map +1 -1
- package/script/midy-GM2.js +319 -132
- package/script/midy-GMLite.d.ts +6 -5
- package/script/midy-GMLite.d.ts.map +1 -1
- package/script/midy-GMLite.js +100 -68
- package/script/midy.d.ts +15 -9
- package/script/midy.d.ts.map +1 -1
- package/script/midy.js +320 -137
- package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.6/+esm.d.ts +0 -149
- package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.6/+esm.d.ts.map +0 -1
- package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.6/+esm.js +0 -180
- package/esm/deps/cdn.jsdelivr.net/npm/midi-file@1.2.4/+esm.d.ts +0 -84
- package/esm/deps/cdn.jsdelivr.net/npm/midi-file@1.2.4/+esm.d.ts.map +0 -1
- package/esm/deps/cdn.jsdelivr.net/npm/midi-file@1.2.4/+esm.js +0 -216
- package/script/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.6/+esm.d.ts +0 -149
- package/script/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.6/+esm.d.ts.map +0 -1
- package/script/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.6/+esm.js +0 -190
- package/script/deps/cdn.jsdelivr.net/npm/midi-file@1.2.4/+esm.d.ts +0 -84
- package/script/deps/cdn.jsdelivr.net/npm/midi-file@1.2.4/+esm.d.ts.map +0 -1
- package/script/deps/cdn.jsdelivr.net/npm/midi-file@1.2.4/+esm.js +0 -221
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@marmooo/midy",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
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",
|
|
@@ -21,6 +21,10 @@
|
|
|
21
21
|
"scripts": {
|
|
22
22
|
"test": "node test_runner.js"
|
|
23
23
|
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@marmooo/soundfont-parser": "^0.0.7",
|
|
26
|
+
"midi-file": "^1.2.4"
|
|
27
|
+
},
|
|
24
28
|
"devDependencies": {
|
|
25
29
|
"@types/node": "^20.9.0",
|
|
26
30
|
"picocolors": "^1.0.0"
|
package/script/midy-GM1.d.ts
CHANGED
|
@@ -37,6 +37,7 @@ export class MidyGM1 {
|
|
|
37
37
|
timeline: any[];
|
|
38
38
|
instruments: any[];
|
|
39
39
|
notePromises: any[];
|
|
40
|
+
exclusiveClassMap: Map<any, any>;
|
|
40
41
|
audioContext: any;
|
|
41
42
|
masterGain: any;
|
|
42
43
|
controlChangeHandlers: {
|
|
@@ -66,7 +67,6 @@ export class MidyGM1 {
|
|
|
66
67
|
createChannels(audioContext: any): any[];
|
|
67
68
|
createNoteBuffer(instrumentKey: any, isSF3: any): Promise<any>;
|
|
68
69
|
createNoteBufferNode(instrumentKey: any, isSF3: any): Promise<any>;
|
|
69
|
-
convertToFloat32Array(uint8Array: any): Float32Array;
|
|
70
70
|
scheduleTimelineEvents(t: any, offset: any, queueIndex: any): Promise<any>;
|
|
71
71
|
getQueueIndex(second: any): number;
|
|
72
72
|
playNotes(): Promise<any>;
|
|
@@ -76,8 +76,8 @@ export class MidyGM1 {
|
|
|
76
76
|
instruments: Set<any>;
|
|
77
77
|
timeline: any[];
|
|
78
78
|
};
|
|
79
|
-
stopChannelNotes(channelNumber: any, velocity: any,
|
|
80
|
-
stopNotes(velocity: any,
|
|
79
|
+
stopChannelNotes(channelNumber: any, velocity: any, force: any): Promise<void>;
|
|
80
|
+
stopNotes(velocity: any, force: any): Promise<any[]>;
|
|
81
81
|
start(): Promise<void>;
|
|
82
82
|
stop(): void;
|
|
83
83
|
pause(): void;
|
|
@@ -99,7 +99,8 @@ export class MidyGM1 {
|
|
|
99
99
|
createNote(channel: any, instrumentKey: any, noteNumber: any, velocity: any, startTime: any, isSF3: any): Promise<Note>;
|
|
100
100
|
scheduleNoteOn(channelNumber: any, noteNumber: any, velocity: any, startTime: any): Promise<void>;
|
|
101
101
|
noteOn(channelNumber: any, noteNumber: any, velocity: any): Promise<void>;
|
|
102
|
-
|
|
102
|
+
stopNote(endTime: any, stopTime: any, scheduledNotes: any, index: any): Promise<any>;
|
|
103
|
+
scheduleNoteRelease(channelNumber: any, noteNumber: any, _velocity: any, endTime: any, force: any): Promise<any> | undefined;
|
|
103
104
|
releaseNote(channelNumber: any, noteNumber: any, velocity: any): Promise<any> | undefined;
|
|
104
105
|
releaseSustainPedal(channelNumber: any, halfVelocity: any): any[];
|
|
105
106
|
handleMIDIMessage(statusByte: any, data1: any, data2: any): void | Promise<any>;
|
|
@@ -131,7 +132,7 @@ export class MidyGM1 {
|
|
|
131
132
|
setPan(channelNumber: any, pan: any): void;
|
|
132
133
|
setExpression(channelNumber: any, expression: any): void;
|
|
133
134
|
dataEntryLSB(channelNumber: any, value: any): void;
|
|
134
|
-
|
|
135
|
+
updateChannelVolume(channel: any): void;
|
|
135
136
|
setSustainPedal(channelNumber: any, value: any): void;
|
|
136
137
|
limitData(channel: any, minMSB: any, maxMSB: any, minLSB: any, maxLSB: any): void;
|
|
137
138
|
limitDataMSB(channel: any, minMSB: any, maxMSB: any): void;
|
package/script/midy-GM1.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"midy-GM1.d.ts","sourceRoot":"","sources":["../src/midy-GM1.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"midy-GM1.d.ts","sourceRoot":"","sources":["../src/midy-GM1.js"],"names":[],"mappings":"AAqBA;IAoBE;;;;;;;;;;;MAWE;IAEF;;;;;;;MAOE;IAEF,+BAOC;IAhDD,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;IAyB5B,kBAAgC;IAChC,gBAA4C;IAC5C;;;;;;;;;;;;;MAA+D;IAC/D,gBAAiD;IAKnD,4BAMC;IAED,mCAWC;IAED,gDAMC;IAED,sCASC;IAED;;;;MAeC;IAED,yCAUC;IAED,+DA2BC;IAED,mEAWC;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,4BAEC;IAED,sCAGC;IAED,mFAGC;IAED,mCAeC;IAED,+CAwBC;IAED,6CAIC;IAED,mCAsBC;IAED,+DA0BC;IAED,wHAgCC;IAED,kGAgDC;IAED,0EAGC;IAED,qFA4BC;IAED,6HAuBC;IAED,0FAGC;IAED,kEAeC;IAED,gFAiBC;IAED,4DAGC;IAED,qEAGC;IAED,uDAOC;IAED;;;;;;;;;;;;;MAeC;IAED,2EASC;IAED,qCAkBC;IAED,8DAIC;IACD,iDAIC;IAED;;;MAMC;IAED,2CAIC;IAED,yDAIC;IAED,mDAGC;IAED,wCAUC;IAED,sDAMC;IAED,kFAeC;IAED,2DAMC;IAED,oCAkBC;IAED,gDAEC;IAED,gDAEC;IAED,mDAGC;IAED,oDAaC;IAED,kDAKC;IAED,iEAOC;IAED,8CAKC;IAED,yDAMC;IAED,gDAKC;IAED,6DAMC;IAED,+CAEC;IAED,8CAEC;IAED,+CAEC;IAED,4DAgBC;IAED,oBASC;IAED,yDAaC;IAED,yCAGC;IAED,mCAQC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF;AA9kCD;IAUE,gFAKC;IAdD,kBAAa;IACb,gBAAW;IACX,gBAAW;IACX,iBAAY;IACZ,mBAAc;IACd,qBAAgB;IAChB,gBAAW;IACX,kBAAa;IAGX,gBAA4B;IAC5B,cAAwB;IACxB,eAA0B;IAC1B,mBAAkC;CAErC"}
|
package/script/midy-GM1.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.MidyGM1 = void 0;
|
|
4
|
-
const
|
|
5
|
-
const
|
|
4
|
+
const midi_file_1 = require("midi-file");
|
|
5
|
+
const soundfont_parser_1 = require("@marmooo/soundfont-parser");
|
|
6
6
|
class Note {
|
|
7
7
|
constructor(noteNumber, velocity, startTime, instrumentKey) {
|
|
8
8
|
Object.defineProperty(this, "bufferSource", {
|
|
@@ -163,6 +163,12 @@ class MidyGM1 {
|
|
|
163
163
|
writable: true,
|
|
164
164
|
value: []
|
|
165
165
|
});
|
|
166
|
+
Object.defineProperty(this, "exclusiveClassMap", {
|
|
167
|
+
enumerable: true,
|
|
168
|
+
configurable: true,
|
|
169
|
+
writable: true,
|
|
170
|
+
value: new Map()
|
|
171
|
+
});
|
|
166
172
|
this.audioContext = audioContext;
|
|
167
173
|
this.masterGain = new GainNode(audioContext);
|
|
168
174
|
this.controlChangeHandlers = this.createControlChangeHandlers();
|
|
@@ -180,24 +186,26 @@ class MidyGM1 {
|
|
|
180
186
|
addSoundFont(soundFont) {
|
|
181
187
|
const index = this.soundFonts.length;
|
|
182
188
|
this.soundFonts.push(soundFont);
|
|
183
|
-
soundFont.parsed.presetHeaders
|
|
189
|
+
const presetHeaders = soundFont.parsed.presetHeaders;
|
|
190
|
+
for (let i = 0; i < presetHeaders.length; i++) {
|
|
191
|
+
const presetHeader = presetHeaders[i];
|
|
184
192
|
if (!presetHeader.presetName.startsWith("\u0000")) { // TODO: Only SF3 generated by PolyPone?
|
|
185
193
|
const banks = this.soundFontTable[presetHeader.preset];
|
|
186
194
|
banks.set(presetHeader.bank, index);
|
|
187
195
|
}
|
|
188
|
-
}
|
|
196
|
+
}
|
|
189
197
|
}
|
|
190
198
|
async loadSoundFont(soundFontUrl) {
|
|
191
199
|
const response = await fetch(soundFontUrl);
|
|
192
200
|
const arrayBuffer = await response.arrayBuffer();
|
|
193
|
-
const parsed = (0,
|
|
194
|
-
const soundFont = new
|
|
201
|
+
const parsed = (0, soundfont_parser_1.parse)(new Uint8Array(arrayBuffer));
|
|
202
|
+
const soundFont = new soundfont_parser_1.SoundFont(parsed);
|
|
195
203
|
this.addSoundFont(soundFont);
|
|
196
204
|
}
|
|
197
205
|
async loadMIDI(midiUrl) {
|
|
198
206
|
const response = await fetch(midiUrl);
|
|
199
207
|
const arrayBuffer = await response.arrayBuffer();
|
|
200
|
-
const midi = (0,
|
|
208
|
+
const midi = (0, midi_file_1.parseMidi)(new Uint8Array(arrayBuffer));
|
|
201
209
|
this.ticksPerBeat = midi.header.ticksPerBeat;
|
|
202
210
|
const midiData = this.extractMidiData(midi);
|
|
203
211
|
this.instruments = midiData.instruments;
|
|
@@ -230,27 +238,31 @@ class MidyGM1 {
|
|
|
230
238
|
return channels;
|
|
231
239
|
}
|
|
232
240
|
async createNoteBuffer(instrumentKey, isSF3) {
|
|
241
|
+
const sampleStart = instrumentKey.start;
|
|
233
242
|
const sampleEnd = instrumentKey.sample.length + instrumentKey.end;
|
|
234
243
|
if (isSF3) {
|
|
235
|
-
const sample =
|
|
236
|
-
sample.
|
|
237
|
-
const
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
channelData.set(channelData.subarray(0, sampleEnd));
|
|
241
|
-
}
|
|
244
|
+
const sample = instrumentKey.sample;
|
|
245
|
+
const start = sample.byteOffset + sampleStart;
|
|
246
|
+
const end = sample.byteOffset + sampleEnd;
|
|
247
|
+
const buffer = sample.buffer.slice(start, end);
|
|
248
|
+
const audioBuffer = await this.audioContext.decodeAudioData(buffer);
|
|
242
249
|
return audioBuffer;
|
|
243
250
|
}
|
|
244
251
|
else {
|
|
245
|
-
const sample = instrumentKey.sample
|
|
246
|
-
const
|
|
252
|
+
const sample = instrumentKey.sample;
|
|
253
|
+
const start = sample.byteOffset + sampleStart;
|
|
254
|
+
const end = sample.byteOffset + sampleEnd;
|
|
255
|
+
const buffer = sample.buffer.slice(start, end);
|
|
247
256
|
const audioBuffer = new AudioBuffer({
|
|
248
257
|
numberOfChannels: 1,
|
|
249
258
|
length: sample.length,
|
|
250
259
|
sampleRate: instrumentKey.sampleRate,
|
|
251
260
|
});
|
|
252
261
|
const channelData = audioBuffer.getChannelData(0);
|
|
253
|
-
|
|
262
|
+
const int16Array = new Int16Array(buffer);
|
|
263
|
+
for (let i = 0; i < int16Array.length; i++) {
|
|
264
|
+
channelData[i] = int16Array[i] / 32768;
|
|
265
|
+
}
|
|
254
266
|
return audioBuffer;
|
|
255
267
|
}
|
|
256
268
|
}
|
|
@@ -266,14 +278,6 @@ class MidyGM1 {
|
|
|
266
278
|
}
|
|
267
279
|
return bufferSource;
|
|
268
280
|
}
|
|
269
|
-
convertToFloat32Array(uint8Array) {
|
|
270
|
-
const int16Array = new Int16Array(uint8Array.buffer);
|
|
271
|
-
const float32Array = new Float32Array(int16Array.length);
|
|
272
|
-
for (let i = 0; i < int16Array.length; i++) {
|
|
273
|
-
float32Array[i] = int16Array[i] / 32768;
|
|
274
|
-
}
|
|
275
|
-
return float32Array;
|
|
276
|
-
}
|
|
277
281
|
async scheduleTimelineEvents(t, offset, queueIndex) {
|
|
278
282
|
while (queueIndex < this.timeline.length) {
|
|
279
283
|
const event = this.timeline[queueIndex];
|
|
@@ -329,6 +333,7 @@ class MidyGM1 {
|
|
|
329
333
|
if (queueIndex >= this.timeline.length) {
|
|
330
334
|
await Promise.all(this.notePromises);
|
|
331
335
|
this.notePromises = [];
|
|
336
|
+
this.exclusiveClassMap.clear();
|
|
332
337
|
resolve();
|
|
333
338
|
return;
|
|
334
339
|
}
|
|
@@ -344,6 +349,7 @@ class MidyGM1 {
|
|
|
344
349
|
}
|
|
345
350
|
else if (this.isStopping) {
|
|
346
351
|
await this.stopNotes(0, true);
|
|
352
|
+
this.exclusiveClassMap.clear();
|
|
347
353
|
this.notePromises = [];
|
|
348
354
|
resolve();
|
|
349
355
|
this.isStopping = false;
|
|
@@ -352,6 +358,7 @@ class MidyGM1 {
|
|
|
352
358
|
}
|
|
353
359
|
else if (this.isSeeking) {
|
|
354
360
|
this.stopNotes(0, true);
|
|
361
|
+
this.exclusiveClassMap.clear();
|
|
355
362
|
this.startTime = this.audioContext.currentTime;
|
|
356
363
|
queueIndex = this.getQueueIndex(this.resumeTime);
|
|
357
364
|
offset = this.resumeTime - this.startTime;
|
|
@@ -384,9 +391,11 @@ class MidyGM1 {
|
|
|
384
391
|
bank: this.channels[i].bank,
|
|
385
392
|
};
|
|
386
393
|
}
|
|
387
|
-
midi.tracks.
|
|
394
|
+
for (let i = 0; i < midi.tracks.length; i++) {
|
|
395
|
+
const track = midi.tracks[i];
|
|
388
396
|
let currentTicks = 0;
|
|
389
|
-
track.
|
|
397
|
+
for (let j = 0; j < track.length; j++) {
|
|
398
|
+
const event = track[j];
|
|
390
399
|
currentTicks += event.deltaTime;
|
|
391
400
|
event.ticks = currentTicks;
|
|
392
401
|
switch (event.type) {
|
|
@@ -406,8 +415,8 @@ class MidyGM1 {
|
|
|
406
415
|
}
|
|
407
416
|
delete event.deltaTime;
|
|
408
417
|
timeline.push(event);
|
|
409
|
-
}
|
|
410
|
-
}
|
|
418
|
+
}
|
|
419
|
+
}
|
|
411
420
|
const priority = {
|
|
412
421
|
controller: 0,
|
|
413
422
|
sysEx: 1,
|
|
@@ -432,7 +441,7 @@ class MidyGM1 {
|
|
|
432
441
|
}
|
|
433
442
|
return { instruments, timeline };
|
|
434
443
|
}
|
|
435
|
-
async stopChannelNotes(channelNumber, velocity,
|
|
444
|
+
async stopChannelNotes(channelNumber, velocity, force) {
|
|
436
445
|
const now = this.audioContext.currentTime;
|
|
437
446
|
const channel = this.channels[channelNumber];
|
|
438
447
|
channel.scheduledNotes.forEach((noteList) => {
|
|
@@ -440,16 +449,16 @@ class MidyGM1 {
|
|
|
440
449
|
const note = noteList[i];
|
|
441
450
|
if (!note)
|
|
442
451
|
continue;
|
|
443
|
-
const promise = this.scheduleNoteRelease(channelNumber, note.noteNumber, velocity, now,
|
|
452
|
+
const promise = this.scheduleNoteRelease(channelNumber, note.noteNumber, velocity, now, force);
|
|
444
453
|
this.notePromises.push(promise);
|
|
445
454
|
}
|
|
446
455
|
});
|
|
447
456
|
channel.scheduledNotes.clear();
|
|
448
457
|
await Promise.all(this.notePromises);
|
|
449
458
|
}
|
|
450
|
-
stopNotes(velocity,
|
|
459
|
+
stopNotes(velocity, force) {
|
|
451
460
|
for (let i = 0; i < this.channels.length; i++) {
|
|
452
|
-
this.stopChannelNotes(i, velocity,
|
|
461
|
+
this.stopChannelNotes(i, velocity, force);
|
|
453
462
|
}
|
|
454
463
|
return Promise.all(this.notePromises);
|
|
455
464
|
}
|
|
@@ -626,7 +635,7 @@ class MidyGM1 {
|
|
|
626
635
|
note.volumeNode = new GainNode(this.audioContext);
|
|
627
636
|
note.filterNode = new BiquadFilterNode(this.audioContext, {
|
|
628
637
|
type: "lowpass",
|
|
629
|
-
Q: instrumentKey.initialFilterQ / 10
|
|
638
|
+
Q: instrumentKey.initialFilterQ / 10, // dB
|
|
630
639
|
});
|
|
631
640
|
this.setVolumeEnvelope(note);
|
|
632
641
|
this.setFilterEnvelope(note);
|
|
@@ -639,7 +648,7 @@ class MidyGM1 {
|
|
|
639
648
|
}
|
|
640
649
|
note.bufferSource.connect(note.filterNode);
|
|
641
650
|
note.filterNode.connect(note.volumeNode);
|
|
642
|
-
note.bufferSource.start(startTime
|
|
651
|
+
note.bufferSource.start(startTime);
|
|
643
652
|
return note;
|
|
644
653
|
}
|
|
645
654
|
async scheduleNoteOn(channelNumber, noteNumber, velocity, startTime) {
|
|
@@ -656,6 +665,19 @@ class MidyGM1 {
|
|
|
656
665
|
const note = await this.createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3);
|
|
657
666
|
note.volumeNode.connect(channel.gainL);
|
|
658
667
|
note.volumeNode.connect(channel.gainR);
|
|
668
|
+
const exclusiveClass = instrumentKey.exclusiveClass;
|
|
669
|
+
if (exclusiveClass !== 0) {
|
|
670
|
+
if (this.exclusiveClassMap.has(exclusiveClass)) {
|
|
671
|
+
const prevEntry = this.exclusiveClassMap.get(exclusiveClass);
|
|
672
|
+
const [prevNote, prevChannelNumber] = prevEntry;
|
|
673
|
+
if (!prevNote.ending) {
|
|
674
|
+
this.scheduleNoteRelease(prevChannelNumber, prevNote.noteNumber, 0, // velocity,
|
|
675
|
+
startTime, undefined, // portamentoNoteNumber
|
|
676
|
+
true);
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
this.exclusiveClassMap.set(exclusiveClass, [note, channelNumber]);
|
|
680
|
+
}
|
|
659
681
|
const scheduledNotes = channel.scheduledNotes;
|
|
660
682
|
if (scheduledNotes.has(noteNumber)) {
|
|
661
683
|
scheduledNotes.get(noteNumber).push(note);
|
|
@@ -668,9 +690,38 @@ class MidyGM1 {
|
|
|
668
690
|
const now = this.audioContext.currentTime;
|
|
669
691
|
return this.scheduleNoteOn(channelNumber, noteNumber, velocity, now);
|
|
670
692
|
}
|
|
671
|
-
|
|
693
|
+
stopNote(endTime, stopTime, scheduledNotes, index) {
|
|
694
|
+
const note = scheduledNotes[index];
|
|
695
|
+
note.volumeNode.gain
|
|
696
|
+
.cancelScheduledValues(endTime)
|
|
697
|
+
.linearRampToValueAtTime(0, stopTime);
|
|
698
|
+
note.ending = true;
|
|
699
|
+
this.scheduleTask(() => {
|
|
700
|
+
note.bufferSource.loop = false;
|
|
701
|
+
}, stopTime);
|
|
702
|
+
return new Promise((resolve) => {
|
|
703
|
+
note.bufferSource.onended = () => {
|
|
704
|
+
scheduledNotes[index] = null;
|
|
705
|
+
note.bufferSource.disconnect();
|
|
706
|
+
note.volumeNode.disconnect();
|
|
707
|
+
note.filterNode.disconnect();
|
|
708
|
+
if (note.modulationDepth) {
|
|
709
|
+
note.volumeDepth.disconnect();
|
|
710
|
+
note.modulationDepth.disconnect();
|
|
711
|
+
note.modulationLFO.stop();
|
|
712
|
+
}
|
|
713
|
+
if (note.vibratoDepth) {
|
|
714
|
+
note.vibratoDepth.disconnect();
|
|
715
|
+
note.vibratoLFO.stop();
|
|
716
|
+
}
|
|
717
|
+
resolve();
|
|
718
|
+
};
|
|
719
|
+
note.bufferSource.stop(stopTime);
|
|
720
|
+
});
|
|
721
|
+
}
|
|
722
|
+
scheduleNoteRelease(channelNumber, noteNumber, _velocity, endTime, force) {
|
|
672
723
|
const channel = this.channels[channelNumber];
|
|
673
|
-
if (
|
|
724
|
+
if (!force && channel.sustainPedal)
|
|
674
725
|
return;
|
|
675
726
|
if (!channel.scheduledNotes.has(noteNumber))
|
|
676
727
|
return;
|
|
@@ -681,33 +732,13 @@ class MidyGM1 {
|
|
|
681
732
|
continue;
|
|
682
733
|
if (note.ending)
|
|
683
734
|
continue;
|
|
684
|
-
const
|
|
685
|
-
note.
|
|
686
|
-
.cancelScheduledValues(stopTime)
|
|
687
|
-
.linearRampToValueAtTime(0, volEndTime);
|
|
688
|
-
const modRelease = stopTime + note.instrumentKey.modRelease;
|
|
735
|
+
const volRelease = endTime + note.instrumentKey.volRelease;
|
|
736
|
+
const modRelease = endTime + note.instrumentKey.modRelease;
|
|
689
737
|
note.filterNode.frequency
|
|
690
|
-
.cancelScheduledValues(
|
|
738
|
+
.cancelScheduledValues(endTime)
|
|
691
739
|
.linearRampToValueAtTime(0, modRelease);
|
|
692
|
-
|
|
693
|
-
this.
|
|
694
|
-
note.bufferSource.loop = false;
|
|
695
|
-
}, stopTime);
|
|
696
|
-
return new Promise((resolve) => {
|
|
697
|
-
note.bufferSource.onended = () => {
|
|
698
|
-
scheduledNotes[i] = null;
|
|
699
|
-
note.bufferSource.disconnect();
|
|
700
|
-
note.volumeNode.disconnect();
|
|
701
|
-
note.filterNode.disconnect();
|
|
702
|
-
if (note.modulationDepth) {
|
|
703
|
-
note.volumeDepth.disconnect();
|
|
704
|
-
note.modulationDepth.disconnect();
|
|
705
|
-
note.modulationLFO.stop();
|
|
706
|
-
}
|
|
707
|
-
resolve();
|
|
708
|
-
};
|
|
709
|
-
note.bufferSource.stop(volEndTime);
|
|
710
|
-
});
|
|
740
|
+
const stopTime = Math.min(volRelease, modRelease);
|
|
741
|
+
return this.stopNote(endTime, stopTime, scheduledNotes, i);
|
|
711
742
|
}
|
|
712
743
|
}
|
|
713
744
|
releaseNote(channelNumber, noteNumber, velocity) {
|
|
@@ -816,7 +847,7 @@ class MidyGM1 {
|
|
|
816
847
|
setVolume(channelNumber, volume) {
|
|
817
848
|
const channel = this.channels[channelNumber];
|
|
818
849
|
channel.volume = volume / 127;
|
|
819
|
-
this.
|
|
850
|
+
this.updateChannelVolume(channel);
|
|
820
851
|
}
|
|
821
852
|
panToGain(pan) {
|
|
822
853
|
const theta = Math.PI / 2 * Math.max(0, pan - 1) / 126;
|
|
@@ -828,18 +859,18 @@ class MidyGM1 {
|
|
|
828
859
|
setPan(channelNumber, pan) {
|
|
829
860
|
const channel = this.channels[channelNumber];
|
|
830
861
|
channel.pan = pan;
|
|
831
|
-
this.
|
|
862
|
+
this.updateChannelVolume(channel);
|
|
832
863
|
}
|
|
833
864
|
setExpression(channelNumber, expression) {
|
|
834
865
|
const channel = this.channels[channelNumber];
|
|
835
866
|
channel.expression = expression / 127;
|
|
836
|
-
this.
|
|
867
|
+
this.updateChannelVolume(channel);
|
|
837
868
|
}
|
|
838
869
|
dataEntryLSB(channelNumber, value) {
|
|
839
870
|
this.channels[channelNumber].dataLSB = value;
|
|
840
871
|
this.handleRPN(channelNumber, 0);
|
|
841
872
|
}
|
|
842
|
-
|
|
873
|
+
updateChannelVolume(channel) {
|
|
843
874
|
const now = this.audioContext.currentTime;
|
|
844
875
|
const volume = channel.volume * channel.expression;
|
|
845
876
|
const { gainLeft, gainRight } = this.panToGain(channel.pan);
|
|
@@ -992,11 +1023,12 @@ class MidyGM1 {
|
|
|
992
1023
|
}
|
|
993
1024
|
}
|
|
994
1025
|
GM1SystemOn() {
|
|
995
|
-
this.channels.
|
|
1026
|
+
for (let i = 0; i < this.channels.length; i++) {
|
|
1027
|
+
const channel = this.channels[i];
|
|
996
1028
|
channel.bankMSB = 0;
|
|
997
1029
|
channel.bankLSB = 0;
|
|
998
1030
|
channel.bank = 0;
|
|
999
|
-
}
|
|
1031
|
+
}
|
|
1000
1032
|
this.channels[9].bankMSB = 1;
|
|
1001
1033
|
this.channels[9].bank = 128;
|
|
1002
1034
|
}
|
package/script/midy-GM2.d.ts
CHANGED
|
@@ -78,6 +78,7 @@ export class MidyGM2 {
|
|
|
78
78
|
timeline: any[];
|
|
79
79
|
instruments: any[];
|
|
80
80
|
notePromises: any[];
|
|
81
|
+
exclusiveClassMap: Map<any, any>;
|
|
81
82
|
defaultOptions: {
|
|
82
83
|
reverbAlgorithm: (audioContext: any) => {
|
|
83
84
|
input: any;
|
|
@@ -144,7 +145,7 @@ export class MidyGM2 {
|
|
|
144
145
|
createChannels(audioContext: any): any[];
|
|
145
146
|
createNoteBuffer(instrumentKey: any, isSF3: any): Promise<any>;
|
|
146
147
|
createNoteBufferNode(instrumentKey: any, isSF3: any): Promise<any>;
|
|
147
|
-
|
|
148
|
+
findPortamentoTarget(queueIndex: any): any;
|
|
148
149
|
scheduleTimelineEvents(t: any, offset: any, queueIndex: any): Promise<any>;
|
|
149
150
|
getQueueIndex(second: any): number;
|
|
150
151
|
playNotes(): Promise<any>;
|
|
@@ -154,8 +155,8 @@ export class MidyGM2 {
|
|
|
154
155
|
instruments: Set<any>;
|
|
155
156
|
timeline: any[];
|
|
156
157
|
};
|
|
157
|
-
stopChannelNotes(channelNumber: any, velocity: any,
|
|
158
|
-
stopNotes(velocity: any,
|
|
158
|
+
stopChannelNotes(channelNumber: any, velocity: any, force: any): Promise<void>;
|
|
159
|
+
stopNotes(velocity: any, force: any): Promise<any[]>;
|
|
159
160
|
start(): Promise<void>;
|
|
160
161
|
stop(): void;
|
|
161
162
|
pause(): void;
|
|
@@ -191,18 +192,21 @@ export class MidyGM2 {
|
|
|
191
192
|
centToHz(cent: any): number;
|
|
192
193
|
calcSemitoneOffset(channel: any): any;
|
|
193
194
|
calcPlaybackRate(instrumentKey: any, noteNumber: any, semitoneOffset: any): number;
|
|
195
|
+
setPortamentoStartVolumeEnvelope(channel: any, note: any): void;
|
|
194
196
|
setVolumeEnvelope(note: any): void;
|
|
195
197
|
setPitch(note: any, semitoneOffset: any): void;
|
|
196
198
|
clampCutoffFrequency(frequency: any): number;
|
|
199
|
+
setPortamentoStartFilterEnvelope(channel: any, note: any): void;
|
|
197
200
|
setFilterEnvelope(channel: any, note: any): void;
|
|
198
201
|
startModulation(channel: any, note: any, startTime: any): void;
|
|
199
202
|
startVibrato(channel: any, note: any, startTime: any): void;
|
|
200
|
-
createNote(channel: any, instrumentKey: any, noteNumber: any, velocity: any, startTime: any, isSF3: any): Promise<Note>;
|
|
203
|
+
createNote(channel: any, instrumentKey: any, noteNumber: any, velocity: any, startTime: any, portamento: any, isSF3: any): Promise<Note>;
|
|
201
204
|
calcBank(channel: any, channelNumber: any): any;
|
|
202
|
-
scheduleNoteOn(channelNumber: any, noteNumber: any, velocity: any, startTime: any): Promise<void>;
|
|
203
|
-
noteOn(channelNumber: any, noteNumber: any, velocity: any): Promise<void>;
|
|
204
|
-
|
|
205
|
-
|
|
205
|
+
scheduleNoteOn(channelNumber: any, noteNumber: any, velocity: any, startTime: any, portamento: any): Promise<void>;
|
|
206
|
+
noteOn(channelNumber: any, noteNumber: any, velocity: any, portamento: any): Promise<void>;
|
|
207
|
+
stopNote(endTime: any, stopTime: any, scheduledNotes: any, index: any): Promise<any>;
|
|
208
|
+
scheduleNoteRelease(channelNumber: any, noteNumber: any, _velocity: any, endTime: any, portamentoNoteNumber: any, force: any): Promise<any> | undefined;
|
|
209
|
+
releaseNote(channelNumber: any, noteNumber: any, velocity: any, portamentoNoteNumber: any): Promise<any> | undefined;
|
|
206
210
|
releaseSustainPedal(channelNumber: any, halfVelocity: any): any[];
|
|
207
211
|
releaseSostenutoPedal(channelNumber: any, halfVelocity: any): any[];
|
|
208
212
|
handleMIDIMessage(statusByte: any, data1: any, data2: any): void | Promise<any>;
|
|
@@ -250,7 +254,7 @@ export class MidyGM2 {
|
|
|
250
254
|
setExpression(channelNumber: any, expression: any): void;
|
|
251
255
|
setBankLSB(channelNumber: any, lsb: any): void;
|
|
252
256
|
dataEntryLSB(channelNumber: any, value: any): void;
|
|
253
|
-
|
|
257
|
+
updateChannelVolume(channel: any): void;
|
|
254
258
|
setSustainPedal(channelNumber: any, value: any): void;
|
|
255
259
|
setPortamento(channelNumber: any, value: any): void;
|
|
256
260
|
setReverbSendLevel(channelNumber: any, reverbSendLevel: any): void;
|
|
@@ -321,6 +325,8 @@ declare class Note {
|
|
|
321
325
|
modulationDepth: any;
|
|
322
326
|
vibratoLFO: any;
|
|
323
327
|
vibratoDepth: any;
|
|
328
|
+
reverbEffectsSend: any;
|
|
329
|
+
chorusEffectsSend: any;
|
|
324
330
|
noteNumber: any;
|
|
325
331
|
velocity: any;
|
|
326
332
|
startTime: any;
|
package/script/midy-GM2.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"midy-GM2.d.ts","sourceRoot":"","sources":["../src/midy-GM2.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"midy-GM2.d.ts","sourceRoot":"","sources":["../src/midy-GM2.js"],"names":[],"mappings":"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"}
|