spessasynth_lib 3.25.2 → 3.25.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/midi_parser/midi_editor.js +5 -5
- package/midi_parser/rmidi_writer.js +11 -6
- package/midi_parser/used_keys_loaded.js +3 -3
- package/package.json +1 -1
- package/sequencer/sequencer.js +34 -12
- package/sequencer/worklet_sequencer/events.js +7 -1
- package/sequencer/worklet_sequencer/sequencer_message.js +10 -8
- package/sequencer/worklet_sequencer/song_control.js +3 -1
- package/synthetizer/worklet_processor.min.js +10 -10
- package/synthetizer/worklet_system/worklet_methods/controller_control/reset_controllers.js +2 -1
- package/synthetizer/worklet_system/worklet_methods/soundfont_management/get_preset.js +4 -2
- package/synthetizer/worklet_system/worklet_methods/system_exclusive.js +5 -4
- package/synthetizer/worklet_system/worklet_utilities/lowpass_filter.js +2 -3
- package/synthetizer/worklet_system/worklet_utilities/worklet_processor_channel.js +2 -2
- package/synthetizer/worklet_system/worklet_utilities/worklet_voice.js +2 -1
- package/utils/sysex_detector.js +13 -1
- package/utils/xg_hacks.js +32 -15
|
@@ -5,8 +5,8 @@ import { consoleColors } from "../utils/other.js";
|
|
|
5
5
|
|
|
6
6
|
import { customControllers } from "../synthetizer/worklet_system/worklet_utilities/controller_tables.js";
|
|
7
7
|
import { DEFAULT_PERCUSSION } from "../synthetizer/synth_constants.js";
|
|
8
|
-
import { isGMOn, isGSOn, isXGOn } from "../utils/sysex_detector.js";
|
|
9
|
-
import { isXGDrums, XG_SFX_VOICE } from "../utils/xg_hacks.js";
|
|
8
|
+
import { isGM2On, isGMOn, isGSOn, isXGOn } from "../utils/sysex_detector.js";
|
|
9
|
+
import { isSystemXG, isXGDrums, XG_SFX_VOICE } from "../utils/xg_hacks.js";
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* @param ticks {number}
|
|
@@ -380,7 +380,7 @@ export function modifyMIDI(
|
|
|
380
380
|
};
|
|
381
381
|
|
|
382
382
|
// on xg, add lsb
|
|
383
|
-
if (system
|
|
383
|
+
if (isSystemXG(system))
|
|
384
384
|
{
|
|
385
385
|
// xg drums: msb can be 120, 126 or 127
|
|
386
386
|
if (change.isDrum)
|
|
@@ -504,9 +504,9 @@ export function modifyMIDI(
|
|
|
504
504
|
}
|
|
505
505
|
else
|
|
506
506
|
// check for GM/2 on
|
|
507
|
-
if (isGMOn(e))
|
|
507
|
+
if (isGMOn(e) || isGM2On(e))
|
|
508
508
|
{
|
|
509
|
-
// that's a
|
|
509
|
+
// that's a GM1 system change, remove it!
|
|
510
510
|
SpessaSynthInfo(
|
|
511
511
|
"%cGM/2 on detected, removing!",
|
|
512
512
|
consoleColors.info
|
|
@@ -7,8 +7,8 @@ import { SpessaSynthGroup, SpessaSynthGroupEnd, SpessaSynthInfo } from "../utils
|
|
|
7
7
|
import { consoleColors } from "../utils/other.js";
|
|
8
8
|
import { writeLittleEndian } from "../utils/byte_functions/little_endian.js";
|
|
9
9
|
import { DEFAULT_PERCUSSION } from "../synthetizer/synth_constants.js";
|
|
10
|
-
import { chooseBank, parseBankSelect } from "../utils/xg_hacks.js";
|
|
11
|
-
import { isGMOn, isGSDrumsOn, isGSOn, isXGOn } from "../utils/sysex_detector.js";
|
|
10
|
+
import { chooseBank, isSystemXG, parseBankSelect } from "../utils/xg_hacks.js";
|
|
11
|
+
import { isGM2On, isGMOn, isGSDrumsOn, isGSOn, isXGOn } from "../utils/sysex_detector.js";
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* @enum {string}
|
|
@@ -183,12 +183,17 @@ export function writeRMIDI(
|
|
|
183
183
|
}
|
|
184
184
|
else if (isGMOn(e))
|
|
185
185
|
{
|
|
186
|
+
// we do not want gm1
|
|
186
187
|
system = "gm";
|
|
187
188
|
unwantedSystems.push({
|
|
188
189
|
tNum: trackNum,
|
|
189
190
|
e: e
|
|
190
191
|
});
|
|
191
192
|
}
|
|
193
|
+
else if (isGM2On(e))
|
|
194
|
+
{
|
|
195
|
+
system = "gm2";
|
|
196
|
+
}
|
|
192
197
|
continue;
|
|
193
198
|
}
|
|
194
199
|
const sysexChannel = [9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 15][e.messageData[5] & 0x0F] + portOffset;
|
|
@@ -204,7 +209,7 @@ export function writeRMIDI(
|
|
|
204
209
|
const channel = channelsInfo[chNum];
|
|
205
210
|
if (status === messageTypes.programChange)
|
|
206
211
|
{
|
|
207
|
-
const isXG = system
|
|
212
|
+
const isXG = isSystemXG(system);
|
|
208
213
|
// check if the preset for this program exists
|
|
209
214
|
const initialProgram = e.messageData[0];
|
|
210
215
|
if (channel.drums)
|
|
@@ -274,7 +279,7 @@ export function writeRMIDI(
|
|
|
274
279
|
{
|
|
275
280
|
// There is a preset with this bank. Add offset. For drums add the normal offset.
|
|
276
281
|
let drumBank = bank;
|
|
277
|
-
if (system
|
|
282
|
+
if (isSystemXG(system) && bank === 128)
|
|
278
283
|
{
|
|
279
284
|
bank = 127;
|
|
280
285
|
}
|
|
@@ -384,7 +389,7 @@ export function writeRMIDI(
|
|
|
384
389
|
const targetBank = (soundfont.getPreset(
|
|
385
390
|
0,
|
|
386
391
|
has.program,
|
|
387
|
-
system
|
|
392
|
+
isSystemXG(system)
|
|
388
393
|
)?.bank + bankOffset) || bankOffset;
|
|
389
394
|
track.splice(indexToAdd, 0, new MIDIMessage(
|
|
390
395
|
ticks,
|
|
@@ -394,7 +399,7 @@ export function writeRMIDI(
|
|
|
394
399
|
});
|
|
395
400
|
|
|
396
401
|
// make sure to put xg if gm
|
|
397
|
-
if (system !== "gs" && system
|
|
402
|
+
if (system !== "gs" && !isSystemXG(system))
|
|
398
403
|
{
|
|
399
404
|
for (const m of unwantedSystems)
|
|
400
405
|
{
|
|
@@ -2,7 +2,7 @@ import { SpessaSynthGroupCollapsed, SpessaSynthGroupEnd, SpessaSynthInfo } from
|
|
|
2
2
|
import { consoleColors } from "../utils/other.js";
|
|
3
3
|
import { messageTypes, midiControllers } from "./midi_message.js";
|
|
4
4
|
import { DEFAULT_PERCUSSION } from "../synthetizer/synth_constants.js";
|
|
5
|
-
import { chooseBank, parseBankSelect } from "../utils/xg_hacks.js";
|
|
5
|
+
import { chooseBank, isSystemXG, parseBankSelect } from "../utils/xg_hacks.js";
|
|
6
6
|
import { isGSDrumsOn, isXGOn } from "../utils/sysex_detector.js";
|
|
7
7
|
|
|
8
8
|
/**
|
|
@@ -42,9 +42,9 @@ export function getUsedProgramsAndKeys(soundfont)
|
|
|
42
42
|
|
|
43
43
|
function updateString(ch)
|
|
44
44
|
{
|
|
45
|
-
const bank = chooseBank(ch.bank, ch.bankLSB, ch.drums, system
|
|
45
|
+
const bank = chooseBank(ch.bank, ch.bankLSB, ch.drums, isSystemXG(system));
|
|
46
46
|
// check if this exists in the soundfont
|
|
47
|
-
let exists = soundfont.getPreset(bank, ch.program, system
|
|
47
|
+
let exists = soundfont.getPreset(bank, ch.program, isSystemXG(system));
|
|
48
48
|
ch.actualBank = exists.bank;
|
|
49
49
|
ch.program = exists.program;
|
|
50
50
|
ch.string = ch.actualBank + ":" + ch.program;
|
package/package.json
CHANGED
package/sequencer/sequencer.js
CHANGED
|
@@ -66,11 +66,17 @@ export class Sequencer
|
|
|
66
66
|
onTextEvent;
|
|
67
67
|
|
|
68
68
|
/**
|
|
69
|
-
* The
|
|
69
|
+
* The current MIDI data, with the exclusion of the embedded sound bank and event data.
|
|
70
70
|
* @type {MIDIData}
|
|
71
71
|
*/
|
|
72
72
|
midiData;
|
|
73
73
|
|
|
74
|
+
/**
|
|
75
|
+
* The current MIDI data for all songs, like the midiData property.
|
|
76
|
+
* @type {MIDIData[]}
|
|
77
|
+
*/
|
|
78
|
+
songListData = [];
|
|
79
|
+
|
|
74
80
|
/**
|
|
75
81
|
* @type {Object<string, function(MIDIData)>}
|
|
76
82
|
* @private
|
|
@@ -285,11 +291,11 @@ export class Sequencer
|
|
|
285
291
|
this._shuffleSongs = value;
|
|
286
292
|
if (value)
|
|
287
293
|
{
|
|
288
|
-
this._sendMessage(WorkletSequencerMessageType.changeSong, SongChangeType.shuffleOn);
|
|
294
|
+
this._sendMessage(WorkletSequencerMessageType.changeSong, [SongChangeType.shuffleOn]);
|
|
289
295
|
}
|
|
290
296
|
else
|
|
291
297
|
{
|
|
292
|
-
this._sendMessage(WorkletSequencerMessageType.changeSong, SongChangeType.shuffleOff);
|
|
298
|
+
this._sendMessage(WorkletSequencerMessageType.changeSong, [SongChangeType.shuffleOff]);
|
|
293
299
|
}
|
|
294
300
|
}
|
|
295
301
|
|
|
@@ -476,14 +482,30 @@ export class Sequencer
|
|
|
476
482
|
});
|
|
477
483
|
}
|
|
478
484
|
|
|
485
|
+
/**
|
|
486
|
+
* Switch to the next song in the playlist
|
|
487
|
+
*/
|
|
479
488
|
nextSong()
|
|
480
489
|
{
|
|
481
|
-
this._sendMessage(WorkletSequencerMessageType.changeSong, SongChangeType.forwards);
|
|
490
|
+
this._sendMessage(WorkletSequencerMessageType.changeSong, [SongChangeType.forwards]);
|
|
482
491
|
}
|
|
483
492
|
|
|
493
|
+
/**
|
|
494
|
+
* Switch to the previous song in the playlist
|
|
495
|
+
*/
|
|
484
496
|
previousSong()
|
|
485
497
|
{
|
|
486
|
-
this._sendMessage(WorkletSequencerMessageType.changeSong, SongChangeType.backwards);
|
|
498
|
+
this._sendMessage(WorkletSequencerMessageType.changeSong, [SongChangeType.backwards]);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
* Sets the song index in the playlist
|
|
503
|
+
* @param index
|
|
504
|
+
*/
|
|
505
|
+
setSongIndex(index)
|
|
506
|
+
{
|
|
507
|
+
const clamped = Math.max(Math.min(this.songsAmount - 1, index), 0);
|
|
508
|
+
this._sendMessage(WorkletSequencerMessageType.changeSong, [SongChangeType.index, clamped]);
|
|
487
509
|
}
|
|
488
510
|
|
|
489
511
|
/**
|
|
@@ -536,19 +558,15 @@ export class Sequencer
|
|
|
536
558
|
break;
|
|
537
559
|
|
|
538
560
|
case WorkletSequencerReturnMessageType.songChange:
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
* @type {MIDIData}
|
|
542
|
-
*/
|
|
543
|
-
let songChangeData = messageData[0];
|
|
544
|
-
this.songIndex = messageData[1];
|
|
561
|
+
this.songIndex = messageData[0];
|
|
562
|
+
const songChangeData = this.songListData[this.songIndex];
|
|
545
563
|
this.midiData = songChangeData;
|
|
546
564
|
this.hasDummyData = false;
|
|
547
565
|
this.absoluteStartTime = 0;
|
|
548
566
|
this.duration = this.midiData.duration;
|
|
549
567
|
this._callEvents(this.onSongChange, songChangeData);
|
|
550
568
|
// if is auto played, unpause
|
|
551
|
-
if (messageData[
|
|
569
|
+
if (messageData[1] === true)
|
|
552
570
|
{
|
|
553
571
|
this.unpause();
|
|
554
572
|
}
|
|
@@ -626,6 +644,10 @@ export class Sequencer
|
|
|
626
644
|
}
|
|
627
645
|
break;
|
|
628
646
|
|
|
647
|
+
case WorkletSequencerReturnMessageType.songListChange:
|
|
648
|
+
this.songListData = messageData;
|
|
649
|
+
break;
|
|
650
|
+
|
|
629
651
|
default:
|
|
630
652
|
break;
|
|
631
653
|
}
|
|
@@ -61,7 +61,7 @@ export function processMessage(messageType, messageData)
|
|
|
61
61
|
break;
|
|
62
62
|
|
|
63
63
|
case WorkletSequencerMessageType.changeSong:
|
|
64
|
-
switch (messageData)
|
|
64
|
+
switch (messageData[0])
|
|
65
65
|
{
|
|
66
66
|
case SongChangeType.forwards:
|
|
67
67
|
this.nextSong();
|
|
@@ -81,6 +81,12 @@ export function processMessage(messageType, messageData)
|
|
|
81
81
|
this.shuffleSongIndexes();
|
|
82
82
|
this.songIndex = 0;
|
|
83
83
|
this.loadCurrentSong();
|
|
84
|
+
break;
|
|
85
|
+
|
|
86
|
+
case SongChangeType.index:
|
|
87
|
+
this.songIndex = messageData[1];
|
|
88
|
+
this.loadCurrentSong();
|
|
89
|
+
break;
|
|
84
90
|
}
|
|
85
91
|
break;
|
|
86
92
|
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
export const SongChangeType = {
|
|
2
|
-
backwards: 0,
|
|
3
|
-
forwards: 1,
|
|
4
|
-
shuffleOn: 2,
|
|
5
|
-
shuffleOff: 3
|
|
2
|
+
backwards: 0, // no additional data
|
|
3
|
+
forwards: 1, // no additional data
|
|
4
|
+
shuffleOn: 2, // no additional data
|
|
5
|
+
shuffleOff: 3, // no additional data
|
|
6
|
+
index: 4 // songIndex<number>
|
|
6
7
|
};
|
|
7
8
|
|
|
8
9
|
/**
|
|
@@ -14,8 +15,8 @@ export const SongChangeType = {
|
|
|
14
15
|
* @property {number} setTime - 4 -> time<number>
|
|
15
16
|
* @property {number} changeMIDIMessageSending - 5 -> sendMIDIMessages<boolean>
|
|
16
17
|
* @property {number} setPlaybackRate - 6 -> playbackRate<number>
|
|
17
|
-
* @property {number} setLoop - 7 -> [loop<boolean>, count<number]
|
|
18
|
-
* @property {number} changeSong - 8 -> changeType<number>
|
|
18
|
+
* @property {number} setLoop - 7 -> [loop<boolean>, count<number>]
|
|
19
|
+
* @property {number} changeSong - 8 -> [changeType<SongChangeType>, data<number>]
|
|
19
20
|
* @property {number} getMIDI - 9 -> (no data)
|
|
20
21
|
* @property {number} setSkipToFirstNote -10 -> skipToFirstNoteOn<boolean>
|
|
21
22
|
* @property {number} setPreservePlaybackState -11 -> preservePlaybackState<boolean>
|
|
@@ -41,12 +42,13 @@ export const WorkletSequencerMessageType = {
|
|
|
41
42
|
*/
|
|
42
43
|
export const WorkletSequencerReturnMessageType = {
|
|
43
44
|
midiEvent: 0, // [...midiEventBytes<number>]
|
|
44
|
-
songChange: 1, // [
|
|
45
|
+
songChange: 1, // [songIndex<number>, isAutoPlayed<boolean>]
|
|
45
46
|
textEvent: 2, // [messageData<number[]>, statusByte<number>, lyricsIndex<number>]
|
|
46
47
|
timeChange: 3, // newAbsoluteTime<number>
|
|
47
48
|
pause: 4, // no data
|
|
48
49
|
getMIDI: 5, // midiData<MIDI>
|
|
49
50
|
midiError: 6, // errorMSG<string>
|
|
50
51
|
metaEvent: 7, // [messageType<number>, messageData<Uint8Array>, trackNum<number>]
|
|
51
|
-
loopCountChange: 8
|
|
52
|
+
loopCountChange: 8, // newLoopCount<number>
|
|
53
|
+
songListChange: 9 // songListData<MIDIData[]>
|
|
52
54
|
};
|
|
@@ -127,7 +127,7 @@ export function loadNewSequence(parsedMidi, autoPlay = true)
|
|
|
127
127
|
this.firstNoteTime = this.midiData.MIDIticksToSeconds(this.midiData.firstNoteOn);
|
|
128
128
|
SpessaSynthInfo(`%cTotal song time: ${formatTime(Math.ceil(this.duration)).time}`, consoleColors.recognized);
|
|
129
129
|
|
|
130
|
-
this.post(WorkletSequencerReturnMessageType.songChange, [
|
|
130
|
+
this.post(WorkletSequencerReturnMessageType.songChange, [this.songIndex, autoPlay]);
|
|
131
131
|
|
|
132
132
|
if (this.duration <= 1)
|
|
133
133
|
{
|
|
@@ -190,6 +190,8 @@ export function loadNewSongList(midiBuffers, autoPlay = true)
|
|
|
190
190
|
this.loop = false;
|
|
191
191
|
}
|
|
192
192
|
this.shuffleSongIndexes();
|
|
193
|
+
const midiDatas = this.songs.map(s => new MIDIData(s));
|
|
194
|
+
this.post(WorkletSequencerReturnMessageType.songListChange, midiDatas);
|
|
193
195
|
this.loadCurrentSong(autoPlay);
|
|
194
196
|
}
|
|
195
197
|
|