spessasynth_lib 3.25.13 → 3.25.15
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/midi_parser/basic_midi.js +13 -25
- package/midi_parser/midi_sequence.js +1 -1
- package/package.json +1 -1
- package/sequencer/sequencer.js +61 -20
- package/sequencer/worklet_sequencer/process_event.js +2 -35
- package/sequencer/worklet_sequencer/sequencer_message.js +7 -8
- package/sequencer/worklet_sequencer/song_control.js +1 -1
- package/soundfont/dls/read_instrument.js +2 -2
- package/synthetizer/audio_effects/rb_compressed.min.js +1 -0
- package/synthetizer/audio_effects/reverb.js +13 -3
- package/synthetizer/audio_effects/reverb_as_binary.js +6 -3
- package/synthetizer/synth_constants.js +4 -4
- package/synthetizer/synthetizer.js +3 -1
- package/synthetizer/worklet_processor.min.js +10 -10
- package/utils/fill_with_defaults.js +4 -0
- package/synthetizer/audio_effects/reverb_buffer.min.js +0 -1
|
@@ -310,31 +310,6 @@ class BasicMIDI extends MIDISequenceData
|
|
|
310
310
|
copyrightComponents.push(name);
|
|
311
311
|
}
|
|
312
312
|
}
|
|
313
|
-
|
|
314
|
-
// if the first event is not at 0 ticks, add a track name
|
|
315
|
-
// https://github.com/spessasus/SpessaSynth/issues/145
|
|
316
|
-
if (track[0].ticks > 0)
|
|
317
|
-
{
|
|
318
|
-
const name = this.trackNames[i];
|
|
319
|
-
if (name.length > 0)
|
|
320
|
-
{
|
|
321
|
-
// can copy
|
|
322
|
-
track.unshift(new MIDIMessage(
|
|
323
|
-
0,
|
|
324
|
-
messageTypes.trackName,
|
|
325
|
-
getStringBytes(name)
|
|
326
|
-
));
|
|
327
|
-
}
|
|
328
|
-
else
|
|
329
|
-
{
|
|
330
|
-
// sequence number
|
|
331
|
-
track.unshift(new MIDIMessage(
|
|
332
|
-
0,
|
|
333
|
-
messageTypes.sequenceNumber,
|
|
334
|
-
new IndexedByteArray([0x0, 0x0]) // two bytes
|
|
335
|
-
));
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
313
|
}
|
|
339
314
|
|
|
340
315
|
// reverse the tempo changes
|
|
@@ -540,6 +515,19 @@ class BasicMIDI extends MIDISequenceData
|
|
|
540
515
|
);
|
|
541
516
|
}
|
|
542
517
|
|
|
518
|
+
// if the first event is not at 0 ticks, add a track name
|
|
519
|
+
// https://github.com/spessasus/SpessaSynth/issues/145
|
|
520
|
+
if (!this.tracks.some(t => t[0].ticks === 0))
|
|
521
|
+
{
|
|
522
|
+
const track = this.tracks[0];
|
|
523
|
+
// can copy
|
|
524
|
+
track.unshift(new MIDIMessage(
|
|
525
|
+
0,
|
|
526
|
+
messageTypes.trackName,
|
|
527
|
+
new IndexedByteArray(this.rawMidiName.buffer)
|
|
528
|
+
));
|
|
529
|
+
}
|
|
530
|
+
|
|
543
531
|
|
|
544
532
|
/**
|
|
545
533
|
* The total playback time, in seconds
|
|
@@ -121,7 +121,7 @@ class MIDISequenceData
|
|
|
121
121
|
* Useful when the MIDI file uses a different code page.
|
|
122
122
|
* @type {Uint8Array}
|
|
123
123
|
*/
|
|
124
|
-
rawMidiName
|
|
124
|
+
rawMidiName;
|
|
125
125
|
|
|
126
126
|
/**
|
|
127
127
|
* The format of the MIDI file, which can be 0, 1, or 2, indicating the type of the MIDI file.
|
package/package.json
CHANGED
package/sequencer/sequencer.js
CHANGED
|
@@ -10,7 +10,6 @@ import { SpessaSynthWarn } from "../utils/loggin.js";
|
|
|
10
10
|
import { DUMMY_MIDI_DATA, MIDIData } from "../midi_parser/midi_data.js";
|
|
11
11
|
import { BasicMIDI } from "../midi_parser/basic_midi.js";
|
|
12
12
|
import { readBytesAsUintBigEndian } from "../utils/byte_functions/big_endian.js";
|
|
13
|
-
import { IndexedByteArray } from "../utils/indexed_array.js";
|
|
14
13
|
import { DEFAULT_SEQUENCER_OPTIONS } from "./default_sequencer_options.js";
|
|
15
14
|
|
|
16
15
|
/**
|
|
@@ -31,6 +30,7 @@ import { DEFAULT_SEQUENCER_OPTIONS } from "./default_sequencer_options.js";
|
|
|
31
30
|
* @typedef {BasicMIDI|MidFile} MIDIFile
|
|
32
31
|
*/
|
|
33
32
|
|
|
33
|
+
// noinspection JSUnusedGlobalSymbols
|
|
34
34
|
/**
|
|
35
35
|
* @typedef {Object} SequencerOptions
|
|
36
36
|
* @property {boolean|undefined} skipToFirstNoteOn - if true, the sequencer will skip to the first note
|
|
@@ -50,7 +50,7 @@ export class Sequencer
|
|
|
50
50
|
|
|
51
51
|
/**
|
|
52
52
|
* Fires on text event
|
|
53
|
-
* @type {
|
|
53
|
+
* @type {Function}
|
|
54
54
|
* @param data {Uint8Array} the data text
|
|
55
55
|
* @param type {number} the status byte of the message (the meta-status byte)
|
|
56
56
|
* @param lyricsIndex {number} if the text is a lyric, the index of the lyric in midiData.lyrics, otherwise -1
|
|
@@ -96,7 +96,7 @@ export class Sequencer
|
|
|
96
96
|
|
|
97
97
|
/**
|
|
98
98
|
* Fires on meta-event
|
|
99
|
-
* @type {Object<string, function([number, Uint8Array, number])>}
|
|
99
|
+
* @type {Object<string, function([number, Uint8Array, number, number])>}
|
|
100
100
|
*/
|
|
101
101
|
onMetaEvent = {};
|
|
102
102
|
|
|
@@ -435,7 +435,8 @@ export class Sequencer
|
|
|
435
435
|
|
|
436
436
|
/**
|
|
437
437
|
* Adds a new event that gets called when a meta-event occurs
|
|
438
|
-
* @param callback {function([number, Uint8Array, number])} the meta-event type,
|
|
438
|
+
* @param callback {function([number, Uint8Array, number, number])} the meta-event type,
|
|
439
|
+
* its data, the track number and MIDI ticks
|
|
439
440
|
* @param id {string} must be unique
|
|
440
441
|
*/
|
|
441
442
|
addOnMetaEvent(callback, id)
|
|
@@ -564,13 +565,6 @@ export class Sequencer
|
|
|
564
565
|
}
|
|
565
566
|
break;
|
|
566
567
|
|
|
567
|
-
case WorkletSequencerReturnMessageType.textEvent:
|
|
568
|
-
if (this.onTextEvent)
|
|
569
|
-
{
|
|
570
|
-
this.onTextEvent(...(messageData));
|
|
571
|
-
}
|
|
572
|
-
break;
|
|
573
|
-
|
|
574
568
|
case WorkletSequencerReturnMessageType.timeChange:
|
|
575
569
|
// message data is absolute time
|
|
576
570
|
const time = this.synth.currentTime - messageData;
|
|
@@ -614,16 +608,63 @@ export class Sequencer
|
|
|
614
608
|
break;
|
|
615
609
|
|
|
616
610
|
case WorkletSequencerReturnMessageType.metaEvent:
|
|
617
|
-
|
|
618
|
-
|
|
611
|
+
/**
|
|
612
|
+
* @type {MIDIMessage}
|
|
613
|
+
*/
|
|
614
|
+
const event = messageData[0];
|
|
615
|
+
switch (event.messageStatusByte)
|
|
619
616
|
{
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
617
|
+
case messageTypes.setTempo:
|
|
618
|
+
event.messageData.currentIndex = 0;
|
|
619
|
+
const bpm = 60000000 / readBytesAsUintBigEndian(event.messageData, 3);
|
|
620
|
+
event.messageData.currentIndex = 0;
|
|
621
|
+
this.currentTempo = Math.round(bpm * 100) / 100;
|
|
622
|
+
if (this.onTempoChange)
|
|
623
|
+
{
|
|
624
|
+
this._callEvents(this.onTempoChange, this.currentTempo);
|
|
625
|
+
}
|
|
626
|
+
break;
|
|
627
|
+
|
|
628
|
+
case messageTypes.text:
|
|
629
|
+
case messageTypes.lyric:
|
|
630
|
+
case messageTypes.copyright:
|
|
631
|
+
case messageTypes.trackName:
|
|
632
|
+
case messageTypes.marker:
|
|
633
|
+
case messageTypes.cuePoint:
|
|
634
|
+
case messageTypes.instrumentName:
|
|
635
|
+
case messageTypes.programName:
|
|
636
|
+
let lyricsIndex = -1;
|
|
637
|
+
if (event.messageStatusByte === messageTypes.lyric)
|
|
638
|
+
{
|
|
639
|
+
lyricsIndex = Math.min(
|
|
640
|
+
this.midiData.lyricsTicks.indexOf(event.ticks),
|
|
641
|
+
this.midiData.lyrics.length - 1
|
|
642
|
+
);
|
|
643
|
+
}
|
|
644
|
+
let sentStatus = event.messageStatusByte;
|
|
645
|
+
// if MIDI is a karaoke file, it uses the "text" event type or "lyrics" for lyrics (duh)
|
|
646
|
+
// why?
|
|
647
|
+
// because the MIDI standard is a messy pile of garbage,
|
|
648
|
+
// and it's not my fault that it's like this :(
|
|
649
|
+
// I'm just trying to make the best out of a bad situation.
|
|
650
|
+
// I'm sorry
|
|
651
|
+
// okay I should get back to work
|
|
652
|
+
// anyway,
|
|
653
|
+
// check for a karaoke file and change the status byte to "lyric"
|
|
654
|
+
// if it's a karaoke file
|
|
655
|
+
if (this.midiData.isKaraokeFile && (
|
|
656
|
+
event.messageStatusByte === messageTypes.text ||
|
|
657
|
+
event.messageStatusByte === messageTypes.lyric
|
|
658
|
+
))
|
|
659
|
+
{
|
|
660
|
+
lyricsIndex = Math.min(
|
|
661
|
+
this.midiData.lyricsTicks.indexOf(event.ticks),
|
|
662
|
+
this.midiData.lyricsTicks.length
|
|
663
|
+
);
|
|
664
|
+
sentStatus = messageTypes.lyric;
|
|
665
|
+
}
|
|
666
|
+
this.onTextEvent(event.messageData, sentStatus, lyricsIndex, event.ticks);
|
|
667
|
+
break;
|
|
627
668
|
}
|
|
628
669
|
this._callEvents(this.onMetaEvent, messageData);
|
|
629
670
|
break;
|
|
@@ -115,8 +115,6 @@ export function _processEvent(event, trackIndex)
|
|
|
115
115
|
case messageTypes.keySignature:
|
|
116
116
|
case messageTypes.sequenceNumber:
|
|
117
117
|
case messageTypes.sequenceSpecific:
|
|
118
|
-
break;
|
|
119
|
-
|
|
120
118
|
case messageTypes.text:
|
|
121
119
|
case messageTypes.lyric:
|
|
122
120
|
case messageTypes.copyright:
|
|
@@ -125,40 +123,9 @@ export function _processEvent(event, trackIndex)
|
|
|
125
123
|
case messageTypes.cuePoint:
|
|
126
124
|
case messageTypes.instrumentName:
|
|
127
125
|
case messageTypes.programName:
|
|
128
|
-
let lyricsIndex = -1;
|
|
129
|
-
if (statusByteData.status === messageTypes.lyric)
|
|
130
|
-
{
|
|
131
|
-
lyricsIndex = Math.min(
|
|
132
|
-
this.midiData.lyricsTicks.indexOf(event.ticks),
|
|
133
|
-
this.midiData.lyrics.length - 1
|
|
134
|
-
);
|
|
135
|
-
}
|
|
136
|
-
let sentStatus = statusByteData.status;
|
|
137
|
-
// if MIDI is a karaoke file, it uses the "text" event type or "lyrics" for lyrics (duh)
|
|
138
|
-
// why?
|
|
139
|
-
// because the MIDI standard is a messy pile of garbage, and it's not my fault that it's like this :(
|
|
140
|
-
// I'm just trying to make the best out of a bad situation.
|
|
141
|
-
// I'm sorry
|
|
142
|
-
// okay I should get back to work
|
|
143
|
-
// anyway,
|
|
144
|
-
// check for a karaoke file and change the status byte to "lyric" if it's a karaoke file
|
|
145
|
-
if (this.midiData.isKaraokeFile && (
|
|
146
|
-
statusByteData.status === messageTypes.text ||
|
|
147
|
-
statusByteData.status === messageTypes.lyric
|
|
148
|
-
))
|
|
149
|
-
{
|
|
150
|
-
lyricsIndex = Math.min(
|
|
151
|
-
this.midiData.lyricsTicks.indexOf(event.ticks),
|
|
152
|
-
this.midiData.lyricsTicks.length
|
|
153
|
-
);
|
|
154
|
-
sentStatus = messageTypes.lyric;
|
|
155
|
-
}
|
|
156
|
-
this.post(
|
|
157
|
-
WorkletSequencerReturnMessageType.textEvent,
|
|
158
|
-
[event.messageData, sentStatus, lyricsIndex]
|
|
159
|
-
);
|
|
160
126
|
break;
|
|
161
127
|
|
|
128
|
+
|
|
162
129
|
case messageTypes.midiPort:
|
|
163
130
|
this.assignMIDIPort(trackIndex, event.messageData[0]);
|
|
164
131
|
break;
|
|
@@ -183,7 +150,7 @@ export function _processEvent(event, trackIndex)
|
|
|
183
150
|
{
|
|
184
151
|
this.post(
|
|
185
152
|
WorkletSequencerReturnMessageType.metaEvent,
|
|
186
|
-
[event
|
|
153
|
+
[event, trackIndex]
|
|
187
154
|
);
|
|
188
155
|
}
|
|
189
156
|
}
|
|
@@ -43,12 +43,11 @@ export const WorkletSequencerMessageType = {
|
|
|
43
43
|
export const WorkletSequencerReturnMessageType = {
|
|
44
44
|
midiEvent: 0, // [...midiEventBytes<number>]
|
|
45
45
|
songChange: 1, // [songIndex<number>, isAutoPlayed<boolean>]
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
songListChange: 9 // songListData<MIDIData[]>
|
|
46
|
+
timeChange: 2, // newAbsoluteTime<number>
|
|
47
|
+
pause: 3, // no data
|
|
48
|
+
getMIDI: 4, // midiData<MIDI>
|
|
49
|
+
midiError: 5, // errorMSG<string>
|
|
50
|
+
metaEvent: 6, // [event<MIDIMessage>, trackNum<number>]
|
|
51
|
+
loopCountChange: 7, // newLoopCount<number>
|
|
52
|
+
songListChange: 8 // songListData<MIDIData[]>
|
|
54
53
|
};
|
|
@@ -144,7 +144,7 @@ export function loadNewSequence(parsedMidi, autoPlay = true)
|
|
|
144
144
|
else
|
|
145
145
|
{
|
|
146
146
|
// this shall not play: play to the first note and then wait
|
|
147
|
-
const targetTime = this.
|
|
147
|
+
const targetTime = this.skipToFirstNoteOn ? this.midiData.firstNoteOn - 1 : 0;
|
|
148
148
|
this.setTimeTicks(targetTime);
|
|
149
149
|
this.pause();
|
|
150
150
|
}
|
|
@@ -2,7 +2,7 @@ import { readBytesAsString } from "../../utils/byte_functions/string.js";
|
|
|
2
2
|
import { readLittleEndian } from "../../utils/byte_functions/little_endian.js";
|
|
3
3
|
import { DLSPreset } from "./dls_preset.js";
|
|
4
4
|
import { findRIFFListType, readRIFFChunk } from "../basic_soundfont/riff_chunk.js";
|
|
5
|
-
import {
|
|
5
|
+
import { SpessaSynthGroupCollapsed, SpessaSynthGroupEnd } from "../../utils/loggin.js";
|
|
6
6
|
import { BasicInstrumentZone } from "../basic_soundfont/basic_zones.js";
|
|
7
7
|
import { consoleColors } from "../../utils/other.js";
|
|
8
8
|
import { generatorLimits, generatorTypes } from "../basic_soundfont/generator.js";
|
|
@@ -54,7 +54,7 @@ export function readDLSInstrument(chunk)
|
|
|
54
54
|
}
|
|
55
55
|
preset.presetName = presetName;
|
|
56
56
|
preset.DLSInstrument.instrumentName = presetName;
|
|
57
|
-
|
|
57
|
+
SpessaSynthGroupCollapsed(
|
|
58
58
|
`%cParsing %c"${presetName}"%c...`,
|
|
59
59
|
consoleColors.info,
|
|
60
60
|
consoleColors.recognized,
|