|
| | Fm6DrumVoice () |
| void | set_algorithm (int index) |
| int | algorithm () const |
| void | set_tune_hz (double hz) |
| void | set_operator_ratio (int op, double ratio) |
| void | set_operator_level (int op, double level) |
| void | set_operator_decay_ms (int op, double ms) |
| void | set_feedback (double amount) |
| | Depth of the routing's designated feedback operator, 0 to 1.
|
| void | set_depth (double depth) |
| | Overall modulation depth, scaling every routed connection.
|
| void | set_pitch_sweep_octaves (double octaves) |
| | Global pitch envelope: every operator's frequency is scaled together, so the spectrum keeps its shape while the whole voice sweeps.
|
| void | set_pitch_sweep_ms (double ms) |
| void | set_formant_hz (double hz) |
| void | set_formant_q (double q) |
| OutputStage & | output () |
| const OutputStage & | output () const |
| OutputStage * | output_stage () noexcept override |
| | The terminal saturation and degradation stage, at the generic voice boundary.
|
| int | latency_samples () const noexcept override |
| | Fixed host-rate delay introduced by this voice's selected output quality.
|
| OutputOversampling | output_oversampling () const noexcept override |
| | Output quality at the generic voice boundary.
|
| void | set_output_oversampling (OutputOversampling factor) override |
| void | prepare (double sample_rate) |
| | Fixes the sample rate and clears all state.
|
| void | reset () |
| | Silences the voice immediately, without a fade.
|
| void | note_on (float velocity) |
| | Starts a hit. velocity is in [0, 1]; values outside are clamped.
|
| void | choke (float fade_ms=4.0f) |
| | Fades the voice out over fade_ms and then silences it.
|
| bool | is_active () const |
| | Whether the voice still has something to render.
|
| VoiceState | state () const |
| float | velocity () const |
| double | sample_rate () const |
| const OutputStage * | output_stage () const noexcept |
| void | set_velocity_response (const VelocityResponse &r) |
| const VelocityResponse & | velocity_response () const |
| void | process (float *out, int num_samples) |
| | Adds num_samples of this voice's output into out.
|
| void | process_stereo (float *left, float *right, int num_samples) |
| | Adds a stereo rendering to left and right.
|
A six-operator FM voice with the classic thirty-two algorithm set.
Six operators rather than the eight of Fm8DrumVoice, because six is what makes this instrument the thing it is. The constraint is the character: with only six operators an algorithm has to spend them, so the familiar routings are each a specific compromise between how many partials reach the output and how deeply they are modulated. A wider engine does not sound like this one with more headroom — it sounds like a different instrument. Fm8DrumVoice is the more capable engine; this is the more recognisable one.
The architecture is six operators, thirty-two fixed routings, one feedback operator per routing, and a global pitch envelope. Envelope shaping is DecayEnvelopeT, so an operator's contour is described in the same milliseconds-and-T60 terms as every other voice here rather than in device-specific rate and level units.
The routing table is transcribed from published descriptions of the instrument's architecture. Three rules the documentation states about the original algorithm set are asserted over every row, which is what makes the transcription checkable rather than merely plausible:
- Modulation only ever flows downward. The set is laid out with the highest-numbered operator at the top, so an operator is modulated only by higher-numbered ones. This is the strongest of the three — it rejects any row whose arrows point the wrong way, which is the easiest transcription error to make and the hardest to hear.
- At most one self-feedback loop per routing, which is why feedback_op is a single index rather than a mask.
- Carrier outputs are scaled by the carrier count, so routings with different numbers of carriers arrive at comparable levels; the fully additive routing is scaled by a sixth. render_add does this.
Alongside those, the structural invariants: at least one carrier, no operator modulating itself through the mask, every index in range, and every operator reachable from a carrier. Algorithm 8 is additionally pinned against its documented topology as a worked example.
What none of that proves is that a given row is the routing that number names. A row can satisfy every rule above and still be the wrong shape. Anything depending on patch-level algorithm compatibility should verify the full table against the operation manual's diagrams first.
Lineage: Chowning, JAES 21(7), 1973, for the synthesis technique.
RT contract: prepare() allocates nothing; every other method allocates nothing and takes no locks.