spessasynth_lib 3.25.7 → 3.25.9

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.
@@ -10,7 +10,7 @@ import {
10
10
  import { stbvorbis } from "../../externals/stbvorbis_sync/stbvorbis_sync.min.js";
11
11
  import { VOLUME_ENVELOPE_SMOOTHING_FACTOR } from "./worklet_utilities/volume_envelope.js";
12
12
  import { handleMessage } from "./message_protocol/handle_message.js";
13
- import { callEvent, sendChannelProperties } from "./message_protocol/message_sending.js";
13
+ import { callEvent } from "./message_protocol/message_sending.js";
14
14
  import { systemExclusive } from "./worklet_methods/system_exclusive.js";
15
15
  import { setMasterGain, setMasterPan, setMIDIVolume } from "./worklet_methods/controller_control/master_parameters.js";
16
16
  import { resetAllControllers } from "./worklet_methods/controller_control/reset_controllers.js";
@@ -437,7 +437,7 @@ class SpessaSynthProcessor extends AudioWorkletProcessor
437
437
  this.sequencer.processTick();
438
438
 
439
439
  // for every channel
440
- let totalCurrentVoices = 0;
440
+ this.totalVoicesAmount = 0;
441
441
  this.workletProcessorChannels.forEach((channel, index) =>
442
442
  {
443
443
  if (channel.voices.length < 1 || channel.isMuted)
@@ -445,6 +445,7 @@ class SpessaSynthProcessor extends AudioWorkletProcessor
445
445
  // skip the channels
446
446
  return;
447
447
  }
448
+ let voiceCount = channel.voices.length;
448
449
  let outputIndex;
449
450
  let outputL;
450
451
  let outputR;
@@ -480,15 +481,14 @@ class SpessaSynthProcessor extends AudioWorkletProcessor
480
481
  chorusL, chorusR
481
482
  );
482
483
 
483
- totalCurrentVoices += channel.voices.length;
484
+ this.totalVoicesAmount += channel.voices.length;
485
+ // if voice count changed, update voice amount
486
+ if (channel.voices.length !== voiceCount)
487
+ {
488
+ channel.sendChannelProperty();
489
+ }
484
490
  });
485
-
486
- // if voice count changed, update voice amount
487
- if (totalCurrentVoices !== this.totalVoicesAmount)
488
- {
489
- this.totalVoicesAmount = totalCurrentVoices;
490
- this.sendChannelProperties();
491
- }
491
+ // keep the processor alive
492
492
  return true;
493
493
  }
494
494
 
@@ -591,7 +591,6 @@ SpessaSynthProcessor.prototype.getWorkletVoices = getWorkletVoices;
591
591
 
592
592
  // message port related
593
593
  SpessaSynthProcessor.prototype.handleMessage = handleMessage;
594
- SpessaSynthProcessor.prototype.sendChannelProperties = sendChannelProperties;
595
594
  SpessaSynthProcessor.prototype.callEvent = callEvent;
596
595
 
597
596
  // system-exclusive related
@@ -1,8 +1,5 @@
1
1
  import { returnMessageType } from "./worklet_message.js";
2
2
 
3
- import { modulatorSources } from "../../../soundfont/basic_soundfont/modulator.js";
4
- import { customControllers, NON_CC_INDEX_OFFSET } from "../worklet_utilities/controller_tables.js";
5
-
6
3
  /**
7
4
  * Calls synth event from the worklet side
8
5
  * @param eventName {EventTypes} the event name
@@ -22,43 +19,4 @@ export function callEvent(eventName, eventData)
22
19
  eventData: eventData
23
20
  }
24
21
  });
25
- }
26
-
27
- /**
28
- * @typedef {Object} ChannelProperty
29
- * @property {number} voicesAmount
30
- * @property {number} pitchBend - from -8192 do 8192
31
- * @property {number} pitchBendRangeSemitones - in semitones
32
- * @property {boolean} isMuted
33
- * @property {boolean} isDrum
34
- * @property {number} transposition
35
- */
36
-
37
- /**
38
- * @this {SpessaSynthProcessor}
39
- */
40
- export function sendChannelProperties()
41
- {
42
- if (!this.enableEventSystem)
43
- {
44
- return;
45
- }
46
- /**
47
- * @type {ChannelProperty[]}
48
- */
49
- const data = this.workletProcessorChannels.map(c =>
50
- {
51
- return {
52
- voicesAmount: c.voices.length,
53
- pitchBend: c.midiControllers[NON_CC_INDEX_OFFSET + modulatorSources.pitchWheel],
54
- pitchBendRangeSemitones: c.midiControllers[NON_CC_INDEX_OFFSET + modulatorSources.pitchWheelRange] / 128,
55
- isMuted: c.isMuted,
56
- isDrum: c.drumChannel,
57
- transposition: c.channelTransposeKeyShift + c.customControllers[customControllers.channelTransposeFine] / 100
58
- };
59
- });
60
- this.post({
61
- messageType: returnMessageType.channelProperties,
62
- messageData: data
63
- });
64
22
  }
@@ -79,16 +79,15 @@ export const ALL_CHANNELS_OR_DIFFERENT_ACTION = -1;
79
79
  /**
80
80
  * @typedef {{
81
81
  * channelNumber: number
82
- * messageType: workletMessageType,
82
+ * messageType: (workletMessageType|number),
83
83
  * messageData: (
84
+ * boolean|
84
85
  * number[]
85
86
  * |undefined
86
87
  * |boolean[]
87
88
  * |WorkletVoice[]
88
89
  * |number
89
- * |{sampleData: Float32Array, sampleID: number}
90
90
  * |{rate: number, depth: number, delay: number}
91
- * |boolean
92
91
  * |ArrayBuffer
93
92
  * |{messageType: WorkletSequencerMessageType, messageData: any}
94
93
  * |{messageType: workletKeyModifierMessageType, messageData: any}
@@ -102,14 +101,14 @@ export const ALL_CHANNELS_OR_DIFFERENT_ACTION = -1;
102
101
  * @property {{
103
102
  * eventName: string,
104
103
  * eventData: any
105
- * }|ChannelProperty[]
104
+ * }|ChannelProperty
106
105
  * |PresetListElement[]
107
106
  * |string
108
107
  * |{messageType: WorkletSequencerReturnMessageType, messageData: any}
109
108
  * |SynthesizerSnapshot
110
109
  * |[WorkletSoundfontManagerMessageType, any]} messageData - the message's data
111
110
  *
112
- * 0 - channel properties -> [...<ChannelProperty>] see message_sending.js line 29
111
+ * 0 - channel property change -> [channel<number>, property<ChannelProperty>] see message_sending.js line 29
113
112
  * 1 - event call -> {eventName<string>, eventData:<the event's data>}
114
113
  * 2 - master parameter change -> [parameter<masterParameterType>, value<string|number>]
115
114
  * 3 - sequencer specific -> [messageType<WorkletSequencerReturnMessageType> messageData<any>] note: refer to sequencer_message.js
@@ -122,7 +121,7 @@ export const ALL_CHANNELS_OR_DIFFERENT_ACTION = -1;
122
121
  * @enum {number}
123
122
  */
124
123
  export const returnMessageType = {
125
- channelProperties: 0,
124
+ channelPropertyChange: 0,
126
125
  eventCall: 1,
127
126
  masterParameterChange: 2,
128
127
  sequencerSpecific: 3,
@@ -14,7 +14,7 @@ export function createWorkletChannel(sendEvent = false)
14
14
  const channel = new WorkletProcessorChannel(this, this.defaultPreset, this.workletProcessorChannels.length);
15
15
  this.workletProcessorChannels.push(channel);
16
16
  channel.resetControllers();
17
- this.sendChannelProperties();
17
+ channel.sendChannelProperty();
18
18
  if (sendEvent)
19
19
  {
20
20
  this.callEvent("newchannel", undefined);
@@ -9,7 +9,7 @@ export function muteChannel(isMuted)
9
9
  this.stopAllNotes(true);
10
10
  }
11
11
  this.isMuted = isMuted;
12
- this.synth.sendChannelProperties();
12
+ this.sendChannelProperty();
13
13
  this.synth.callEvent("mutechannel", {
14
14
  channel: this.channelNumber,
15
15
  isMuted: isMuted
@@ -165,7 +165,7 @@ export function noteOn(midiNote, velocity, enableDebugging = false, sendEvent =
165
165
  channelVoices.push(...voices);
166
166
  if (sendEvent)
167
167
  {
168
- this.synth.sendChannelProperties();
168
+ this.sendChannelProperty();
169
169
  this.synth.callEvent("noteon", {
170
170
  midiNote: midiNote,
171
171
  channel: this.channelNumber,
@@ -13,6 +13,9 @@ export function programChange(programNumber, userChange = false)
13
13
  // always 128 for percussion
14
14
  let bank = this.getBankSelect();
15
15
  let sentBank;
16
+ /**
17
+ * @type {BasicPreset}
18
+ */
16
19
  let preset;
17
20
 
18
21
  const isXG = this.isXGChannel;
@@ -34,21 +37,27 @@ export function programChange(programNumber, userChange = false)
34
37
  else
35
38
  {
36
39
  preset = this.synth.soundfontManager.getPreset(bank, programNumber, isXG);
37
- sentBank = preset.bank;
40
+ const offset = this.synth.soundfontManager.soundfontList
41
+ .find(s => s.soundfont === preset.parentSoundBank).bankOffset;
42
+ sentBank = preset.bank - offset;
38
43
  this.presetUsesOverride = false;
39
44
  }
40
45
  }
41
46
  else
42
47
  {
43
48
  preset = this.synth.soundfontManager.getPreset(bank, programNumber, isXG);
44
- sentBank = preset.bank;
49
+ const offset = this.synth.soundfontManager.soundfontList
50
+ .find(s => s.soundfont === preset.parentSoundBank).bankOffset;
51
+ sentBank = preset.bank - offset;
45
52
  this.presetUsesOverride = false;
46
53
  }
47
54
  this.setPreset(preset);
55
+ this.sentBank = sentBank;
48
56
  this.synth.callEvent("programchange", {
49
57
  channel: this.channelNumber,
50
58
  program: preset.program,
51
59
  bank: sentBank,
52
60
  userCalled: userChange
53
61
  });
62
+ this.sendChannelProperty();
54
63
  }
@@ -10,7 +10,7 @@ export function stopAllNotes(force = false)
10
10
  // force stop all
11
11
  this.voices.length = 0;
12
12
  this.sustainedVoices.length = 0;
13
- this.synth.sendChannelProperties();
13
+ this.sendChannelProperty();
14
14
  }
15
15
  else
16
16
  {
@@ -29,5 +29,5 @@ export function pitchWheel(MSB, LSB)
29
29
  0,
30
30
  modulatorSources.pitchWheel
31
31
  ));
32
- this.synth.sendChannelProperties();
32
+ this.sendChannelProperty();
33
33
  }
@@ -30,5 +30,5 @@ export function transposeChannel(semitones, force = false)
30
30
  // apply transpose
31
31
  this.channelTransposeKeyShift = keyShift;
32
32
  this.setCustomController(customControllers.channelTransposeFine, (semitones - keyShift) * 100);
33
- this.synth.sendChannelProperties();
33
+ this.sendChannelProperty();
34
34
  }
@@ -2,7 +2,8 @@ import {
2
2
  CONTROLLER_TABLE_SIZE,
3
3
  CUSTOM_CONTROLLER_TABLE_SIZE,
4
4
  customControllers,
5
- dataEntryStates
5
+ dataEntryStates,
6
+ NON_CC_INDEX_OFFSET
6
7
  } from "./controller_tables.js";
7
8
  import {
8
9
  resetControllers,
@@ -29,6 +30,8 @@ import { setOctaveTuning } from "../worklet_methods/tuning_control/set_octave_tu
29
30
  import { programChange } from "../worklet_methods/program_change.js";
30
31
  import { chooseBank, isSystemXG, parseBankSelect } from "../../../utils/xg_hacks.js";
31
32
  import { DEFAULT_PERCUSSION } from "../../synth_constants.js";
33
+ import { modulatorSources } from "../../../soundfont/basic_soundfont/modulator.js";
34
+ import { returnMessageType } from "../message_protocol/worklet_message.js";
32
35
 
33
36
  /**
34
37
  * This class represents a single MIDI Channel within the synthesizer.
@@ -115,6 +118,12 @@ class WorkletProcessorChannel
115
118
  */
116
119
  bank = 0;
117
120
 
121
+ /**
122
+ * The bank number sent as channel properties.
123
+ * @type {number}
124
+ */
125
+ sentBank = 0;
126
+
118
127
  /**
119
128
  * The bank LSB number of the channel (used for patch changes in XG mode).
120
129
  * @type {number}
@@ -359,7 +368,7 @@ class WorkletProcessorChannel
359
368
  isDrumChannel: this.drumChannel
360
369
  });
361
370
  this.programChange(this.preset.program);
362
- this.synth.sendChannelProperties();
371
+ this.sendChannelProperty();
363
372
  }
364
373
 
365
374
  /**
@@ -386,6 +395,48 @@ class WorkletProcessorChannel
386
395
  this.channelVibrato.delay = 0;
387
396
  this.channelVibrato.depth = 0;
388
397
  }
398
+
399
+
400
+ /**
401
+ * @typedef {Object} ChannelProperty
402
+ * @property {number} voicesAmount - the channel's current voice amount
403
+ * @property {number} pitchBend - the channel's current pitch bend from -8192 do 8192
404
+ * @property {number} pitchBendRangeSemitones - the pitch bend's range, in semitones
405
+ * @property {boolean} isMuted - indicates whether the channel is muted
406
+ * @property {boolean} isDrum - indicates whether the channel is a drum channel
407
+ * @property {number} transposition - the channel's transposition, in semitones
408
+ * @property {number} bank - the bank number of the current preset
409
+ * @property {number} program - the MIDI program number of the current preset
410
+ */
411
+
412
+
413
+ /**
414
+ * Sends this channel's property
415
+ */
416
+ sendChannelProperty()
417
+ {
418
+ if (!this.synth.enableEventSystem)
419
+ {
420
+ return;
421
+ }
422
+ /**
423
+ * @type {ChannelProperty}
424
+ */
425
+ const data = {
426
+ voicesAmount: this.voices.length,
427
+ pitchBend: this.midiControllers[NON_CC_INDEX_OFFSET + modulatorSources.pitchWheel],
428
+ pitchBendRangeSemitones: this.midiControllers[NON_CC_INDEX_OFFSET + modulatorSources.pitchWheelRange] / 128,
429
+ isMuted: this.isMuted,
430
+ isDrum: this.drumChannel,
431
+ transposition: this.channelTransposeKeyShift + this.customControllers[customControllers.channelTransposeFine] / 100,
432
+ bank: this.sentBank,
433
+ program: this.preset.program
434
+ };
435
+ this.synth.post({
436
+ messageType: returnMessageType.channelPropertyChange,
437
+ messageData: [this.channelNumber, data]
438
+ });
439
+ }
389
440
  }
390
441
 
391
442
  // voice