Skip to main content

embedded_audio/
voice.rs

1use crate::envelope::{Adsr, AdsrSpec};
2use crate::fixed::apply_gain_q8;
3use crate::source::VoiceSource;
4
5/// One of two mixer voices.
6#[derive(Debug, Clone, Copy)]
7pub struct Voice<'a> {
8    pub source: VoiceSource<'a>,
9    adsr: Adsr,
10    gain_q8: u8,
11    pub priority: u8,
12    sample_rate_hz: u32,
13}
14
15impl<'a> Voice<'a> {
16    pub const fn silent(sample_rate_hz: u32) -> Self {
17        Self {
18            source: VoiceSource::idle(),
19            adsr: Adsr::new(AdsrSpec::click(), sample_rate_hz),
20            gain_q8: 255,
21            priority: 0,
22            sample_rate_hz,
23        }
24    }
25
26    pub fn set_gain_q8(&mut self, gain_q8: u8) {
27        self.gain_q8 = gain_q8;
28    }
29
30    pub fn gain_q8(self) -> u8 {
31        self.gain_q8
32    }
33
34    pub fn trigger_adsr(&mut self, spec: AdsrSpec) {
35        self.adsr = Adsr::new(spec, self.sample_rate_hz);
36        self.adsr.trigger();
37    }
38
39    pub fn release(&mut self) {
40        self.adsr.release();
41    }
42
43    pub fn stop_immediate(&mut self) {
44        self.source.stop();
45        self.adsr = Adsr::new(AdsrSpec::click(), self.sample_rate_hz);
46    }
47
48    pub fn is_audible(&self) -> bool {
49        self.source.is_active() || self.adsr.is_active()
50    }
51
52    pub fn tick_envelope(&mut self) {
53        self.adsr.tick();
54        if !self.source.is_active() && !self.adsr.is_active() {
55            self.source.stop();
56        }
57    }
58
59    /// Sample after envelope and per-voice gain.
60    pub fn next_sample(&mut self) -> Option<i8> {
61        let raw = self.source.next_raw_sample()?;
62        let env = self.adsr.level_q8();
63        let s = apply_gain_q8(raw, env);
64        Some(apply_gain_q8(s, self.gain_q8))
65    }
66}