@marmooo/midy 0.2.4 → 0.2.5
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 +20 -3
- package/esm/midy-GM1.d.ts.map +1 -1
- package/esm/midy-GM1.js +104 -10
- package/esm/midy-GM2.d.ts +22 -5
- package/esm/midy-GM2.d.ts.map +1 -1
- package/esm/midy-GM2.js +116 -25
- package/esm/midy-GMLite.d.ts +20 -3
- package/esm/midy-GMLite.d.ts.map +1 -1
- package/esm/midy-GMLite.js +104 -10
- package/esm/midy.d.ts +23 -5
- package/esm/midy.d.ts.map +1 -1
- package/esm/midy.js +145 -27
- package/package.json +1 -1
- package/script/midy-GM1.d.ts +20 -3
- package/script/midy-GM1.d.ts.map +1 -1
- package/script/midy-GM1.js +104 -10
- package/script/midy-GM2.d.ts +22 -5
- package/script/midy-GM2.d.ts.map +1 -1
- package/script/midy-GM2.js +116 -25
- package/script/midy-GMLite.d.ts +20 -3
- package/script/midy-GMLite.d.ts.map +1 -1
- package/script/midy-GMLite.js +104 -10
- package/script/midy.d.ts +23 -5
- package/script/midy.d.ts.map +1 -1
- package/script/midy.js +145 -27
package/script/midy-GM2.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ export class MidyGM2 {
|
|
|
2
2
|
static channelSettings: {
|
|
3
3
|
currentBufferSource: null;
|
|
4
4
|
detune: number;
|
|
5
|
-
scaleOctaveTuningTable:
|
|
5
|
+
scaleOctaveTuningTable: Int8Array<ArrayBuffer>;
|
|
6
6
|
channelPressureTable: Uint8Array<ArrayBuffer>;
|
|
7
7
|
keyBasedInstrumentControlTable: Int8Array<ArrayBuffer>;
|
|
8
8
|
program: number;
|
|
@@ -47,6 +47,8 @@ export class MidyGM2 {
|
|
|
47
47
|
resumeTime: number;
|
|
48
48
|
soundFonts: any[];
|
|
49
49
|
soundFontTable: any[];
|
|
50
|
+
audioBufferCounter: Map<any, any>;
|
|
51
|
+
audioBufferCache: Map<any, any>;
|
|
50
52
|
isPlaying: boolean;
|
|
51
53
|
isPausing: boolean;
|
|
52
54
|
isPaused: boolean;
|
|
@@ -55,7 +57,7 @@ export class MidyGM2 {
|
|
|
55
57
|
timeline: any[];
|
|
56
58
|
instruments: any[];
|
|
57
59
|
notePromises: any[];
|
|
58
|
-
exclusiveClassMap:
|
|
60
|
+
exclusiveClassMap: SparseMap;
|
|
59
61
|
defaultOptions: {
|
|
60
62
|
reverbAlgorithm: (audioContext: any) => {
|
|
61
63
|
input: any;
|
|
@@ -133,13 +135,14 @@ export class MidyGM2 {
|
|
|
133
135
|
};
|
|
134
136
|
createChannels(audioContext: any): any[];
|
|
135
137
|
createNoteBuffer(voiceParams: any, isSF3: any): Promise<any>;
|
|
136
|
-
createNoteBufferNode(
|
|
138
|
+
createNoteBufferNode(audioBuffer: any, voiceParams: any): any;
|
|
137
139
|
findPortamentoTarget(queueIndex: any): any;
|
|
138
140
|
scheduleTimelineEvents(t: any, offset: any, queueIndex: any): Promise<any>;
|
|
139
141
|
getQueueIndex(second: any): number;
|
|
140
142
|
playNotes(): Promise<any>;
|
|
141
143
|
ticksToSecond(ticks: any, secondsPerBeat: any): number;
|
|
142
144
|
secondToTicks(second: any, secondsPerBeat: any): number;
|
|
145
|
+
getAudioBufferId(programNumber: any, noteNumber: any, velocity: any): string;
|
|
143
146
|
extractMidiData(midi: any): {
|
|
144
147
|
instruments: Set<any>;
|
|
145
148
|
timeline: any[];
|
|
@@ -153,7 +156,7 @@ export class MidyGM2 {
|
|
|
153
156
|
seekTo(second: any): void;
|
|
154
157
|
calcTotalTime(): number;
|
|
155
158
|
currentTime(): number;
|
|
156
|
-
getActiveNotes(channel: any, time: any):
|
|
159
|
+
getActiveNotes(channel: any, time: any): SparseMap;
|
|
157
160
|
getActiveNote(noteList: any, time: any): any;
|
|
158
161
|
createConvolutionReverbImpulse(audioContext: any, decay: any, preDecay: any): any;
|
|
159
162
|
createConvolutionReverb(audioContext: any, impulse: any): {
|
|
@@ -194,6 +197,7 @@ export class MidyGM2 {
|
|
|
194
197
|
setFilterEnvelope(channel: any, note: any, pressure: any): void;
|
|
195
198
|
startModulation(channel: any, note: any, startTime: any): void;
|
|
196
199
|
startVibrato(channel: any, note: any, startTime: any): void;
|
|
200
|
+
getAudioBuffer(program: any, noteNumber: any, velocity: any, voiceParams: any, isSF3: any): Promise<any>;
|
|
197
201
|
createNote(channel: any, voice: any, noteNumber: any, velocity: any, startTime: any, portamento: any, isSF3: any): Promise<Note>;
|
|
198
202
|
calcBank(channel: any, channelNumber: any): any;
|
|
199
203
|
scheduleNoteOn(channelNumber: any, noteNumber: any, velocity: any, startTime: any, portamento: any): Promise<void>;
|
|
@@ -330,7 +334,7 @@ export class MidyGM2 {
|
|
|
330
334
|
setChorusSendToReverb(value: any): void;
|
|
331
335
|
getChorusSendToReverb(value: any): number;
|
|
332
336
|
getChannelBitmap(data: any): any[];
|
|
333
|
-
handleScaleOctaveTuning1ByteFormatSysEx(data: any): void;
|
|
337
|
+
handleScaleOctaveTuning1ByteFormatSysEx(data: any, realtime: any): void;
|
|
334
338
|
applyDestinationSettings(channel: any, note: any, table: any): void;
|
|
335
339
|
handleChannelPressureSysEx(data: any, tableName: any): void;
|
|
336
340
|
initControlTable(): Uint8Array<ArrayBuffer>;
|
|
@@ -342,6 +346,19 @@ export class MidyGM2 {
|
|
|
342
346
|
handleSysEx(data: any): any;
|
|
343
347
|
scheduleTask(callback: any, startTime: any): Promise<any>;
|
|
344
348
|
}
|
|
349
|
+
declare class SparseMap {
|
|
350
|
+
constructor(size: any);
|
|
351
|
+
data: any[];
|
|
352
|
+
activeIndices: any[];
|
|
353
|
+
set(key: any, value: any): void;
|
|
354
|
+
get(key: any): any;
|
|
355
|
+
delete(key: any): boolean;
|
|
356
|
+
has(key: any): boolean;
|
|
357
|
+
get size(): number;
|
|
358
|
+
clear(): void;
|
|
359
|
+
forEach(callback: any): void;
|
|
360
|
+
[Symbol.iterator](): Generator<any[], void, unknown>;
|
|
361
|
+
}
|
|
345
362
|
declare class Note {
|
|
346
363
|
constructor(noteNumber: any, velocity: any, startTime: any, voice: any, voiceParams: any);
|
|
347
364
|
bufferSource: 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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"}
|
package/script/midy-GM2.js
CHANGED
|
@@ -3,6 +3,58 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.MidyGM2 = void 0;
|
|
4
4
|
const midi_file_1 = require("midi-file");
|
|
5
5
|
const soundfont_parser_1 = require("@marmooo/soundfont-parser");
|
|
6
|
+
// 2-3 times faster than Map
|
|
7
|
+
class SparseMap {
|
|
8
|
+
constructor(size) {
|
|
9
|
+
this.data = new Array(size);
|
|
10
|
+
this.activeIndices = [];
|
|
11
|
+
}
|
|
12
|
+
set(key, value) {
|
|
13
|
+
if (this.data[key] === undefined) {
|
|
14
|
+
this.activeIndices.push(key);
|
|
15
|
+
}
|
|
16
|
+
this.data[key] = value;
|
|
17
|
+
}
|
|
18
|
+
get(key) {
|
|
19
|
+
return this.data[key];
|
|
20
|
+
}
|
|
21
|
+
delete(key) {
|
|
22
|
+
if (this.data[key] !== undefined) {
|
|
23
|
+
this.data[key] = undefined;
|
|
24
|
+
const index = this.activeIndices.indexOf(key);
|
|
25
|
+
if (index !== -1) {
|
|
26
|
+
this.activeIndices.splice(index, 1);
|
|
27
|
+
}
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
has(key) {
|
|
33
|
+
return this.data[key] !== undefined;
|
|
34
|
+
}
|
|
35
|
+
get size() {
|
|
36
|
+
return this.activeIndices.length;
|
|
37
|
+
}
|
|
38
|
+
clear() {
|
|
39
|
+
for (let i = 0; i < this.activeIndices.length; i++) {
|
|
40
|
+
const key = this.activeIndices[i];
|
|
41
|
+
this.data[key] = undefined;
|
|
42
|
+
}
|
|
43
|
+
this.activeIndices = [];
|
|
44
|
+
}
|
|
45
|
+
*[Symbol.iterator]() {
|
|
46
|
+
for (let i = 0; i < this.activeIndices.length; i++) {
|
|
47
|
+
const key = this.activeIndices[i];
|
|
48
|
+
yield [key, this.data[key]];
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
forEach(callback) {
|
|
52
|
+
for (let i = 0; i < this.activeIndices.length; i++) {
|
|
53
|
+
const key = this.activeIndices[i];
|
|
54
|
+
callback(this.data[key], key, this);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
6
58
|
class Note {
|
|
7
59
|
constructor(noteNumber, velocity, startTime, voice, voiceParams) {
|
|
8
60
|
Object.defineProperty(this, "bufferSource", {
|
|
@@ -283,6 +335,18 @@ class MidyGM2 {
|
|
|
283
335
|
writable: true,
|
|
284
336
|
value: this.initSoundFontTable()
|
|
285
337
|
});
|
|
338
|
+
Object.defineProperty(this, "audioBufferCounter", {
|
|
339
|
+
enumerable: true,
|
|
340
|
+
configurable: true,
|
|
341
|
+
writable: true,
|
|
342
|
+
value: new Map()
|
|
343
|
+
});
|
|
344
|
+
Object.defineProperty(this, "audioBufferCache", {
|
|
345
|
+
enumerable: true,
|
|
346
|
+
configurable: true,
|
|
347
|
+
writable: true,
|
|
348
|
+
value: new Map()
|
|
349
|
+
});
|
|
286
350
|
Object.defineProperty(this, "isPlaying", {
|
|
287
351
|
enumerable: true,
|
|
288
352
|
configurable: true,
|
|
@@ -335,7 +399,7 @@ class MidyGM2 {
|
|
|
335
399
|
enumerable: true,
|
|
336
400
|
configurable: true,
|
|
337
401
|
writable: true,
|
|
338
|
-
value: new
|
|
402
|
+
value: new SparseMap(128)
|
|
339
403
|
});
|
|
340
404
|
Object.defineProperty(this, "defaultOptions", {
|
|
341
405
|
enumerable: true,
|
|
@@ -375,7 +439,7 @@ class MidyGM2 {
|
|
|
375
439
|
initSoundFontTable() {
|
|
376
440
|
const table = new Array(128);
|
|
377
441
|
for (let i = 0; i < 128; i++) {
|
|
378
|
-
table[i] = new
|
|
442
|
+
table[i] = new SparseMap(128);
|
|
379
443
|
}
|
|
380
444
|
return table;
|
|
381
445
|
}
|
|
@@ -429,8 +493,8 @@ class MidyGM2 {
|
|
|
429
493
|
state: new ControllerState(),
|
|
430
494
|
controlTable: this.initControlTable(),
|
|
431
495
|
...this.setChannelAudioNodes(audioContext),
|
|
432
|
-
scheduledNotes: new
|
|
433
|
-
sostenutoNotes: new
|
|
496
|
+
scheduledNotes: new SparseMap(128),
|
|
497
|
+
sostenutoNotes: new SparseMap(128),
|
|
434
498
|
};
|
|
435
499
|
});
|
|
436
500
|
return channels;
|
|
@@ -464,9 +528,8 @@ class MidyGM2 {
|
|
|
464
528
|
return audioBuffer;
|
|
465
529
|
}
|
|
466
530
|
}
|
|
467
|
-
|
|
531
|
+
createNoteBufferNode(audioBuffer, voiceParams) {
|
|
468
532
|
const bufferSource = new AudioBufferSourceNode(this.audioContext);
|
|
469
|
-
const audioBuffer = await this.createNoteBuffer(voiceParams, isSF3);
|
|
470
533
|
bufferSource.buffer = audioBuffer;
|
|
471
534
|
bufferSource.loop = voiceParams.sampleModes % 2 !== 0;
|
|
472
535
|
if (bufferSource.loop) {
|
|
@@ -555,6 +618,7 @@ class MidyGM2 {
|
|
|
555
618
|
await Promise.all(this.notePromises);
|
|
556
619
|
this.notePromises = [];
|
|
557
620
|
this.exclusiveClassMap.clear();
|
|
621
|
+
this.audioBufferCache.clear();
|
|
558
622
|
resolve();
|
|
559
623
|
return;
|
|
560
624
|
}
|
|
@@ -570,8 +634,9 @@ class MidyGM2 {
|
|
|
570
634
|
}
|
|
571
635
|
else if (this.isStopping) {
|
|
572
636
|
await this.stopNotes(0, true);
|
|
573
|
-
this.exclusiveClassMap.clear();
|
|
574
637
|
this.notePromises = [];
|
|
638
|
+
this.exclusiveClassMap.clear();
|
|
639
|
+
this.audioBufferCache.clear();
|
|
575
640
|
resolve();
|
|
576
641
|
this.isStopping = false;
|
|
577
642
|
this.isPaused = false;
|
|
@@ -602,6 +667,9 @@ class MidyGM2 {
|
|
|
602
667
|
secondToTicks(second, secondsPerBeat) {
|
|
603
668
|
return second * this.ticksPerBeat / secondsPerBeat;
|
|
604
669
|
}
|
|
670
|
+
getAudioBufferId(programNumber, noteNumber, velocity) {
|
|
671
|
+
return `${programNumber}:${noteNumber}:${velocity}`;
|
|
672
|
+
}
|
|
605
673
|
extractMidiData(midi) {
|
|
606
674
|
const instruments = new Set();
|
|
607
675
|
const timeline = [];
|
|
@@ -623,6 +691,8 @@ class MidyGM2 {
|
|
|
623
691
|
switch (event.type) {
|
|
624
692
|
case "noteOn": {
|
|
625
693
|
const channel = tmpChannels[event.channel];
|
|
694
|
+
const audioBufferId = this.getAudioBufferId(channel.programNumber, event.noteNumber, event.velocity);
|
|
695
|
+
this.audioBufferCounter.set(audioBufferId, (this.audioBufferCounter.get(audioBufferId) ?? 0) + 1);
|
|
626
696
|
if (channel.programNumber < 0) {
|
|
627
697
|
channel.programNumber = event.programNumber;
|
|
628
698
|
switch (channel.bankMSB) {
|
|
@@ -672,6 +742,10 @@ class MidyGM2 {
|
|
|
672
742
|
timeline.push(event);
|
|
673
743
|
}
|
|
674
744
|
}
|
|
745
|
+
for (const [audioBufferId, count] of this.audioBufferCounter) {
|
|
746
|
+
if (count === 1)
|
|
747
|
+
this.audioBufferCounter.delete(audioBufferId);
|
|
748
|
+
}
|
|
675
749
|
const priority = {
|
|
676
750
|
controller: 0,
|
|
677
751
|
sysEx: 1,
|
|
@@ -765,7 +839,7 @@ class MidyGM2 {
|
|
|
765
839
|
return this.resumeTime + now - this.startTime - this.startDelay;
|
|
766
840
|
}
|
|
767
841
|
getActiveNotes(channel, time) {
|
|
768
|
-
const activeNotes = new
|
|
842
|
+
const activeNotes = new SparseMap(128);
|
|
769
843
|
channel.scheduledNotes.forEach((noteList) => {
|
|
770
844
|
const activeNote = this.getActiveNote(noteList, time);
|
|
771
845
|
if (activeNote) {
|
|
@@ -1100,12 +1174,31 @@ class MidyGM2 {
|
|
|
1100
1174
|
note.vibratoLFO.connect(note.vibratoDepth);
|
|
1101
1175
|
note.vibratoDepth.connect(note.bufferSource.detune);
|
|
1102
1176
|
}
|
|
1177
|
+
async getAudioBuffer(program, noteNumber, velocity, voiceParams, isSF3) {
|
|
1178
|
+
const audioBufferId = this.getAudioBufferId(program, noteNumber, velocity);
|
|
1179
|
+
const cache = this.audioBufferCache.get(audioBufferId);
|
|
1180
|
+
if (cache) {
|
|
1181
|
+
cache.counter += 1;
|
|
1182
|
+
if (cache.maxCount <= cache.counter) {
|
|
1183
|
+
this.audioBufferCache.delete(audioBufferId);
|
|
1184
|
+
}
|
|
1185
|
+
return cache.audioBuffer;
|
|
1186
|
+
}
|
|
1187
|
+
else {
|
|
1188
|
+
const maxCount = this.audioBufferCounter.get(audioBufferId) ?? 0;
|
|
1189
|
+
const audioBuffer = await this.createNoteBuffer(voiceParams, isSF3);
|
|
1190
|
+
const cache = { audioBuffer, maxCount, counter: 1 };
|
|
1191
|
+
this.audioBufferCache.set(audioBufferId, cache);
|
|
1192
|
+
return audioBuffer;
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1103
1195
|
async createNote(channel, voice, noteNumber, velocity, startTime, portamento, isSF3) {
|
|
1104
1196
|
const state = channel.state;
|
|
1105
1197
|
const controllerState = this.getControllerState(channel, noteNumber, velocity);
|
|
1106
1198
|
const voiceParams = voice.getAllParams(controllerState);
|
|
1107
1199
|
const note = new Note(noteNumber, velocity, startTime, voice, voiceParams);
|
|
1108
|
-
|
|
1200
|
+
const audioBuffer = await this.getAudioBuffer(channel.program, noteNumber, velocity, voiceParams, isSF3);
|
|
1201
|
+
note.bufferSource = this.createNoteBufferNode(audioBuffer, voiceParams);
|
|
1109
1202
|
note.volumeNode = new GainNode(this.audioContext);
|
|
1110
1203
|
note.gainL = new GainNode(this.audioContext);
|
|
1111
1204
|
note.gainR = new GainNode(this.audioContext);
|
|
@@ -1165,10 +1258,10 @@ class MidyGM2 {
|
|
|
1165
1258
|
if (soundFontIndex === undefined)
|
|
1166
1259
|
return;
|
|
1167
1260
|
const soundFont = this.soundFonts[soundFontIndex];
|
|
1168
|
-
const isSF3 = soundFont.parsed.info.version.major === 3;
|
|
1169
1261
|
const voice = soundFont.getVoice(bankNumber, channel.program, noteNumber, velocity);
|
|
1170
1262
|
if (!voice)
|
|
1171
1263
|
return;
|
|
1264
|
+
const isSF3 = soundFont.parsed.info.version.major === 3;
|
|
1172
1265
|
const note = await this.createNote(channel, voice, noteNumber, velocity, startTime, portamento, isSF3);
|
|
1173
1266
|
note.gainL.connect(channel.gainL);
|
|
1174
1267
|
note.gainR.connect(channel.gainR);
|
|
@@ -1337,6 +1430,7 @@ class MidyGM2 {
|
|
|
1337
1430
|
channel.program = program;
|
|
1338
1431
|
}
|
|
1339
1432
|
handleChannelPressure(channelNumber, value) {
|
|
1433
|
+
const now = this.audioContext.currentTime;
|
|
1340
1434
|
const channel = this.channels[channelNumber];
|
|
1341
1435
|
const prev = channel.state.channelPressure;
|
|
1342
1436
|
const next = value / 127;
|
|
@@ -1346,13 +1440,8 @@ class MidyGM2 {
|
|
|
1346
1440
|
channel.detune += pressureDepth * (next - prev);
|
|
1347
1441
|
}
|
|
1348
1442
|
const table = channel.channelPressureTable;
|
|
1349
|
-
channel.
|
|
1350
|
-
|
|
1351
|
-
const note = noteList[i];
|
|
1352
|
-
if (!note)
|
|
1353
|
-
continue;
|
|
1354
|
-
this.applyDestinationSettings(channel, note, table);
|
|
1355
|
-
}
|
|
1443
|
+
this.getActiveNotes(channel, now).forEach((note) => {
|
|
1444
|
+
this.applyDestinationSettings(channel, note, table);
|
|
1356
1445
|
});
|
|
1357
1446
|
// this.applyVoiceParams(channel, 13);
|
|
1358
1447
|
}
|
|
@@ -1753,8 +1842,7 @@ class MidyGM2 {
|
|
|
1753
1842
|
channel.state.sostenutoPedal = value / 127;
|
|
1754
1843
|
if (64 <= value) {
|
|
1755
1844
|
const now = this.audioContext.currentTime;
|
|
1756
|
-
|
|
1757
|
-
channel.sostenutoNotes = new Map(activeNotes);
|
|
1845
|
+
channel.sostenutoNotes = this.getActiveNotes(channel, now);
|
|
1758
1846
|
}
|
|
1759
1847
|
else {
|
|
1760
1848
|
this.releaseSostenutoPedal(channelNumber, value);
|
|
@@ -2007,7 +2095,7 @@ class MidyGM2 {
|
|
|
2007
2095
|
switch (data[3]) {
|
|
2008
2096
|
case 8:
|
|
2009
2097
|
// https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca21.pdf
|
|
2010
|
-
return this.handleScaleOctaveTuning1ByteFormatSysEx(data);
|
|
2098
|
+
return this.handleScaleOctaveTuning1ByteFormatSysEx(data, false);
|
|
2011
2099
|
default:
|
|
2012
2100
|
console.warn(`Unsupported Exclusive Message: ${data}`);
|
|
2013
2101
|
}
|
|
@@ -2329,8 +2417,8 @@ class MidyGM2 {
|
|
|
2329
2417
|
}
|
|
2330
2418
|
return bitmap;
|
|
2331
2419
|
}
|
|
2332
|
-
handleScaleOctaveTuning1ByteFormatSysEx(data) {
|
|
2333
|
-
if (data.length <
|
|
2420
|
+
handleScaleOctaveTuning1ByteFormatSysEx(data, realtime) {
|
|
2421
|
+
if (data.length < 19) {
|
|
2334
2422
|
console.error("Data length is too short");
|
|
2335
2423
|
return;
|
|
2336
2424
|
}
|
|
@@ -2338,10 +2426,13 @@ class MidyGM2 {
|
|
|
2338
2426
|
for (let i = 0; i < channelBitmap.length; i++) {
|
|
2339
2427
|
if (!channelBitmap[i])
|
|
2340
2428
|
continue;
|
|
2429
|
+
const channel = this.channels[i];
|
|
2341
2430
|
for (let j = 0; j < 12; j++) {
|
|
2342
|
-
const
|
|
2343
|
-
|
|
2431
|
+
const centValue = data[j + 7] - 64;
|
|
2432
|
+
channel.scaleOctaveTuningTable[j] = centValue;
|
|
2344
2433
|
}
|
|
2434
|
+
if (realtime)
|
|
2435
|
+
this.updateChannelDetune(channel);
|
|
2345
2436
|
}
|
|
2346
2437
|
}
|
|
2347
2438
|
applyDestinationSettings(channel, note, table) {
|
|
@@ -2474,7 +2565,7 @@ Object.defineProperty(MidyGM2, "channelSettings", {
|
|
|
2474
2565
|
value: {
|
|
2475
2566
|
currentBufferSource: null,
|
|
2476
2567
|
detune: 0,
|
|
2477
|
-
scaleOctaveTuningTable: new
|
|
2568
|
+
scaleOctaveTuningTable: new Int8Array(12), // [-64, 63] cent
|
|
2478
2569
|
channelPressureTable: new Uint8Array([64, 64, 64, 0, 0, 0]),
|
|
2479
2570
|
keyBasedInstrumentControlTable: new Int8Array(128 * 128), // [-64, 63]
|
|
2480
2571
|
program: 0,
|
package/script/midy-GMLite.d.ts
CHANGED
|
@@ -19,6 +19,8 @@ export class MidyGMLite {
|
|
|
19
19
|
resumeTime: number;
|
|
20
20
|
soundFonts: any[];
|
|
21
21
|
soundFontTable: any[];
|
|
22
|
+
audioBufferCounter: Map<any, any>;
|
|
23
|
+
audioBufferCache: Map<any, any>;
|
|
22
24
|
isPlaying: boolean;
|
|
23
25
|
isPausing: boolean;
|
|
24
26
|
isPaused: boolean;
|
|
@@ -27,7 +29,7 @@ export class MidyGMLite {
|
|
|
27
29
|
timeline: any[];
|
|
28
30
|
instruments: any[];
|
|
29
31
|
notePromises: any[];
|
|
30
|
-
exclusiveClassMap:
|
|
32
|
+
exclusiveClassMap: SparseMap;
|
|
31
33
|
audioContext: any;
|
|
32
34
|
masterVolume: any;
|
|
33
35
|
voiceParamsHandlers: {
|
|
@@ -68,12 +70,13 @@ export class MidyGMLite {
|
|
|
68
70
|
};
|
|
69
71
|
createChannels(audioContext: any): any[];
|
|
70
72
|
createNoteBuffer(voiceParams: any, isSF3: any): Promise<any>;
|
|
71
|
-
createNoteBufferNode(
|
|
73
|
+
createNoteBufferNode(audioBuffer: any, voiceParams: any): any;
|
|
72
74
|
scheduleTimelineEvents(t: any, offset: any, queueIndex: any): Promise<any>;
|
|
73
75
|
getQueueIndex(second: any): number;
|
|
74
76
|
playNotes(): Promise<any>;
|
|
75
77
|
ticksToSecond(ticks: any, secondsPerBeat: any): number;
|
|
76
78
|
secondToTicks(second: any, secondsPerBeat: any): number;
|
|
79
|
+
getAudioBufferId(programNumber: any, noteNumber: any, velocity: any): string;
|
|
77
80
|
extractMidiData(midi: any): {
|
|
78
81
|
instruments: Set<any>;
|
|
79
82
|
timeline: any[];
|
|
@@ -87,7 +90,7 @@ export class MidyGMLite {
|
|
|
87
90
|
seekTo(second: any): void;
|
|
88
91
|
calcTotalTime(): number;
|
|
89
92
|
currentTime(): number;
|
|
90
|
-
getActiveNotes(channel: any, time: any):
|
|
93
|
+
getActiveNotes(channel: any, time: any): SparseMap;
|
|
91
94
|
getActiveNote(noteList: any, time: any): any;
|
|
92
95
|
cbToRatio(cb: any): number;
|
|
93
96
|
rateToCent(rate: any): number;
|
|
@@ -101,6 +104,7 @@ export class MidyGMLite {
|
|
|
101
104
|
clampCutoffFrequency(frequency: any): number;
|
|
102
105
|
setFilterEnvelope(note: any): void;
|
|
103
106
|
startModulation(channel: any, note: any, startTime: any): void;
|
|
107
|
+
getAudioBuffer(program: any, noteNumber: any, velocity: any, voiceParams: any, isSF3: any): Promise<any>;
|
|
104
108
|
createNote(channel: any, voice: any, noteNumber: any, velocity: any, startTime: any, isSF3: any): Promise<Note>;
|
|
105
109
|
scheduleNoteOn(channelNumber: any, noteNumber: any, velocity: any, startTime: any): Promise<void>;
|
|
106
110
|
noteOn(channelNumber: any, noteNumber: any, velocity: any): Promise<void>;
|
|
@@ -177,6 +181,19 @@ export class MidyGMLite {
|
|
|
177
181
|
handleSysEx(data: any): void;
|
|
178
182
|
scheduleTask(callback: any, startTime: any): Promise<any>;
|
|
179
183
|
}
|
|
184
|
+
declare class SparseMap {
|
|
185
|
+
constructor(size: any);
|
|
186
|
+
data: any[];
|
|
187
|
+
activeIndices: any[];
|
|
188
|
+
set(key: any, value: any): void;
|
|
189
|
+
get(key: any): any;
|
|
190
|
+
delete(key: any): boolean;
|
|
191
|
+
has(key: any): boolean;
|
|
192
|
+
get size(): number;
|
|
193
|
+
clear(): void;
|
|
194
|
+
forEach(callback: any): void;
|
|
195
|
+
[Symbol.iterator](): Generator<any[], void, unknown>;
|
|
196
|
+
}
|
|
180
197
|
declare class Note {
|
|
181
198
|
constructor(noteNumber: any, velocity: any, startTime: any, voice: any, voiceParams: any);
|
|
182
199
|
bufferSource: any;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"midy-GMLite.d.ts","sourceRoot":"","sources":["../src/midy-GMLite.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"midy-GMLite.d.ts","sourceRoot":"","sources":["../src/midy-GMLite.js"],"names":[],"mappings":"AAgJA;IAsBE;;;;;;;;;MASE;IAEF,+BAQC;IAxCD,qBAAmB;IACnB,kBAAc;IACd,0BAAwB;IACxB,kBAAc;IACd,mBAAiB;IACjB,kBAAc;IACd,mBAAe;IACf,kBAAgB;IAChB,sBAA2C;IAC3C,kCAA+B;IAC/B,gCAA6B;IAC7B,mBAAkB;IAClB,mBAAkB;IAClB,kBAAiB;IACjB,oBAAmB;IACnB,mBAAkB;IAClB,gBAAc;IACd,mBAAiB;IACjB,oBAAkB;IAClB,6BAAuC;IAcrC,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,8DASC;IAED,2EA+CC;IAED,mCAOC;IAED,0BAoDC;IAED,uDAEC;IAED,wDAEC;IAED,6EAEC;IAED;;;MA4EC;IAED,+EAmBC;IAED,qDAKC;IAED,uBAKC;IAED,aAGC;IAED,cAKC;IAED,wBAIC;IAED,0BAKC;IAED,wBAOC;IAED,sBAGC;IAED,mDASC;IAED,6CAQC;IAED,2BAEC;IAED,8BAEC;IAED,8BAEC;IAED,4BAEC;IAED,wCAIC;IAED,wCAQC;IAED,4CAKC;IAED,mCAgBC;IAED,kCAqBC;IAED,6CAIC;IAED,mCAuBC;IAED,+DAoBC;IAED,yGAgBC;IAED,gHAuCC;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,wDASC;IAED,+CAEC;IAED,8CAqBC;IAED,+CAEC;IAED,4DAgBC;IAED,oBAMC;IAED,yDAaC;IAED,yCAGC;IAED,mCAQC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF;AAv0CD;IACE,uBAGC;IAFC,YAA2B;IAC3B,qBAAuB;IAGzB,gCAKC;IAED,mBAEC;IAED,0BAUC;IAED,uBAEC;IAED,mBAEC;IAED,cAMC;IASD,6BAKC;IAZD,qDAKC;CAQF;AAED;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"}
|