spessasynth_lib 0.0.1
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/.idea/modules.xml +8 -0
- package/.idea/spessasynth_lib.iml +12 -0
- package/.idea/vcs.xml +6 -0
- package/copy_version.sh +38 -0
- package/index.js +73 -0
- package/package/@types/externals/stbvorbis_sync/stbvorbis_sync.min.d.ts +1 -0
- package/package/@types/index.d.ts +34 -0
- package/package/@types/midi_handler/midi_handler.d.ts +39 -0
- package/package/@types/midi_handler/web_midi_link.d.ts +12 -0
- package/package/@types/midi_parser/midi_data.d.ts +95 -0
- package/package/@types/midi_parser/midi_editor.d.ts +45 -0
- package/package/@types/midi_parser/midi_loader.d.ts +100 -0
- package/package/@types/midi_parser/midi_message.d.ts +154 -0
- package/package/@types/midi_parser/midi_writer.d.ts +6 -0
- package/package/@types/midi_parser/rmidi_writer.d.ts +9 -0
- package/package/@types/midi_parser/used_keys_loaded.d.ts +7 -0
- package/package/@types/sequencer/sequencer.d.ts +180 -0
- package/package/@types/sequencer/worklet_sequencer/sequencer_message.d.ts +28 -0
- package/package/@types/soundfont/read/generators.d.ts +98 -0
- package/package/@types/soundfont/read/instruments.d.ts +50 -0
- package/package/@types/soundfont/read/modulators.d.ts +73 -0
- package/package/@types/soundfont/read/presets.d.ts +87 -0
- package/package/@types/soundfont/read/riff_chunk.d.ts +31 -0
- package/package/@types/soundfont/read/samples.d.ts +134 -0
- package/package/@types/soundfont/read/zones.d.ts +141 -0
- package/package/@types/soundfont/soundfont.d.ts +76 -0
- package/package/@types/soundfont/write/ibag.d.ts +6 -0
- package/package/@types/soundfont/write/igen.d.ts +6 -0
- package/package/@types/soundfont/write/imod.d.ts +6 -0
- package/package/@types/soundfont/write/inst.d.ts +6 -0
- package/package/@types/soundfont/write/pbag.d.ts +6 -0
- package/package/@types/soundfont/write/pgen.d.ts +6 -0
- package/package/@types/soundfont/write/phdr.d.ts +6 -0
- package/package/@types/soundfont/write/pmod.d.ts +6 -0
- package/package/@types/soundfont/write/sdta.d.ts +11 -0
- package/package/@types/soundfont/write/shdr.d.ts +8 -0
- package/package/@types/soundfont/write/soundfont_trimmer.d.ts +6 -0
- package/package/@types/soundfont/write/write.d.ts +21 -0
- package/package/@types/synthetizer/audio_effects/effects_config.d.ts +29 -0
- package/package/@types/synthetizer/audio_effects/fancy_chorus.d.ts +93 -0
- package/package/@types/synthetizer/audio_effects/reverb.d.ts +7 -0
- package/package/@types/synthetizer/synth_event_handler.d.ts +161 -0
- package/package/@types/synthetizer/synthetizer.d.ts +294 -0
- package/package/@types/synthetizer/worklet_system/message_protocol/worklet_message.d.ts +89 -0
- package/package/@types/synthetizer/worklet_system/worklet_utilities/worklet_processor_channel.d.ts +134 -0
- package/package/@types/synthetizer/worklet_url.d.ts +5 -0
- package/package/@types/utils/buffer_to_wav.d.ts +8 -0
- package/package/@types/utils/byte_functions/big_endian.d.ts +13 -0
- package/package/@types/utils/byte_functions/little_endian.d.ts +35 -0
- package/package/@types/utils/byte_functions/string.d.ts +22 -0
- package/package/@types/utils/byte_functions/variable_length_quantity.d.ts +12 -0
- package/package/@types/utils/indexed_array.d.ts +21 -0
- package/package/@types/utils/loggin.d.ts +26 -0
- package/package/@types/utils/other.d.ts +32 -0
- package/package/LICENSE +26 -0
- package/package/README.md +84 -0
- package/package/externals/NOTICE +9 -0
- package/package/externals/libvorbis/@types/OggVorbisEncoder.d.ts +34 -0
- package/package/externals/libvorbis/OggVorbisEncoder.min.js +1 -0
- package/package/externals/stbvorbis_sync/@types/stbvorbis_sync.d.ts +12 -0
- package/package/externals/stbvorbis_sync/LICENSE +202 -0
- package/package/externals/stbvorbis_sync/stbvorbis_sync.min.js +1 -0
- package/package/index.js +73 -0
- package/package/midi_handler/README.md +3 -0
- package/package/midi_handler/midi_handler.js +118 -0
- package/package/midi_handler/web_midi_link.js +41 -0
- package/package/midi_parser/README.md +3 -0
- package/package/midi_parser/midi_data.js +121 -0
- package/package/midi_parser/midi_editor.js +557 -0
- package/package/midi_parser/midi_loader.js +502 -0
- package/package/midi_parser/midi_message.js +234 -0
- package/package/midi_parser/midi_writer.js +95 -0
- package/package/midi_parser/rmidi_writer.js +271 -0
- package/package/midi_parser/used_keys_loaded.js +172 -0
- package/package/package.json +43 -0
- package/package/sequencer/README.md +23 -0
- package/package/sequencer/sequencer.js +439 -0
- package/package/sequencer/worklet_sequencer/events.js +92 -0
- package/package/sequencer/worklet_sequencer/play.js +309 -0
- package/package/sequencer/worklet_sequencer/process_event.js +167 -0
- package/package/sequencer/worklet_sequencer/process_tick.js +85 -0
- package/package/sequencer/worklet_sequencer/sequencer_message.js +39 -0
- package/package/sequencer/worklet_sequencer/song_control.js +193 -0
- package/package/sequencer/worklet_sequencer/worklet_sequencer.js +218 -0
- package/package/soundfont/README.md +8 -0
- package/package/soundfont/read/generators.js +212 -0
- package/package/soundfont/read/instruments.js +125 -0
- package/package/soundfont/read/modulators.js +249 -0
- package/package/soundfont/read/presets.js +300 -0
- package/package/soundfont/read/riff_chunk.js +81 -0
- package/package/soundfont/read/samples.js +398 -0
- package/package/soundfont/read/zones.js +310 -0
- package/package/soundfont/soundfont.js +357 -0
- package/package/soundfont/write/ibag.js +39 -0
- package/package/soundfont/write/igen.js +75 -0
- package/package/soundfont/write/imod.js +46 -0
- package/package/soundfont/write/inst.js +34 -0
- package/package/soundfont/write/pbag.js +39 -0
- package/package/soundfont/write/pgen.js +77 -0
- package/package/soundfont/write/phdr.js +42 -0
- package/package/soundfont/write/pmod.js +46 -0
- package/package/soundfont/write/sdta.js +72 -0
- package/package/soundfont/write/shdr.js +54 -0
- package/package/soundfont/write/soundfont_trimmer.js +169 -0
- package/package/soundfont/write/write.js +180 -0
- package/package/synthetizer/README.md +6 -0
- package/package/synthetizer/audio_effects/effects_config.js +21 -0
- package/package/synthetizer/audio_effects/fancy_chorus.js +120 -0
- package/package/synthetizer/audio_effects/impulse_response_2.flac +0 -0
- package/package/synthetizer/audio_effects/reverb.js +24 -0
- package/package/synthetizer/synth_event_handler.js +156 -0
- package/package/synthetizer/synthetizer.js +766 -0
- package/package/synthetizer/worklet_processor.min.js +13 -0
- package/package/synthetizer/worklet_system/README.md +6 -0
- package/package/synthetizer/worklet_system/main_processor.js +363 -0
- package/package/synthetizer/worklet_system/message_protocol/handle_message.js +197 -0
- package/package/synthetizer/worklet_system/message_protocol/message_sending.js +74 -0
- package/package/synthetizer/worklet_system/message_protocol/worklet_message.js +121 -0
- package/package/synthetizer/worklet_system/minify_processor.sh +4 -0
- package/package/synthetizer/worklet_system/worklet_methods/controller_control.js +230 -0
- package/package/synthetizer/worklet_system/worklet_methods/data_entry.js +277 -0
- package/package/synthetizer/worklet_system/worklet_methods/note_off.js +109 -0
- package/package/synthetizer/worklet_system/worklet_methods/note_on.js +91 -0
- package/package/synthetizer/worklet_system/worklet_methods/program_control.js +183 -0
- package/package/synthetizer/worklet_system/worklet_methods/reset_controllers.js +177 -0
- package/package/synthetizer/worklet_system/worklet_methods/snapshot.js +129 -0
- package/package/synthetizer/worklet_system/worklet_methods/system_exclusive.js +272 -0
- package/package/synthetizer/worklet_system/worklet_methods/tuning_control.js +195 -0
- package/package/synthetizer/worklet_system/worklet_methods/vibrato_control.js +29 -0
- package/package/synthetizer/worklet_system/worklet_methods/voice_control.js +233 -0
- package/package/synthetizer/worklet_system/worklet_processor.js +9 -0
- package/package/synthetizer/worklet_system/worklet_utilities/lfo.js +23 -0
- package/package/synthetizer/worklet_system/worklet_utilities/lowpass_filter.js +130 -0
- package/package/synthetizer/worklet_system/worklet_utilities/modulation_envelope.js +73 -0
- package/package/synthetizer/worklet_system/worklet_utilities/modulator_curves.js +86 -0
- package/package/synthetizer/worklet_system/worklet_utilities/stereo_panner.js +81 -0
- package/package/synthetizer/worklet_system/worklet_utilities/unit_converter.js +66 -0
- package/package/synthetizer/worklet_system/worklet_utilities/volume_envelope.js +265 -0
- package/package/synthetizer/worklet_system/worklet_utilities/wavetable_oscillator.js +83 -0
- package/package/synthetizer/worklet_system/worklet_utilities/worklet_modulator.js +234 -0
- package/package/synthetizer/worklet_system/worklet_utilities/worklet_processor_channel.js +116 -0
- package/package/synthetizer/worklet_system/worklet_utilities/worklet_voice.js +272 -0
- package/package/synthetizer/worklet_url.js +5 -0
- package/package/utils/README.md +4 -0
- package/package/utils/buffer_to_wav.js +101 -0
- package/package/utils/byte_functions/big_endian.js +28 -0
- package/package/utils/byte_functions/little_endian.js +74 -0
- package/package/utils/byte_functions/string.js +97 -0
- package/package/utils/byte_functions/variable_length_quantity.js +37 -0
- package/package/utils/encode_vorbis.js +30 -0
- package/package/utils/indexed_array.js +41 -0
- package/package/utils/loggin.js +79 -0
- package/package/utils/other.js +54 -0
- package/package.json +43 -0
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @enum {number}
|
|
3
|
+
* // NOTE: Every message needs a channel number (if not relevant or all, set to -1)
|
|
4
|
+
* @property {number} noteOff - 0 -> midiNote<number>
|
|
5
|
+
* @property {number} noteOn - 1 -> [midiNote<number>, velocity<number>, enableDebugging<boolean>]
|
|
6
|
+
* @property {number} ccChange - 2 -> [ccNumber<number>, ccValue<number>, force<boolean>]
|
|
7
|
+
* @property {number} programChange - 3 -> [programNumber<number>, userChange<boolean>]
|
|
8
|
+
* @property {number} channelPressure - 4 -> pressure<number>
|
|
9
|
+
* @property {number} polyPressure - 5 -> [midiNote<number>, pressure<number>]
|
|
10
|
+
* @property {number} killNote - 6 -> midiNote<number>
|
|
11
|
+
* @property {number} ccReset - 7 -> (no data) note: if channel is -1 then reset all channels
|
|
12
|
+
* @property {number} setChannelVibrato - 8 -> {frequencyHz: number, depthCents: number, delaySeconds: number} note: if channel is -1 then stop all channels note 2: if rate is -1, it means locking
|
|
13
|
+
* @property {number} reloadSoundFont - 9 -> (no data)
|
|
14
|
+
* @property {number} stopAll - 10 -> force<number> (0 false, 1 true) note: if channel is -1 then stop all channels
|
|
15
|
+
* @property {number} killNotes - 11 -> amount<number>
|
|
16
|
+
* @property {number} muteChannel - 12 -> isMuted<boolean>
|
|
17
|
+
* @property {number} addNewChannel - 13 -> (no data)
|
|
18
|
+
* @property {number} customCcChange - 14 -> [ccNumber<number>, ccValue<number>]
|
|
19
|
+
* @property {number} debugMessage - 15 -> (no data)
|
|
20
|
+
* @property {number} systemExclusive - 16 -> message data <number[]> (without the F0 byte)
|
|
21
|
+
* @property {number} setMasterParameter - 17 -> [parameter<masterParameterType>, value<number>]
|
|
22
|
+
* @property {number} setDrums - 18 -> isDrums<boolean>
|
|
23
|
+
* @property {number} pitchWheel - 19 -> [MSB<number>, LSB<number>]
|
|
24
|
+
* @property {number} transpose - 20 -> [semitones<number>, force<boolean>] note: if channel is -1 then transpose all channels
|
|
25
|
+
* @property {number} highPerformanceMode - 21 -> isOn<boolean>
|
|
26
|
+
* @property {number} lockController - 22 -> [controllerNumber<number>, isLocked<boolean>]
|
|
27
|
+
* @property {number} sequencerSpecific - 23 -> [messageType<WorkletSequencerMessageType> messageData<any>] note: refer to sequencer_message.js
|
|
28
|
+
* @property {number} requestSynthesizerSnapshot - 24 -> (no data)
|
|
29
|
+
* @property {number} setLogLevel - 25 -> [enableInfo<boolean>, enableWarning<boolean>, enableGroup<boolean>, enableTable<boolean>]
|
|
30
|
+
*/
|
|
31
|
+
export const workletMessageType = {
|
|
32
|
+
noteOff: 0,
|
|
33
|
+
noteOn: 1,
|
|
34
|
+
ccChange: 2,
|
|
35
|
+
programChange: 3,
|
|
36
|
+
channelPressure: 4,
|
|
37
|
+
polyPressure: 5,
|
|
38
|
+
killNote: 6,
|
|
39
|
+
ccReset: 7,
|
|
40
|
+
setChannelVibrato: 8,
|
|
41
|
+
reloadSoundFont: 9,
|
|
42
|
+
stopAll: 10,
|
|
43
|
+
killNotes: 11,
|
|
44
|
+
muteChannel: 12,
|
|
45
|
+
addNewChannel: 13,
|
|
46
|
+
customcCcChange: 14,
|
|
47
|
+
debugMessage: 15,
|
|
48
|
+
systemExclusive: 16,
|
|
49
|
+
setMasterParameter: 17,
|
|
50
|
+
setDrums: 18,
|
|
51
|
+
pitchWheel: 19,
|
|
52
|
+
transpose: 20,
|
|
53
|
+
highPerformanceMode: 21,
|
|
54
|
+
lockController: 22,
|
|
55
|
+
sequencerSpecific: 23,
|
|
56
|
+
requestSynthesizerSnapshot: 24,
|
|
57
|
+
setLogLevel: 25
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* @enum {number}
|
|
62
|
+
*/
|
|
63
|
+
export const masterParameterType = {
|
|
64
|
+
mainVolume: 0,
|
|
65
|
+
masterPan: 1,
|
|
66
|
+
voicesCap: 2,
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
export const ALL_CHANNELS_OR_DIFFERENT_ACTION = -1;
|
|
71
|
+
/**
|
|
72
|
+
* @typedef {{
|
|
73
|
+
* channelNumber: number
|
|
74
|
+
* messageType: workletMessageType,
|
|
75
|
+
* messageData: (
|
|
76
|
+
* number[]
|
|
77
|
+
* |boolean[]
|
|
78
|
+
* |WorkletVoice[]
|
|
79
|
+
* |number
|
|
80
|
+
* |{sampleData: Float32Array, sampleID: number}
|
|
81
|
+
* |{rate: number, depth: number, delay: number}
|
|
82
|
+
* |boolean
|
|
83
|
+
* |ArrayBuffer
|
|
84
|
+
* |{messageType: WorkletSequencerMessageType, messageData: any}
|
|
85
|
+
* )
|
|
86
|
+
* }} WorkletMessage
|
|
87
|
+
*/
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* @typedef {Object} WorkletReturnMessage
|
|
91
|
+
* @property {returnMessageType} messageType - the message's type
|
|
92
|
+
* @property {{
|
|
93
|
+
* eventName: string,
|
|
94
|
+
* eventData: any
|
|
95
|
+
* }|ChannelProperty[]
|
|
96
|
+
* |PresetListElement[]
|
|
97
|
+
* |string
|
|
98
|
+
* |{messageType: WorkletSequencerReturnMessageType, messageData: any}
|
|
99
|
+
* |SynthesizerSnapshot} messageData - the message's data
|
|
100
|
+
*
|
|
101
|
+
* 0 - channel properties -> [...<ChannelProperty>] see message_sending.js line 29
|
|
102
|
+
* 1 - event call -> {eventName<string>, eventData:<the event's data>}
|
|
103
|
+
* 2 - reported current time -> currentTime<number>
|
|
104
|
+
* 3 - sequencer specific -> [messageType<WorkletSequencerReturnMessageType> messageData<any>] note: refer to sequencer_message.js
|
|
105
|
+
* 4 - synthesizer snapshot -> snapshot<SynthesizerSnapshot> note: refer to snapshot.js
|
|
106
|
+
* 5 - ready -> (no data)
|
|
107
|
+
* 6 - soundfontError -> errorMessage<string>
|
|
108
|
+
*/
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* @enum {number}
|
|
112
|
+
*/
|
|
113
|
+
export const returnMessageType = {
|
|
114
|
+
channelProperties: 0,
|
|
115
|
+
eventCall: 1,
|
|
116
|
+
reportedCurrentTime: 2,
|
|
117
|
+
sequencerSpecific: 3,
|
|
118
|
+
synthesizerSnapshot: 4,
|
|
119
|
+
ready: 5,
|
|
120
|
+
soundfontError: 6,
|
|
121
|
+
}
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import { consoleColors } from '../../../utils/other.js'
|
|
2
|
+
import { midiControllers } from '../../../midi_parser/midi_message.js'
|
|
3
|
+
import { dataEntryStates } from '../worklet_utilities/worklet_processor_channel.js'
|
|
4
|
+
import { computeModulators } from '../worklet_utilities/worklet_modulator.js'
|
|
5
|
+
import { SpessaSynthInfo } from '../../../utils/loggin.js'
|
|
6
|
+
import { SYNTHESIZER_GAIN } from '../main_processor.js'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @param channel {number}
|
|
10
|
+
* @param controllerNumber {midiControllers}
|
|
11
|
+
* @param controllerValue {number}
|
|
12
|
+
* @param force {boolean}
|
|
13
|
+
* @this {SpessaSynthProcessor}
|
|
14
|
+
*/
|
|
15
|
+
export function controllerChange(channel, controllerNumber, controllerValue, force = false)
|
|
16
|
+
{
|
|
17
|
+
/**
|
|
18
|
+
* @type {WorkletProcessorChannel}
|
|
19
|
+
*/
|
|
20
|
+
const channelObject = this.workletProcessorChannels[channel];
|
|
21
|
+
// lsb controller values: append them as the lower nibble of the 14 bit value
|
|
22
|
+
// excluding bank select and data entry as it's handled separately
|
|
23
|
+
if(
|
|
24
|
+
controllerNumber >= midiControllers.lsbForControl1ModulationWheel
|
|
25
|
+
&& controllerNumber <= midiControllers.lsbForControl13EffectControl2
|
|
26
|
+
&& controllerNumber !== midiControllers.lsbForControl6DataEntry
|
|
27
|
+
)
|
|
28
|
+
{
|
|
29
|
+
const actualCCNum = controllerNumber - 32;
|
|
30
|
+
if(channelObject.lockedControllers[actualCCNum])
|
|
31
|
+
{
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
// append the lower nibble to the main controller
|
|
35
|
+
channelObject.midiControllers[actualCCNum] = (channelObject.midiControllers[actualCCNum] & 0x3F80) | (controllerValue & 0x7F);
|
|
36
|
+
channelObject.voices.forEach(v => computeModulators(v, channelObject.midiControllers, 1, actualCCNum));
|
|
37
|
+
}
|
|
38
|
+
switch (controllerNumber) {
|
|
39
|
+
case midiControllers.allNotesOff:
|
|
40
|
+
this.stopAll(channel);
|
|
41
|
+
break;
|
|
42
|
+
|
|
43
|
+
case midiControllers.allSoundOff:
|
|
44
|
+
this.stopAll(channel, true);
|
|
45
|
+
break;
|
|
46
|
+
|
|
47
|
+
// special case: bank select
|
|
48
|
+
case midiControllers.bankSelect:
|
|
49
|
+
let bankNr = controllerValue;
|
|
50
|
+
if(!force)
|
|
51
|
+
{
|
|
52
|
+
switch (this.system) {
|
|
53
|
+
case "gm":
|
|
54
|
+
// gm ignores bank select
|
|
55
|
+
SpessaSynthInfo(`%cIgnoring the Bank Select (${controllerValue}), as the synth is in GM mode.`, consoleColors.info);
|
|
56
|
+
return;
|
|
57
|
+
|
|
58
|
+
case "xg":
|
|
59
|
+
// for xg, if msb is 120, 126 or 127, then it's drums
|
|
60
|
+
if (bankNr === 120 || bankNr === 126 || bankNr === 127)
|
|
61
|
+
{
|
|
62
|
+
this.setDrums(channel, true);
|
|
63
|
+
}
|
|
64
|
+
else
|
|
65
|
+
{
|
|
66
|
+
this.setDrums(channel, false);
|
|
67
|
+
}
|
|
68
|
+
break;
|
|
69
|
+
|
|
70
|
+
case "gm2":
|
|
71
|
+
if (bankNr === 120)
|
|
72
|
+
{
|
|
73
|
+
channelObject.drumChannel = true;
|
|
74
|
+
this.callEvent("drumchange", {
|
|
75
|
+
channel: channel,
|
|
76
|
+
isDrumChannel: true
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (channelObject.drumChannel)
|
|
82
|
+
{
|
|
83
|
+
// 128 for percussion channel
|
|
84
|
+
bankNr = 128;
|
|
85
|
+
}
|
|
86
|
+
if (bankNr === 128 && !channelObject.drumChannel)
|
|
87
|
+
{
|
|
88
|
+
// if channel is not for percussion, default to bank current
|
|
89
|
+
bankNr = channelObject.midiControllers[midiControllers.bankSelect];
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
channelObject.midiControllers[midiControllers.bankSelect] = bankNr;
|
|
94
|
+
break;
|
|
95
|
+
|
|
96
|
+
case midiControllers.lsbForControl0BankSelect:
|
|
97
|
+
if(this.system === 'xg')
|
|
98
|
+
{
|
|
99
|
+
if(!channelObject.drumChannel)
|
|
100
|
+
{
|
|
101
|
+
// some soundfonts use 127 as drums and
|
|
102
|
+
// if it's not marked as drums by bank MSB (line 47), then we DO NOT want the drums!
|
|
103
|
+
if(controllerValue !== 127)
|
|
104
|
+
{
|
|
105
|
+
channelObject.midiControllers[midiControllers.bankSelect] = controllerValue;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
else
|
|
110
|
+
if(this.system === "gm2")
|
|
111
|
+
{
|
|
112
|
+
channelObject.midiControllers[midiControllers.bankSelect] = controllerValue;
|
|
113
|
+
}
|
|
114
|
+
break;
|
|
115
|
+
|
|
116
|
+
// check for RPN and NPRN and data entry
|
|
117
|
+
case midiControllers.RPNLsb:
|
|
118
|
+
channelObject.RPValue = channelObject.RPValue << 7 | controllerValue;
|
|
119
|
+
channelObject.dataEntryState = dataEntryStates.RPFine;
|
|
120
|
+
break;
|
|
121
|
+
|
|
122
|
+
case midiControllers.RPNMsb:
|
|
123
|
+
channelObject.RPValue = controllerValue;
|
|
124
|
+
channelObject.dataEntryState = dataEntryStates.RPCoarse;
|
|
125
|
+
break;
|
|
126
|
+
|
|
127
|
+
case midiControllers.NRPNMsb:
|
|
128
|
+
channelObject.NRPCoarse = controllerValue;
|
|
129
|
+
channelObject.dataEntryState = dataEntryStates.NRPCoarse;
|
|
130
|
+
break;
|
|
131
|
+
|
|
132
|
+
case midiControllers.NRPNLsb:
|
|
133
|
+
channelObject.NRPFine = controllerValue;
|
|
134
|
+
channelObject.dataEntryState = dataEntryStates.NRPFine;
|
|
135
|
+
break;
|
|
136
|
+
|
|
137
|
+
case midiControllers.dataEntryMsb:
|
|
138
|
+
this.dataEntryCoarse(channel, controllerValue);
|
|
139
|
+
break;
|
|
140
|
+
|
|
141
|
+
case midiControllers.lsbForControl6DataEntry:
|
|
142
|
+
this.dataEntryFine(channel, controllerValue);
|
|
143
|
+
break;
|
|
144
|
+
|
|
145
|
+
case midiControllers.resetAllControllers:
|
|
146
|
+
this.resetControllers(channel);
|
|
147
|
+
break;
|
|
148
|
+
|
|
149
|
+
case midiControllers.sustainPedal:
|
|
150
|
+
if (controllerValue >= 64)
|
|
151
|
+
{
|
|
152
|
+
channelObject.holdPedal = true;
|
|
153
|
+
}
|
|
154
|
+
else
|
|
155
|
+
{
|
|
156
|
+
channelObject.holdPedal = false;
|
|
157
|
+
channelObject.sustainedVoices.forEach(v => {
|
|
158
|
+
this.releaseVoice(v)
|
|
159
|
+
});
|
|
160
|
+
channelObject.sustainedVoices = [];
|
|
161
|
+
}
|
|
162
|
+
break;
|
|
163
|
+
|
|
164
|
+
// default: apply the controller to the table
|
|
165
|
+
default:
|
|
166
|
+
if(channelObject.lockedControllers[controllerNumber])
|
|
167
|
+
{
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
channelObject.midiControllers[controllerNumber] = controllerValue << 7;
|
|
171
|
+
channelObject.voices.forEach(v => computeModulators(v, channelObject.midiControllers, 1, controllerNumber));
|
|
172
|
+
this.callEvent("controllerchange", {
|
|
173
|
+
channel: channel,
|
|
174
|
+
controllerNumber: controllerNumber,
|
|
175
|
+
controllerValue: controllerValue
|
|
176
|
+
});
|
|
177
|
+
break;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* @this {SpessaSynthProcessor}
|
|
183
|
+
*/
|
|
184
|
+
export function setMIDIVolume(volume)
|
|
185
|
+
{
|
|
186
|
+
this.midiVolume = volume;
|
|
187
|
+
this.setMasterPan(this.pan);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* @param volume {number} 0-1
|
|
192
|
+
* @this {SpessaSynthProcessor}
|
|
193
|
+
*/
|
|
194
|
+
export function setMasterGain(volume)
|
|
195
|
+
{
|
|
196
|
+
this.masterGain = volume * SYNTHESIZER_GAIN;
|
|
197
|
+
this.setMasterPan(this.pan);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* @param pan {number} -1 to 1
|
|
202
|
+
* @this {SpessaSynthProcessor}
|
|
203
|
+
*/
|
|
204
|
+
export function setMasterPan(pan)
|
|
205
|
+
{
|
|
206
|
+
this.pan = pan;
|
|
207
|
+
// clamp to 0-1 (0 is left)
|
|
208
|
+
pan = (pan / 2) + 0.5;
|
|
209
|
+
this.panLeft = (1 - pan) * this.currentGain;
|
|
210
|
+
this.panRight = (pan) * this.currentGain;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* @param channel {number}
|
|
215
|
+
* @param isMuted {boolean}
|
|
216
|
+
* @this {SpessaSynthProcessor}
|
|
217
|
+
*/
|
|
218
|
+
export function muteChannel(channel, isMuted)
|
|
219
|
+
{
|
|
220
|
+
if(isMuted)
|
|
221
|
+
{
|
|
222
|
+
this.stopAll(channel, true);
|
|
223
|
+
}
|
|
224
|
+
this.workletProcessorChannels[channel].isMuted = isMuted;
|
|
225
|
+
this.sendChannelProperties();
|
|
226
|
+
this.callEvent("mutechannel", {
|
|
227
|
+
channel: channel,
|
|
228
|
+
isMuted: isMuted
|
|
229
|
+
});
|
|
230
|
+
}
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import { consoleColors } from '../../../utils/other.js'
|
|
2
|
+
import { midiControllers } from '../../../midi_parser/midi_message.js'
|
|
3
|
+
import {
|
|
4
|
+
customControllers,
|
|
5
|
+
dataEntryStates,
|
|
6
|
+
NON_CC_INDEX_OFFSET,
|
|
7
|
+
} from '../worklet_utilities/worklet_processor_channel.js'
|
|
8
|
+
import { modulatorSources } from '../../../soundfont/read/modulators.js'
|
|
9
|
+
import { SpessaSynthInfo, SpessaSynthWarn } from '../../../utils/loggin.js'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Executes a data entry for an NRP for a sc88pro NRP (because touhou yes) and RPN tuning
|
|
13
|
+
* @param channel {number}
|
|
14
|
+
* @param dataValue {number} dataEntryCoarse MSB
|
|
15
|
+
* @this {SpessaSynthProcessor}
|
|
16
|
+
* @private
|
|
17
|
+
*/
|
|
18
|
+
export function dataEntryCoarse(channel, dataValue)
|
|
19
|
+
{
|
|
20
|
+
/**
|
|
21
|
+
* @type {WorkletProcessorChannel}
|
|
22
|
+
*/
|
|
23
|
+
const channelObject = this.workletProcessorChannels[channel];
|
|
24
|
+
let addDefaultVibrato = () =>
|
|
25
|
+
{
|
|
26
|
+
if(channelObject.channelVibrato.delay === 0 && channelObject.channelVibrato.rate === 0 && channelObject.channelVibrato.depth === 0)
|
|
27
|
+
{
|
|
28
|
+
channelObject.channelVibrato.depth = 50;
|
|
29
|
+
channelObject.channelVibrato.rate = 8;
|
|
30
|
+
channelObject.channelVibrato.delay = 0.6;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
switch(channelObject.dataEntryState)
|
|
34
|
+
{
|
|
35
|
+
default:
|
|
36
|
+
case dataEntryStates.Idle:
|
|
37
|
+
break;
|
|
38
|
+
|
|
39
|
+
// https://cdn.roland.com/assets/media/pdf/SC-88PRO_OM.pdf
|
|
40
|
+
// http://hummer.stanford.edu/sig/doc/classes/MidiOutput/rpn.html
|
|
41
|
+
case dataEntryStates.NRPFine:
|
|
42
|
+
switch(channelObject.NRPCoarse)
|
|
43
|
+
{
|
|
44
|
+
default:
|
|
45
|
+
if(dataValue === 64)
|
|
46
|
+
{
|
|
47
|
+
// default value
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
SpessaSynthWarn(
|
|
51
|
+
`%cUnrecognized NRPN for %c${channel}%c: %c(0x${channelObject.NRPCoarse.toString(16).toUpperCase()} 0x${channelObject.NRPFine.toString(16).toUpperCase()})%c data value: %c${dataValue}`,
|
|
52
|
+
consoleColors.warn,
|
|
53
|
+
consoleColors.recognized,
|
|
54
|
+
consoleColors.warn,
|
|
55
|
+
consoleColors.unrecognized,
|
|
56
|
+
consoleColors.warn,
|
|
57
|
+
consoleColors.value);
|
|
58
|
+
break;
|
|
59
|
+
|
|
60
|
+
case 0x01:
|
|
61
|
+
switch(channelObject.NRPFine)
|
|
62
|
+
{
|
|
63
|
+
default:
|
|
64
|
+
if(dataValue === 64)
|
|
65
|
+
{
|
|
66
|
+
// default value
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
SpessaSynthWarn(
|
|
70
|
+
`%cUnrecognized NRPN for %c${channel}%c: %c(0x${channelObject.NRPCoarse.toString(16)} 0x${channelObject.NRPFine.toString(16)})%c data value: %c${dataValue}`,
|
|
71
|
+
consoleColors.warn,
|
|
72
|
+
consoleColors.recognized,
|
|
73
|
+
consoleColors.warn,
|
|
74
|
+
consoleColors.unrecognized,
|
|
75
|
+
consoleColors.warn,
|
|
76
|
+
consoleColors.value);
|
|
77
|
+
break;
|
|
78
|
+
|
|
79
|
+
// vibrato rate
|
|
80
|
+
case 0x08:
|
|
81
|
+
if(channelObject.lockVibrato)
|
|
82
|
+
{
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
if(dataValue === 64)
|
|
86
|
+
{
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
addDefaultVibrato();
|
|
90
|
+
channelObject.channelVibrato.rate = (dataValue / 64) * 8;
|
|
91
|
+
SpessaSynthInfo(`%cVibrato rate for channel %c${channel}%c is now set to %c${channelObject.channelVibrato.rate}%cHz.`,
|
|
92
|
+
consoleColors.info,
|
|
93
|
+
consoleColors.recognized,
|
|
94
|
+
consoleColors.info,
|
|
95
|
+
consoleColors.value,
|
|
96
|
+
consoleColors.info);
|
|
97
|
+
break;
|
|
98
|
+
|
|
99
|
+
// vibrato depth
|
|
100
|
+
case 0x09:
|
|
101
|
+
if(channelObject.lockVibrato)
|
|
102
|
+
{
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if(dataValue === 64)
|
|
106
|
+
{
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
addDefaultVibrato();
|
|
110
|
+
channelObject.channelVibrato.depth = dataValue / 2;
|
|
111
|
+
SpessaSynthInfo(`%cVibrato depth for %c${channel}%c is now set to %c${channelObject.channelVibrato.depth}%c cents range of detune.`,
|
|
112
|
+
consoleColors.info,
|
|
113
|
+
consoleColors.recognized,
|
|
114
|
+
consoleColors.info,
|
|
115
|
+
consoleColors.value,
|
|
116
|
+
consoleColors.info);
|
|
117
|
+
break;
|
|
118
|
+
|
|
119
|
+
// vibrato delay
|
|
120
|
+
case 0x0A:
|
|
121
|
+
if(channelObject.lockVibrato)
|
|
122
|
+
{
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
if(dataValue === 64)
|
|
126
|
+
{
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
addDefaultVibrato();
|
|
130
|
+
channelObject.channelVibrato.delay = (dataValue / 64) / 3;
|
|
131
|
+
SpessaSynthInfo(`%cVibrato delay for %c${channel}%c is now set to %c${channelObject.channelVibrato.delay}%c seconds.`,
|
|
132
|
+
consoleColors.info,
|
|
133
|
+
consoleColors.recognized,
|
|
134
|
+
consoleColors.info,
|
|
135
|
+
consoleColors.value,
|
|
136
|
+
consoleColors.info);
|
|
137
|
+
break;
|
|
138
|
+
|
|
139
|
+
// filter cutoff
|
|
140
|
+
case 0x20:
|
|
141
|
+
// affect the "brightness" controller as we have a default modulator that controls it
|
|
142
|
+
const ccValue = dataValue;
|
|
143
|
+
this.controllerChange(channel, midiControllers.brightness, dataValue)
|
|
144
|
+
SpessaSynthInfo(`%cFilter cutoff for %c${channel}%c is now set to %c${ccValue}`,
|
|
145
|
+
consoleColors.info,
|
|
146
|
+
consoleColors.recognized,
|
|
147
|
+
consoleColors.info,
|
|
148
|
+
consoleColors.value);
|
|
149
|
+
}
|
|
150
|
+
break;
|
|
151
|
+
|
|
152
|
+
// drum reverb
|
|
153
|
+
case 0x1D:
|
|
154
|
+
if(!channelObject.drumChannel)
|
|
155
|
+
{
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
const reverb = dataValue;
|
|
159
|
+
this.controllerChange(channel, midiControllers.effects1Depth, reverb);
|
|
160
|
+
SpessaSynthInfo(
|
|
161
|
+
`%cGS Drum reverb for %c${channel}%c: %c${reverb}`,
|
|
162
|
+
consoleColors.info,
|
|
163
|
+
consoleColors.recognized,
|
|
164
|
+
consoleColors.info,
|
|
165
|
+
consoleColors.value);
|
|
166
|
+
break;
|
|
167
|
+
}
|
|
168
|
+
break;
|
|
169
|
+
|
|
170
|
+
case dataEntryStates.RPCoarse:
|
|
171
|
+
case dataEntryStates.RPFine:
|
|
172
|
+
switch(channelObject.RPValue)
|
|
173
|
+
{
|
|
174
|
+
default:
|
|
175
|
+
SpessaSynthWarn(
|
|
176
|
+
`%cUnrecognized RPN for %c${channel}%c: %c(0x${channelObject.RPValue.toString(16)})%c data value: %c${dataValue}`,
|
|
177
|
+
consoleColors.warn,
|
|
178
|
+
consoleColors.recognized,
|
|
179
|
+
consoleColors.warn,
|
|
180
|
+
consoleColors.unrecognized,
|
|
181
|
+
consoleColors.warn,
|
|
182
|
+
consoleColors.value);
|
|
183
|
+
break;
|
|
184
|
+
|
|
185
|
+
// pitch bend range
|
|
186
|
+
case 0x0000:
|
|
187
|
+
channelObject.midiControllers[NON_CC_INDEX_OFFSET + modulatorSources.pitchWheelRange] = dataValue << 7;
|
|
188
|
+
SpessaSynthInfo(`%cChannel ${channel} bend range. Semitones: %c${dataValue}`,
|
|
189
|
+
consoleColors.info,
|
|
190
|
+
consoleColors.value);
|
|
191
|
+
break;
|
|
192
|
+
|
|
193
|
+
// coarse tuning
|
|
194
|
+
case 0x0002:
|
|
195
|
+
// semitones
|
|
196
|
+
this.setChannelTuningSemitones(channel, dataValue - 64);
|
|
197
|
+
break;
|
|
198
|
+
|
|
199
|
+
// fine tuning
|
|
200
|
+
case 0x0001:
|
|
201
|
+
// note: this will not work properly unless the lsb is sent!
|
|
202
|
+
// here we store the raw value to then adjust in fine
|
|
203
|
+
this.setChannelTuning(channel, (dataValue - 64), false);
|
|
204
|
+
break;
|
|
205
|
+
|
|
206
|
+
// modulation depth
|
|
207
|
+
case 0x0005:
|
|
208
|
+
this.setModulationDepth(channel, dataValue * 100);
|
|
209
|
+
break
|
|
210
|
+
|
|
211
|
+
case 0x3FFF:
|
|
212
|
+
this.resetParameters(channel);
|
|
213
|
+
break;
|
|
214
|
+
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Executes a data entry for an RPN tuning
|
|
222
|
+
* @param channel {number}
|
|
223
|
+
* @param dataValue {number} dataEntry LSB
|
|
224
|
+
* @this {SpessaSynthProcessor}
|
|
225
|
+
* @private
|
|
226
|
+
*/
|
|
227
|
+
export function dataEntryFine(channel, dataValue)
|
|
228
|
+
{
|
|
229
|
+
const channelObject = this.workletProcessorChannels[channel];
|
|
230
|
+
switch (channelObject.dataEntryState)
|
|
231
|
+
{
|
|
232
|
+
default:
|
|
233
|
+
break;
|
|
234
|
+
|
|
235
|
+
case dataEntryStates.RPCoarse:
|
|
236
|
+
case dataEntryStates.RPFine:
|
|
237
|
+
switch(channelObject.RPValue)
|
|
238
|
+
{
|
|
239
|
+
default:
|
|
240
|
+
break;
|
|
241
|
+
|
|
242
|
+
// pitch bend range fine tune
|
|
243
|
+
case 0x0000:
|
|
244
|
+
if(dataValue === 0)
|
|
245
|
+
{
|
|
246
|
+
break;
|
|
247
|
+
}
|
|
248
|
+
channelObject.midiControllers[NON_CC_INDEX_OFFSET + modulatorSources.pitchWheelRange] |= dataValue; // 14 bit value so upper 7 are coarse and lower 7 are fine!
|
|
249
|
+
const actualTune = (channelObject.midiControllers[NON_CC_INDEX_OFFSET + modulatorSources.pitchWheelRange] >> 7) + dataValue / 127;
|
|
250
|
+
SpessaSynthInfo(`%cChannel ${channel} bend range. Semitones: %c${actualTune}`,
|
|
251
|
+
consoleColors.info,
|
|
252
|
+
consoleColors.value);
|
|
253
|
+
break;
|
|
254
|
+
|
|
255
|
+
// fine tuning
|
|
256
|
+
case 0x0001:
|
|
257
|
+
// grab the data and shift
|
|
258
|
+
const coarse = channelObject.customControllers[customControllers.channelTuning];
|
|
259
|
+
const finalTuning = (coarse << 7) | dataValue;
|
|
260
|
+
this.setChannelTuning(channel, finalTuning * 0.01220703125); // multiply by 8192 / 100 (cent increment)
|
|
261
|
+
break;
|
|
262
|
+
|
|
263
|
+
// modulation depth
|
|
264
|
+
case 0x0005:
|
|
265
|
+
const currentModulationDepthCents = channelObject.customControllers[customControllers.modulationMultiplier] * 50;
|
|
266
|
+
let cents = currentModulationDepthCents + (dataValue / 128) * 100;
|
|
267
|
+
this.setModulationDepth(channel, cents);
|
|
268
|
+
break
|
|
269
|
+
|
|
270
|
+
case 0x3FFF:
|
|
271
|
+
this.resetParameters(channel);
|
|
272
|
+
break;
|
|
273
|
+
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
}
|
|
277
|
+
}
|