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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
//! Audio specifications for SDL2_mixer.

use std::{borrow::Cow, ffi::CStr, marker::PhantomData};

use crate::{
    audio::format::AudioFormat,
    bind,
    mixer::{
        chunk::channel::{
            effect::{effect_attach_done_handler, effect_attach_effect_handler, Effect},
            ChannelGroup,
        },
        Mix,
    },
    Result, Sdl, SdlError,
};

/// A builder for [`MixDevice`].
#[derive(Debug, Clone)]
pub struct MixDeviceBuilder {
    frequency: u32,
    format: AudioFormat,
    channels: u32,
    chunk_size: u32,
}

impl MixDeviceBuilder {
    /// Constructs a default builder.
    pub fn new() -> Self {
        Self {
            frequency: bind::MIX_DEFAULT_FREQUENCY,
            format: if cfg!(target_endian = "big") {
                AudioFormat::signed16_msb()
            } else {
                AudioFormat::signed16_lsb()
            },
            channels: 2,
            chunk_size: bind::MIX_DEFAULT_CHANNELS,
        }
    }

    /// Changes the sampling frequencies.
    pub fn frequency(&mut self, frequency: u32) -> &mut Self {
        self.frequency = frequency;
        self
    }

    /// Changes the audio format.
    pub fn format(&mut self, format: AudioFormat) -> &mut Self {
        self.format = format;
        self
    }

    /// Changes the numbers of channels.
    pub fn channels(&mut self, channels: u32) -> &mut Self {
        self.channels = channels;
        self
    }

    /// Changes the output chunk size. If it is too low, the sound may skip.
    pub fn chunk_size(&mut self, chunk_size: u32) -> &mut Self {
        self.chunk_size = chunk_size;
        self
    }

    /// Opens a [`MixDevice`] with a root controller, or `Err` on failure.
    pub fn build(self, _mix: &Mix) -> Result<MixDevice> {
        let format = (self.format.flag.bits() as u16) << 8 | self.format.bit_size as u16;
        let ret = unsafe {
            bind::Mix_OpenAudio(
                self.frequency as _,
                format,
                self.channels as _,
                self.chunk_size as _,
            )
        };
        if ret != 0 {
            Err(SdlError::Others { msg: Sdl::error() })
        } else {
            Ok(MixDevice {
                _phantom: PhantomData,
            })
        }
    }
}

impl Default for MixDeviceBuilder {
    fn default() -> Self {
        Self::new()
    }
}

/// A specification of the [`MixDevice`], returned from [`MixDevice::query`].
#[derive(Debug, Clone, Copy)]
pub struct MixSpec {
    /// The sampling frequency of the audio device.
    pub frequency: u32,
    /// The output format of the audio device.
    pub format: AudioFormat,
    /// The numbers of channels of the audio device.
    pub channels: u32,
}

/// An audio device built from [`MixDeviceBuilder`].
pub struct MixDevice<'mix> {
    _phantom: PhantomData<&'mix Mix>,
}

impl MixDevice<'_> {
    /// Queries the specification of the audio device.
    pub fn query(&self) -> MixSpec {
        let mut frequency = 0;
        let mut format = 0;
        let mut channels = 0;
        let _ = unsafe {
            bind::Mix_QuerySpec(
                &mut frequency as *mut _,
                &mut format as *mut _,
                &mut channels as *mut _,
            )
        };
        MixSpec {
            frequency: frequency as _,
            format: format.into(),
            channels: channels as _,
        }
    }

    /// Returns the decoder names for the mix chunk.
    pub fn chunk_decoders(&self) -> Vec<Cow<str>> {
        let num = unsafe { bind::Mix_GetNumChunkDecoders() };
        (0..num)
            .map(|index| {
                let cstr = unsafe { CStr::from_ptr(bind::Mix_GetChunkDecoder(index)) };
                cstr.to_string_lossy()
            })
            .collect()
    }

    /// Returns the decoder names for the mix music.
    pub fn music_decoders(&self) -> Vec<Cow<str>> {
        let num = unsafe { bind::Mix_GetNumMusicDecoders() };
        (0..num)
            .map(|index| {
                let cstr = unsafe { CStr::from_ptr(bind::Mix_GetMusicDecoder(index)) };
                cstr.to_string_lossy()
            })
            .collect()
    }

    /// Returns the numbers of playing channels.
    pub fn playing_channels(&self) -> usize {
        unsafe { bind::Mix_Playing(-1) as _ }
    }

    /// Constructs the channel group.
    pub fn new_channels(&self, len: usize) -> ChannelGroup {
        ChannelGroup::new(self, len)
    }

    /// Attaches the effect to the special, post effect channel.
    pub fn attach_post_effect(&self, effect: Effect) {
        let wrapped = Box::new(effect);
        let raw = Box::into_raw(wrapped);
        let _ = unsafe {
            bind::Mix_RegisterEffect(
                bind::MIX_CHANNEL_POST,
                Some(effect_attach_effect_handler),
                Some(effect_attach_done_handler),
                raw.cast(),
            )
        };
    }

    /// Detaches all the effect from the special, post effect channel.
    pub fn detach_all_post_effect(&self) {
        let _ = unsafe { bind::Mix_UnregisterAllEffects(bind::MIX_CHANNEL_POST) };
    }
}

impl Drop for MixDevice<'_> {
    fn drop(&mut self) {
        unsafe { bind::Mix_CloseAudio() }
    }
}