cubeb_core/
stream.rs

1// Copyright © 2017-2018 Mozilla Foundation
2//
3// This program is made available under an ISC-style license.  See the
4// accompanying file LICENSE for details.
5
6use ffi;
7use std::ffi::CStr;
8use std::os::raw::{c_int, c_void};
9use std::ptr;
10use {ChannelLayout, DeviceRef, Result, SampleFormat};
11
12/// Stream states signaled via `state_callback`.
13#[derive(PartialEq, Eq, Clone, Debug, Copy)]
14pub enum State {
15    /// Stream started.
16    Started,
17    /// Stream stopped.
18    Stopped,
19    /// Stream drained.
20    Drained,
21    /// Stream disabled due to error.
22    Error,
23}
24
25impl From<ffi::cubeb_state> for State {
26    fn from(x: ffi::cubeb_state) -> Self {
27        use State::*;
28        match x {
29            ffi::CUBEB_STATE_STARTED => Started,
30            ffi::CUBEB_STATE_STOPPED => Stopped,
31            ffi::CUBEB_STATE_DRAINED => Drained,
32            _ => Error,
33        }
34    }
35}
36
37impl From<State> for ffi::cubeb_state {
38    fn from(x: State) -> Self {
39        use State::*;
40        match x {
41            Started => ffi::CUBEB_STATE_STARTED,
42            Stopped => ffi::CUBEB_STATE_STOPPED,
43            Drained => ffi::CUBEB_STATE_DRAINED,
44            Error => ffi::CUBEB_STATE_ERROR,
45        }
46    }
47}
48
49bitflags! {
50    /// Miscellaneous stream preferences.
51    pub struct StreamPrefs: ffi::cubeb_stream_prefs {
52        const LOOPBACK = ffi::CUBEB_STREAM_PREF_LOOPBACK;
53        const DISABLE_DEVICE_SWITCHING = ffi::CUBEB_STREAM_PREF_DISABLE_DEVICE_SWITCHING;
54        const VOICE = ffi::CUBEB_STREAM_PREF_VOICE;
55    }
56}
57
58impl StreamPrefs {
59    pub const NONE: Self = Self::empty();
60}
61
62bitflags! {
63    /// Input stream processing parameters.
64    pub struct InputProcessingParams: ffi::cubeb_input_processing_params {
65        const ECHO_CANCELLATION = ffi::CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION;
66        const NOISE_SUPPRESSION = ffi::CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION;
67        const AUTOMATIC_GAIN_CONTROL = ffi::CUBEB_INPUT_PROCESSING_PARAM_AUTOMATIC_GAIN_CONTROL;
68        const VOICE_ISOLATION = ffi::CUBEB_INPUT_PROCESSING_PARAM_VOICE_ISOLATION;
69    }
70}
71
72impl InputProcessingParams {
73    pub const NONE: Self = Self::empty();
74}
75
76ffi_type_stack! {
77    /// Stream format initialization parameters.
78    type CType = ffi::cubeb_stream_params;
79    #[derive(Debug)]
80    pub struct StreamParams;
81    pub struct StreamParamsRef;
82}
83
84impl StreamParamsRef {
85    fn get_ref(&self) -> &ffi::cubeb_stream_params {
86        unsafe { &*self.as_ptr() }
87    }
88
89    pub fn format(&self) -> SampleFormat {
90        use SampleFormat::*;
91        match self.get_ref().format {
92            ffi::CUBEB_SAMPLE_S16LE => S16LE,
93            ffi::CUBEB_SAMPLE_S16BE => S16BE,
94            ffi::CUBEB_SAMPLE_FLOAT32LE => Float32LE,
95            ffi::CUBEB_SAMPLE_FLOAT32BE => Float32BE,
96            x => panic!("unknown sample format: {}", x),
97        }
98    }
99
100    pub fn rate(&self) -> u32 {
101        self.get_ref().rate
102    }
103    pub fn channels(&self) -> u32 {
104        self.get_ref().channels
105    }
106
107    pub fn layout(&self) -> ChannelLayout {
108        ChannelLayout::from(self.get_ref().layout)
109    }
110
111    pub fn prefs(&self) -> StreamPrefs {
112        StreamPrefs::from_bits_truncate(self.get_ref().prefs)
113    }
114}
115
116unsafe fn wrapped_cubeb_stream_destroy(stream: *mut ffi::cubeb_stream) {
117    ffi::cubeb_stream_stop(stream);
118    ffi::cubeb_stream_destroy(stream);
119}
120
121ffi_type_heap! {
122    type CType = ffi::cubeb_stream;
123    fn drop = wrapped_cubeb_stream_destroy;
124    pub struct Stream;
125    pub struct StreamRef;
126}
127
128impl StreamRef {
129    /// Start playback.
130    pub fn start(&self) -> Result<()> {
131        unsafe { call!(ffi::cubeb_stream_start(self.as_ptr())) }
132    }
133
134    /// Stop playback.
135    pub fn stop(&self) -> Result<()> {
136        unsafe { call!(ffi::cubeb_stream_stop(self.as_ptr())) }
137    }
138
139    /// Get the current stream playback position.
140    pub fn position(&self) -> Result<u64> {
141        let mut position = 0u64;
142        unsafe {
143            call!(ffi::cubeb_stream_get_position(self.as_ptr(), &mut position))?;
144        }
145        Ok(position)
146    }
147
148    /// Get the latency for this stream, in frames. This is the number
149    /// of frames between the time cubeb acquires the data in the
150    /// callback and the listener can hear the sound.
151    pub fn latency(&self) -> Result<u32> {
152        let mut latency = 0u32;
153        unsafe {
154            call!(ffi::cubeb_stream_get_latency(self.as_ptr(), &mut latency))?;
155        }
156        Ok(latency)
157    }
158
159    /// Get the input latency for this stream, in frames. This is the number of frames between the
160    /// time the audio input device records the audio, and the cubeb callback delivers it.
161    /// This returns an error if the stream is output-only.
162    pub fn input_latency(&self) -> Result<u32> {
163        let mut latency = 0u32;
164        unsafe {
165            call!(ffi::cubeb_stream_get_input_latency(
166                self.as_ptr(),
167                &mut latency
168            ))?;
169        }
170        Ok(latency)
171    }
172
173    /// Set the volume for a stream.
174    pub fn set_volume(&self, volume: f32) -> Result<()> {
175        unsafe { call!(ffi::cubeb_stream_set_volume(self.as_ptr(), volume)) }
176    }
177
178    /// Change a stream's name
179    pub fn set_name(&self, name: &CStr) -> Result<()> {
180        unsafe { call!(ffi::cubeb_stream_set_name(self.as_ptr(), name.as_ptr())) }
181    }
182
183    /// Get the current output device for this stream.
184    pub fn current_device(&self) -> Result<&DeviceRef> {
185        let mut device: *mut ffi::cubeb_device = ptr::null_mut();
186        unsafe {
187            call!(ffi::cubeb_stream_get_current_device(
188                self.as_ptr(),
189                &mut device
190            ))?;
191            Ok(DeviceRef::from_ptr(device))
192        }
193    }
194
195    /// Set the mute state for an input stream.
196    pub fn set_input_mute(&self, mute: bool) -> Result<()> {
197        let mute: c_int = if mute { 1 } else { 0 };
198        unsafe { call!(ffi::cubeb_stream_set_input_mute(self.as_ptr(), mute)) }
199    }
200
201    /// Set the processing parameters for an input stream.
202    pub fn set_input_processing_params(&self, params: InputProcessingParams) -> Result<()> {
203        unsafe {
204            call!(ffi::cubeb_stream_set_input_processing_params(
205                self.as_ptr(),
206                params.bits()
207            ))
208        }
209    }
210
211    /// Destroy a cubeb_device structure.
212    pub fn device_destroy(&self, device: DeviceRef) -> Result<()> {
213        unsafe {
214            call!(ffi::cubeb_stream_device_destroy(
215                self.as_ptr(),
216                device.as_ptr()
217            ))
218        }
219    }
220
221    /// Set a callback to be notified when the output device changes.
222    pub fn register_device_changed_callback(
223        &self,
224        callback: ffi::cubeb_device_changed_callback,
225    ) -> Result<()> {
226        unsafe {
227            call!(ffi::cubeb_stream_register_device_changed_callback(
228                self.as_ptr(),
229                callback
230            ))
231        }
232    }
233
234    pub fn user_ptr(&self) -> *mut c_void {
235        unsafe { ffi::cubeb_stream_user_ptr(self.as_ptr()) }
236    }
237}
238
239#[cfg(test)]
240mod tests {
241    use std::mem;
242    use {StreamParams, StreamParamsRef, StreamPrefs};
243
244    #[test]
245    fn stream_params_default() {
246        let params = StreamParams::default();
247        assert_eq!(params.channels(), 0);
248    }
249
250    #[test]
251    fn stream_params_raw_channels() {
252        let mut raw: super::ffi::cubeb_stream_params = unsafe { mem::zeroed() };
253        raw.channels = 2;
254        let params = unsafe { StreamParamsRef::from_ptr(&mut raw) };
255        assert_eq!(params.channels(), 2);
256    }
257
258    #[test]
259    fn stream_params_raw_format() {
260        let mut raw: super::ffi::cubeb_stream_params = unsafe { mem::zeroed() };
261        macro_rules! check(
262            ($($raw:ident => $real:ident),*) => (
263                $(raw.format = super::ffi::$raw;
264                  let params = unsafe {
265                      StreamParamsRef::from_ptr(&mut raw)
266                  };
267                  assert_eq!(params.format(), super::SampleFormat::$real);
268                )*
269            ) );
270
271        check!(CUBEB_SAMPLE_S16LE => S16LE,
272               CUBEB_SAMPLE_S16BE => S16BE,
273               CUBEB_SAMPLE_FLOAT32LE => Float32LE,
274               CUBEB_SAMPLE_FLOAT32BE => Float32BE);
275    }
276
277    #[test]
278    fn stream_params_raw_format_native_endian() {
279        let mut raw: super::ffi::cubeb_stream_params = unsafe { mem::zeroed() };
280        raw.format = super::ffi::CUBEB_SAMPLE_S16NE;
281        let params = unsafe { StreamParamsRef::from_ptr(&mut raw) };
282        assert_eq!(
283            params.format(),
284            if cfg!(target_endian = "little") {
285                super::SampleFormat::S16LE
286            } else {
287                super::SampleFormat::S16BE
288            }
289        );
290
291        raw.format = super::ffi::CUBEB_SAMPLE_FLOAT32NE;
292        let params = unsafe { StreamParamsRef::from_ptr(&mut raw) };
293        assert_eq!(
294            params.format(),
295            if cfg!(target_endian = "little") {
296                super::SampleFormat::Float32LE
297            } else {
298                super::SampleFormat::Float32BE
299            }
300        );
301    }
302
303    #[test]
304    fn stream_params_raw_layout() {
305        let mut raw: super::ffi::cubeb_stream_params = unsafe { mem::zeroed() };
306        macro_rules! check(
307            ($($raw:ident => $real:ident),*) => (
308                $(raw.layout = super::ffi::$raw;
309                  let params = unsafe {
310                      StreamParamsRef::from_ptr(&mut raw)
311                  };
312                  assert_eq!(params.layout(), super::ChannelLayout::$real);
313                )*
314            ) );
315
316        check!(CUBEB_LAYOUT_UNDEFINED => UNDEFINED,
317               CUBEB_LAYOUT_MONO => MONO,
318               CUBEB_LAYOUT_MONO_LFE => MONO_LFE,
319               CUBEB_LAYOUT_STEREO => STEREO,
320               CUBEB_LAYOUT_STEREO_LFE => STEREO_LFE,
321               CUBEB_LAYOUT_3F => _3F,
322               CUBEB_LAYOUT_3F_LFE => _3F_LFE,
323               CUBEB_LAYOUT_2F1 => _2F1,
324               CUBEB_LAYOUT_2F1_LFE => _2F1_LFE,
325               CUBEB_LAYOUT_3F1 => _3F1,
326               CUBEB_LAYOUT_3F1_LFE => _3F1_LFE,
327               CUBEB_LAYOUT_2F2 => _2F2,
328               CUBEB_LAYOUT_2F2_LFE => _2F2_LFE,
329               CUBEB_LAYOUT_QUAD => QUAD,
330               CUBEB_LAYOUT_QUAD_LFE => QUAD_LFE,
331               CUBEB_LAYOUT_3F2 => _3F2,
332               CUBEB_LAYOUT_3F2_LFE => _3F2_LFE,
333               CUBEB_LAYOUT_3F2_BACK => _3F2_BACK,
334               CUBEB_LAYOUT_3F2_LFE_BACK => _3F2_LFE_BACK,
335               CUBEB_LAYOUT_3F3R_LFE => _3F3R_LFE,
336               CUBEB_LAYOUT_3F4_LFE => _3F4_LFE);
337    }
338
339    #[test]
340    fn stream_params_raw_rate() {
341        let mut raw: super::ffi::cubeb_stream_params = unsafe { mem::zeroed() };
342        raw.rate = 44_100;
343        let params = unsafe { StreamParamsRef::from_ptr(&mut raw) };
344        assert_eq!(params.rate(), 44_100);
345    }
346
347    #[test]
348    fn stream_params_raw_prefs() {
349        let mut raw: super::ffi::cubeb_stream_params = unsafe { mem::zeroed() };
350        raw.prefs = super::ffi::CUBEB_STREAM_PREF_LOOPBACK;
351        let params = unsafe { StreamParamsRef::from_ptr(&mut raw) };
352        assert_eq!(params.prefs(), StreamPrefs::LOOPBACK);
353    }
354
355    #[test]
356    fn stream_params_stream_params_ref_ptr_match() {
357        let params = StreamParams::default();
358        assert_eq!(params.as_ptr(), params.as_ref().as_ptr());
359    }
360}