1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
//! External-instrument trait + per-format adapters + pure-tone
//! fallback.
//!
//! The [`Instrument`] trait describes a small surface — "give me one
//! voice (a sound source) for this MIDI program at this pitch" — so
//! the synth core can stay format-agnostic.
//!
//! - [`sf2`] is a working SoundFont 2 reader + voice generator: it
//! loads a `.sf2` bank into memory, cross-resolves the preset →
//! instrument → zone → sample chain, and renders sm24-aware 24-bit
//! PCM at the requested pitch via linear interpolation. Honours the
//! volume + modulation DAHDSR envelopes, the initial low-pass biquad
//! filter, mod-env → pitch / filter routing, exclusive-class drum
//! cuts, and native stereo zones.
//! - [`sfz`] is a working text patch reader **plus voice generator**.
//! The reader strips comments, walks `<control>` / `<global>` /
//! `<master>` / `<group>` / `<region>` sections, flattens
//! inheritance into one fully-resolved opcode map per region, and
//! (when constructed via [`sfz::SfzInstrument::open`]) reads every
//! referenced sample off disk. `make_voice` decodes the WAV bytes
//! (8/16/24/32-bit PCM and IEEE_FLOAT), picks the matching region
//! by (key, velocity), shifts pitch off `pitch_keycenter` + `tune` +
//! `transpose`, and runs a DAHDSR amplitude envelope (`ampeg_*`) +
//! vibrato LFO (`lfo01_*`).
//! - [`dls`] is a working DLS Level 1 + Level 2 RIFF reader **plus
//! voice generator**. The reader walks the `DLS ` form, pulls the
//! `colh` / `vers` / `ptbl` / `lins` / `wvpl` chunks apart, and
//! surfaces a fully-resolved bank ([`dls::DlsBank`]) of instruments
//! → regions → wave-pool samples with their `wsmp` loop info,
//! `wlnk` cue-table references, and `art1` / `art2` connection
//! blocks. `make_voice` picks the matching instrument by program,
//! picks a region by (key, velocity), resolves wlnk → ptbl →
//! wave-pool, decodes the PCM, evaluates the region + instrument
//! articulation through [`articulation::Articulation`] (round 80),
//! and plays the sample through the shared
//! [`sample_voice::SamplePlayer`] with the resolved DAHDSR envelope
//! + vibrato LFO + tuning + gain applied.
//! - [`articulation`] is the DLS Level 1/2 connection-block evaluator
//! used by [`dls`] at voice-build time. Honours the `SRC_NONE →
//! DST_x` default-override connections for the Vol EG, the
//! modulator + vibrato LFO, tuning, gain and pan, plus a handful of
//! `SRC_x → DST_y` modulator routings — see the module's doc for
//! the supported subset.
//! - [`sample_voice`] is the shared sample-playback voice both `sfz`
//! and `dls` use. Mono in, mono out — the [`mixer`](crate::mixer)
//! handles stereo panning. Covers DAHDSR amplitude envelope, four
//! loop modes (no-loop / one-shot / continuous / sustain), pitch
//! bend, and a vibrato LFO (rate/depth/delay).
//! - [`wav_pcm`] is a minimal RIFF/WAVE PCM decoder — 8-bit unsigned,
//! 16-bit signed LE, 24-bit signed LE, 32-bit signed LE PCM, and
//! 32-bit IEEE_FLOAT — used by the SFZ and DLS sample loaders.
//! - [`tone::ToneInstrument`] is the canary: if no SoundFont is
//! available, the synth still produces *something*.
use Result;
/// One voice rendered into a planar f32 buffer.
///
/// Voices are ephemeral — the synth holds them while a note is on,
/// drops them when it releases. A voice produces samples until it
/// reports `done()`; the synth then frees the slot.
/// Source of voices for one MIDI program (a "bank").
///
/// `Send + Sync` so an `Arc<dyn Instrument>` is `Send`-able into the
/// `MidiDecoder` (which itself must be `Send` per the `Decoder` trait).
/// `make_voice` takes `&self` so concrete impls only need shared
/// references to whatever cross-cutting state they hold (sample arena
/// in [`sf2::Sf2Bank`] etc.).