AUDIO
Source snapshot · 12 October 2026

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 --locked
Chapters
  1. Imports, controls and fixed capacities lines 1–39
  2. Represent a prepared patch lines 40–51
  3. Validate the rate and every physical control lines 52–87
  4. Expose values and decode waveform selection lines 88–100
  5. Keep per-voice histories together lines 101–117
  6. Document the instrument boundary lines 118–137
  7. Create the inactive default synth lines 138–148
  8. Apply a prepared patch without heap operations lines 149–161
  9. Read voice and envelope snapshots lines 162–169
  10. Translate recognized notes into receiver messages lines 170–194
  11. Update only changed slots lines 195–226
  12. Implement Default through the same constructor lines 227–231
  13. Validate preparation and build rate-matched slots lines 232–251
  14. Validate before changing histories lines 252–266
  15. Process a timed frame and active voice slots lines 267–288
  16. Render oscillator → filter → amplitude lines 289–309
  17. 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 1
2use ravet_foundation::{

Bring these module/type names into scope for “Imports, controls and fixed capacities”; the surrounding chapter explains their responsibilities.

Link to line 2
3 AudioLayout, EventData, MidiMessage, PrepareError, ProcessBlock, ProcessConfig, ProcessError,

Supply these names as the remaining imports, arguments or fields of the surrounding declaration.

Link to line 3
4 ProcessStatus, Processor, Tail,

Supply these names as the remaining imports, arguments or fields of the surrounding declaration.

Link to line 4
5};

Close the surrounding scope, value or argument list in “Imports, controls and fixed capacities”.

Link to line 5
6use 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 6
7use ravet_signal::{

Bring these module/type names into scope for “Imports, controls and fixed capacities”; the surrounding chapter explains their responsibilities.

Link to line 7
8 Adsr, AdsrConfig, EnvelopePhase, Oscillator, StateVariableCoefficients, StateVariableFilter,

Supply these names as the remaining imports, arguments or fields of the surrounding declaration.

Link to line 8
9 Waveform,

Supply these names as the remaining imports, arguments or fields of the surrounding declaration.

Link to line 9
10};

Close the surrounding scope, value or argument list in “Imports, controls and fixed capacities”.

Link to line 10
11

Blank line separating the surrounding statements.

Link to line 11
12/// Fixed polyphony; mix scales by this capacity.

Source note: Fixed polyphony; mix scales by this capacity.

Link to line 12
13pub 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 13
14/// 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 14
15pub 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 15
16/// 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 16
17pub 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 17
18 (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 18
19 (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 19
20 (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 20
21 (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 21
22 (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 22
23 (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 23
24 (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 24
25 (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 25
26 (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 26
27];

Close the surrounding scope, value or argument list in “Imports, controls and fixed capacities”.

Link to line 27
28const 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 28
29

Blank line separating the surrounding statements.

Link to line 29
30/// 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 30
31#[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 31
32pub 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 32
33impl 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 33
34 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 34
35 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 35
36 }

Close the surrounding scope, value or argument list in “Imports, controls and fixed capacities”.

Link to line 36
37}

Close the surrounding scope, value or argument list in “Imports, controls and fixed capacities”.

Link to line 37
38impl 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 38
39

Blank line separating the surrounding statements.

Link to line 39

Represent 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 40
41/// 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 41
42/// 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 42
43/// 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 43
44/// 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 44
45#[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 45
46pub 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 46
47 values: [f32; 9],

Requested physical controls in the order documented for this processor.

Link to line 47
48 rate: f64,

Sample rate used to design this patch’s envelope and coefficients.

Link to line 48
49 envelope: AdsrConfig,

The shared VCA/VCF envelope state or its prepared configuration.

Link to line 49
50 coefficients: [StateVariableCoefficients; FILTER_STEPS],

Fixed prepared coefficient storage, avoiding coefficient design in the sample loop.

Link to line 50
51}

Close the surrounding scope, value or argument list in “Represent a prepared patch”.

Link to line 51

Validate 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 52
53 /// 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 53
54 /// 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 54
55 /// 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 55
56 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 56
57 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 57
58 || !(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 58
59 || values

Accept this alternative condition in the surrounding check; evaluation stops once the combined condition is true.

Link to line 59
60 .iter()

Borrow collection entries for iteration without moving or copying the whole collection.

Link to line 60
61 .zip(RANGES)

Pair each entry with the corresponding entry in the second sequence. Iteration ends when either sequence ends.

Link to line 61
62 .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 62
63 || values[0].fract() != 0.

Require the waveform selector to be an integer as well as within its numeric range.

Link to line 63
64 {

Close the surrounding scope, value or argument list in “Validate the rate and every physical control”.

Link to line 64
65 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 65
66 }

Close the surrounding scope, value or argument list in “Validate the rate and every physical control”.

Link to line 66
67 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 67
68 rate,

Supply these names as the remaining imports, arguments or fields of the surrounding declaration.

Link to line 68
69 f64::from(values[5]) / 1000.,

Convert attack time from milliseconds to seconds for AdsrConfig.

Link to line 69
70 f64::from(values[6]) / 1000.,

Convert decay time from milliseconds to seconds for AdsrConfig.

Link to line 70
71 f64::from(values[7]),

Pass the linear sustain level directly; sustain is a level rather than a duration.

Link to line 71
72 f64::from(values[8]) / 1000.,

Convert release time from milliseconds to seconds for AdsrConfig.

Link to line 72
73 )

Close the surrounding scope, value or argument list in “Validate the rate and every physical control”.

Link to line 73
74 .map_err(|_| PatchError)?;

The ? operator returns an error immediately on failure and unwraps the success value for the next step.

Link to line 74
75 let coefficients = std::array::from_fn(|index| {

Build all 256 coefficient entries in a fixed array on the control/preparation path.

Link to line 75
76 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 76
77 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 77
78 StateVariableCoefficients::new(rate, cutoff, f64::from(values[3]))

Design filter coefficients from sample rate, bounded cutoff and the physical resonance Q.

Link to line 78
79 .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 79
80 });

Close the current expression or scope; no additional processing operation occurs on this line.

Link to line 80
81 Ok(Self {

Construct the success/error variant selected by the surrounding match or function return. The enclosed value becomes its payload.

Link to line 81
82 values,

Supply these names as the remaining imports, arguments or fields of the surrounding declaration.

Link to line 82
83 rate,

Supply these names as the remaining imports, arguments or fields of the surrounding declaration.

Link to line 83
84 envelope,

Supply these names as the remaining imports, arguments or fields of the surrounding declaration.

Link to line 84
85 coefficients,

Supply these names as the remaining imports, arguments or fields of the surrounding declaration.

Link to line 85
86 })

Close the current expression or scope; no additional processing operation occurs on this line.

Link to line 86
87 }

Close the surrounding scope, value or argument list in “Validate the rate and every physical control”.

Link to line 87

Expose 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 88
89 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 89
90 self.values

Return a copy of the fixed-size requested control array; this does not advance a smoother.

Link to line 90
91 }

Close the surrounding scope, value or argument list in “Expose values and decode waveform selection”.

Link to line 91
92 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 92
93 [

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 93
94 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 94
95 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 95
96 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 96
97 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 97
98 ][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 98
99 }

Close the surrounding scope, value or argument list in “Expose values and decode waveform selection”.

Link to line 99
100}

Close the surrounding scope, value or argument list in “Expose values and decode waveform selection”.

Link to line 100

Keep 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 101
102 oscillator: Oscillator,

Per-voice oscillator phase and waveform state.

Link to line 102
103 envelope: Adsr,

The shared VCA/VCF envelope state or its prepared configuration.

Link to line 103
104 filter: StateVariableFilter,

Per-voice filter history, owned independently of other voices.

Link to line 104
105 releasing: bool,

Whether release has already been triggered for this slot.

Link to line 105
106}

Close the surrounding scope, value or argument list in “Keep per-voice histories together”.

Link to line 106
107impl 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 107
108 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 108
109 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 109
110 oscillator: Oscillator::new(patch.rate, 440., patch.waveform())

Per-voice oscillator phase and waveform state.

Link to line 110
111 .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 111
112 envelope: Adsr::new(patch.envelope),

The shared VCA/VCF envelope state or its prepared configuration.

Link to line 112
113 filter: StateVariableFilter::new(patch.coefficients[0]),

Per-voice filter history, owned independently of other voices.

Link to line 113
114 releasing: false,

Whether release has already been triggered for this slot.

Link to line 114
115 }

Close the surrounding scope, value or argument list in “Keep per-voice histories together”.

Link to line 115
116 }

Close the surrounding scope, value or argument list in “Keep per-voice histories together”.

Link to line 116
117}

Close the surrounding scope, value or argument list in “Keep per-voice histories together”.

Link to line 117

Document 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 118
119///

Source note:

Link to line 119
120/// 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 120
121/// 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 121
122/// 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 122
123/// 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 123
124/// 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 124
125/// 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 125
126/// 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 126
127///

Source note:

Link to line 127
128/// 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 128
129/// 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 129
130/// 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 130
131/// 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 131
132pub 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 132
133 voices: MpeReceiver<VOICES>,

Fixed-capacity voice and expression ownership managed by the MPE receiver.

Link to line 133
134 slots: [Slot; VOICES],

The fixed array of per-voice signal histories.

Link to line 134
135 patch: SynthPatch,

The latest validated, rate-matched physical controls and prepared table.

Link to line 135
136 config: Option<ProcessConfig>,

Processing limits. An optional configuration in processor-owned state is None until preparation succeeds.

Link to line 136
137}

Close the surrounding scope, value or argument list in “Document the instrument boundary”.

Link to line 137

Create 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 138
139 /// Construct an inactive synth with the default saw patch.

Source note: Construct an inactive synth with the default saw patch.

Link to line 139
140 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 140
141 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 141
142 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 142
143 voices: MpeReceiver::new().expect("fixed voice count"),

Fixed-capacity voice and expression ownership managed by the MPE receiver.

Link to line 143
144 slots: std::array::from_fn(|_| Slot::new(&patch)),

The fixed array of per-voice signal histories.

Link to line 144
145 patch,

Supply these names as the remaining imports, arguments or fields of the surrounding declaration.

Link to line 145
146 config: None,

Processing limits. An optional configuration in processor-owned state is None until preparation succeeds.

Link to line 146
147 }

Close the surrounding scope, value or argument list in “Create the inactive default synth”.

Link to line 147
148 }

Close the surrounding scope, value or argument list in “Create the inactive default synth”.

Link to line 148

Apply 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 149
150 /// 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 150
151 /// 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 151
152 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 152
153 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 153
154 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 154
155 }

Close the surrounding scope, value or argument list in “Apply a prepared patch without heap operations”.

Link to line 155
156 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 156
157 for slot in &mut self.slots {

Reset all fixed voice histories without allocating or removing their storage.

Link to line 157
158 slot.oscillator.set_waveform(patch.waveform());

Change waveform for this slot while retaining its oscillator phase.

Link to line 158
159 }

Close the surrounding scope, value or argument list in “Apply a prepared patch without heap operations”.

Link to line 159
160 Ok(())

Report successful completion without returning a payload.

Link to line 160
161 }

Close the surrounding scope, value or argument list in “Apply a prepared patch without heap operations”.

Link to line 161

Read 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 162
163 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 163
164 self.voices.voices()

Borrow the voice receiver’s fixed snapshot array; presentation remains outside processing.

Link to line 164
165 }

Close the surrounding scope, value or argument list in “Read voice and envelope snapshots”.

Link to line 165
166 /// 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 166
167 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 167
168 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 168
169 }

Close the surrounding scope, value or argument list in “Read voice and envelope snapshots”.

Link to line 169

Translate 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 170
171 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 171
172 EventData::Midi(m) => m,

Pass an already validated short MIDI message through unchanged.

Link to line 172
173 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 173
174 channel,

Supply these names as the remaining imports, arguments or fields of the surrounding declaration.

Link to line 174
175 key,

Supply these names as the remaining imports, arguments or fields of the surrounding declaration.

Link to line 175
176 velocity,

Supply these names as the remaining imports, arguments or fields of the surrounding declaration.

Link to line 176
177 } => 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 177
178 0x90 | channel,

Combine the note-on status nibble with the zero-based MIDI channel.

Link to line 178
179 key,

Supply these names as the remaining imports, arguments or fields of the surrounding declaration.

Link to line 179
180 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 180
181 (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 181
182 } 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 182
183 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 183
184 },

Close the surrounding scope, value or argument list in “Translate recognized notes into receiver messages”.

Link to line 184
185 ])

Close the current expression or scope; no additional processing operation occurs on this line.

Link to line 185
186 .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 186
187 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 187
188 channel,

Supply these names as the remaining imports, arguments or fields of the surrounding declaration.

Link to line 188
189 key,

Supply these names as the remaining imports, arguments or fields of the surrounding declaration.

Link to line 189
190 velocity,

Supply these names as the remaining imports, arguments or fields of the surrounding declaration.

Link to line 190
191 } => 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 191
192 .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 192
193 _ => return,

Ignore the remaining event variants. This synth does not interpret parameter edits or UMP packets.

Link to line 193
194 };

Close the surrounding scope, value or argument list in “Translate recognized notes into receiver messages”.

Link to line 194

Update 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 195
196 for i in 0..VOICES {

Visit the fixed eight-slot voice capacity; no dynamic voice list is created.

Link to line 196
197 if changes.changed & (1 << i) == 0 {

Skip a slot whose bit is absent from the changed mask.

Link to line 197
198 continue;

Skip the rest of this iteration and move to the next item or frame.

Link to line 198
199 }

Close the surrounding scope, value or argument list in “Update only changed slots”.

Link to line 199
200 let voice = self.voices.voices()[i];

Copy the policy snapshot for this slot so DSP state can be borrowed separately.

Link to line 200
201 let slot = &mut self.slots[i];

Borrow this slot’s oscillator, envelope and filter histories mutably.

Link to line 201
202 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 202
203 slot.envelope.reset();

Clear the envelope’s current level and phase while retaining its prepared configuration.

Link to line 203
204 slot.filter.reset();

Clear the filter’s integrator history while retaining its coefficients.

Link to line 204
205 continue;

Skip the rest of this iteration and move to the next item or frame.

Link to line 205
206 }

Close the surrounding scope, value or argument list in “Update only changed slots”.

Link to line 206
207 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 207
208 slot.oscillator.reset();

Reset oscillator phase for a new/stolen voice or an explicit processor reset.

Link to line 208
209 slot.filter.reset();

Clear the filter’s integrator history while retaining its coefficients.

Link to line 209
210 slot.envelope = Adsr::new(self.patch.envelope);

Latch the current patch’s ADSR configuration at note start.

Link to line 210
211 slot.envelope.note_on();

Trigger the attack stage for the new note.

Link to line 211
212 slot.releasing = false;

Record that this slot has not yet triggered release.

Link to line 212
213 }

Close the surrounding scope, value or argument list in “Update only changed slots”.

Link to line 213
214 if voice.phase == VoicePhase::Released && !slot.releasing {

Trigger the DSP release only once after the voice policy marks the note released.

Link to line 214
215 slot.envelope.note_off();

Begin the latched envelope’s release from its current level.

Link to line 215
216 slot.releasing = true;

Remember that release has begun so later expression changes do not restart it.

Link to line 216
217 }

Close the surrounding scope, value or argument list in “Update only changed slots”.

Link to line 217
218 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 218
219 + 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 219
220 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 220
221 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 221
222 .set_frequency(frequency)

Retune the prepared oscillator without resetting its phase.

Link to line 222
223 .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 223
224 }

Close the surrounding scope, value or argument list in “Update only changed slots”.

Link to line 224
225 }

Close the surrounding scope, value or argument list in “Update only changed slots”.

Link to line 225
226}

Close the surrounding scope, value or argument list in “Update only changed slots”.

Link to line 226

Implement 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 227
228 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 228
229 Self::new()

Delegate Default to the same constructor so default initialization has one implementation.

Link to line 229
230 }

Close the surrounding scope, value or argument list in “Implement Default through the same constructor”.

Link to line 230
231}

Close the surrounding scope, value or argument list in “Implement Default through the same constructor”.

Link to line 231

Validate 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 232
233 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 233
234 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 234
235 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 235
236 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 236
237 }

Close the surrounding scope, value or argument list in “Validate preparation and build rate-matched slots”.

Link to line 237
238 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 238
239 .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 239
240 || !config.auxiliary_buses().is_empty()

Accept this alternative condition in the surrounding check; evaluation stops once the combined condition is true.

Link to line 240
241 || config.maximum_output_events() != 0

Accept this alternative condition in the surrounding check; evaluation stops once the combined condition is true.

Link to line 241
242 {

Close the surrounding scope, value or argument list in “Validate preparation and build rate-matched slots”.

Link to line 242
243 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 243
244 }

Close the surrounding scope, value or argument list in “Validate preparation and build rate-matched slots”.

Link to line 244
245 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 245
246 .map_err(|_| PrepareError::InvalidSampleRate)?;

The ? operator returns an error immediately on failure and unwraps the success value for the next step.

Link to line 246
247 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 247
248 self.voices.reset();

Clear voice ownership, expression and receiver state.

Link to line 248
249 self.config = Some(config);

Mark preparation successful only after the required state and storage have been established.

Link to line 249
250 Ok(())

Report successful completion without returning a payload.

Link to line 250
251 }

Close the surrounding scope, value or argument list in “Validate preparation and build rate-matched slots”.

Link to line 251

Validate 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 252
253 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 253
254 .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 254
255 .ok_or(ProcessError::NotPrepared)

Convert missing preparation into a typed fault instead of processing with unknown limits.

Link to line 255
256 .and_then(|c| c.validate_block(block))

Validate timing, layout and storage against the prepared configuration before changing state.

Link to line 256
257 {

Close the surrounding scope, value or argument list in “Validate before changing histories”.

Link to line 257
258 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 258
259 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 259
260 }

Close the surrounding scope, value or argument list in “Validate before changing histories”.

Link to line 260
261 let mut next = 0;

Start the cursor at the first input event; the cursor advances as frame-local edits are consumed.

Link to line 261
262 if block.audio.frames() == 0 {

Handle event-only blocks separately: controls or note state can change without rendering a sample.

Link to line 262
263 for event in block.events {

Visit the supplied event slice in order. No temporary event collection is created.

Link to line 263
264 self.apply(event.data);

Apply this validated event payload to the processor’s control or voice state.

Link to line 264
265 }

Close the surrounding scope, value or argument list in “Validate before changing histories”.

Link to line 265
266 }

Close the surrounding scope, value or argument list in “Validate before changing histories”.

Link to line 266

Process 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 267
268 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 268
269 self.apply(block.events[next].data);

Apply the next event before rendering the sample at its exact block-local offset.

Link to line 269
270 next += 1;

Advance the event cursor so the same edit cannot be applied twice.

Link to line 270
271 }

Close the surrounding scope, value or argument list in “Process a timed frame and active voice slots”.

Link to line 271
272 let mut mix = 0.;

Initialize the accumulated voice output for this frame.

Link to line 272
273 for i in 0..VOICES {

Visit the fixed eight-slot voice capacity; no dynamic voice list is created.

Link to line 273
274 let voice = self.voices.voices()[i];

Copy the policy snapshot for this slot so DSP state can be borrowed separately.

Link to line 274
275 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 275
276 continue;

Skip the rest of this iteration and move to the next item or frame.

Link to line 276
277 }

Close the surrounding scope, value or argument list in “Process a timed frame and active voice slots”.

Link to line 277
278 let slot = &mut self.slots[i];

Borrow this slot’s oscillator, envelope and filter histories mutably.

Link to line 278
279 let envelope = slot.envelope.process_sample();

Advance the slot’s single shared VCA/VCF envelope by one sample.

Link to line 279
280 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 280
281 (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 281
282 );

Close the current expression or scope; no additional processing operation occurs on this line.

Link to line 282
283 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 283
284 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 284
285 * (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 285
286 .round() as usize;

Round to the nearest integer-valued result before the following conversion or indexing operation.

Link to line 286
287 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 287
288 .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 288

Render 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 289
290 (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 290
291 );

Close the current expression or scope; no additional processing operation occurs on this line.

Link to line 291
292 mix += slot

Add the computed value to mix. The right-hand expression supplies the new value for this step.

Link to line 292
293 .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 293
294 .process_sample(slot.oscillator.process_sample())

Generate one oscillator sample and run it through this slot’s filter.

Link to line 294
295 .low_pass

Select the filter’s low-pass output from its simultaneous response record.

Link to line 295
296 * envelope

Apply the same envelope level as the voice’s VCA gain.

Link to line 296
297 * f64::from(voice.velocity)

Scale this voice by its normalized note velocity.

Link to line 297
298 * (1. + 0.5 * pressure);

Map pressure to a gain factor from one to 1.5 after the normalized clamp.

Link to line 298
299 if slot.envelope.phase() == EnvelopePhase::Idle {

The release has reached zero, so this voice no longer owns audible envelope output.

Link to line 299
300 self.voices.finish(i).expect("fixed slot");

Return the finished DSP slot to the receiver’s idle pool.

Link to line 300
301 }

Close the surrounding scope, value or argument list in “Render oscillator → filter → amplitude”.

Link to line 301
302 }

Close the surrounding scope, value or argument list in “Render oscillator → filter → amplitude”.

Link to line 302
303 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 303
304 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 304
305 block.audio.output_mut(channel).expect("output")[frame] = sample;

Write the same mixed instrument sample to each active output channel.

Link to line 305
306 }

Close the surrounding scope, value or argument list in “Render oscillator → filter → amplitude”.

Link to line 306
307 }

Close the surrounding scope, value or argument list in “Render oscillator → filter → amplitude”.

Link to line 307
308 ProcessStatus::Continue

Represent a successfully processed block. The offline renderer treats any different status as a failed rendering session.

Link to line 308
309 }

Close the surrounding scope, value or argument list in “Render oscillator → filter → amplitude”.

Link to line 309

Reset 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 310
311 self.voices.reset();

Clear voice ownership, expression and receiver state.

Link to line 311
312 for slot in &mut self.slots {

Reset all fixed voice histories without allocating or removing their storage.

Link to line 312
313 slot.envelope.reset();

Clear the envelope’s current level and phase while retaining its prepared configuration.

Link to line 313
314 slot.filter.reset();

Clear the filter’s integrator history while retaining its coefficients.

Link to line 314
315 slot.oscillator.reset();

Reset oscillator phase for a new/stolen voice or an explicit processor reset.

Link to line 315
316 slot.releasing = false;

Record that this slot has not yet triggered release.

Link to line 316
317 }

Close the surrounding scope, value or argument list in “Reset the complete fixed voice state”.

Link to line 317
318 }

Close the surrounding scope, value or argument list in “Reset the complete fixed voice state”.

Link to line 318
319 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 319
320 self.reset();

Clear the processor’s signal history through its existing reset implementation.

Link to line 320
321 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 321
322 }

Close the surrounding scope, value or argument list in “Reset the complete fixed voice state”.

Link to line 322
323 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 323
324 Tail::Infinite

Declare a conservative unbounded tail. This is a host scheduling hint, not a promise of endless audible output.

Link to line 324
325 }

Close the surrounding scope, value or argument list in “Reset the complete fixed voice state”.

Link to line 325
326}

Close the surrounding scope, value or argument list in “Reset the complete fixed voice state”.

Link to line 326

Source SHA-256: ec453817b76f4c33ba3cd11de793d2ad49716f3e877482672a2a6417b17cee01

API: ravet_foundation · ravet_instrument · ravet_signal

All tutorial walkthroughs