1use std::time::Duration;
2
3use sim_lib_sound_core::{Frequency, Tone};
4use sim_lib_sound_timbre::Timbre;
5
6use crate::{SoundBridgeError, TimbreBank};
7
8#[derive(Clone, Debug, PartialEq)]
11pub struct ScheduledTone {
12 pub start: Duration,
14 pub tone: Tone,
16 pub pan: f32,
18 pub channel: u8,
20 pub key: u8,
22}
23
24#[derive(Copy, Clone, Debug, PartialEq, Eq)]
26pub enum VoicePhase {
27 Held,
29 SustainHeld,
31 Released,
33}
34
35#[derive(Clone, Debug, PartialEq)]
37pub struct Voice {
38 pub channel: u8,
40 pub key: u8,
42 pub started_at: Duration,
44 pub release_at: Option<Duration>,
46 pub release_end: Option<Duration>,
48 pub frequency: Frequency,
50 pub amplitude_gain: f64,
52 pub pan: f32,
54 pub phase: VoicePhase,
56 pub timbre: Timbre,
58}
59
60impl Voice {
61 fn to_scheduled_tone(&self) -> ScheduledTone {
62 let released_at = self.release_at.unwrap_or(self.started_at);
63 let total_duration =
64 released_at.saturating_sub(self.started_at) + self.timbre.default_envelope.release;
65 let tone = self
66 .timbre
67 .render(self.frequency, total_duration)
68 .amplify(self.amplitude_gain);
69 ScheduledTone {
70 start: self.started_at,
71 tone,
72 pan: self.pan,
73 channel: self.channel,
74 key: self.key,
75 }
76 }
77}
78
79#[derive(Clone, Debug, PartialEq)]
82pub struct VoicePool {
83 polyphony_limit: usize,
84 voices: Vec<Voice>,
85 emitted: Vec<ScheduledTone>,
86 stolen_voice_count: usize,
87}
88
89impl VoicePool {
90 pub fn new(polyphony_limit: usize) -> Result<Self, SoundBridgeError> {
92 if polyphony_limit == 0 {
93 return Err(SoundBridgeError::ZeroPolyphony);
94 }
95 Ok(Self {
96 polyphony_limit,
97 voices: Vec::new(),
98 emitted: Vec::new(),
99 stolen_voice_count: 0,
100 })
101 }
102
103 pub fn polyphony_limit(&self) -> usize {
105 self.polyphony_limit
106 }
107
108 pub fn active_voice_count(&self) -> usize {
110 self.voices.len()
111 }
112
113 pub fn voices(&self) -> &[Voice] {
115 &self.voices
116 }
117
118 pub fn emitted(&self) -> &[ScheduledTone] {
120 &self.emitted
121 }
122
123 pub fn stolen_voice_count(&self) -> usize {
125 self.stolen_voice_count
126 }
127
128 pub fn drain_emitted(&mut self) -> Vec<ScheduledTone> {
130 std::mem::take(&mut self.emitted)
131 }
132
133 pub fn note_on(&mut self, mut voice: Voice, now: Duration) {
136 self.reap_finished(now);
137 self.note_off(voice.channel, voice.key, now, false);
138 if self.voices.len() >= self.polyphony_limit {
139 self.steal_voice(now);
140 }
141 voice.phase = VoicePhase::Held;
142 voice.release_at = None;
143 voice.release_end = None;
144 self.voices.push(voice);
145 }
146
147 pub fn note_off(&mut self, channel: u8, key: u8, now: Duration, sustain: bool) {
150 self.reap_finished(now);
151 if let Some(voice) = self
152 .voices
153 .iter_mut()
154 .rev()
155 .find(|voice| voice.channel == channel && voice.key == key)
156 {
157 if sustain {
158 voice.phase = VoicePhase::SustainHeld;
159 } else {
160 voice.phase = VoicePhase::Released;
161 voice.release_at = Some(now);
162 voice.release_end = Some(now + voice.timbre.default_envelope.release);
163 }
164 }
165 }
166
167 pub fn release_channel_sustain(&mut self, channel: u8, now: Duration) {
169 self.reap_finished(now);
170 for voice in &mut self.voices {
171 if voice.channel == channel && voice.phase == VoicePhase::SustainHeld {
172 voice.phase = VoicePhase::Released;
173 voice.release_at = Some(now);
174 voice.release_end = Some(now + voice.timbre.default_envelope.release);
175 }
176 }
177 }
178
179 pub fn transpose_channel_cents(&mut self, channel: u8, cents: f64) {
181 for voice in &mut self.voices {
182 if voice.channel == channel && voice.phase != VoicePhase::Released {
183 voice.frequency = voice.frequency.shift_cents(cents);
184 }
185 }
186 }
187
188 pub fn reap_finished(&mut self, now: Duration) {
190 let mut keep = Vec::with_capacity(self.voices.len());
191 for voice in self.voices.drain(..) {
192 if voice.release_end.is_some_and(|release_end| {
193 release_end <= now && voice.phase == VoicePhase::Released
194 }) {
195 self.emitted.push(voice.to_scheduled_tone());
196 } else {
197 keep.push(voice);
198 }
199 }
200 self.voices = keep;
201 }
202
203 pub fn flush_all(&mut self, now: Duration) {
205 for voice in &mut self.voices {
206 if voice.phase != VoicePhase::Released {
207 voice.phase = VoicePhase::Released;
208 voice.release_at = Some(now);
209 voice.release_end = Some(now + voice.timbre.default_envelope.release);
210 }
211 }
212 let max_end = self
213 .voices
214 .iter()
215 .filter_map(|voice| voice.release_end)
216 .max()
217 .unwrap_or(now);
218 self.reap_finished(max_end);
219 }
220
221 fn steal_voice(&mut self, now: Duration) {
222 let release_candidate = self
223 .voices
224 .iter()
225 .enumerate()
226 .filter(|(_, voice)| voice.phase == VoicePhase::Released)
227 .min_by_key(|(_, voice)| voice.release_at.unwrap_or(voice.started_at))
228 .map(|(index, _)| index);
229 let active_candidate = self
230 .voices
231 .iter()
232 .enumerate()
233 .min_by_key(|(_, voice)| voice.started_at)
234 .map(|(index, _)| index);
235 let index = release_candidate.or(active_candidate).unwrap_or(0);
236 let mut stolen = self.voices.swap_remove(index);
237 if stolen.release_at.is_none() {
238 stolen.release_at = Some(now);
239 stolen.release_end = Some(now);
240 }
241 self.stolen_voice_count += 1;
242 self.emitted.push(stolen.to_scheduled_tone());
243 }
244}
245
246#[derive(Clone, Debug, PartialEq)]
248pub struct BridgeChannelState {
249 pub bank_msb: u8,
251 pub bank_lsb: u8,
253 pub program: u8,
255 pub volume: f64,
257 pub expression: f64,
259 pub pan: f32,
261 pub sustain: bool,
263 pub pitch_bend_cents: f64,
265}
266
267impl Default for BridgeChannelState {
268 fn default() -> Self {
269 Self {
270 bank_msb: 0,
271 bank_lsb: 0,
272 program: 0,
273 volume: 1.0,
274 expression: 1.0,
275 pan: 0.0,
276 sustain: false,
277 pitch_bend_cents: 0.0,
278 }
279 }
280}
281
282#[derive(Clone, Debug, PartialEq)]
284pub struct BridgeOptions {
285 pub polyphony_limit: usize,
287 pub bend_range_cents: f64,
289}
290
291impl Default for BridgeOptions {
292 fn default() -> Self {
293 Self {
294 polyphony_limit: 32,
295 bend_range_cents: 200.0,
296 }
297 }
298}
299
300impl BridgeOptions {
301 pub fn new(polyphony_limit: usize, bend_range_cents: f64) -> Result<Self, SoundBridgeError> {
303 if polyphony_limit == 0 {
304 return Err(SoundBridgeError::ZeroPolyphony);
305 }
306 Ok(Self {
307 polyphony_limit,
308 bend_range_cents,
309 })
310 }
311}
312
313pub(crate) fn tone_gain(channel: &BridgeChannelState, velocity: u8) -> f64 {
314 let velocity_gain = f64::from(velocity) / 127.0;
315 velocity_gain * channel.volume * channel.expression
316}
317
318pub(crate) fn current_timbre<'a>(bank: &'a TimbreBank, channel: &BridgeChannelState) -> &'a Timbre {
319 bank.get(channel.bank_msb, channel.bank_lsb, channel.program)
320}