spectrusty_audio/
synth.rs

1/*
2    Copyright (C) 2020-2022  Rafal Michalski
3
4    This file is part of SPECTRUSTY, a Rust library for building emulators.
5
6    For the full copyright notice, see the lib.rs file.
7*/
8//! Hard sync without aliasing.
9//!
10//! For the original implementation refer to [this web site](http://www.slack.net/~ant/bl-synth).
11//!
12// TODO: re-implement the more efficient integer based blip buffer, this implementation is just enough to get going
13use core::marker::PhantomData;
14use core::num::NonZeroUsize;
15use core::cell::Cell;
16use core::ops::AddAssign;
17
18use spectrusty_core::{
19    clock::FTs,
20    audio::{SampleDelta, Blep, IntoSample, FromSample, MulNorm}
21};
22
23const PI2: f64 = core::f64::consts::PI * 2.0;
24/// A number of phase offsets to sample band-limited step at
25const PHASE_COUNT: usize = 32;
26/// A number of samples in each final band-limited step
27const STEP_WIDTH: usize = 24;
28
29/// A trait to implement high-pass and low-pass frequency filter limits for [BandLimited].
30pub trait BandLimOpt {
31    /// lower values filter more high frequency
32    const LOW_PASS: f64 = 0.999;
33    /// lower values filter more low frequency
34    const HIGH_PASS: f32 = 0.999;
35}
36
37/// A wide pass filter limits for [BandLimited].
38pub struct BandLimWide;
39impl BandLimOpt for BandLimWide {}
40
41/// A low-frequency pass filter limits for [BandLimited].
42pub struct BandLimLowTreb;
43impl BandLimOpt for BandLimLowTreb {
44    const LOW_PASS: f64 = 0.899;
45}
46
47/// A high-frequency pass filter limits for [BandLimited].
48pub struct BandLimLowBass;
49impl BandLimOpt for BandLimLowBass {
50    const HIGH_PASS: f32 = 0.899;
51}
52
53/// A narrow pass filter limits for [BandLimited].
54pub struct BandLimNarrow;
55impl BandLimOpt for BandLimNarrow {
56    const LOW_PASS: f64 = 0.899;
57    const HIGH_PASS: f32 = 0.899;
58}
59
60/// Bandwidth-Limited Pulse Buffer implementation with high-pass and low-pass filtering,
61/// for an efficient band-limited synthesis.
62///
63/// `T` specifies pulse step amplitude unit types. Currently, implementations are provided for:
64///  `f32`, `i16`, and `i32`.
65pub struct BandLimited<T, O=BandLimWide> {
66    steps: [[T; STEP_WIDTH]; PHASE_COUNT],
67    diffs: Vec<T>,
68    channels: NonZeroUsize,
69    time_rate: f64,
70    frame_time: f64,
71    start_time: f64,
72    sums: Box<[(T, Cell<Option<T>>)]>,
73    last_nsamples: Option<usize>,
74    _options: PhantomData<O>
75}
76
77impl<T: Copy + Default, O> BandLimited<T, O> {
78    /// Clears buffered data and resets `frame start` to default.
79    pub fn reset(&mut self) {
80        for d in self.diffs.iter_mut() { *d = T::default(); }
81        for s in self.sums.iter_mut() { *s = (T::default(), Cell::default()); }
82        self.last_nsamples = None;
83        self.start_time = 0.0;
84    }
85    /// Shrinks the excessive capacity of the buffer as much as possible.
86    pub fn shrink_to_fit(&mut self) {
87        self.diffs.shrink_to_fit();
88    }
89    /// Ensures the frame buffer length is large enough to fit data for the specified `frame_time`
90    /// with additional `margin_time`.
91    ///
92    /// Sets the duration of the average frame to `frame_time` which should be specified with as much
93    /// precision as possible.
94    ///
95    /// `margin_time` specifies a frame duration fluctuation margin and should be significantly smaller
96    /// than `frame_time`.
97    ///
98    /// Both `frame_time` and `margin_time` are specified in the sample time units (1.0 = 1 audio sample).
99    pub fn set_frame_time(&mut self, frame_time: f64, margin_time: f64) {
100        let max_samples = (frame_time + margin_time).ceil() as usize;
101        let required_len = (max_samples + STEP_WIDTH + 1) * self.channels.get();
102        // eprintln!("max_samples {} required_len {} frame_time {}", max_samples, required_len, frame_time);
103        if self.diffs.len() != required_len {
104            self.diffs.resize(required_len, T::default());
105        }
106        self.frame_time = frame_time;
107    }
108    /// Returns `true` if [BandLimited::end_frame] has been called before the call to [BandLimited::next_frame].
109    ///
110    /// It indicates if audio samples can be digitized from the last frame data.
111    pub fn is_frame_ended(&mut self) -> bool {
112        self.last_nsamples.is_some()
113    }
114    /// Finalizes audio frame.
115    ///
116    /// Returns the number of audio samples, single channel-wise, which are ready to be produced from
117    /// the frame.
118    ///
119    /// `time_end` is specified in the sample time units (1.0 = 1 audio sample).
120    pub fn end_frame(&mut self, time_end: f64) -> usize {
121        if self.last_nsamples.is_none() {
122            let samples = (time_end - self.start_time).trunc();
123            let num_samples = samples as usize;
124            self.last_nsamples = Some(num_samples);
125            num_samples
126        }
127        else {
128            panic!("BandLimited frame already over");
129        }
130    }
131
132    #[inline]
133    fn prepare_next_frame(&mut self, num_samples: usize) {
134        let channels = self.channels.get();
135        let chans_nsamples = num_samples*channels;
136        let chans_step_width = STEP_WIDTH*channels;
137        self.diffs.copy_within(chans_nsamples..chans_nsamples+chans_step_width, 0);
138        for p in self.diffs[chans_step_width..chans_nsamples+chans_step_width].iter_mut() {
139            *p = T::default();
140        }
141    }
142}
143
144impl<T,O> BandLimited<T,O>
145where T: Copy + Default + AddAssign + MulNorm + FromSample<f32>,
146      O: BandLimOpt,
147      // f32: FromSample<T>,
148{
149    /// Returns a new instance of `BandLimited` buffer.
150    ///
151    /// * `channels` - specifies the maximum number of audio channels that the sound can be rendered for.
152    ///
153    /// Before any pulse steps are added to the buffer the method [BandLimited::set_frame_time] or
154    /// [Blep::ensure_frame_time] must be called first.
155    ///
156    /// # Panics
157    /// Panics if `channels` equals to `0`.
158    pub fn new(channels: usize) -> Self {
159        let channels = NonZeroUsize::new(channels).expect("BandLimited: channels should be 1 or more");
160        // Generate master band-limited step by adding sine components of a square wave
161        let mut steps = [[T::default();STEP_WIDTH];PHASE_COUNT];
162        const MASTER_SIZE: usize = STEP_WIDTH * PHASE_COUNT;
163        let mut master = [0.5f64;MASTER_SIZE];
164        let mut gain: f64 = 0.5 / 0.777; // adjust normal square wave's amplitude of ~0.777 to 0.5
165        const SINE_SIZE: usize = 256 * PHASE_COUNT + 2;
166        let max_harmonic: usize = SINE_SIZE / 2 / PHASE_COUNT;
167        for h in (1..=max_harmonic).step_by(2) {
168            let amplitude: f64 = gain / h as f64;
169            let to_angle: f64 = PI2 / SINE_SIZE as f64 * h as f64;
170            // println!("h: {} amp: {} ang: {}", h, amplitude, to_angle);
171
172            for (i, m) in master.iter_mut().enumerate() {
173                *m += ( (i as isize - MASTER_SIZE as isize / 2) as f64 * to_angle ).sin() * amplitude;
174            }
175            gain *= O::LOW_PASS;
176        }
177        // Sample master step at several phases
178        for (phase, step) in steps.iter_mut().enumerate() {
179            let mut error: f64 = 1.0;
180            let mut prev: f64 = 0.0;
181            for (i, s) in step.iter_mut().enumerate() {
182                let cur: f64 = master[i * PHASE_COUNT + (PHASE_COUNT - 1 - phase)];
183                let delta: f64 = cur - prev;
184                error -= delta;
185                prev = cur;
186                *s = T::from_sample(delta as f32);
187            }
188            // each delta should total 1.0
189            // println!("PHASE: {} sum: {}", phase, step.iter().copied().fold(0.0, |acc, x| acc + f32::from_sample(x)));
190            // println!("{:?}", &step[STEP_WIDTH / 2 - 1..STEP_WIDTH / 2 + 2]);
191            // let max = step.iter().max().unwrap();
192            // let index = step.iter().position(|x| x == max).unwrap();
193            // println!("PHASE: {} {} {}  err: {}", phase, max, index, T::from_sample(error as f32));
194            step[STEP_WIDTH / 2    ] += T::from_sample((error * 0.5) as f32);
195            if phase < 16 {
196                step[STEP_WIDTH / 2 - 1] += T::from_sample((error * 0.5) as f32);
197            }
198            else {
199                step[STEP_WIDTH / 2 + 1] += T::from_sample((error * 0.5) as f32);
200            }
201            // println!("{:?} {}", &step[STEP_WIDTH / 2 - 1..STEP_WIDTH / 2], step.iter().max().unwrap());
202            // println!("PHASE: {} sum: {}", phase, step.iter().copied().fold(0.0, |acc, x| acc + f32::from_sample(x)));
203            // for (i, m) in step.iter().enumerate() {
204            //     println!("{}: {}", i, "@".repeat((50.0 + f32::from_sample(*m) * 100.0).round() as usize));
205            //     // println!("{}", (*m * 32768.0).trunc() as i16);
206            // }
207        }
208
209        BandLimited {
210            steps,
211            diffs: Vec::new(),
212            channels,
213            time_rate: 0.0,
214            frame_time: 0.0,
215            start_time: 0.0,
216            sums: vec![(T::default(), Cell::default()); channels.get()].into_boxed_slice(),
217            last_nsamples: None,
218            _options: PhantomData
219        }
220    }
221    /// Prepares the buffer for the next audio frame.
222    ///
223    /// This method must be called after the call to [BandLimited::end_frame] or to [Blep::end_frame]
224    /// and optionally after audio data has been produced with [BandLimited::sum_iter].
225    pub fn next_frame(&mut self) {
226        let num_samples = self.last_nsamples.take().expect("BandLimited frame not ended");
227        self.start_time += num_samples as f64 - self.frame_time;
228        for (channel, (sum_tgt, sum_end)) in self.sums.iter_mut().enumerate() {
229            *sum_tgt = match sum_end.take() {
230                Some(sum) => sum,
231                None => {
232                    let channels = self.channels.get();
233                    let mut sum = *sum_tgt;
234                    for diff in self.diffs[..num_samples*channels].iter().skip(channel).step_by(channels) {
235                        sum = sum.saturating_add(*diff)
236                                 .mul_norm(T::from_sample(O::HIGH_PASS));
237                    }
238                    sum
239                }
240            };
241        }
242        self.prepare_next_frame(num_samples);
243    }
244}
245
246impl<T,O> BandLimited<T,O>
247where T: Copy + MulNorm + FromSample<f32>,
248      O: BandLimOpt
249{
250    /// Returns an iterator that produces audio samples in the specified sample format `S`
251    /// from the specified `channel`.
252    ///
253    /// This method must be called after the call to [BandLimited::end_frame] or [Blep::end_frame]
254    /// and before [BandLimited::next_frame].
255    pub fn sum_iter<'a, S: 'a>(&'a self, channel: usize) -> impl Iterator<Item=S> + ExactSizeIterator + 'a
256    where T: IntoSample<S>
257    {
258        let channels = self.channels.get();
259        if channel >= channels {
260            panic!("Invalid channel: {}, should match: 0..{}", channel, channels);
261        }
262        let num_samples = self.last_nsamples.expect("BandLimited frame not ended");
263        let diffs = self.diffs[..num_samples*channels].iter().skip(channel).step_by(channels);
264        BandLimitedSumIter {
265            diffs,
266            sum_end: &self.sums[channel].1,
267            sum: self.sums[channel].0,
268            _output: PhantomData::<S>,
269            _options: PhantomData::<O>,
270        }
271    }
272}
273
274struct BandLimitedSumIter<'a, T: Copy + MulNorm + IntoSample<S> + FromSample<f32>,
275                              O: BandLimOpt,
276                              I: Iterator<Item=&'a T>,
277                              S> {
278    diffs: I,
279    sum_end: &'a Cell<Option<T>>,
280    sum: T,
281    _output: PhantomData<S>,
282    _options: PhantomData<O>,
283}
284
285impl<'a, T, O, I, S> Drop for BandLimitedSumIter<'a, T, O, I, S>
286where I: Iterator<Item=&'a T>,
287      O: BandLimOpt,
288      T: Copy + MulNorm + IntoSample<S> + FromSample<f32>
289{
290    #[allow(clippy::useless_conversion)]
291    fn drop(&mut self) {
292        if self.sum_end.get().is_none() {
293            for _ in self.into_iter() {}
294            self.sum_end.set(Some(self.sum))
295        }
296    }
297}
298
299impl<'a, T, O, I, S> std::iter::ExactSizeIterator for BandLimitedSumIter<'a, T, O, I, S>
300where I: Iterator<Item=&'a T> + ExactSizeIterator,
301      T: Copy + MulNorm + IntoSample<S> + FromSample<f32>,
302      O: BandLimOpt
303{
304    fn len(&self) -> usize {
305        self.diffs.len()
306    }
307}
308
309impl<'a, T, O, I, S> Iterator for BandLimitedSumIter<'a, T, O, I, S>
310where I: Iterator<Item=&'a T>,
311      T: Copy + MulNorm + IntoSample<S> + FromSample<f32>,
312      O: BandLimOpt
313{
314    type Item = S;
315
316    fn next(&mut self) -> Option<S> {
317        self.diffs.next().map(|&delta| {
318            let sum = self.sum.saturating_add(delta);
319            self.sum = sum.mul_norm(T::from_sample(O::HIGH_PASS));
320            sum.into_sample()
321        })
322    }
323}
324
325impl<T, O> Blep for BandLimited<T, O>
326where T: Copy + Default + SampleDelta + MulNorm
327{
328    type SampleDelta = T;
329
330    #[inline]
331    fn ensure_frame_time(&mut self, sample_rate: u32, ts_rate: f64, frame_ts: FTs, margin_ts: FTs) {
332        let time_rate = sample_rate as f64 / ts_rate;
333        assert!(time_rate > 0.0);
334        let frame_time = time_rate * frame_ts as f64;
335        assert!(frame_time > 0.0);
336        let margin_time = time_rate * 2.0 * margin_ts as f64;
337        assert!(margin_time >= 0.0);
338        self.time_rate = time_rate;
339        self.set_frame_time(frame_time, margin_time);
340    }
341
342    #[inline]
343    fn end_frame(&mut self, timestamp: FTs) -> usize {
344        debug_assert!(timestamp > 0);
345        self.end_frame(self.time_rate * timestamp as f64)
346    }
347
348    #[inline]
349    fn add_step(&mut self, channel: usize, timestamp: FTs, delta: T) {
350        let channels = self.channels.get();
351        debug_assert!(channel < channels);
352        let time = self.time_rate * timestamp as f64 - self.start_time;
353        let index = time.trunc() as usize * channels + channel;
354        // FIX: better handle negative timestamps
355        let phase = ((time.fract() * PHASE_COUNT as f64).trunc() as usize).rem_euclid(PHASE_COUNT);
356        for (dp, phase) in self.diffs[index..index + STEP_WIDTH*channels]
357                          .iter_mut().step_by(channels)
358                          .zip(self.steps[phase].iter()) {
359            *dp = dp.saturating_add(phase.mul_norm(delta));
360        }
361    }    
362}