Skip to main content

ym2149_core/
envelopes.rs

1use crate::errors::Error;
2
3// An enum for all EnvelopeShape types
4#[derive(Debug, Clone, Copy)]
5pub enum Envelope {
6    Builtin(BuiltinEnvelopeShape),
7    InvertedBuiltin(BuiltinEnvelopeShape),
8    CustomBuiltin(u8),
9    RawEnvelope(RawEnvelope),
10}
11
12impl From<&Envelope> for u8 {
13    fn from(value: &Envelope) -> Self {
14        use Envelope::{Builtin, CustomBuiltin, InvertedBuiltin, RawEnvelope};
15
16        match value {
17            Builtin(builtin) => *builtin as u8,
18            InvertedBuiltin(builtin) => (*builtin as u8) ^ (0b00000100),
19            CustomBuiltin(n) => *n,
20
21            #[allow(unused)]
22            RawEnvelope(raw) => unimplemented!(),
23        }
24    }
25}
26
27/// A helper enum for setting the envelope's shape.
28///
29/// To invert the shape use Envelope::InvertedBuiltin(BuiltinEnvelopeShape).
30#[derive(Debug, Clone, Copy)]
31#[repr(u8)]
32pub enum BuiltinEnvelopeShape {
33    /// Fade out and hold low
34    FadeOut = 0b00001001,
35    /// Fade in and hold high
36    FadeIn = 0b00001101,
37    /// Fade in then hold low
38    Tooth = 0b00001111,
39    /// Fade in every repetition
40    Saw = 0b00001100,
41    /// Alternate between fade out and fade in
42    Triangle = 0b00001110,
43}
44
45/// A raw envelope for more precise control of channel levels.
46///
47/// It consists of a `data` field - which is an array of u8 values with length 4096,
48/// and a length given in beats.
49#[allow(unused)]
50#[derive(Debug, Clone, Copy)]
51pub struct RawEnvelope {
52    data: [u8; 4096],
53    length_beats: u8,
54}
55
56#[allow(unused)]
57impl RawEnvelope {
58    fn invert(&mut self) {
59        for i in 0..4096 {
60            self.data[i] = 0xF - self.data[i];
61        }
62    }
63
64    fn scale(&mut self, scale: f32) {
65        for i in 0..4096 {
66            let scaled = (self.data[i] as f32 * scale).clamp(0.0, 255.0) as u8;
67            self.data[i] = scaled;
68        }
69    }
70
71    fn offset(&mut self, offset: i8) {
72        for i in 0..4096 {
73            self.data[i] += offset as u8;
74        }
75    }
76}
77
78/// A helper enum for setting the envelope repetition frequency f_e.
79#[derive(Debug)]
80pub enum EnvelopeFrequency {
81    Hertz(u16),
82    BeatsPerMinute(u16),
83    Integer(u16),
84}
85
86impl EnvelopeFrequency {
87    /// Returns the EnvelopeFrequency as a u16 value for registers 11 (LSB) and 12 (MSB).
88    pub fn as_ep(self, master_clock_frequency: u32) -> Result<u16, Error> {
89        match self {
90            Self::Hertz(f_e) => {
91                if f_e == 0 {
92                    return Err(Error::DivisionByZero);
93                }
94                let period = master_clock_frequency / (256 * f_e as u32);
95                period.try_into().map_err(|_| Error::TonePeriodOutOfRange(period as u16))
96            }
97            Self::BeatsPerMinute(bpm) => {
98                let hz = Self::Hertz(bpm).as_ep(master_clock_frequency)?;
99                (60 * hz).try_into().map_err(|_| Error::TonePeriodOutOfRange(0))
100            }
101            Self::Integer(x) => Ok(x),
102        }
103    }
104}