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.
Files changed (154) hide show
  1. package/.idea/modules.xml +8 -0
  2. package/.idea/spessasynth_lib.iml +12 -0
  3. package/.idea/vcs.xml +6 -0
  4. package/copy_version.sh +38 -0
  5. package/index.js +73 -0
  6. package/package/@types/externals/stbvorbis_sync/stbvorbis_sync.min.d.ts +1 -0
  7. package/package/@types/index.d.ts +34 -0
  8. package/package/@types/midi_handler/midi_handler.d.ts +39 -0
  9. package/package/@types/midi_handler/web_midi_link.d.ts +12 -0
  10. package/package/@types/midi_parser/midi_data.d.ts +95 -0
  11. package/package/@types/midi_parser/midi_editor.d.ts +45 -0
  12. package/package/@types/midi_parser/midi_loader.d.ts +100 -0
  13. package/package/@types/midi_parser/midi_message.d.ts +154 -0
  14. package/package/@types/midi_parser/midi_writer.d.ts +6 -0
  15. package/package/@types/midi_parser/rmidi_writer.d.ts +9 -0
  16. package/package/@types/midi_parser/used_keys_loaded.d.ts +7 -0
  17. package/package/@types/sequencer/sequencer.d.ts +180 -0
  18. package/package/@types/sequencer/worklet_sequencer/sequencer_message.d.ts +28 -0
  19. package/package/@types/soundfont/read/generators.d.ts +98 -0
  20. package/package/@types/soundfont/read/instruments.d.ts +50 -0
  21. package/package/@types/soundfont/read/modulators.d.ts +73 -0
  22. package/package/@types/soundfont/read/presets.d.ts +87 -0
  23. package/package/@types/soundfont/read/riff_chunk.d.ts +31 -0
  24. package/package/@types/soundfont/read/samples.d.ts +134 -0
  25. package/package/@types/soundfont/read/zones.d.ts +141 -0
  26. package/package/@types/soundfont/soundfont.d.ts +76 -0
  27. package/package/@types/soundfont/write/ibag.d.ts +6 -0
  28. package/package/@types/soundfont/write/igen.d.ts +6 -0
  29. package/package/@types/soundfont/write/imod.d.ts +6 -0
  30. package/package/@types/soundfont/write/inst.d.ts +6 -0
  31. package/package/@types/soundfont/write/pbag.d.ts +6 -0
  32. package/package/@types/soundfont/write/pgen.d.ts +6 -0
  33. package/package/@types/soundfont/write/phdr.d.ts +6 -0
  34. package/package/@types/soundfont/write/pmod.d.ts +6 -0
  35. package/package/@types/soundfont/write/sdta.d.ts +11 -0
  36. package/package/@types/soundfont/write/shdr.d.ts +8 -0
  37. package/package/@types/soundfont/write/soundfont_trimmer.d.ts +6 -0
  38. package/package/@types/soundfont/write/write.d.ts +21 -0
  39. package/package/@types/synthetizer/audio_effects/effects_config.d.ts +29 -0
  40. package/package/@types/synthetizer/audio_effects/fancy_chorus.d.ts +93 -0
  41. package/package/@types/synthetizer/audio_effects/reverb.d.ts +7 -0
  42. package/package/@types/synthetizer/synth_event_handler.d.ts +161 -0
  43. package/package/@types/synthetizer/synthetizer.d.ts +294 -0
  44. package/package/@types/synthetizer/worklet_system/message_protocol/worklet_message.d.ts +89 -0
  45. package/package/@types/synthetizer/worklet_system/worklet_utilities/worklet_processor_channel.d.ts +134 -0
  46. package/package/@types/synthetizer/worklet_url.d.ts +5 -0
  47. package/package/@types/utils/buffer_to_wav.d.ts +8 -0
  48. package/package/@types/utils/byte_functions/big_endian.d.ts +13 -0
  49. package/package/@types/utils/byte_functions/little_endian.d.ts +35 -0
  50. package/package/@types/utils/byte_functions/string.d.ts +22 -0
  51. package/package/@types/utils/byte_functions/variable_length_quantity.d.ts +12 -0
  52. package/package/@types/utils/indexed_array.d.ts +21 -0
  53. package/package/@types/utils/loggin.d.ts +26 -0
  54. package/package/@types/utils/other.d.ts +32 -0
  55. package/package/LICENSE +26 -0
  56. package/package/README.md +84 -0
  57. package/package/externals/NOTICE +9 -0
  58. package/package/externals/libvorbis/@types/OggVorbisEncoder.d.ts +34 -0
  59. package/package/externals/libvorbis/OggVorbisEncoder.min.js +1 -0
  60. package/package/externals/stbvorbis_sync/@types/stbvorbis_sync.d.ts +12 -0
  61. package/package/externals/stbvorbis_sync/LICENSE +202 -0
  62. package/package/externals/stbvorbis_sync/stbvorbis_sync.min.js +1 -0
  63. package/package/index.js +73 -0
  64. package/package/midi_handler/README.md +3 -0
  65. package/package/midi_handler/midi_handler.js +118 -0
  66. package/package/midi_handler/web_midi_link.js +41 -0
  67. package/package/midi_parser/README.md +3 -0
  68. package/package/midi_parser/midi_data.js +121 -0
  69. package/package/midi_parser/midi_editor.js +557 -0
  70. package/package/midi_parser/midi_loader.js +502 -0
  71. package/package/midi_parser/midi_message.js +234 -0
  72. package/package/midi_parser/midi_writer.js +95 -0
  73. package/package/midi_parser/rmidi_writer.js +271 -0
  74. package/package/midi_parser/used_keys_loaded.js +172 -0
  75. package/package/package.json +43 -0
  76. package/package/sequencer/README.md +23 -0
  77. package/package/sequencer/sequencer.js +439 -0
  78. package/package/sequencer/worklet_sequencer/events.js +92 -0
  79. package/package/sequencer/worklet_sequencer/play.js +309 -0
  80. package/package/sequencer/worklet_sequencer/process_event.js +167 -0
  81. package/package/sequencer/worklet_sequencer/process_tick.js +85 -0
  82. package/package/sequencer/worklet_sequencer/sequencer_message.js +39 -0
  83. package/package/sequencer/worklet_sequencer/song_control.js +193 -0
  84. package/package/sequencer/worklet_sequencer/worklet_sequencer.js +218 -0
  85. package/package/soundfont/README.md +8 -0
  86. package/package/soundfont/read/generators.js +212 -0
  87. package/package/soundfont/read/instruments.js +125 -0
  88. package/package/soundfont/read/modulators.js +249 -0
  89. package/package/soundfont/read/presets.js +300 -0
  90. package/package/soundfont/read/riff_chunk.js +81 -0
  91. package/package/soundfont/read/samples.js +398 -0
  92. package/package/soundfont/read/zones.js +310 -0
  93. package/package/soundfont/soundfont.js +357 -0
  94. package/package/soundfont/write/ibag.js +39 -0
  95. package/package/soundfont/write/igen.js +75 -0
  96. package/package/soundfont/write/imod.js +46 -0
  97. package/package/soundfont/write/inst.js +34 -0
  98. package/package/soundfont/write/pbag.js +39 -0
  99. package/package/soundfont/write/pgen.js +77 -0
  100. package/package/soundfont/write/phdr.js +42 -0
  101. package/package/soundfont/write/pmod.js +46 -0
  102. package/package/soundfont/write/sdta.js +72 -0
  103. package/package/soundfont/write/shdr.js +54 -0
  104. package/package/soundfont/write/soundfont_trimmer.js +169 -0
  105. package/package/soundfont/write/write.js +180 -0
  106. package/package/synthetizer/README.md +6 -0
  107. package/package/synthetizer/audio_effects/effects_config.js +21 -0
  108. package/package/synthetizer/audio_effects/fancy_chorus.js +120 -0
  109. package/package/synthetizer/audio_effects/impulse_response_2.flac +0 -0
  110. package/package/synthetizer/audio_effects/reverb.js +24 -0
  111. package/package/synthetizer/synth_event_handler.js +156 -0
  112. package/package/synthetizer/synthetizer.js +766 -0
  113. package/package/synthetizer/worklet_processor.min.js +13 -0
  114. package/package/synthetizer/worklet_system/README.md +6 -0
  115. package/package/synthetizer/worklet_system/main_processor.js +363 -0
  116. package/package/synthetizer/worklet_system/message_protocol/handle_message.js +197 -0
  117. package/package/synthetizer/worklet_system/message_protocol/message_sending.js +74 -0
  118. package/package/synthetizer/worklet_system/message_protocol/worklet_message.js +121 -0
  119. package/package/synthetizer/worklet_system/minify_processor.sh +4 -0
  120. package/package/synthetizer/worklet_system/worklet_methods/controller_control.js +230 -0
  121. package/package/synthetizer/worklet_system/worklet_methods/data_entry.js +277 -0
  122. package/package/synthetizer/worklet_system/worklet_methods/note_off.js +109 -0
  123. package/package/synthetizer/worklet_system/worklet_methods/note_on.js +91 -0
  124. package/package/synthetizer/worklet_system/worklet_methods/program_control.js +183 -0
  125. package/package/synthetizer/worklet_system/worklet_methods/reset_controllers.js +177 -0
  126. package/package/synthetizer/worklet_system/worklet_methods/snapshot.js +129 -0
  127. package/package/synthetizer/worklet_system/worklet_methods/system_exclusive.js +272 -0
  128. package/package/synthetizer/worklet_system/worklet_methods/tuning_control.js +195 -0
  129. package/package/synthetizer/worklet_system/worklet_methods/vibrato_control.js +29 -0
  130. package/package/synthetizer/worklet_system/worklet_methods/voice_control.js +233 -0
  131. package/package/synthetizer/worklet_system/worklet_processor.js +9 -0
  132. package/package/synthetizer/worklet_system/worklet_utilities/lfo.js +23 -0
  133. package/package/synthetizer/worklet_system/worklet_utilities/lowpass_filter.js +130 -0
  134. package/package/synthetizer/worklet_system/worklet_utilities/modulation_envelope.js +73 -0
  135. package/package/synthetizer/worklet_system/worklet_utilities/modulator_curves.js +86 -0
  136. package/package/synthetizer/worklet_system/worklet_utilities/stereo_panner.js +81 -0
  137. package/package/synthetizer/worklet_system/worklet_utilities/unit_converter.js +66 -0
  138. package/package/synthetizer/worklet_system/worklet_utilities/volume_envelope.js +265 -0
  139. package/package/synthetizer/worklet_system/worklet_utilities/wavetable_oscillator.js +83 -0
  140. package/package/synthetizer/worklet_system/worklet_utilities/worklet_modulator.js +234 -0
  141. package/package/synthetizer/worklet_system/worklet_utilities/worklet_processor_channel.js +116 -0
  142. package/package/synthetizer/worklet_system/worklet_utilities/worklet_voice.js +272 -0
  143. package/package/synthetizer/worklet_url.js +5 -0
  144. package/package/utils/README.md +4 -0
  145. package/package/utils/buffer_to_wav.js +101 -0
  146. package/package/utils/byte_functions/big_endian.js +28 -0
  147. package/package/utils/byte_functions/little_endian.js +74 -0
  148. package/package/utils/byte_functions/string.js +97 -0
  149. package/package/utils/byte_functions/variable_length_quantity.js +37 -0
  150. package/package/utils/encode_vorbis.js +30 -0
  151. package/package/utils/indexed_array.js +41 -0
  152. package/package/utils/loggin.js +79 -0
  153. package/package/utils/other.js +54 -0
  154. 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,4 @@
1
+ #!/bin/bash
2
+ cd "$(dirname "$0")"
3
+ esbuild worklet_processor.js --bundle --minify --format=esm --outfile=../worklet_processor.min.js --platform=browser
4
+ echo "Processor minifed succesfully"
@@ -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
+ }