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.
@@ -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 === "xg")
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 GM/2 system change, remove it!
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 === "xg";
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 === "xg" && bank === 128)
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 === "xg"
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 !== "xg")
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 === "xg");
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 === "xg");
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spessasynth_lib",
3
- "version": "3.25.2",
3
+ "version": "3.25.4",
4
4
  "description": "MIDI and SoundFont2/DLS library with no compromises",
5
5
  "browser": "index.js",
6
6
  "type": "module",
@@ -66,11 +66,17 @@ export class Sequencer
66
66
  onTextEvent;
67
67
 
68
68
  /**
69
- * The sequence's data, except for the track data.
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
- * messageData is expected to be {MIDIData}
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[2] === true)
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> 0 - back, 1 - forward, 2 - shuffle ON, 3 - shuffle OFF
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, // [midiData<MIDIData>, songIndex<number>, isAutoPlayed<boolean>]
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 // newLoopCount<number>
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, [new MIDIData(this.midiData), this.songIndex, autoPlay]);
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