1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
//! Provides tools to make a specification to require what an audio device is.

use bitflags::bitflags;
use std::{
    ffi::c_void,
    marker::PhantomData,
    os::raw::c_int,
    sync::{Arc, Mutex},
};
use typed_builder::TypedBuilder;

use crate::bind;

use super::format::AudioFormat;

/// A builder to build an information representing what specification is required for an audio device.
#[derive(TypedBuilder)]
pub struct AudioSpecBuilder<'callback, T: AudioCallback<'callback>> {
    #[builder(default = 44100)]
    sample_freq: u32,
    #[builder(default = AudioFormat::signed32_lsb())]
    format: AudioFormat,
    #[builder(default = 2)]
    channels: u8,
    #[builder(default = 4096)]
    samples: u16,
    #[builder(default, setter(strip_option))]
    callback: Option<&'callback mut T>,
}

/// A type of the callback to interact with the raw audio buffer.
pub trait AudioCallback<'callback>: FnMut(&mut [u8]) + 'callback {}

/// A specification to require what an audio device is.
pub struct AudioSpec<'callback, T> {
    raw: bind::SDL_AudioSpec,
    _phantom: PhantomData<&'callback mut T>,
}

impl<T> std::fmt::Debug for AudioSpec<'_, T> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("AudioSpec").finish_non_exhaustive()
    }
}

impl<'callback, T: AudioCallback<'callback>> AudioSpec<'callback, T> {
    /// Constructs an audio specification with the optional callback.
    #[must_use]
    pub fn new(mut builder: AudioSpecBuilder<'callback, T>) -> Self {
        Self {
            raw: bind::SDL_AudioSpec {
                freq: builder.sample_freq as c_int,
                format: builder.format.as_raw(),
                channels: builder.channels,
                silence: 0,
                samples: builder.samples,
                padding: 0,
                size: 0,
                callback: builder
                    .callback
                    .as_ref()
                    .map(|_| audio_spec_wrap_handler::<T> as _),
                userdata: builder
                    .callback
                    .map_or(std::ptr::null_mut(), |callback| (callback as *mut T).cast()),
            },
            _phantom: PhantomData,
        }
    }

    pub(super) fn raw(&self) -> &bind::SDL_AudioSpec {
        &self.raw
    }

    pub(super) fn raw_mut(&mut self) -> &mut bind::SDL_AudioSpec {
        &mut self.raw
    }
}

unsafe extern "C" fn audio_spec_wrap_handler<'callback, T: AudioCallback<'callback>>(
    userdata: *mut c_void,
    stream: *mut u8,
    len: c_int,
) {
    if userdata.is_null() {
        return;
    }
    let func = &mut *userdata.cast::<T>();
    let slice = std::slice::from_raw_parts_mut(stream, len as usize);
    slice.fill(0);
    func(slice);
}

bitflags! {
    /// A flag what component may fallback into an actual audio device.
    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
    pub struct FallbackFlag : u32 {
        /// Allows to fallback frequencies.
        const FREQUENCY = 1 << 0;
        /// Allows to fallback a format.
        const FORMAT = 1 << 0;
        /// Allows to fallback numbers of channels.
        const CHANNELS = 1 << 0;
        /// Allows to fallback sample rates.
        const SAMPLES = 1 << 0;
    }
}