Portable subtractive synthesizer
examples/tutorials/src/synth.rs · 326 source lines
Read the chapters in source order. Select a numbered source line to expand its explanation; the line numbers and code match this snapshot exactly.
cargo test -p ravet-examples --test subtractive --no-default-features --lockedChapters
- Imports, controls and fixed capacities lines 1–39
- Represent a prepared patch lines 40–51
- Validate the rate and every physical control lines 52–87
- Expose values and decode waveform selection lines 88–100
- Keep per-voice histories together lines 101–117
- Document the instrument boundary lines 118–137
- Create the inactive default synth lines 138–148
- Apply a prepared patch without heap operations lines 149–161
- Read voice and envelope snapshots lines 162–169
- Translate recognized notes into receiver messages lines 170–194
- Update only changed slots lines 195–226
- Implement Default through the same constructor lines 227–231
- Validate preparation and build rate-matched slots lines 232–251
- Validate before changing histories lines 252–266
- Process a timed frame and active voice slots lines 267–288
- Render oscillator → filter → amplitude lines 289–309
- Reset the complete fixed voice state lines 310–326
Imports, controls and fixed capacities
The processor combines foundation events with MPE-aware voice policy and signal oscillators, ADSR and state-variable filters. VOICES is eight; DEFAULT_VALUES/RANGES share nine physical positions. FILTER_STEPS fixes table storage at 256 entries. PatchError reports invalid controls, rate or active-rate mismatch.
1//! Eight-voice subtractive synthesis with one ADSR per voice shared by VCA and VCF.
Source note: Eight-voice subtractive synthesis with one ADSR per voice shared by VCA and VCF.
Link to line 12use ravet_foundation::{
Bring these module/type names into scope for “Imports, controls and fixed capacities”; the surrounding chapter explains their responsibilities.
Link to line 23 AudioLayout, EventData, MidiMessage, PrepareError, ProcessBlock, ProcessConfig, ProcessError,
Supply these names as the remaining imports, arguments or fields of the surrounding declaration.
Link to line 34 ProcessStatus, Processor, Tail,
Supply these names as the remaining imports, arguments or fields of the surrounding declaration.
Link to line 45};
Close the surrounding scope, value or argument list in “Imports, controls and fixed capacities”.
Link to line 56use ravet_instrument::{MpeReceiver, Voice, VoicePhase};
Bring these module/type names into scope for “Imports, controls and fixed capacities”; the surrounding chapter explains their responsibilities.
Link to line 67use ravet_signal::{
Bring these module/type names into scope for “Imports, controls and fixed capacities”; the surrounding chapter explains their responsibilities.
Link to line 78 Adsr, AdsrConfig, EnvelopePhase, Oscillator, StateVariableCoefficients, StateVariableFilter,
Supply these names as the remaining imports, arguments or fields of the surrounding declaration.
Link to line 89 Waveform,
Supply these names as the remaining imports, arguments or fields of the surrounding declaration.
Link to line 910};
Close the surrounding scope, value or argument list in “Imports, controls and fixed capacities”.
Link to line 1011
Blank line separating the surrounding statements.
Link to line 1112/// Fixed polyphony; mix scales by this capacity.
Source note: Fixed polyphony; mix scales by this capacity.
Link to line 1213pub const VOICES: usize = 8;
Declare this compile-time constant or fixed array. The value and physical/capacity meaning are given by its source comment and this chapter.
Link to line 1314/// Waveform, level, cutoff Hz, resonance Q, envelope semitones, A ms, D ms, S, R ms.
Source note: Waveform, level, cutoff Hz, resonance Q, envelope semitones, A ms, D ms, S, R ms.
Link to line 1415pub const DEFAULT_VALUES: [f32; 9] = [1., 0.6, 800., 0.7, 36., 10., 150., 0.7, 300.];
Declare this compile-time constant or fixed array. The value and physical/capacity meaning are given by its source comment and this chapter.
Link to line 1516/// Physical bounds; waveform must additionally be an integer 0..3.
Source note: Physical bounds; waveform must additionally be an integer 0..3.
Link to line 1617pub const RANGES: [(f32, f32); 9] = [
Declare this compile-time constant or fixed array. The value and physical/capacity meaning are given by its source comment and this chapter.
Link to line 1718 (0., 3.),
Continue the surrounding value or expression with this exact operand. Read it together with the preceding statement and the “Imports, controls and fixed capacities” explanation above.
Link to line 1819 (0., 1.),
Continue the surrounding value or expression with this exact operand. Read it together with the preceding statement and the “Imports, controls and fixed capacities” explanation above.
Link to line 1920 (20., 20_000.),
Continue the surrounding value or expression with this exact operand. Read it together with the preceding statement and the “Imports, controls and fixed capacities” explanation above.
Link to line 2021 (0.5, 8.),
Continue the surrounding value or expression with this exact operand. Read it together with the preceding statement and the “Imports, controls and fixed capacities” explanation above.
Link to line 2122 (0., 72.),
Continue the surrounding value or expression with this exact operand. Read it together with the preceding statement and the “Imports, controls and fixed capacities” explanation above.
Link to line 2223 (0., 5_000.),
Continue the surrounding value or expression with this exact operand. Read it together with the preceding statement and the “Imports, controls and fixed capacities” explanation above.
Link to line 2324 (0., 5_000.),
Continue the surrounding value or expression with this exact operand. Read it together with the preceding statement and the “Imports, controls and fixed capacities” explanation above.
Link to line 2425 (0., 1.),
Continue the surrounding value or expression with this exact operand. Read it together with the preceding statement and the “Imports, controls and fixed capacities” explanation above.
Link to line 2526 (0., 5_000.),
Continue the surrounding value or expression with this exact operand. Read it together with the preceding statement and the “Imports, controls and fixed capacities” explanation above.
Link to line 2627];
Close the surrounding scope, value or argument list in “Imports, controls and fixed capacities”.
Link to line 2728const FILTER_STEPS: usize = 256;
Declare this compile-time constant or fixed array. The value and physical/capacity meaning are given by its source comment and this chapter.
Link to line 2829
Blank line separating the surrounding statements.
Link to line 2930/// Invalid physical controls, unsupported rate, or prepared-patch rate mismatch.
Source note: Invalid physical controls, unsupported rate, or prepared-patch rate mismatch.
Link to line 3031#[derive(Debug, Clone, Copy, PartialEq, Eq)]
Apply this Rust attribute to the following item. cfg selects which target/feature/test build contains it; derive generates the named trait implementations.
Link to line 3132pub struct PatchError;
Declare the state/type or implementation used by this chapter. The processor combines foundation events with MPE-aware voice policy and signal oscillators, ADSR and state-variable filters. VOICES is eight; DEFAULT_VALUES/RANGES share nine physical positions. FILTER_STEPS fixes table storage at 256 entries. PatchError reports invalid controls, rate or active-rate mismatch.
Link to line 3233impl std::fmt::Display for PatchError {
Declare the state/type or implementation used by this chapter. The processor combines foundation events with MPE-aware voice policy and signal oscillators, ADSR and state-variable filters. VOICES is eight; DEFAULT_VALUES/RANGES share nine physical positions. FILTER_STEPS fixes table storage at 256 entries. PatchError reports invalid controls, rate or active-rate mismatch.
Link to line 3334 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Define fmt. The processor combines foundation events with MPE-aware voice policy and signal oscillators, ADSR and state-variable filters. VOICES is eight; DEFAULT_VALUES/RANGES share nine physical positions. FILTER_STEPS fixes table storage at 256 entries. PatchError reports invalid controls, rate or active-rate mismatch.
Link to line 3435 f.write_str("invalid synth patch or sample rate")
Call the displayed operation with these arguments as the next step in “Imports, controls and fixed capacities”. The chapter describes its role and validation.
Link to line 3536 }
Close the surrounding scope, value or argument list in “Imports, controls and fixed capacities”.
Link to line 3637}
Close the surrounding scope, value or argument list in “Imports, controls and fixed capacities”.
Link to line 3738impl std::error::Error for PatchError {}
Declare the state/type or implementation used by this chapter. The processor combines foundation events with MPE-aware voice policy and signal oscillators, ADSR and state-variable filters. VOICES is eight; DEFAULT_VALUES/RANGES share nine physical positions. FILTER_STEPS fixes table storage at 256 entries. PatchError reports invalid controls, rate or active-rate mismatch.
Link to line 3839
Blank line separating the surrounding statements.
Link to line 39Represent a prepared patch
SynthPatch stores values, sample rate, ADSR configuration and a fixed filter table. It is designed on the control thread. The comments distinguish latched ADSR from waveform/level/filter values that can change held notes.
40/// Fixed-storage patch designed on the control thread, including a 256-step VCF table.
Source note: Fixed-storage patch designed on the control thread, including a 256-step VCF table.
Link to line 4041/// Envelope amount is a positive cutoff excursion in semitones. Member/master
Source note: Envelope amount is a positive cutoff excursion in semitones. Member/master
Link to line 4142/// CC74 adds up to 24 semitones; the combined excursion clamps below 45% of rate.
Source note: CC74 adds up to 24 semitones; the combined excursion clamps below 45% of rate.
Link to line 4243/// ADSR settings latch at note-on. Waveform, level and VCF controls also affect
Source note: ADSR settings latch at note-on. Waveform, level and VCF controls also affect
Link to line 4344/// sounding notes when a new patch is applied. No parameter crossfade is provided.
Source note: sounding notes when a new patch is applied. No parameter crossfade is provided.
Link to line 4445#[derive(Clone)]
Apply this Rust attribute to the following item. cfg selects which target/feature/test build contains it; derive generates the named trait implementations.
Link to line 4546pub struct SynthPatch {
Declare the state/type or implementation used by this chapter. SynthPatch stores values, sample rate, ADSR configuration and a fixed filter table. It is designed on the control thread. The comments distinguish latched ADSR from waveform/level/filter values that can change held notes.
Link to line 4647 values: [f32; 9],
Requested physical controls in the order documented for this processor.
Link to line 4748 rate: f64,
Sample rate used to design this patch’s envelope and coefficients.
Link to line 4849 envelope: AdsrConfig,
The shared VCA/VCF envelope state or its prepared configuration.
Link to line 4950 coefficients: [StateVariableCoefficients; FILTER_STEPS],
Fixed prepared coefficient storage, avoiding coefficient design in the sample loop.
Link to line 5051}
Close the surrounding scope, value or argument list in “Represent a prepared patch”.
Link to line 51Validate the rate and every physical control
new rejects nonfinite/out-of-range values and a nonintegral waveform index. Convert millisecond ADSR times to seconds. Design a coefficient table covering envelope excursion plus up to 24 semitones of timbre, capped at 45% of sample rate.
52impl SynthPatch {
Declare the state/type or implementation used by this chapter. new rejects nonfinite/out-of-range values and a nonintegral waveform index. Convert millisecond ADSR times to seconds. Design a coefficient table covering envelope excursion plus up to 24 semitones of timbre, capped at 45% of sample rate.
Link to line 5253 /// Validate all controls and design the envelope/VCF table outside processing.
Source note: Validate all controls and design the envelope/VCF table outside processing.
Link to line 5354 /// Rates are 8000..192000 Hz. Time controls are milliseconds, sustain/level
Source note: Rates are 8000..192000 Hz. Time controls are milliseconds, sustain/level
Link to line 5455 /// are linear 0..1, resonance is Q. Saw/square use polyBLEP; triangle may alias.
Source note: are linear 0..1, resonance is Q. Saw/square use polyBLEP; triangle may alias.
Link to line 5556 pub fn new(rate: f64, values: [f32; 9]) -> Result<Self, PatchError> {
Define new. new rejects nonfinite/out-of-range values and a nonintegral waveform index. Convert millisecond ADSR times to seconds. Design a coefficient table covering envelope excursion plus up to 24 semitones of timbre, capped at 45% of sample rate.
Link to line 5657 if !rate.is_finite()
Evaluate this condition or visit this range in source order. The statements in the following scope implement “Validate the rate and every physical control”.
Link to line 5758 || !(8_000.0..=192_000.).contains(&rate)
Accept this alternative condition in the surrounding check; evaluation stops once the combined condition is true.
Link to line 5859 || values
Accept this alternative condition in the surrounding check; evaluation stops once the combined condition is true.
Link to line 5960 .iter()
Borrow collection entries for iteration without moving or copying the whole collection.
Link to line 6061 .zip(RANGES)
Pair each entry with the corresponding entry in the second sequence. Iteration ends when either sequence ends.
Link to line 6162 .any(|(v, (lo, hi))| !v.is_finite() || !(lo..=hi).contains(v))
Return true if any entry satisfies this predicate; stop at the first match.
Link to line 6263 || values[0].fract() != 0.
Require the waveform selector to be an integer as well as within its numeric range.
Link to line 6364 {
Close the surrounding scope, value or argument list in “Validate the rate and every physical control”.
Link to line 6465 return Err(PatchError);
Exit this path with an explicit error/fault; the caller must not treat this branch as successfully applied state.
Link to line 6566 }
Close the surrounding scope, value or argument list in “Validate the rate and every physical control”.
Link to line 6667 let envelope = AdsrConfig::new(
Bind envelope to the expression on this and any following continuation lines. The chapter above explains how this intermediate value is used.
Link to line 6768 rate,
Supply these names as the remaining imports, arguments or fields of the surrounding declaration.
Link to line 6869 f64::from(values[5]) / 1000.,
Convert attack time from milliseconds to seconds for AdsrConfig.
Link to line 6970 f64::from(values[6]) / 1000.,
Convert decay time from milliseconds to seconds for AdsrConfig.
Link to line 7071 f64::from(values[7]),
Pass the linear sustain level directly; sustain is a level rather than a duration.
Link to line 7172 f64::from(values[8]) / 1000.,
Convert release time from milliseconds to seconds for AdsrConfig.
Link to line 7273 )
Close the surrounding scope, value or argument list in “Validate the rate and every physical control”.
Link to line 7374 .map_err(|_| PatchError)?;
The ? operator returns an error immediately on failure and unwraps the success value for the next step.
Link to line 7475 let coefficients = std::array::from_fn(|index| {
Build all 256 coefficient entries in a fixed array on the control/preparation path.
Link to line 7576 let semitones = f64::from(values[4] + 24.) * index as f64 / (FILTER_STEPS - 1) as f64;
Map the table index over the full envelope excursion plus a possible 24-semitone timbre excursion.
Link to line 7677 let cutoff = (f64::from(values[2]) * 2_f64.powf(semitones / 12.)).min(rate * 0.45);
Raise the base cutoff by the selected semitone ratio and cap it at 45% of sample rate.
Link to line 7778 StateVariableCoefficients::new(rate, cutoff, f64::from(values[3]))
Design filter coefficients from sample rate, bounded cutoff and the physical resonance Q.
Link to line 7879 .expect("validated table bounds")
Require the invariant “validated table bounds”. expect panics if it is broken; external inputs must be validated before relying on it.
Link to line 7980 });
Close the current expression or scope; no additional processing operation occurs on this line.
Link to line 8081 Ok(Self {
Construct the success/error variant selected by the surrounding match or function return. The enclosed value becomes its payload.
Link to line 8182 values,
Supply these names as the remaining imports, arguments or fields of the surrounding declaration.
Link to line 8283 rate,
Supply these names as the remaining imports, arguments or fields of the surrounding declaration.
Link to line 8384 envelope,
Supply these names as the remaining imports, arguments or fields of the surrounding declaration.
Link to line 8485 coefficients,
Supply these names as the remaining imports, arguments or fields of the surrounding declaration.
Link to line 8586 })
Close the current expression or scope; no additional processing operation occurs on this line.
Link to line 8687 }
Close the surrounding scope, value or argument list in “Validate the rate and every physical control”.
Link to line 87Expose values and decode waveform selection
values returns the fixed physical array. waveform indexes Sine, Saw, Square and Triangle using the already-validated integer. The explicit order matches the editor and DEFAULT_VALUES.
88 /// Current physical controls in the order documented by `DEFAULT_VALUES`.
Source note: Current physical controls in the order documented by `DEFAULT_VALUES`.
Link to line 8889 pub fn values(&self) -> [f32; 9] {
Define values. values returns the fixed physical array. waveform indexes Sine, Saw, Square and Triangle using the already-validated integer. The explicit order matches the editor and DEFAULT_VALUES.
Link to line 8990 self.values
Return a copy of the fixed-size requested control array; this does not advance a smoother.
Link to line 9091 }
Close the surrounding scope, value or argument list in “Expose values and decode waveform selection”.
Link to line 9192 fn waveform(&self) -> Waveform {
Define waveform. values returns the fixed physical array. waveform indexes Sine, Saw, Square and Triangle using the already-validated integer. The explicit order matches the editor and DEFAULT_VALUES.
Link to line 9293 [
Continue the surrounding value or expression with this exact operand. Read it together with the preceding statement and the “Expose values and decode waveform selection” explanation above.
Link to line 9394 Waveform::Sine,
Define or initialize Waveform with the displayed type/value. Its ownership and use are described in “Expose values and decode waveform selection”.
Link to line 9495 Waveform::Saw,
Define or initialize Waveform with the displayed type/value. Its ownership and use are described in “Expose values and decode waveform selection”.
Link to line 9596 Waveform::Square,
Define or initialize Waveform with the displayed type/value. Its ownership and use are described in “Expose values and decode waveform selection”.
Link to line 9697 Waveform::Triangle,
Define or initialize Waveform with the displayed type/value. Its ownership and use are described in “Expose values and decode waveform selection”.
Link to line 9798 ][self.values[0] as usize]
Continue the surrounding value or expression with this exact operand. Read it together with the preceding statement and the “Expose values and decode waveform selection” explanation above.
Link to line 9899 }
Close the surrounding scope, value or argument list in “Expose values and decode waveform selection”.
Link to line 99100}
Close the surrounding scope, value or argument list in “Expose values and decode waveform selection”.
Link to line 100Keep per-voice histories together
Each slot owns oscillator, envelope, filter and a flag recording whether release was triggered. Slot::new initializes those from one validated patch without allocating a heap table.
101struct Slot {
Declare the state/type or implementation used by this chapter. Each slot owns oscillator, envelope, filter and a flag recording whether release was triggered. Slot::new initializes those from one validated patch without allocating a heap table.
Link to line 101102 oscillator: Oscillator,
Per-voice oscillator phase and waveform state.
Link to line 102103 envelope: Adsr,
The shared VCA/VCF envelope state or its prepared configuration.
Link to line 103104 filter: StateVariableFilter,
Per-voice filter history, owned independently of other voices.
Link to line 104105 releasing: bool,
Whether release has already been triggered for this slot.
Link to line 105106}
Close the surrounding scope, value or argument list in “Keep per-voice histories together”.
Link to line 106107impl Slot {
Declare the state/type or implementation used by this chapter. Each slot owns oscillator, envelope, filter and a flag recording whether release was triggered. Slot::new initializes those from one validated patch without allocating a heap table.
Link to line 107108 fn new(patch: &SynthPatch) -> Self {
Define new. Each slot owns oscillator, envelope, filter and a flag recording whether release was triggered. Slot::new initializes those from one validated patch without allocating a heap table.
Link to line 108109 Self {
Continue the surrounding value or expression with this exact operand. Read it together with the preceding statement and the “Keep per-voice histories together” explanation above.
Link to line 109110 oscillator: Oscillator::new(patch.rate, 440., patch.waveform())
Per-voice oscillator phase and waveform state.
Link to line 110111 .expect("prepared oscillator"),
Require the invariant “prepared oscillator”. expect panics if it is broken; external inputs must be validated before relying on it.
Link to line 111112 envelope: Adsr::new(patch.envelope),
The shared VCA/VCF envelope state or its prepared configuration.
Link to line 112113 filter: StateVariableFilter::new(patch.coefficients[0]),
Per-voice filter history, owned independently of other voices.
Link to line 113114 releasing: false,
Whether release has already been triggered for this slot.
Link to line 114115 }
Close the surrounding scope, value or argument list in “Keep per-voice histories together”.
Link to line 115116 }
Close the surrounding scope, value or argument list in “Keep per-voice histories together”.
Link to line 116117}
Close the surrounding scope, value or argument list in “Keep per-voice histories together”.
Link to line 117Document the instrument boundary
The instrument has no audio inputs, eight voices and mono/stereo output. Complete blocks validate before mutation. It accepts MIDI/normalized notes, ignores parameter/UMP events and receives prepared patches through a separate API. It has no limiter, MIDI 2.0 synthesis or plug-in export.
118/// Example mono/stereo f32 instrument with eight fixed MPE-aware voices.
Source note: Example mono/stereo f32 instrument with eight fixed MPE-aware voices.
Link to line 118119///
Source note:
Link to line 119120/// Each voice runs oscillator -> low-pass VCF -> VCA. Its single ADSR drives
Source note: Each voice runs oscillator -> low-pass VCF -> VCA. Its single ADSR drives
Link to line 120121/// VCA amplitude and VCF cutoff; MIDI velocity scales amplitude, combined bend
Source note: VCA amplitude and VCF cutoff; MIDI velocity scales amplitude, combined bend
Link to line 121122/// retunes the oscillator, pressure adds up to 50% amplitude and CC74 opens the
Source note: retunes the oscillator, pressure adds up to 50% amplitude and CC74 opens the
Link to line 122123/// filter by up to 24 semitones. One shared voice pool accepts the existing
Source note: filter by up to 24 semitones. One shared voice pool accepts the existing
Link to line 123124/// two-zone MPE receiver's RPN/pedal policy. New/stolen voices reset histories.
Source note: two-zone MPE receiver's RPN/pedal policy. New/stolen voices reset histories.
Link to line 124125/// Release finishes when the VCA envelope reaches zero; there is no separate
Source note: Release finishes when the VCA envelope reaches zero; there is no separate
Link to line 125126/// filter tail. Mix scales by level/8, without an output limiter or headroom guarantee.
Source note: filter tail. Mix scales by level/8, without an output limiter or headroom guarantee.
Link to line 126127///
Source note:
Link to line 127128/// Prepared processing/reset allocate nothing. Up to 1024 frames and 128 MIDI
Source note: Prepared processing/reset allocate nothing. Up to 1024 frames and 128 MIDI
Link to line 128129/// events, no inputs/aux/output events, rates 8000..192000. Complete blocks
Source note: events, no inputs/aux/output events, rates 8000..192000. Complete blocks
Link to line 129130/// validate before mutation. Parameter and UMP events are ignored; patches are
Source note: validate before mutation. Parameter and UMP events are ignored; patches are
Link to line 130131/// an explicit separate prepared API. No MIDI 2.0 synthesis or plug-in export.
Source note: an explicit separate prepared API. No MIDI 2.0 synthesis or plug-in export.
Link to line 131132pub struct SubtractiveSynth {
Declare the state/type or implementation used by this chapter. The instrument has no audio inputs, eight voices and mono/stereo output. Complete blocks validate before mutation. It accepts MIDI/normalized notes, ignores parameter/UMP events and receives prepared patches through a separate API. It has no limiter, MIDI 2.0 synthesis or plug-in export.
Link to line 132133 voices: MpeReceiver<VOICES>,
Fixed-capacity voice and expression ownership managed by the MPE receiver.
Link to line 133134 slots: [Slot; VOICES],
The fixed array of per-voice signal histories.
Link to line 134135 patch: SynthPatch,
The latest validated, rate-matched physical controls and prepared table.
Link to line 135136 config: Option<ProcessConfig>,
Processing limits. An optional configuration in processor-owned state is None until preparation succeeds.
Link to line 136137}
Close the surrounding scope, value or argument list in “Document the instrument boundary”.
Link to line 137Create the inactive default synth
Prepare a default 48 kHz patch, initialize the voice receiver and construct all eight fixed slots. config remains None until a consumer calls prepare.
138impl SubtractiveSynth {
Declare the state/type or implementation used by this chapter. Prepare a default 48 kHz patch, initialize the voice receiver and construct all eight fixed slots. config remains None until a consumer calls prepare.
Link to line 138139 /// Construct an inactive synth with the default saw patch.
Source note: Construct an inactive synth with the default saw patch.
Link to line 139140 pub fn new() -> Self {
Define new. Prepare a default 48 kHz patch, initialize the voice receiver and construct all eight fixed slots. config remains None until a consumer calls prepare.
Link to line 140141 let patch = SynthPatch::new(48_000., DEFAULT_VALUES).expect("default patch");
Design the known-valid default patch at 48 kHz before a consumer has prepared its actual rate.
Link to line 141142 Self {
Continue the surrounding value or expression with this exact operand. Read it together with the preceding statement and the “Create the inactive default synth” explanation above.
Link to line 142143 voices: MpeReceiver::new().expect("fixed voice count"),
Fixed-capacity voice and expression ownership managed by the MPE receiver.
Link to line 143144 slots: std::array::from_fn(|_| Slot::new(&patch)),
The fixed array of per-voice signal histories.
Link to line 144145 patch,
Supply these names as the remaining imports, arguments or fields of the surrounding declaration.
Link to line 145146 config: None,
Processing limits. An optional configuration in processor-owned state is None until preparation succeeds.
Link to line 146147 }
Close the surrounding scope, value or argument list in “Create the inactive default synth”.
Link to line 147148 }
Close the surrounding scope, value or argument list in “Create the inactive default synth”.
Link to line 148Apply a prepared patch without heap operations
Reject a patch whose rate differs from the active configuration before modifying state. Copy fixed patch storage and update oscillator waveform while retaining oscillator phase/filter history. ADSR configuration is latched only when a voice starts.
149 /// Copy a predesigned patch without heap operations. Reject a mismatched
Source note: Copy a predesigned patch without heap operations. Reject a mismatched
Link to line 149150 /// active rate before mutation. ADSR changes apply only to subsequent notes;
Source note: active rate before mutation. ADSR changes apply only to subsequent notes;
Link to line 150151 /// waveform changes retain phase and filter changes retain integrator history.
Source note: waveform changes retain phase and filter changes retain integrator history.
Link to line 151152 pub fn set_patch(&mut self, patch: &SynthPatch) -> Result<(), PatchError> {
Define set_patch. Reject a patch whose rate differs from the active configuration before modifying state. Copy fixed patch storage and update oscillator waveform while retaining oscillator phase/filter history. ADSR configuration is latched only when a voice starts.
Link to line 152153 if self.config.is_some_and(|c| c.sample_rate() != patch.rate) {
Evaluate this condition or visit this range in source order. The statements in the following scope implement “Apply a prepared patch without heap operations”.
Link to line 153154 return Err(PatchError);
Exit this path with an explicit error/fault; the caller must not treat this branch as successfully applied state.
Link to line 154155 }
Close the surrounding scope, value or argument list in “Apply a prepared patch without heap operations”.
Link to line 155156 self.patch.clone_from(patch);
Copy the fixed prepared patch into the owner. Its coefficient array has no heap allocation to destroy.
Link to line 156157 for slot in &mut self.slots {
Reset all fixed voice histories without allocating or removing their storage.
Link to line 157158 slot.oscillator.set_waveform(patch.waveform());
Change waveform for this slot while retaining its oscillator phase.
Link to line 158159 }
Close the surrounding scope, value or argument list in “Apply a prepared patch without heap operations”.
Link to line 159160 Ok(())
Report successful completion without returning a payload.
Link to line 160161 }
Close the surrounding scope, value or argument list in “Apply a prepared patch without heap operations”.
Link to line 161Read voice and envelope snapshots
voices exposes the receiver’s fixed voice array. envelope_levels returns current levels in slot order; presentation and serialization remain outside the audio owner.
162 /// Voice expression snapshots; presentation belongs outside processing.
Source note: Voice expression snapshots; presentation belongs outside processing.
Link to line 162163 pub fn voices(&self) -> &[Voice; VOICES] {
Define voices. voices exposes the receiver’s fixed voice array. envelope_levels returns current levels in slot order; presentation and serialization remain outside the audio owner.
Link to line 163164 self.voices.voices()
Borrow the voice receiver’s fixed snapshot array; presentation remains outside processing.
Link to line 164165 }
Close the surrounding scope, value or argument list in “Read voice and envelope snapshots”.
Link to line 165166 /// VCA/VCF's shared instantaneous envelope levels, in voice-slot order.
Source note: VCA/VCF's shared instantaneous envelope levels, in voice-slot order.
Link to line 166167 pub fn envelope_levels(&self) -> [f64; VOICES] {
Define envelope_levels. voices exposes the receiver’s fixed voice array. envelope_levels returns current levels in slot order; presentation and serialization remain outside the audio owner.
Link to line 167168 self.slots.each_ref().map(|s| s.envelope.level())
Read each slot’s current envelope level into a fixed array without advancing envelope time.
Link to line 168169 }
Close the surrounding scope, value or argument list in “Read voice and envelope snapshots”.
Link to line 169Translate recognized notes into receiver messages
Typed MIDI is passed through. Normalized note-on/off becomes valid short bytes; positive note-on velocity is rounded and kept at least one, while zero velocity remains zero. Other event kinds return without changing the synth.
170 fn apply(&mut self, data: EventData) {
Define apply. Typed MIDI is passed through. Normalized note-on/off becomes valid short bytes; positive note-on velocity is rounded and kept at least one, while zero velocity remains zero. Other event kinds return without changing the synth.
Link to line 170171 let message = match data {
Bind message to the expression on this and any following continuation lines. The chapter above explains how this intermediate value is used.
Link to line 171172 EventData::Midi(m) => m,
Pass an already validated short MIDI message through unchanged.
Link to line 172173 EventData::NoteOn {
Define or initialize EventData with the displayed type/value. Its ownership and use are described in “Translate recognized notes into receiver messages”.
Link to line 173174 channel,
Supply these names as the remaining imports, arguments or fields of the surrounding declaration.
Link to line 174175 key,
Supply these names as the remaining imports, arguments or fields of the surrounding declaration.
Link to line 175176 velocity,
Supply these names as the remaining imports, arguments or fields of the surrounding declaration.
Link to line 176177 } => MidiMessage::from_bytes(&[
Handle the matching pattern on the left using the expression or scope on the right. Other variants are handled by the following arms.
Link to line 177178 0x90 | channel,
Combine the note-on status nibble with the zero-based MIDI channel.
Link to line 178179 key,
Supply these names as the remaining imports, arguments or fields of the surrounding declaration.
Link to line 179180 if velocity > 0. {
Evaluate this condition or visit this range in source order. The statements in the following scope implement “Translate recognized notes into receiver messages”.
Link to line 180181 (velocity * 127.).round().max(1.) as u8
Convert positive normalized velocity to seven-bit MIDI velocity and keep it at least one to avoid note-off semantics.
Link to line 181182 } else {
Continue the surrounding value or expression with this exact operand. Read it together with the preceding statement and the “Translate recognized notes into receiver messages” explanation above.
Link to line 182183 0
Supply this literal numeric entry to the array or argument list being constructed. Its units/addressing follow the containing fixture or configuration.
Link to line 183184 },
Close the surrounding scope, value or argument list in “Translate recognized notes into receiver messages”.
Link to line 184185 ])
Close the current expression or scope; no additional processing operation occurs on this line.
Link to line 185186 .expect("validated note"),
Require the invariant “validated note”. expect panics if it is broken; external inputs must be validated before relying on it.
Link to line 186187 EventData::NoteOff {
Define or initialize EventData with the displayed type/value. Its ownership and use are described in “Translate recognized notes into receiver messages”.
Link to line 187188 channel,
Supply these names as the remaining imports, arguments or fields of the surrounding declaration.
Link to line 188189 key,
Supply these names as the remaining imports, arguments or fields of the surrounding declaration.
Link to line 189190 velocity,
Supply these names as the remaining imports, arguments or fields of the surrounding declaration.
Link to line 190191 } => MidiMessage::from_bytes(&[0x80 | channel, key, (velocity * 127.).round() as u8])
Handle the matching pattern on the left using the expression or scope on the right. Other variants are handled by the following arms.
Link to line 191192 .expect("validated note"),
Require the invariant “validated note”. expect panics if it is broken; external inputs must be validated before relying on it.
Link to line 192193 _ => return,
Ignore the remaining event variants. This synth does not interpret parameter edits or UMP packets.
Link to line 193194 };
Close the surrounding scope, value or argument list in “Translate recognized notes into receiver messages”.
Link to line 194Update only changed slots
Use the receiver’s changed mask. Idle slots clear envelope/filter history. New or stolen slots reset oscillator/filter, latch a new ADSR and start attack. Trigger release once for Released voices. Combine member/master bend with note key to retune frequency, capped below 49% of rate.
195 let changes = self.voices.apply(message).voices;
Apply the message to the voice policy and retain its changed-slot mask and start information.
Link to line 195196 for i in 0..VOICES {
Visit the fixed eight-slot voice capacity; no dynamic voice list is created.
Link to line 196197 if changes.changed & (1 << i) == 0 {
Skip a slot whose bit is absent from the changed mask.
Link to line 197198 continue;
Skip the rest of this iteration and move to the next item or frame.
Link to line 198199 }
Close the surrounding scope, value or argument list in “Update only changed slots”.
Link to line 199200 let voice = self.voices.voices()[i];
Copy the policy snapshot for this slot so DSP state can be borrowed separately.
Link to line 200201 let slot = &mut self.slots[i];
Borrow this slot’s oscillator, envelope and filter histories mutably.
Link to line 201202 if voice.phase == VoicePhase::Idle {
An idle policy slot owns no sounding note and can be skipped or cleared in this path.
Link to line 202203 slot.envelope.reset();
Clear the envelope’s current level and phase while retaining its prepared configuration.
Link to line 203204 slot.filter.reset();
Clear the filter’s integrator history while retaining its coefficients.
Link to line 204205 continue;
Skip the rest of this iteration and move to the next item or frame.
Link to line 205206 }
Close the surrounding scope, value or argument list in “Update only changed slots”.
Link to line 206207 if changes.started.is_some_and(|s| s.index == i) {
Recognize that this changed slot is the newly started or stolen voice.
Link to line 207208 slot.oscillator.reset();
Reset oscillator phase for a new/stolen voice or an explicit processor reset.
Link to line 208209 slot.filter.reset();
Clear the filter’s integrator history while retaining its coefficients.
Link to line 209210 slot.envelope = Adsr::new(self.patch.envelope);
Latch the current patch’s ADSR configuration at note start.
Link to line 210211 slot.envelope.note_on();
Trigger the attack stage for the new note.
Link to line 211212 slot.releasing = false;
Record that this slot has not yet triggered release.
Link to line 212213 }
Close the surrounding scope, value or argument list in “Update only changed slots”.
Link to line 213214 if voice.phase == VoicePhase::Released && !slot.releasing {
Trigger the DSP release only once after the voice policy marks the note released.
Link to line 214215 slot.envelope.note_off();
Begin the latched envelope’s release from its current level.
Link to line 215216 slot.releasing = true;
Remember that release has begun so later expression changes do not restart it.
Link to line 216217 }
Close the surrounding scope, value or argument list in “Update only changed slots”.
Link to line 217218 let pitch = f64::from(voice.note.expect("occupied").key)
Start pitch from the occupied voice’s MIDI key; expression is added on the following line.
Link to line 218219 + f64::from(voice.expression.member_bend + voice.expression.master_bend);
Add per-member and shared master pitch expression. Their independent ownership is demonstrated by the MPE trace.
Link to line 219220 let frequency = (440. * 2_f64.powf((pitch - 69.) / 12.)).min(self.patch.rate * 0.49);
Convert semitone pitch relative to MIDI key 69 (A4) into a frequency ratio. Twelve semitones multiply frequency by two.
Link to line 220221 slot.oscillator
Continue the surrounding value or expression with this exact operand. Read it together with the preceding statement and the “Update only changed slots” explanation above.
Link to line 221222 .set_frequency(frequency)
Retune the prepared oscillator without resetting its phase.
Link to line 222223 .expect("bounded pitch");
Require the invariant “bounded pitch”. expect panics if it is broken; external inputs must be validated before relying on it.
Link to line 223224 }
Close the surrounding scope, value or argument list in “Update only changed slots”.
Link to line 224225 }
Close the surrounding scope, value or argument list in “Update only changed slots”.
Link to line 225226}
Close the surrounding scope, value or argument list in “Update only changed slots”.
Link to line 226Implement Default through the same constructor
Default delegates to new, avoiding a separate set of defaults. The following Processor implementation shares the same lifecycle as the delay and gain references.
227impl Default for SubtractiveSynth {
Declare the state/type or implementation used by this chapter. Default delegates to new, avoiding a separate set of defaults. The following Processor implementation shares the same lifecycle as the delay and gain references.
Link to line 227228 fn default() -> Self {
Define default. Default delegates to new, avoiding a separate set of defaults. The following Processor implementation shares the same lifecycle as the delay and gain references.
Link to line 228229 Self::new()
Delegate Default to the same constructor so default initialization has one implementation.
Link to line 229230 }
Close the surrounding scope, value or argument list in “Implement Default through the same constructor”.
Link to line 230231}
Close the surrounding scope, value or argument list in “Implement Default through the same constructor”.
Link to line 231Validate preparation and build rate-matched slots
Clear the active configuration and validate capacities, no-input mono/stereo layout, auxiliary buses and output-event capacity. Rebuild the patch for the actual rate, reset voices, reconstruct slots and store configuration only after setup succeeds.
232impl Processor for SubtractiveSynth {
Declare the state/type or implementation used by this chapter. Clear the active configuration and validate capacities, no-input mono/stereo layout, auxiliary buses and output-event capacity. Rebuild the patch for the actual rate, reset voices, reconstruct slots and store configuration only after setup succeeds.
Link to line 232233 fn prepare(&mut self, config: ProcessConfig) -> Result<(), PrepareError> {
Define prepare. Clear the active configuration and validate capacities, no-input mono/stereo layout, auxiliary buses and output-event capacity. Rebuild the patch for the actual rate, reset voices, reconstruct slots and store configuration only after setup succeeds.
Link to line 233234 self.config = None;
Mark the processor inactive before preparation or after deactivation; a failed setup must not retain an apparently valid configuration.
Link to line 234235 if config.maximum_frames() > 1024 || config.maximum_events() > 128 {
Evaluate this condition or visit this range in source order. The statements in the following scope implement “Validate preparation and build rate-matched slots”.
Link to line 235236 return Err(PrepareError::InvalidMaximumFrames);
Exit this path with an explicit error/fault; the caller must not treat this branch as successfully applied state.
Link to line 236237 }
Close the surrounding scope, value or argument list in “Validate preparation and build rate-matched slots”.
Link to line 237238 if ![AudioLayout::MONO_INSTRUMENT, AudioLayout::STEREO_INSTRUMENT]
Evaluate this condition or visit this range in source order. The statements in the following scope implement “Validate preparation and build rate-matched slots”.
Link to line 238239 .contains(&config.layout())
Call the displayed operation with these arguments as the next step in “Validate preparation and build rate-matched slots”. The chapter describes its role and validation.
Link to line 239240 || !config.auxiliary_buses().is_empty()
Accept this alternative condition in the surrounding check; evaluation stops once the combined condition is true.
Link to line 240241 || config.maximum_output_events() != 0
Accept this alternative condition in the surrounding check; evaluation stops once the combined condition is true.
Link to line 241242 {
Close the surrounding scope, value or argument list in “Validate preparation and build rate-matched slots”.
Link to line 242243 return Err(PrepareError::UnsupportedLayout);
Exit this path with an explicit error/fault; the caller must not treat this branch as successfully applied state.
Link to line 243244 }
Close the surrounding scope, value or argument list in “Validate preparation and build rate-matched slots”.
Link to line 244245 self.patch = SynthPatch::new(config.sample_rate(), self.patch.values)
Assign the computed value to self.patch. The right-hand expression supplies the new value for this step.
Link to line 245246 .map_err(|_| PrepareError::InvalidSampleRate)?;
The ? operator returns an error immediately on failure and unwraps the success value for the next step.
Link to line 246247 self.slots = std::array::from_fn(|_| Slot::new(&self.patch));
Rebuild the fixed eight DSP slots using the rate-matched prepared patch.
Link to line 247248 self.voices.reset();
Clear voice ownership, expression and receiver state.
Link to line 248249 self.config = Some(config);
Mark preparation successful only after the required state and storage have been established.
Link to line 249250 Ok(())
Report successful completion without returning a payload.
Link to line 250251 }
Close the surrounding scope, value or argument list in “Validate preparation and build rate-matched slots”.
Link to line 251Validate before changing histories
Require prepared configuration and a completely valid block. Faults clear active audio/emitted events. Empty blocks still apply their offset-zero events without stepping an envelope or oscillator.
252 fn process(&mut self, block: &mut ProcessBlock<'_, '_, '_>) -> ProcessStatus {
Define process. Require prepared configuration and a completely valid block. Faults clear active audio/emitted events. Empty blocks still apply their offset-zero events without stepping an envelope or oscillator.
Link to line 252253 if let Err(error) = self
Evaluate this condition or visit this range in source order. The statements in the following scope implement “Validate before changing histories”.
Link to line 253254 .config
Continue the surrounding value or expression with this exact operand. Read it together with the preceding statement and the “Validate before changing histories” explanation above.
Link to line 254255 .ok_or(ProcessError::NotPrepared)
Convert missing preparation into a typed fault instead of processing with unknown limits.
Link to line 255256 .and_then(|c| c.validate_block(block))
Validate timing, layout and storage against the prepared configuration before changing state.
Link to line 256257 {
Close the surrounding scope, value or argument list in “Validate before changing histories”.
Link to line 257258 block.clear_outputs();
Silence active output samples and discard emitted events. This is the documented failure path, and also clears unselected device channels before routing.
Link to line 258259 return ProcessStatus::Fault(error);
Exit this path with an explicit error/fault; the caller must not treat this branch as successfully applied state.
Link to line 259260 }
Close the surrounding scope, value or argument list in “Validate before changing histories”.
Link to line 260261 let mut next = 0;
Start the cursor at the first input event; the cursor advances as frame-local edits are consumed.
Link to line 261262 if block.audio.frames() == 0 {
Handle event-only blocks separately: controls or note state can change without rendering a sample.
Link to line 262263 for event in block.events {
Visit the supplied event slice in order. No temporary event collection is created.
Link to line 263264 self.apply(event.data);
Apply this validated event payload to the processor’s control or voice state.
Link to line 264265 }
Close the surrounding scope, value or argument list in “Validate before changing histories”.
Link to line 265266 }
Close the surrounding scope, value or argument list in “Validate before changing histories”.
Link to line 266Process a timed frame and active voice slots
Apply current-frame events first. For each occupied slot, advance the shared envelope and combine bounded member/master timbre. Use envelope plus timbre excursion to select a prepared coefficient-table entry. No coefficient design occurs inside this loop.
267 for frame in 0..block.audio.frames() {
Render each active frame once. This is the outer sample loop, independent of output channel count.
Link to line 267268 while next < block.events.len() && block.events[next].frame_offset == frame {
Consume all events scheduled for this frame before its sample is computed, preserving their supplied order.
Link to line 268269 self.apply(block.events[next].data);
Apply the next event before rendering the sample at its exact block-local offset.
Link to line 269270 next += 1;
Advance the event cursor so the same edit cannot be applied twice.
Link to line 270271 }
Close the surrounding scope, value or argument list in “Process a timed frame and active voice slots”.
Link to line 271272 let mut mix = 0.;
Initialize the accumulated voice output for this frame.
Link to line 272273 for i in 0..VOICES {
Visit the fixed eight-slot voice capacity; no dynamic voice list is created.
Link to line 273274 let voice = self.voices.voices()[i];
Copy the policy snapshot for this slot so DSP state can be borrowed separately.
Link to line 274275 if voice.phase == VoicePhase::Idle {
An idle policy slot owns no sounding note and can be skipped or cleared in this path.
Link to line 275276 continue;
Skip the rest of this iteration and move to the next item or frame.
Link to line 276277 }
Close the surrounding scope, value or argument list in “Process a timed frame and active voice slots”.
Link to line 277278 let slot = &mut self.slots[i];
Borrow this slot’s oscillator, envelope and filter histories mutably.
Link to line 278279 let envelope = slot.envelope.process_sample();
Advance the slot’s single shared VCA/VCF envelope by one sample.
Link to line 279280 let timbre = f64::from(
Bind timbre to the expression on this and any following continuation lines. The chapter above explains how this intermediate value is used.
Link to line 280281 (voice.expression.timbre + voice.expression.master_timbre).clamp(0., 1.),
Combine member and master timbre, clamping the sum to the supported normalized range before choosing a filter-table entry.
Link to line 281282 );
Close the current expression or scope; no additional processing operation occurs on this line.
Link to line 282283 let excursion = envelope * f64::from(self.patch.values[4]) + timbre * 24.;
Add envelope-controlled cutoff excursion to the extra excursion from normalized timbre.
Link to line 283284 let index = (excursion / f64::from(self.patch.values[4] + 24.)
Bind index to the expression on this and any following continuation lines. The chapter above explains how this intermediate value is used.
Link to line 284285 * (FILTER_STEPS - 1) as f64)
Continue the surrounding value or expression with this exact operand. Read it together with the preceding statement and the “Process a timed frame and active voice slots” explanation above.
Link to line 285286 .round() as usize;
Round to the nearest integer-valued result before the following conversion or indexing operation.
Link to line 286287 slot.filter
Continue the surrounding value or expression with this exact operand. Read it together with the preceding statement and the “Process a timed frame and active voice slots” explanation above.
Link to line 287288 .set_coefficients(self.patch.coefficients[index.min(FILTER_STEPS - 1)]);
Select the nearest prepared filter entry, clamping the index to the table’s last element.
Link to line 288Render oscillator → filter → amplitude
Combine bounded pressure, render the oscillator through the low-pass output and scale by envelope, velocity and up to 50% pressure boost. Finish voice ownership only after the envelope becomes Idle. Mix all voices with level/8 and initialize each output channel; there is no output limiter.
289 let pressure = f64::from(
Bind pressure to the expression on this and any following continuation lines. The chapter above explains how this intermediate value is used.
Link to line 289290 (voice.expression.pressure + voice.expression.master_pressure).clamp(0., 1.),
Combine member and master pressure with a normalized clamp before applying the example’s amplitude mapping.
Link to line 290291 );
Close the current expression or scope; no additional processing operation occurs on this line.
Link to line 291292 mix += slot
Add the computed value to mix. The right-hand expression supplies the new value for this step.
Link to line 292293 .filter
Continue the surrounding value or expression with this exact operand. Read it together with the preceding statement and the “Render oscillator → filter → amplitude” explanation above.
Link to line 293294 .process_sample(slot.oscillator.process_sample())
Generate one oscillator sample and run it through this slot’s filter.
Link to line 294295 .low_pass
Select the filter’s low-pass output from its simultaneous response record.
Link to line 295296 * envelope
Apply the same envelope level as the voice’s VCA gain.
Link to line 296297 * f64::from(voice.velocity)
Scale this voice by its normalized note velocity.
Link to line 297298 * (1. + 0.5 * pressure);
Map pressure to a gain factor from one to 1.5 after the normalized clamp.
Link to line 298299 if slot.envelope.phase() == EnvelopePhase::Idle {
The release has reached zero, so this voice no longer owns audible envelope output.
Link to line 299300 self.voices.finish(i).expect("fixed slot");
Return the finished DSP slot to the receiver’s idle pool.
Link to line 300301 }
Close the surrounding scope, value or argument list in “Render oscillator → filter → amplitude”.
Link to line 301302 }
Close the surrounding scope, value or argument list in “Render oscillator → filter → amplitude”.
Link to line 302303 let sample = (mix * f64::from(self.patch.values[1]) / VOICES as f64) as f32;
Scale the sum by the fixed eight-voice capacity rather than the currently active voice count. This is gain scaling, not overload protection.
Link to line 303304 for channel in 0..block.audio.output_channels() {
Evaluate this condition or visit this range in source order. The statements in the following scope implement “Render oscillator → filter → amplitude”.
Link to line 304305 block.audio.output_mut(channel).expect("output")[frame] = sample;
Write the same mixed instrument sample to each active output channel.
Link to line 305306 }
Close the surrounding scope, value or argument list in “Render oscillator → filter → amplitude”.
Link to line 306307 }
Close the surrounding scope, value or argument list in “Render oscillator → filter → amplitude”.
Link to line 307308 ProcessStatus::Continue
Represent a successfully processed block. The offline renderer treats any different status as a failed rendering session.
Link to line 308309 }
Close the surrounding scope, value or argument list in “Render oscillator → filter → amplitude”.
Link to line 309Reset the complete fixed voice state
Reset the receiver, envelope, filter and oscillator for every slot and clear release flags. deactivate resets then clears active configuration. Tail is conservatively Infinite because held notes need not end by themselves.
310 fn reset(&mut self) {
Define reset. Reset the receiver, envelope, filter and oscillator for every slot and clear release flags. deactivate resets then clears active configuration. Tail is conservatively Infinite because held notes need not end by themselves.
Link to line 310311 self.voices.reset();
Clear voice ownership, expression and receiver state.
Link to line 311312 for slot in &mut self.slots {
Reset all fixed voice histories without allocating or removing their storage.
Link to line 312313 slot.envelope.reset();
Clear the envelope’s current level and phase while retaining its prepared configuration.
Link to line 313314 slot.filter.reset();
Clear the filter’s integrator history while retaining its coefficients.
Link to line 314315 slot.oscillator.reset();
Reset oscillator phase for a new/stolen voice or an explicit processor reset.
Link to line 315316 slot.releasing = false;
Record that this slot has not yet triggered release.
Link to line 316317 }
Close the surrounding scope, value or argument list in “Reset the complete fixed voice state”.
Link to line 317318 }
Close the surrounding scope, value or argument list in “Reset the complete fixed voice state”.
Link to line 318319 fn deactivate(&mut self) {
Define deactivate. Reset the receiver, envelope, filter and oscillator for every slot and clear release flags. deactivate resets then clears active configuration. Tail is conservatively Infinite because held notes need not end by themselves.
Link to line 319320 self.reset();
Clear the processor’s signal history through its existing reset implementation.
Link to line 320321 self.config = None;
Mark the processor inactive before preparation or after deactivation; a failed setup must not retain an apparently valid configuration.
Link to line 321322 }
Close the surrounding scope, value or argument list in “Reset the complete fixed voice state”.
Link to line 322323 fn tail(&self) -> Tail {
Define tail. Reset the receiver, envelope, filter and oscillator for every slot and clear release flags. deactivate resets then clears active configuration. Tail is conservatively Infinite because held notes need not end by themselves.
Link to line 323324 Tail::Infinite
Declare a conservative unbounded tail. This is a host scheduling hint, not a promise of endless audible output.
Link to line 324325 }
Close the surrounding scope, value or argument list in “Reset the complete fixed voice state”.
Link to line 325326}
Close the surrounding scope, value or argument list in “Reset the complete fixed voice state”.
Link to line 326