1use crate::envelope::{Adsr, AdsrSpec};
2use crate::fixed::apply_gain_q8;
3use crate::source::VoiceSource;
4
5#[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 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}