spessasynth_lib 3.24.16 → 3.24.18

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spessasynth_lib",
3
- "version": "3.24.16",
3
+ "version": "3.24.18",
4
4
  "description": "MIDI and SoundFont2/DLS library with no compromises",
5
5
  "browser": "index.js",
6
6
  "type": "module",
@@ -3,6 +3,7 @@ import { Synthetizer } from "../synthetizer/synthetizer.js";
3
3
  import { messageTypes } from "../midi_parser/midi_message.js";
4
4
  import { workletMessageType } from "../synthetizer/worklet_system/message_protocol/worklet_message.js";
5
5
  import {
6
+ SongChangeType,
6
7
  WorkletSequencerMessageType,
7
8
  WorkletSequencerReturnMessageType
8
9
  } from "./worklet_sequencer/sequencer_message.js";
@@ -261,6 +262,38 @@ export class Sequencer
261
262
  this._playbackRate = value;
262
263
  }
263
264
 
265
+ /**
266
+ * @type {boolean}
267
+ * @private
268
+ */
269
+ _shuffleSongs = false;
270
+
271
+ /**
272
+ * Indicates if the song order is random
273
+ * @returns {boolean}
274
+ */
275
+ get shuffleSongs()
276
+ {
277
+ return this._shuffleSongs;
278
+ }
279
+
280
+ /**
281
+ * Indicates if the song order is random
282
+ * @param value {boolean}
283
+ */
284
+ set shuffleSongs(value)
285
+ {
286
+ this._shuffleSongs = value;
287
+ if (value)
288
+ {
289
+ this._sendMessage(WorkletSequencerMessageType.changeSong, SongChangeType.shuffleOn);
290
+ }
291
+ else
292
+ {
293
+ this._sendMessage(WorkletSequencerMessageType.changeSong, SongChangeType.shuffleOff);
294
+ }
295
+ }
296
+
264
297
  /**
265
298
  * Indicates if the sequencer should skip to first note on
266
299
  * @return {boolean}
@@ -446,12 +479,12 @@ export class Sequencer
446
479
 
447
480
  nextSong()
448
481
  {
449
- this._sendMessage(WorkletSequencerMessageType.changeSong, true);
482
+ this._sendMessage(WorkletSequencerMessageType.changeSong, SongChangeType.forwards);
450
483
  }
451
484
 
452
485
  previousSong()
453
486
  {
454
- this._sendMessage(WorkletSequencerMessageType.changeSong, false);
487
+ this._sendMessage(WorkletSequencerMessageType.changeSong, SongChangeType.backwards);
455
488
  }
456
489
 
457
490
  /**
@@ -2,7 +2,7 @@ import {
2
2
  ALL_CHANNELS_OR_DIFFERENT_ACTION,
3
3
  returnMessageType
4
4
  } from "../../synthetizer/worklet_system/message_protocol/worklet_message.js";
5
- import { WorkletSequencerMessageType, WorkletSequencerReturnMessageType } from "./sequencer_message.js";
5
+ import { SongChangeType, WorkletSequencerMessageType, WorkletSequencerReturnMessageType } from "./sequencer_message.js";
6
6
  import { messageTypes, midiControllers } from "../../midi_parser/midi_message.js";
7
7
  import { MIDI_CHANNEL_COUNT } from "../../synthetizer/synthetizer.js";
8
8
 
@@ -60,13 +60,26 @@ export function processMessage(messageType, messageData)
60
60
  break;
61
61
 
62
62
  case WorkletSequencerMessageType.changeSong:
63
- if (messageData)
63
+ switch (messageData)
64
64
  {
65
- this.nextSong();
66
- }
67
- else
68
- {
69
- this.previousSong();
65
+ case SongChangeType.forwards:
66
+ this.nextSong();
67
+ break;
68
+
69
+ case SongChangeType.backwards:
70
+ this.previousSong();
71
+ break;
72
+
73
+ case SongChangeType.shuffleOff:
74
+ this.shuffleMode = false;
75
+ this.songIndex = this.shuffledSongIndexes[this.songIndex];
76
+ break;
77
+
78
+ case SongChangeType.shuffleOn:
79
+ this.shuffleMode = true;
80
+ this.shuffleSongIndexes();
81
+ this.songIndex = 0;
82
+ this.loadCurrentSong();
70
83
  }
71
84
  break;
72
85
 
@@ -13,10 +13,6 @@ import { readBytesAsUintBigEndian } from "../../utils/byte_functions/big_endian.
13
13
  */
14
14
  export function _processEvent(event, trackIndex)
15
15
  {
16
- if (this.ignoreEvents)
17
- {
18
- return;
19
- }
20
16
  if (this.sendMIDIMessages)
21
17
  {
22
18
  if (event.messageStatusByte >= 0x80)
@@ -100,7 +96,7 @@ export function _processEvent(event, trackIndex)
100
96
  }
101
97
  break;
102
98
 
103
- // recongized but ignored
99
+ // recognized but ignored
104
100
  case messageTypes.timeSignature:
105
101
  case messageTypes.endOfTrack:
106
102
  case messageTypes.midiChannelPrefix:
@@ -123,7 +119,7 @@ export function _processEvent(event, trackIndex)
123
119
  if (statusByteData.status === messageTypes.lyric)
124
120
  {
125
121
  lyricsIndex = Math.min(
126
- this.midiData.lyricsTicks.indexOf(event.ticks) + 1,
122
+ this.midiData.lyricsTicks.indexOf(event.ticks),
127
123
  this.midiData.lyrics.length - 1
128
124
  );
129
125
  }
@@ -1,3 +1,10 @@
1
+ export const SongChangeType = {
2
+ backwards: 0,
3
+ forwards: 1,
4
+ shuffleOn: 2,
5
+ shuffleOff: 3
6
+ };
7
+
1
8
  /**
2
9
  * @enum {number}
3
10
  * @property {number} loadNewSongList - 0 -> [...song<MIDI>]
@@ -8,7 +15,7 @@
8
15
  * @property {number} changeMIDIMessageSending - 5 -> sendMIDIMessages<boolean>
9
16
  * @property {number} setPlaybackRate - 6 -> playbackRate<number>
10
17
  * @property {number} setLoop - 7 -> [loop<boolean>, count<number]
11
- * @property {number} changeSong - 8 -> goForwards<boolean> if true, next song, if false, previous
18
+ * @property {number} changeSong - 8 -> changeType<number> 0 - back, 1 - forward, 2 - shuffle ON, 3 - shuffle OFF
12
19
  * @property {number} getMIDI - 9 -> (no data)
13
20
  * @property {number} setSkipToFirstNote -10 -> skipToFirstNoteOn<boolean>
14
21
  * @property {number} setPreservePlaybackState -11 -> preservePlaybackState<boolean>
@@ -120,7 +120,7 @@ export function loadNewSequence(parsedMidi, autoPlay = true)
120
120
  });
121
121
 
122
122
  /**
123
- * Same as Audio.duration (seconds)
123
+ * Same as "audio.duration" property (seconds)
124
124
  * @type {number}
125
125
  */
126
126
  this.duration = this.midiData.duration;
@@ -188,7 +188,8 @@ export function loadNewSongList(midiBuffers, autoPlay = true)
188
188
  {
189
189
  this.loop = false;
190
190
  }
191
- this.loadNewSequence(this.songs[this.songIndex], autoPlay);
191
+ this.shuffleSongIndexes();
192
+ this.loadCurrentSong(autoPlay);
192
193
  }
193
194
 
194
195
  /**
@@ -203,7 +204,7 @@ export function nextSong()
203
204
  }
204
205
  this.songIndex++;
205
206
  this.songIndex %= this.songs.length;
206
- this.loadNewSequence(this.songs[this.songIndex]);
207
+ this.loadCurrentSong();
207
208
  }
208
209
 
209
210
  /**
@@ -221,5 +222,5 @@ export function previousSong()
221
222
  {
222
223
  this.songIndex = this.songs.length - 1;
223
224
  }
224
- this.loadNewSequence(this.songs[this.songIndex]);
225
+ this.loadCurrentSong();
225
226
  }
@@ -18,100 +18,139 @@ import { MIDI_CHANNEL_COUNT } from "../../synthetizer/synthetizer.js";
18
18
 
19
19
  class WorkletSequencer
20
20
  {
21
+ /**
22
+ * All the sequencer's songs
23
+ * @type {BasicMIDI[]}
24
+ */
25
+ songs = [];
26
+
27
+ /**
28
+ * Current song index
29
+ * @type {number}
30
+ */
31
+ songIndex = 0;
32
+
33
+ /**
34
+ * shuffled song indexes
35
+ * @type {number[]}
36
+ */
37
+ shuffledSongIndexes = [];
38
+
39
+ /**
40
+ * the synth to use
41
+ * @type {SpessaSynthProcessor}
42
+ */
43
+ synth;
44
+
45
+ /**
46
+ * if the sequencer is active
47
+ * @type {boolean}
48
+ */
49
+ isActive = false;
50
+
51
+ /**
52
+ * If the event should instead be sent back to the main thread instead of synth
53
+ * @type {boolean}
54
+ */
55
+ sendMIDIMessages = false;
56
+
57
+ /**
58
+ * sequencer's loop count
59
+ * @type {number}
60
+ */
61
+ loopCount = Infinity;
62
+
63
+ /**
64
+ * event's number in this.events
65
+ * @type {number[]}
66
+ */
67
+ eventIndex = [];
68
+
69
+ /**
70
+ * tracks the time that has already been played
71
+ * @type {number}
72
+ */
73
+ playedTime = 0;
74
+
75
+ /**
76
+ * The (relative) time when the sequencer was paused. If it's not paused, then it's undefined.
77
+ * @type {number}
78
+ */
79
+ pausedTime = undefined;
80
+
81
+ /**
82
+ * Absolute playback startTime, bases on the synth's time
83
+ * @type {number}
84
+ */
85
+ absoluteStartTime = currentTime;
86
+ /**
87
+ * Currently playing notes (for pausing and resuming)
88
+ * @type {{
89
+ * midiNote: number,
90
+ * channel: number,
91
+ * velocity: number
92
+ * }[]}
93
+ */
94
+ playingNotes = [];
95
+
96
+ /**
97
+ * controls if the sequencer loops (defaults to true)
98
+ * @type {boolean}
99
+ */
100
+ loop = true;
101
+
102
+ /**
103
+ * controls if the songs are ordered randomly
104
+ * @type {boolean}
105
+ */
106
+ shuffleMode = false;
107
+
108
+ /**
109
+ * the current track data
110
+ * @type {BasicMIDI}
111
+ */
112
+ midiData = undefined;
113
+
114
+ /**
115
+ * midi port number for the corresponding track
116
+ * @type {number[]}
117
+ */
118
+ midiPorts = [];
119
+ midiPortChannelOffset = 0;
120
+ /**
121
+ * stored as:
122
+ * Object<midi port, channel offset>
123
+ * @type {Object<number, number>}
124
+ */
125
+ midiPortChannelOffsets = {};
126
+
127
+ /**
128
+ * @type {boolean}
129
+ * @private
130
+ */
131
+ _skipToFirstNoteOn = true;
132
+
133
+ /**
134
+ * If true, seq will stay paused when seeking or changing the playback rate
135
+ * @type {boolean}
136
+ */
137
+ preservePlaybackState = false;
138
+
21
139
  /**
22
140
  * @param spessasynthProcessor {SpessaSynthProcessor}
23
141
  */
24
142
  constructor(spessasynthProcessor)
25
143
  {
26
144
  this.synth = spessasynthProcessor;
27
- this.ignoreEvents = false;
28
- this.isActive = false;
29
-
30
- /**
31
- * If the event should instead be sent back to the main thread instead of synth
32
- * @type {boolean}
33
- */
34
- this.sendMIDIMessages = false;
35
-
36
- this.loopCount = Infinity;
37
-
38
- // event's number in this.events
39
- /**
40
- * @type {number[]}
41
- */
42
- this.eventIndex = [];
43
- this.songIndex = 0;
44
-
45
- // tracks the time that we have already played
46
- /**
47
- * @type {number}
48
- */
49
- this.playedTime = 0;
50
-
51
- /**
52
- * The (relative) time when the sequencer was paused. If it's not paused then it's undefined.
53
- * @type {number}
54
- */
55
- this.pausedTime = undefined;
56
-
57
- /**
58
- * Absolute playback startTime, bases on the synth's time
59
- * @type {number}
60
- */
61
- this.absoluteStartTime = currentTime;
62
-
63
- /**
64
- * Controls the playback's rate
65
- * @type {number}
66
- */
67
- this._playbackRate = 1;
68
-
69
- /**
70
- * Currently playing notes (for pausing and resuming)
71
- * @type {{
72
- * midiNote: number,
73
- * channel: number,
74
- * velocity: number
75
- * }[]}
76
- */
77
- this.playingNotes = [];
78
-
79
- // controls if the sequencer loops (defaults to true)
80
- this.loop = true;
81
-
82
- /**
83
- * the current track data
84
- * @type {BasicMIDI}
85
- */
86
- this.midiData = undefined;
87
-
88
- /**
89
- * midi port number for the corresponding track
90
- * @type {number[]}
91
- */
92
- this.midiPorts = [];
93
-
94
- this.midiPortChannelOffset = 0;
95
-
96
- /**
97
- * midi port: channel offset
98
- * @type {Object<number, number>}
99
- */
100
- this.midiPortChannelOffsets = {};
101
-
102
- /**
103
- * @type {boolean}
104
- * @private
105
- */
106
- this._skipToFirstNoteOn = true;
107
-
108
- /**
109
- * If true, seq will stay paused when seeking or changing the playback rate
110
- * @type {boolean}
111
- */
112
- this.preservePlaybackState = false;
113
145
  }
114
146
 
147
+ /**
148
+ * Controls the playback's rate
149
+ * @type {number}
150
+ * @private
151
+ */
152
+ _playbackRate = 1;
153
+
115
154
  /**
116
155
  * @param value {number}
117
156
  */
@@ -229,6 +268,16 @@ class WorkletSequencer
229
268
  }
230
269
  }
231
270
 
271
+ loadCurrentSong(autoPlay = true)
272
+ {
273
+ let index = this.songIndex;
274
+ if (this.shuffleMode)
275
+ {
276
+ index = this.shuffledSongIndexes[this.songIndex];
277
+ }
278
+ this.loadNewSequence(this.songs[index], autoPlay);
279
+ }
280
+
232
281
  _resetTimers()
233
282
  {
234
283
  this.playedTime = 0;
@@ -244,6 +293,18 @@ class WorkletSequencer
244
293
  {
245
294
  this.isActive = false;
246
295
  }
296
+
297
+ shuffleSongIndexes()
298
+ {
299
+ const indexes = this.songs.map((_, i) => i);
300
+ this.shuffledSongIndexes = [];
301
+ while (indexes.length > 0)
302
+ {
303
+ const index = indexes[Math.floor(Math.random() * indexes.length)];
304
+ this.shuffledSongIndexes.push(index);
305
+ indexes.splice(indexes.indexOf(index), 1);
306
+ }
307
+ }
247
308
  }
248
309
 
249
310
  // Web MIDI sending