One of the sixteen parts: the channel state a running engine keeps between events.
More...
|
| | Part () |
| int | key_pressure (int note) const noexcept |
| | The pressure on one key, or zero for anything out of range.
|
| PartModifiers | modifiers () const noexcept |
| | The seven live modify offsets, in the form the synthesis chains take.
|
| 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 1B offset).
|
| bool | damper_down () const noexcept |
| | Whether the damper is holding notes on.
|
| int | volume () const noexcept |
| | CC#7 volume.
|
| void | set_volume (int value) noexcept |
| int | expression () const noexcept |
| | CC#11 expression.
|
| void | set_expression (int value) noexcept |
| int | master () const noexcept |
| | Master volume, which is global but folds into the same law.
|
| void | set_master (int value) noexcept |
| double | volume_scale () const noexcept |
| | The combined volume multiplier, 1.0 with everything at 127.
|
| ControlMatrix::Modulation | matrix (int key_pressure=0) const noexcept |
| | Everything the control matrix modulates, each destination in its own unit.
|
| double | matrix_pitch_milli_semitones (int key_pressure=0) const noexcept |
| | The control matrix's contribution to pitch alone, in milli-semitones.
|
| double | bend_milli_semitones () const noexcept |
| | The bend offset in milli-semitones.
|
| 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 shift.
|
| 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.
|
| bool | rpn_is_null () const noexcept |
| | Whether the RPN address currently selected is the null set (7F/7F), which parks data entry.
|
| void | reset () |
| | Returns the part to its power-on state.
|
| void | reset_controllers () |
| | Applies CC#121, which resets the controllers a reset message covers.
|
|
| int | program = 0 |
| | Program in force, from the last program change.
|
| int | bank = 0 |
| | Bank select MSB, which carries the variation.
|
| int | bank_lsb = 0 |
| | Bank select LSB, which on this module selects the tone map: 1-4 name a vintage and 0 keeps the configured default.
|
| int | rx_channel = 0 |
| | The MIDI channel this part listens on, 0-15, or 16 for off.
|
| RxSwitches | rx |
| | The GS receive switches, written by SysEx 40 1x 03-12 and 23/24.
|
| int | pan = sequence_builder::default_pan |
| | CC#10 pan.
|
| int | modulation = 0 |
| | CC#1 modulation.
|
| int | damper = 0 |
| | CC#64 damper.
|
| int | reverb_send = sequence_builder::default_reverb_send |
| | CC#91 reverb send.
|
| int | chorus_send = sequence_builder::default_chorus_send |
| | CC#93 chorus send.
|
| int | delay_send = 0 |
| | Part delay send, which has no Control Change and arrives only over SysEx.
|
| int | bend = 8192 |
| | CC#94 delay send.
|
| int | bend_range = 2 |
| | Bend range in semitones, from RPN 00/00.
|
| int | fine_tune = 0x2000 |
| | RPN 00/01 fine tune, as the full 14-bit value with 0x2000 centred; +-100 cents at the ends.
|
| int | coarse_tune = 0x40 |
| | RPN 00/02 coarse tune, 0x40 centred, one semitone a step.
|
| int | key_shift = 0x40 |
| | GS part key shift (40 1x 16), 0x40 centred; the engine clamps it to 0x28-0x58.
|
| ControlMatrix | control |
| | The controller assignment matrix (40 2x), which decides what each source modulates.
|
| int | channel_pressure = 0 |
| | Channel aftertouch, which reaches pitch through the control matrix.
|
| int | cc1_number = 16 |
| | Which Control Change numbers this part's two assignable sources listen to (40 1x 1F and 20), and what those controllers currently read.
|
| int | cc2_number = 17 |
| int | cc1 = 0 |
| int | cc2 = 0 |
| int | xg_bank_msb = -1 |
| | Bank select MSB while the engine is in XG mode, where it means something else entirely.
|
| std::array< std::uint8_t, 128 > | poly_pressure {} |
| | Polyphonic aftertouch, per key.
|
| int | vibrato_rate = 0x40 |
| | The GS part modify offsets, 0x40 centred.
|
| int | vibrato_depth = 0x40 |
| int | vibrato_delay = 0x40 |
| int | tvf_cutoff = 0x40 |
| int | env_attack = 0x40 |
| int | env_decay = 0x40 |
| int | env_release = 0x40 |
| int | tvf_resonance = 0x40 |
| | The eighth: CC#71 / NRPN 01 21 / 40 1x 33, and the one that goes nowhere.
|
| std::array< int, 12 > | scale_tuning |
| | GS scale tuning (40 1x 40-4B), one entry per pitch class, 0x40 centred, a cent a step.
|
| int | rhythm = -1 |
| | GS use-for-rhythm (40 1x 15): -1 follows the channel default, 0 forces melodic, 1 or 2 route the part to the drum path on that drum map.
|
| int | key_low = 0 |
| | GS keyboard range (40 1x 1D/1E); note-ons outside it are ignored on melodic parts.
|
| int | key_high = 0x7F |
| bool | eq_enabled = false |
| | Whether this part runs through the four-band EQ (40 4x 20), which is part+0x450.
|
| bool | efx_enabled = false |
| | Whether this part feeds the insertion EFX block (40 4x 22), which is part+0x452.
|
| int | velocity_depth = 0x40 |
| | GS velocity sense depth and offset (40 1x 1A/1B), applied by effective_velocity.
|
| int | velocity_offset = 0x40 |
| int | pitch_offset_fine = 0x08 |
| | GS pitch offset fine (40 1x 17), stored raw: the unit is Hz on the module, which nothing downstream consumes yet.
|
| int | rpn_msb = 0x7F |
| int | rpn_lsb = 0x7F |
| int | nrpn_msb = 0x7F |
| int | nrpn_lsb = 0x7F |
| | The selected NRPN's LSB, which for the drum parameters is the key number.
|
| bool | data_entry_is_nrpn = false |
| | Whether a CC#6 data entry commits to the selected NRPN rather than the selected RPN.
|
| DrumKeyOverrides | drum_keys |
| | Per-drum-key overrides this part has taken from NRPN.
|
| std::vector< int > | sustained |
| | Notes whose release is waiting for the damper to lift.
|
| int | portamento_time = 0 |
| | CC#5 portamento time; indexes the glide-step table.
|
| bool | portamento_on = false |
| | Whether CC#65 portamento is on.
|
| bool | mono = false |
| | Whether CC#126 mono mode is on, which flushes the part's voices at each note-on.
|
| bool | soft = false |
| | Whether the CC#67 soft pedal is down.
|
| int | portamento_control_key = -1 |
| | CC#84 portamento control: the key the next note glides from, or -1 when unset.
|
| int | last_key = -1 |
| | The key the part last sounded, which portamento glides from, or -1.
|
| bool | sostenuto_down = false |
| | Whether the sostenuto pedal is down. CC#66 is binary — bit 6 only, as the engine reads it.
|
| std::vector< int > | sostenuto_captured |
| | Notes the sostenuto pedal captured — the ones sounding when it went down.
|
| std::vector< int > | sostenuto_released |
| | Captured notes whose note-off arrived while the pedal held them.
|
One of the sixteen parts: the channel state a running engine keeps between events.
The engine has no MIDI output, so nothing can be read back from it — every piece of channel state has to be tracked here. This is the live counterpart of PartTimelines, which records the same values as breakpoints for the offline renderer.
Volume, expression and master are set through methods rather than left as public fields: each one recomputes the combined scale, and a plain assignment that skipped that would leave the part sounding at its previous level until the next one happened to move.
Everything the control matrix modulates, each destination in its own unit.
part_mod_depth_recalc keeps eleven running sums a part, one a destination, each the total of the sources' contributions, and clamps and scales each by its own law. This is that function, computed on demand rather than cached behind a dirty mask — the mask is a way of not recomputing, not a difference in what is computed.
All six sources reach it: the mod wheel, bend, both aftertouches, and the two assignable controllers. Every one is summed raw before anything is clamped, which is why they are gathered here rather than scaled separately and added up — clamping each in turn would let the total escape the rail.
key_pressure is the polyphonic aftertouch on the note being rendered, which is why this takes an argument at all: everything else here belongs to the part, and that one belongs to the key. The module applies it at exactly this point too, and for the same reason — it is the one source whose amount is not a property of the part, so it cannot be folded into a per-part cache. A caller with no note in hand passes nothing.
| void ts::Part::reset_controllers |
( |
| ) |
|
|
inline |
Applies CC#121, which resets the controllers a reset message covers.
The engine's caseD_79: modulation and pressure cleared, damper and soft and portamento lifted, expression back to full, the one-shot portamento key consumed, and the RPN/NRPN selection parked at null (rpn_nrpn_decode case 0x79). Volume, pan, the sends and the tuning stay – a reset is not a power cycle.
Releasing what the damper held is the caller's job, since it owns the voices.
| int ts::Part::cc1_number = 16 |
Which Control Change numbers this part's two assignable sources listen to (40 1x 1F and 20), and what those controllers currently read.
The numbers default to 16 and 17 — General Purpose 1 and 2, which is what makes those two controllers do anything on a GS module at all. Nothing else in the engine reads CC#16 or CC#17; they exist to be pointed at the matrix.
An assignable source is not a controller with a fixed meaning, so it is tracked as a pair: the number decides which message feeds it, and the amount is what the matrix scales. Pointing CC1 at a controller that already means something — the mod wheel, say — does not take that meaning away; the message does both jobs.
| int ts::Part::xg_bank_msb = -1 |
Bank select MSB while the engine is in XG mode, where it means something else entirely.
XG inverts the pair: the LSB carries the variation and lands in bank, while the MSB chooses between melodic (below 0x7E), the SFX voice bank (0x40), the SFX kits (0x7E) and the drum kits (0x7F). It needs its own field because it decides melodic-versus-drum for the part and so has to survive until the next program change, which is when that decision is taken.
-1 means no bank select has been sent, which is not the same as zero. A program change on a part whose bank was never written must not decide drum routing at all – it leaves the part on its default, so channel 10 stays drums the way XG starts it. Reading an unwritten bank as 0 would silently make every default drum part melodic, which is exactly what a file that sends nothing but program changes would suffer.
| std::array<std::uint8_t, 128> ts::Part::poly_pressure {} |
Polyphonic aftertouch, per key.
One byte a note rather than one a part, which is the whole of what makes it polyphonic: the engine's poly_aftertouch_apply takes the part's matrix depths and the pressure belonging to that key, so two notes held on one part can be modulated by different amounts.
Not cleared by note-on, which is the opposite of the obvious guess and was measured rather than assumed. Press a key to full pressure, release it, strike it again and say nothing about pressure: the module sounds the new note still bent, an octave and a half up on a part whose 40 2x 30 depth is 0x58. The pressure belongs to the key, not to the note that happened to be sounding on it, and only another poly message or a reset moves it.