fluffl/
audio.rs

1use crate::math::FP64;
2
3pub mod audio_backends;
4pub mod interval;
5pub mod mixer;
6pub mod pcm_util;
7
8//expose these from audio itself
9pub use interval::Interval;
10pub use pcm_util::PCMSlice;
11
12/// When playing/generating sound a callback will be required and it will need to be of this format.
13pub type DeviceCB<State> = fn(&mut State, &mut [f32]);
14
15/// Platform specific code awaits
16pub use audio_backends::*;
17
18use self::mixer::SampleTime;
19/// A trait used to define properties of the sound before playing
20pub trait GenericAudioSpecs {
21    fn sample_rate(&self) -> Option<u32>;
22    fn bits_per_sample(&self) -> Option<u32>;
23    fn channels(&self) -> Option<u32>;
24}
25
26/// A POD-ish struct for defining properties of the sound we with to play \
27/// If one of the fields isn't defined it will fallback to a somewhat sane default value
28#[derive(Copy, Clone, Default)]
29pub struct DesiredSpecs {
30    pub sample_rate: Option<u32>,
31    pub channels: Option<u32>,
32    /// number of samples PER channel (frames)
33    pub buffer_size: Option<u32>,
34}
35
36#[derive(Copy, Clone)]
37pub struct ConcreteSpecs {
38    pub sample_rate: u32,
39    pub channels: usize,
40    /// number of samples PER channel (frames)
41    pub buffer_size: usize,
42}
43
44impl DesiredSpecs {
45    /// Anything you haven't specified in the `DesiredSpecs` pod will be chosen for you
46    pub fn make_concrete(&self) -> ConcreteSpecs {
47        ConcreteSpecs {
48            sample_rate: self.sample_rate.unwrap_or(48000),
49            channels: self.channels.unwrap_or(2) as usize,
50            buffer_size: self.buffer_size.unwrap_or(1024) as usize,
51        }
52    }
53}
54
55/// The core of `AudioDeviceCore` has common resources all platform-specific implementations will need. \
56/// Creating this object is not enough to play sound. We will need to convert this into a `FlufflAudioDeviceContex`\
57/// This struct is mostly used as a way to setup state, define a callback and specify things like channels, frequency, etc.\
58/// Look at the examples for a complete example on how to do this.
59#[derive(Default)]
60pub struct AudioDeviceCore<Callback, State> {
61    cb: Option<Callback>,
62    state: Option<State>,
63    desired_specs: DesiredSpecs,
64}
65
66impl<Callback, State> AudioDeviceCore<Callback, State>
67where
68    Callback: FnMut(&mut State, &mut [f32]) + Copy + 'static,
69    State: 'static,
70{
71    pub fn new() -> Self {
72        Self {
73            cb: None,
74            state: None,
75            desired_specs: DesiredSpecs {
76                sample_rate: None,
77                channels: None,
78                buffer_size: None,
79            },
80        }
81    }
82    ///A callback is needed to supply the audio backend with sound samples.
83    ///Sound samples are expected to be interleaved-pcm
84    pub fn with_callback(mut self, cb: Callback) -> Self {
85        self.cb = Some(cb);
86        self
87    }
88
89    pub fn with_state(mut self, state: State) -> Self {
90        self.state = Some(state);
91        self
92    }
93
94    pub fn with_specs(mut self, specs: DesiredSpecs) -> Self {
95        self.desired_specs = specs;
96        self
97    }
98
99    pub fn callback(&self) -> Callback {
100        self.cb.unwrap()
101    }
102}
103
104/// given `frequency` (in sample/sec) and `dt`(in milliseconds), it can calculate samples required per channel
105pub fn calculate_samples_needed_per_channel_st(frequency: u32, dt: FP64) -> SampleTime {
106    const MILLISECONDS_IN_ONE_SEC: i32 = 1000;
107    let sample_count = (FP64::from(frequency) * dt) / MILLISECONDS_IN_ONE_SEC;
108    SampleTime::new()
109        .with_sample_rate(frequency)
110        .with_sample_count(sample_count.as_i64() as u64)
111}
112
113/// given `frequency` (in sample/sec) and `dt`(in milliseconds), it can calculate samples required per channel
114pub fn calculate_samples_needed_per_channel_fp(frequency: u32, dt: FP64) -> FP64 {
115    const MILLISECONDS_IN_ONE_SEC: i32 = 1000;
116    (FP64::from(frequency) * dt) / MILLISECONDS_IN_ONE_SEC
117}
118
119/// given a `num_samples` and `frequency` it returns the elapsed time in ms
120/// ## Comments
121/// this is a single channel calculation
122pub fn calculate_elapsed_time_in_ms_fp(frequency: u32, num_samples: usize) -> FP64 {
123    FP64::from(num_samples as u64 * 1000) / FP64::from(frequency)
124}