Tabula Sonora 0.1.0
A native C++20 implementation of the Roland Sound Canvas VA synth voice
Loading...
Searching...
No Matches
part.hpp
Go to the documentation of this file.
1#pragma once
2
9
10#include <algorithm>
11#include <array>
12#include <cstdlib>
13#include <vector>
14
15namespace ts {
16
26struct RxSwitches {
27 bool pitch_bend = true;
28 bool channel_pressure = true;
29 bool program_change = true;
31 bool control_change = true;
32 bool poly_pressure = true;
33 bool notes = true;
34 bool rpn = true;
35 bool nrpn = true;
36 bool modulation = true;
37 bool volume = true;
38 bool panpot = true;
39 bool expression = true;
40 bool hold = true;
41 bool portamento = true;
42 bool sostenuto = true;
43 bool soft = true;
45 bool bank_msb = true;
46 bool bank_lsb = true;
47};
48
58class Part {
59public:
60 Part() { recompute(); }
61
63 int program = 0;
65 int bank = 0;
68 int bank_lsb = 0;
69
74 int rx_channel = 0;
75
81 int modulation = 0;
83 int damper = 0;
89 int delay_send = 0;
93
95 int bend = 8192;
97 int bend_range = 2;
99 int fine_tune = 0x2000;
101 int coarse_tune = 0x40;
103 int key_shift = 0x40;
104
110
113
125 int cc1_number = 16;
126 int cc2_number = 17;
127 int cc1 = 0;
128 int cc2 = 0;
129
142 int xg_bank_msb = -1;
143
155 std::array<std::uint8_t, 128> poly_pressure{};
156
158 [[nodiscard]] int key_pressure(int note) const noexcept
159 {
160 return note >= 0 && note < 128 ? poly_pressure[static_cast<std::size_t>(note)] : 0;
161 }
162
168 int vibrato_rate = 0x40;
169 int vibrato_depth = 0x40;
170 int vibrato_delay = 0x40;
171 int tvf_cutoff = 0x40;
172 int env_attack = 0x40;
173 int env_decay = 0x40;
174 int env_release = 0x40;
175
181 int tvf_resonance = 0x40;
182
184 [[nodiscard]] PartModifiers modifiers() const noexcept
185 {
191 env_decay,
193 }
194
202 [[nodiscard]] int effective_velocity(int velocity) const noexcept
203 {
204 int value = velocity;
205 if (velocity_depth == 0) {
206 value = 1;
207 } else if (velocity_depth != 0x40) {
208 value = (value * velocity_depth) >> 6;
209 }
210
211 value += (velocity_offset - 0x40) * 2;
212 if (value < 0) {
213 return 1;
214 }
215 return value > 0x7F ? 0x7F : value;
216 }
217
219 std::array<int, 12> scale_tuning{
220 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40};
221
224 int rhythm = -1;
225
227 int key_low = 0;
228 int key_high = 0x7F;
229
239 bool eq_enabled = false;
240
247 bool efx_enabled = false;
248
250 int velocity_depth = 0x40;
251 int velocity_offset = 0x40;
252
256
257 int rpn_msb = 0x7F;
258 int rpn_lsb = 0x7F;
259 int nrpn_msb = 0x7F;
261 int nrpn_lsb = 0x7F;
262
267 bool data_entry_is_nrpn = false;
268
271
276 std::vector<int> sustained;
277
281 bool portamento_on = false;
283 bool mono = false;
284
287 bool soft = false;
288
294
296 int last_key = -1;
297
299 bool sostenuto_down = false;
300
302 std::vector<int> sostenuto_captured;
303
305 std::vector<int> sostenuto_released;
306
308 [[nodiscard]] bool damper_down() const noexcept { return damper >= 0x40; }
309
311 [[nodiscard]] int volume() const noexcept { return volume_; }
312
313 void set_volume(int value) noexcept
314 {
315 volume_ = value;
316 recompute();
317 }
318
320 [[nodiscard]] int expression() const noexcept { return expression_; }
321
322 void set_expression(int value) noexcept
323 {
324 expression_ = value;
325 recompute();
326 }
327
329 [[nodiscard]] int master() const noexcept { return master_; }
330
331 void set_master(int value) noexcept
332 {
333 master_ = value;
334 recompute();
335 }
336
338 [[nodiscard]] double volume_scale() const noexcept { return volume_scale_; }
339
357 [[nodiscard]] ControlMatrix::Modulation matrix(int key_pressure = 0) const noexcept
358 {
363 sums += control.applied_linear(ControlMatrix::Source::cc1, cc1);
364 sums += control.applied_linear(ControlMatrix::Source::cc2, cc2);
365 sums += control.applied_bipolar(
367 return ControlMatrix::scaled(sums);
368 }
369
375 [[nodiscard]] double matrix_pitch_milli_semitones(int key_pressure = 0) const noexcept
376 {
377 return static_cast<double>(matrix(key_pressure).pitch);
378 }
379
385 [[nodiscard]] double bend_milli_semitones() const noexcept
386 {
388 }
389
395 [[nodiscard]] double tune_milli_semitones() const noexcept
396 {
397 return ((key_shift - 0x40) * 1000.0) + ((coarse_tune - 0x40) * 1000.0)
398 + ((fine_tune - 0x2000) * 1000.0 / 0x2000);
399 }
400
403 [[nodiscard]] double scale_offset_milli_semitones(int key) const noexcept
404 {
405 return (scale_tuning[static_cast<std::size_t>(((key % 12) + 12) % 12)] - 0x40) * 10.0;
406 }
407
409 [[nodiscard]] bool rpn_is_null() const noexcept { return rpn_msb == 0x7F && rpn_lsb == 0x7F; }
410
412 void reset();
413
423 {
425 bend = 8192;
426 damper = 0;
427 soft = false;
428 modulation = 0;
430 poly_pressure.fill(0);
431 portamento_on = false;
433 rpn_msb = 0x7F;
434 rpn_lsb = 0x7F;
435 nrpn_msb = 0x7F;
436 nrpn_lsb = 0x7F;
437 data_entry_is_nrpn = false;
438 }
439
440private:
441 void recompute() noexcept
442 {
443 volume_scale_ = TvaChain::part_volume_scale(volume_, expression_, master_);
444 }
445
447 int expression_ = sequence_builder::default_expression;
449 double volume_scale_ = 1.0;
450};
451
452} // namespace ts
Per-part coarse-pitch and panpot overrides, set by NRPN.
Definition sequence.hpp:50
bool mono
Whether CC#126 mono mode is on, which flushes the part's voices at each note-on.
Definition part.hpp:283
int delay_send
Part delay send, which has no Control Change and arrives only over SysEx.
Definition part.hpp:89
int env_release
Definition part.hpp:174
void reset_controllers()
Applies CC#121, which resets the controllers a reset message covers.
Definition part.hpp:422
int program
Program in force, from the last program change.
Definition part.hpp:63
int last_key
The key the part last sounded, which portamento glides from, or -1.
Definition part.hpp:296
int cc2
Definition part.hpp:128
int nrpn_msb
Definition part.hpp:259
int velocity_depth
GS velocity sense depth and offset (40 1x 1A/1B), applied by effective_velocity.
Definition part.hpp:250
int modulation
CC#1 modulation.
Definition part.hpp:81
double volume_scale() const noexcept
The combined volume multiplier, 1.0 with everything at 127.
Definition part.hpp:338
int bend_range
Bend range in semitones, from RPN 00/00.
Definition part.hpp:97
int rhythm
GS use-for-rhythm (40 1x 15): -1 follows the channel default, 0 forces melodic, 1 or 2 route the part...
Definition part.hpp:224
bool efx_enabled
Whether this part feeds the insertion EFX block (40 4x 22), which is part+0x452.
Definition part.hpp:247
int env_decay
Definition part.hpp:173
int vibrato_delay
Definition part.hpp:170
int expression() const noexcept
CC#11 expression.
Definition part.hpp:320
std::vector< int > sostenuto_captured
Notes the sostenuto pedal captured — the ones sounding when it went down.
Definition part.hpp:302
int fine_tune
RPN 00/01 fine tune, as the full 14-bit value with 0x2000 centred; +-100 cents at the ends.
Definition part.hpp:99
int channel_pressure
Channel aftertouch, which reaches pitch through the control matrix.
Definition part.hpp:112
void reset()
Returns the part to its power-on state.
int bank
Bank select MSB, which carries the variation.
Definition part.hpp:65
int volume() const noexcept
CC#7 volume.
Definition part.hpp:311
int bend
CC#94 delay send.
Definition part.hpp:95
int env_attack
Definition part.hpp:172
bool damper_down() const noexcept
Whether the damper is holding notes on.
Definition part.hpp:308
int tvf_cutoff
Definition part.hpp:171
bool sostenuto_down
Whether the sostenuto pedal is down. CC#66 is binary — bit 6 only, as the engine reads it.
Definition part.hpp:299
RxSwitches rx
The GS receive switches, written by SysEx 40 1x 03-12 and 23/24.
Definition part.hpp:77
ControlMatrix control
The controller assignment matrix (40 2x), which decides what each source modulates.
Definition part.hpp:109
int reverb_send
CC#91 reverb send.
Definition part.hpp:85
std::array< int, 12 > scale_tuning
GS scale tuning (40 1x 40-4B), one entry per pitch class, 0x40 centred, a cent a step.
Definition part.hpp:219
int rpn_msb
Definition part.hpp:257
int xg_bank_msb
Bank select MSB while the engine is in XG mode, where it means something else entirely.
Definition part.hpp:142
int damper
CC#64 damper.
Definition part.hpp:83
int tvf_resonance
The eighth: CC#71 / NRPN 01 21 / 40 1x 33, and the one that goes nowhere.
Definition part.hpp:181
double scale_offset_milli_semitones(int key) const noexcept
The scale-tuning offset for a key, in milli-semitones: a cent a step, ten milli-semitones a cent.
Definition part.hpp:403
void set_master(int value) noexcept
Definition part.hpp:331
int key_high
Definition part.hpp:228
int rx_channel
The MIDI channel this part listens on, 0-15, or 16 for off.
Definition part.hpp:74
int vibrato_rate
The GS part modify offsets, 0x40 centred.
Definition part.hpp:168
int nrpn_lsb
The selected NRPN's LSB, which for the drum parameters is the key number.
Definition part.hpp:261
double tune_milli_semitones() const noexcept
The part's static tune in milli-semitones: the net of RPN coarse and fine tune and the GS part key sh...
Definition part.hpp:395
DrumKeyOverrides drum_keys
Per-drum-key overrides this part has taken from NRPN.
Definition part.hpp:270
int rpn_lsb
Definition part.hpp:258
int coarse_tune
RPN 00/02 coarse tune, 0x40 centred, one semitone a step.
Definition part.hpp:101
int cc2_number
Definition part.hpp:126
int cc1_number
Which Control Change numbers this part's two assignable sources listen to (40 1x 1F and 20),...
Definition part.hpp:125
Part()
Definition part.hpp:60
PartModifiers modifiers() const noexcept
The seven live modify offsets, in the form the synthesis chains take.
Definition part.hpp:184
bool soft
Whether the CC#67 soft pedal is down.
Definition part.hpp:287
void set_volume(int value) noexcept
Definition part.hpp:313
int velocity_offset
Definition part.hpp:251
bool eq_enabled
Whether this part runs through the four-band EQ (40 4x 20), which is part+0x450.
Definition part.hpp:239
bool data_entry_is_nrpn
Whether a CC#6 data entry commits to the selected NRPN rather than the selected RPN.
Definition part.hpp:267
int vibrato_depth
Definition part.hpp:169
int bank_lsb
Bank select LSB, which on this module selects the tone map: 1-4 name a vintage and 0 keeps the config...
Definition part.hpp:68
int effective_velocity(int velocity) const noexcept
The velocity a note-on actually sounds at, after this part's velocity sense (40 1x 1A depth and 40 1x...
Definition part.hpp:202
double matrix_pitch_milli_semitones(int key_pressure=0) const noexcept
The control matrix's contribution to pitch alone, in milli-semitones.
Definition part.hpp:375
int portamento_time
CC#5 portamento time; indexes the glide-step table.
Definition part.hpp:279
int chorus_send
CC#93 chorus send.
Definition part.hpp:87
int key_pressure(int note) const noexcept
The pressure on one key, or zero for anything out of range.
Definition part.hpp:158
int cc1
Definition part.hpp:127
std::vector< int > sostenuto_released
Captured notes whose note-off arrived while the pedal held them.
Definition part.hpp:305
int key_low
GS keyboard range (40 1x 1D/1E); note-ons outside it are ignored on melodic parts.
Definition part.hpp:227
std::array< std::uint8_t, 128 > poly_pressure
Polyphonic aftertouch, per key.
Definition part.hpp:155
int master() const noexcept
Master volume, which is global but folds into the same law.
Definition part.hpp:329
bool portamento_on
Whether CC#65 portamento is on.
Definition part.hpp:281
std::vector< int > sustained
Notes whose release is waiting for the damper to lift.
Definition part.hpp:276
double bend_milli_semitones() const noexcept
The bend offset in milli-semitones.
Definition part.hpp:385
int key_shift
GS part key shift (40 1x 16), 0x40 centred; the engine clamps it to 0x28-0x58.
Definition part.hpp:103
int portamento_control_key
CC#84 portamento control: the key the next note glides from, or -1 when unset.
Definition part.hpp:293
bool rpn_is_null() const noexcept
Whether the RPN address currently selected is the null set (7F/7F), which parks data entry.
Definition part.hpp:409
void set_expression(int value) noexcept
Definition part.hpp:322
int pitch_offset_fine
GS pitch offset fine (40 1x 17), stored raw: the unit is Hz on the module, which nothing downstream c...
Definition part.hpp:255
int pan
CC#10 pan.
Definition part.hpp:79
ControlMatrix::Modulation matrix(int key_pressure=0) const noexcept
Everything the control matrix modulates, each destination in its own unit.
Definition part.hpp:357
static double bend_offset_milli_semitones(int bend=8192, double semitone_range=2.0) noexcept
Pitch-bend offset in milli-semitones, from a 14-bit bend with 8192 centre.
static double part_volume_scale(int volume=127, int expression=127, int master=127) noexcept
The part-level volume multiplier, relative to everything at 127.
constexpr int default_chorus_send
GM default chorus send.
Definition sequence.hpp:276
constexpr int default_pan
Power-on pan.
Definition sequence.hpp:270
constexpr int default_reverb_send
GM default reverb send.
Definition sequence.hpp:274
constexpr int default_expression
Power-on expression.
Definition sequence.hpp:268
constexpr int default_master
Power-on master volume.
Definition sequence.hpp:272
constexpr int default_volume
Power-on volume after a GM or GS reset.
Definition sequence.hpp:266
Definition control_decode.hpp:7
What one source contributes, once its depths have been scaled by its current amount.
Definition control_matrix.hpp:107
int pitch
added to the voice's pitch
Definition control_matrix.hpp:108
The GS controller assignment matrix (40 2x): six sources, each with eleven depths.
Definition control_matrix.hpp:18
@ poly_pressure
polyphonic aftertouch, 40 2x 30-3A
Definition control_matrix.hpp:24
@ cc2
the part's CC2, 40 2x 50-5A
Definition control_matrix.hpp:26
@ modulation
CC#1, 40 2x 00-0A.
Definition control_matrix.hpp:21
@ cc1
the part's CC1, 40 2x 40-4A
Definition control_matrix.hpp:25
@ bend
pitch bend, 40 2x 10-1A
Definition control_matrix.hpp:22
@ channel_pressure
channel aftertouch, 40 2x 20-2A
Definition control_matrix.hpp:23
static constexpr int neutral
The value every depth centres on, and the value of an unassigned route.
Definition control_matrix.hpp:48
static constexpr int scaled(Destination destination, int sum) noexcept
Clamps and scales a destination's summed sources into that destination's unit.
Definition control_matrix.hpp:352
The GS part modify offsets, as the synthesis chains consume them.
Definition part_modifiers.hpp:21
The GS receive switches, one per message class the module can be told to ignore.
Definition part.hpp:26
bool control_change
The master gate every controller shares; the channel-mode messages (CC#120 up) bypass it.
Definition part.hpp:31
bool volume
Definition part.hpp:37
bool poly_pressure
Definition part.hpp:32
bool pitch_bend
Definition part.hpp:27
bool rpn
Definition part.hpp:34
bool bank_msb
Bank select, split MSB/LSB the way 40 1x 23/24 split it.
Definition part.hpp:45
bool panpot
Definition part.hpp:38
bool notes
Definition part.hpp:33
bool hold
Definition part.hpp:40
bool sostenuto
Definition part.hpp:42
bool modulation
Definition part.hpp:36
bool bank_lsb
Definition part.hpp:46
bool portamento
Definition part.hpp:41
bool program_change
Definition part.hpp:29
bool nrpn
Definition part.hpp:35
bool soft
Definition part.hpp:43
bool expression
Definition part.hpp:39
bool channel_pressure
Definition part.hpp:28