spessasynth_lib 3.14.1 → 3.14.2
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 +1 -1
- package/synthetizer/worklet_processor.min.js +6 -6
- package/synthetizer/worklet_system/main_processor.js +10 -0
- package/synthetizer/worklet_system/worklet_methods/note_on.js +8 -3
- package/synthetizer/worklet_system/worklet_methods/reset_controllers.js +7 -0
- package/synthetizer/worklet_system/worklet_methods/system_exclusive.js +35 -27
- package/synthetizer/worklet_system/worklet_methods/voice_control.js +10 -1
|
@@ -57,6 +57,16 @@ export class SpessaSynthProcessor extends AudioWorkletProcessor
|
|
|
57
57
|
|
|
58
58
|
this.transposition = 0;
|
|
59
59
|
|
|
60
|
+
/**
|
|
61
|
+
* this.tunings[program][key] = tuning
|
|
62
|
+
* @type {MTSProgramTuning[]}
|
|
63
|
+
*/
|
|
64
|
+
this.tunings = [];
|
|
65
|
+
for (let i = 0; i < 127; i++)
|
|
66
|
+
{
|
|
67
|
+
this.tunings.push([]);
|
|
68
|
+
}
|
|
69
|
+
|
|
60
70
|
/**
|
|
61
71
|
* Bank offset for things like embedded RMIDIS. Added for every program change
|
|
62
72
|
* @type {number}
|
|
@@ -31,17 +31,22 @@ export function noteOn(channel, midiNote, velocity, enableDebugging = false, sen
|
|
|
31
31
|
return;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
midiNote
|
|
34
|
+
let sentMidiNote = midiNote + channelObject.channelTransposeKeyShift;
|
|
35
35
|
|
|
36
36
|
if(midiNote > 127 || midiNote < 0)
|
|
37
37
|
{
|
|
38
38
|
return;
|
|
39
39
|
}
|
|
40
|
+
const program = channelObject.preset.program;
|
|
41
|
+
if(this.tunings[program][midiNote]?.midiNote >= 0)
|
|
42
|
+
{
|
|
43
|
+
sentMidiNote = this.tunings[program][midiNote].midiNote;
|
|
44
|
+
}
|
|
40
45
|
|
|
41
46
|
// get voices
|
|
42
47
|
const voices = getWorkletVoices(
|
|
43
48
|
channel,
|
|
44
|
-
|
|
49
|
+
sentMidiNote,
|
|
45
50
|
velocity,
|
|
46
51
|
channelObject.preset,
|
|
47
52
|
startTime,
|
|
@@ -89,7 +94,7 @@ export function noteOn(channel, midiNote, velocity, enableDebugging = false, sen
|
|
|
89
94
|
{
|
|
90
95
|
this.sendChannelProperties();
|
|
91
96
|
this.callEvent("noteon", {
|
|
92
|
-
midiNote: midiNote
|
|
97
|
+
midiNote: midiNote,
|
|
93
98
|
channel: channel,
|
|
94
99
|
velocity: velocity,
|
|
95
100
|
});
|
|
@@ -100,6 +100,13 @@ export function resetAllControllers()
|
|
|
100
100
|
})
|
|
101
101
|
}
|
|
102
102
|
}
|
|
103
|
+
this.tunings = [];
|
|
104
|
+
this.tunings = [];
|
|
105
|
+
for (let i = 0; i < 127; i++)
|
|
106
|
+
{
|
|
107
|
+
this.tunings.push([]);
|
|
108
|
+
}
|
|
109
|
+
|
|
103
110
|
this.setMIDIVolume(1);
|
|
104
111
|
this.system = DEFAULT_SYNTH_MODE;
|
|
105
112
|
}
|
|
@@ -3,34 +3,37 @@ import { SpessaSynthInfo, SpessaSynthWarn } from '../../../utils/loggin.js'
|
|
|
3
3
|
import { midiControllers } from '../../../midi_parser/midi_message.js'
|
|
4
4
|
import { ALL_CHANNELS_OR_DIFFERENT_ACTION } from '../message_protocol/worklet_message.js'
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* KeyNum: tuning
|
|
8
|
+
* @typedef {MTSNoteTuning[]} MTSProgramTuning
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @typedef {Object} MTSNoteTuning
|
|
13
|
+
* @property {number} midiNote - the base midi note to use, -1 means no change
|
|
14
|
+
* @property {number} centTuning - additional tuning
|
|
15
|
+
*/
|
|
6
16
|
|
|
7
17
|
/**
|
|
8
18
|
* Calculates freqency for MIDI Tuning Standard
|
|
9
19
|
* @param byte1 {number}
|
|
10
20
|
* @param byte2 {number}
|
|
11
21
|
* @param byte3 {number}
|
|
12
|
-
* @return {number|null}
|
|
22
|
+
* @return {{midiNote: number, centTuning: number|null}}
|
|
13
23
|
*/
|
|
14
|
-
function
|
|
24
|
+
function getTuning(byte1, byte2, byte3)
|
|
15
25
|
{
|
|
16
|
-
|
|
26
|
+
const midiNote = byte1;
|
|
27
|
+
const fraction = (byte2 << 7) | byte3; // Combine byte2 and byte3 into a 14-bit number
|
|
28
|
+
|
|
29
|
+
// no change
|
|
17
30
|
if (byte1 === 0x7F && byte2 === 0x7F && byte3 === 0x7F)
|
|
18
31
|
{
|
|
19
|
-
return null;
|
|
32
|
+
return { midiNote: -1, centTuning: null };
|
|
20
33
|
}
|
|
21
34
|
|
|
22
|
-
//
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const semitone = byte1;
|
|
26
|
-
|
|
27
|
-
// combine byte2 and byte3
|
|
28
|
-
const fraction = (byte2 << 7) | byte3;
|
|
29
|
-
|
|
30
|
-
// get total cents
|
|
31
|
-
const totalCents = semitone * 100 + (fraction / 16384) * 100;
|
|
32
|
-
|
|
33
|
-
return baseFrequency * Math.pow(2, totalCents / 1200);
|
|
35
|
+
// calculate cent tuning
|
|
36
|
+
return { midiNote: midiNote, centTuning: fraction * 0.0061 };
|
|
34
37
|
}
|
|
35
38
|
|
|
36
39
|
|
|
@@ -144,26 +147,32 @@ export function systemExclusive(messageData, channelOffset = 0)
|
|
|
144
147
|
switch(messageData[3])
|
|
145
148
|
{
|
|
146
149
|
// single note change
|
|
150
|
+
// single note change bank
|
|
147
151
|
case 0x02:
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
let currentMessageIndex = 6
|
|
152
|
-
for (let i = 0; i < numberOfChanges; i++)
|
|
152
|
+
case 0x07:
|
|
153
|
+
let currentMessageIndex = 4;
|
|
154
|
+
if(messageData[3] === 0x07)
|
|
153
155
|
{
|
|
154
|
-
|
|
156
|
+
// skip bank
|
|
155
157
|
currentMessageIndex++;
|
|
156
158
|
}
|
|
157
|
-
|
|
159
|
+
// get program and number of cahnges
|
|
160
|
+
const tuningProgram = messageData[currentMessageIndex++];
|
|
161
|
+
const numberOfChanges = messageData[currentMessageIndex++];
|
|
158
162
|
for (let i = 0; i < numberOfChanges; i++)
|
|
159
163
|
{
|
|
160
|
-
|
|
164
|
+
// set the given tuning to the program
|
|
165
|
+
this.tunings[tuningProgram][messageData[currentMessageIndex++]] = getTuning(
|
|
161
166
|
messageData[currentMessageIndex++],
|
|
162
167
|
messageData[currentMessageIndex++],
|
|
163
168
|
messageData[currentMessageIndex++],
|
|
164
|
-
)
|
|
169
|
+
);
|
|
165
170
|
}
|
|
166
|
-
|
|
171
|
+
SpessaSynthInfo(`%cSingle Note Tuning. Program: %c${tuningProgram}%c Keys affected: ${numberOfChanges}`,
|
|
172
|
+
consoleColors.info,
|
|
173
|
+
consoleColors.recognized,
|
|
174
|
+
consoleColors.info,
|
|
175
|
+
consoleColors.recognized)
|
|
167
176
|
break;
|
|
168
177
|
|
|
169
178
|
// octave tuning (1 byte)
|
|
@@ -250,7 +259,6 @@ export function systemExclusive(messageData, channelOffset = 0)
|
|
|
250
259
|
// http://www.bandtrax.com.au/sysex.htm
|
|
251
260
|
// https://cdn.roland.com/assets/media/pdf/AT-20R_30R_MI.pdf
|
|
252
261
|
case 0x41:
|
|
253
|
-
// messagedata[1] is device id (ignore as we're everything >:) )
|
|
254
262
|
if(messageData[2] === 0x42 && messageData[3] === 0x12)
|
|
255
263
|
{
|
|
256
264
|
// this is a GS sysex
|
|
@@ -55,6 +55,7 @@ export function renderVoice(
|
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
// TUNING
|
|
58
|
+
let targetKey = voice.targetKey;
|
|
58
59
|
|
|
59
60
|
// calculate tuning
|
|
60
61
|
let cents = voice.modulatedGenerators[generatorTypes.fineTune]
|
|
@@ -65,8 +66,16 @@ export function renderVoice(
|
|
|
65
66
|
let semitones = voice.modulatedGenerators[generatorTypes.coarseTune]
|
|
66
67
|
+ channel.customControllers[customControllers.channelTuningSemitones];
|
|
67
68
|
|
|
69
|
+
// midi tuning standard
|
|
70
|
+
const tuning = this.tunings[channel.preset.program][targetKey];
|
|
71
|
+
if(tuning?.midiNote >= 0)
|
|
72
|
+
{
|
|
73
|
+
targetKey = tuning.midiNote;
|
|
74
|
+
cents += tuning.centTuning;
|
|
75
|
+
}
|
|
76
|
+
|
|
68
77
|
// calculate tuning by key
|
|
69
|
-
cents += (
|
|
78
|
+
cents += (targetKey - voice.sample.rootKey) * voice.modulatedGenerators[generatorTypes.scaleTuning];
|
|
70
79
|
|
|
71
80
|
// vibrato LFO
|
|
72
81
|
const vibratoDepth = voice.modulatedGenerators[generatorTypes.vibLfoToPitch];
|