Skip to main content

imagemusic/
envelope.rs

1use serde::de::{self, Error};
2use serde::ser::{self, SerializeTuple};
3
4use std::cell::RefCell;
5use std::fmt;
6
7#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Default)]
8pub struct Point {
9    /// Stop point in seconds.  May be positive to be distance from the beginning and negative to
10    /// be distance from the end.  If these overlap, the envelope will change shape in ways that
11    /// may be unpredictable, because they will reorder automatically.
12    pub stop: f32,
13
14    /// Height of the wave at this stop
15    pub amplitude: f32,
16}
17
18impl ser::Serialize for Point {
19    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
20    where
21        S: ser::Serializer,
22    {
23        let mut tup = serializer.serialize_tuple(2)?;
24        tup.serialize_element(&((self.stop * 100.0) as i8))?;
25        tup.serialize_element(&((self.amplitude * 255.0) as u8))?;
26        tup.end()
27    }
28}
29
30struct PointVisitor;
31
32impl<'de> de::Visitor<'de> for PointVisitor {
33    type Value = Point;
34
35    fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
36        formatter.write_str("A pair of tuples")
37    }
38
39    fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
40    where
41        A: de::SeqAccess<'de>,
42    {
43        let stop: Option<i8> = seq.next_element()?;
44        let stop = stop.ok_or_else(|| A::Error::invalid_length(0, &self))?;
45        let amplitude: Option<u8> = seq.next_element()?;
46        let amplitude = amplitude.ok_or_else(|| A::Error::invalid_length(1, &self))?;
47        let stop = stop as f32 / 100.0;
48        let amplitude = amplitude as f32 / 255.0;
49        Ok(Point { stop, amplitude })
50    }
51}
52
53impl<'de> de::Deserialize<'de> for Point {
54    fn deserialize<D>(deserializer: D) -> Result<Point, D::Error>
55    where
56        D: de::Deserializer<'de>,
57    {
58        deserializer.deserialize_tuple(2, PointVisitor)
59    }
60}
61
62#[derive(Debug, Clone, PartialEq)]
63pub struct Envelope {
64    pub points: Vec<Point>,
65
66    // Specific points for note
67    note_points: RefCell<Vec<Point>>,
68}
69
70impl ser::Serialize for Envelope {
71    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
72    where
73        S: ser::Serializer,
74    {
75        use ser::SerializeSeq;
76        let mut seq = serializer.serialize_seq(Some(self.points.len()))?;
77        for point in &self.points {
78            seq.serialize_element(point)?;
79        }
80        seq.end()
81    }
82}
83
84struct EnvelopeVisitor;
85
86impl<'de> de::Visitor<'de> for EnvelopeVisitor {
87    type Value = Envelope;
88
89    fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
90        formatter.write_str("A sequence of points")
91    }
92
93    fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
94    where
95        A: de::SeqAccess<'de>,
96    {
97        let mut points = match seq.size_hint() {
98            Some(size) => Vec::with_capacity(size),
99            None => Vec::new(),
100        };
101        while let Some(point) = seq.next_element()? {
102            points.push(point);
103        }
104        Ok(Envelope {
105            points,
106            note_points: RefCell::new(Vec::new()),
107        })
108    }
109}
110
111impl<'de> de::Deserialize<'de> for Envelope {
112    fn deserialize<D>(deserializer: D) -> Result<Envelope, D::Error>
113    where
114        D: de::Deserializer<'de>,
115    {
116        deserializer.deserialize_seq(EnvelopeVisitor)
117    }
118}
119
120impl Default for Envelope {
121    fn default() -> Self {
122        Envelope {
123            points: vec![
124                Point {
125                    stop: 0.0,
126                    amplitude: 0.0,
127                },
128                Point {
129                    stop: 0.05,
130                    amplitude: 1.0,
131                },
132                Point {
133                    stop: -0.05,
134                    amplitude: 0.8,
135                },
136                Point {
137                    stop: -0.01,
138                    amplitude: 0.0,
139                },
140            ],
141            note_points: RefCell::new(Vec::new()),
142        }
143    }
144}
145
146#[inline]
147fn lerp(x: f32, a: (f32, f32), b: (f32, f32)) -> f32 {
148    a.1 + (x - a.0) * (b.1 - a.1) / (b.0 - a.0)
149}
150
151impl Envelope {
152    pub fn prepare_note(&self, note_length: f32) {
153        // Envelope points are made absolute here (all to time from beginning).  Also remove points
154        // with a stop outside the note's range.
155        let mut points: Vec<Point> = self
156            .points
157            .iter()
158            .filter_map(|point| {
159                let stop = if point.stop < 0.0 {
160                    // Negative, so will subtract
161                    note_length + point.stop
162                } else {
163                    point.stop
164                };
165
166                Some(Point {
167                    amplitude: point.amplitude,
168                    stop,
169                })
170                .filter(|p| (0.0..=note_length).contains(&p.stop))
171            })
172            .collect();
173
174        let mut lastmax = -1.0;
175        // Remove out-of-order stops.  This may cause buggy results, but less buggy results than
176        // simple sorting.
177        //points.sort_by(|a, b| a.stop.partial_cmp(&b.stop).unwrap());
178        points.retain(move |point| {
179            if point.stop > lastmax {
180                lastmax = point.stop;
181                true
182            } else {
183                false
184            }
185        });
186
187        let mut note_points = self.note_points.borrow_mut();
188        *note_points = points;
189    }
190
191    pub fn amplitude_at_time(&self, time_point: f32) -> f32 {
192        let mut points = self.note_points.borrow_mut();
193
194        // Can probably make all of this much nicer.
195        if points.first().unwrap().stop >= time_point {
196            points.first().unwrap().amplitude
197        } else if points.last().unwrap().stop <= time_point {
198            points.last().unwrap().amplitude
199        } else {
200            loop {
201                let (first, second) = (&points[0], &points[1]);
202                if first.stop <= time_point && time_point <= second.stop {
203                    return lerp(
204                        time_point,
205                        (first.stop, first.amplitude),
206                        (second.stop, second.amplitude),
207                    );
208                } else {
209                    points.remove(0);
210                }
211            }
212        }
213    }
214}