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
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
// Copyright © 2017-2018 Mozilla Foundation
//
// This program is made available under an ISC-style license.  See the
// accompanying file LICENSE for details.

use ffi;
use std::str;
use util::opt_bytes;

/// The state of a device.
#[derive(PartialEq, Eq, Clone, Debug, Copy)]
pub enum DeviceState {
    /// The device has been disabled at the system level.
    Disabled,
    /// The device is enabled, but nothing is plugged into it.
    Unplugged,
    /// The device is enabled.
    Enabled,
}

/// Architecture specific sample type.
bitflags! {
    pub struct DeviceFormat: ffi::cubeb_device_fmt {
        const S16LE = ffi::CUBEB_DEVICE_FMT_S16LE;
        const S16BE = ffi::CUBEB_DEVICE_FMT_S16BE;
        const F32LE = ffi::CUBEB_DEVICE_FMT_F32LE;
        const F32BE = ffi::CUBEB_DEVICE_FMT_F32BE;
    }
}

/// Channel type for a `cubeb_stream`. Depending on the backend and platform
/// used, this can control inter-stream interruption, ducking, and volume
/// control.
bitflags! {
    pub struct DevicePref: ffi::cubeb_device_pref {
        const NONE = ffi::CUBEB_DEVICE_PREF_NONE;
        const MULTIMEDIA = ffi::CUBEB_DEVICE_PREF_MULTIMEDIA;
        const VOICE = ffi::CUBEB_DEVICE_PREF_VOICE;
        const NOTIFICATION = ffi::CUBEB_DEVICE_PREF_NOTIFICATION;
        const ALL = ffi::CUBEB_DEVICE_PREF_ALL;
    }
}

/// Whether a particular device is an input device (e.g. a microphone), or an
/// output device (e.g. headphones).
bitflags! {
    pub struct DeviceType: ffi::cubeb_device_type {
        const UNKNOWN = ffi::CUBEB_DEVICE_TYPE_UNKNOWN as _;
        const INPUT = ffi::CUBEB_DEVICE_TYPE_INPUT as _;
        const OUTPUT = ffi::CUBEB_DEVICE_TYPE_OUTPUT as _;
    }
}

/// An opaque handle used to refer to a particular input or output device
/// across calls.
pub type DeviceId = ffi::cubeb_devid;

/// Audio device description
ffi_type_heap! {
    type CType = ffi::cubeb_device;
    #[derive(Debug)]
    pub struct Device;
    pub struct DeviceRef;
}

impl DeviceRef {
    fn get_ref(&self) -> &ffi::cubeb_device {
        unsafe { &*self.as_ptr() }
    }

    /// Gets the output device name.
    ///
    /// May return `None` if there is no output device.
    pub fn output_name(&self) -> Option<&str> {
        self.output_name_bytes().map(|b| str::from_utf8(b).unwrap())
    }

    pub fn output_name_bytes(&self) -> Option<&[u8]> {
        unsafe { opt_bytes(self.get_ref().output_name) }
    }

    /// Gets the input device name.
    ///
    /// May return `None` if there is no input device.
    pub fn input_name(&self) -> Option<&str> {
        self.input_name_bytes().map(|b| str::from_utf8(b).unwrap())
    }

    pub fn input_name_bytes(&self) -> Option<&[u8]> {
        unsafe { opt_bytes(self.get_ref().input_name) }
    }
}

/// This structure holds the characteristics of an input or output
/// audio device. It is obtained using `enumerate_devices`, which
/// returns these structures via `device_collection` and must be
/// destroyed via `device_collection_destroy`.
ffi_type_stack! {
    type CType = ffi::cubeb_device_info;
    pub struct DeviceInfo;
    pub struct DeviceInfoRef;
}

impl DeviceInfoRef {
    fn get_ref(&self) -> &ffi::cubeb_device_info {
        unsafe { &*self.as_ptr() }
    }

    /// Device identifier handle.
    pub fn devid(&self) -> DeviceId {
        self.get_ref().devid
    }

    /// Device identifier which might be presented in a UI.
    pub fn device_id(&self) -> Option<&str> {
        self.device_id_bytes().map(|b| str::from_utf8(b).unwrap())
    }

    pub fn device_id_bytes(&self) -> Option<&[u8]> {
        unsafe { opt_bytes(self.get_ref().device_id) }
    }

    /// Friendly device name which might be presented in a UI.
    pub fn friendly_name(&self) -> Option<&str> {
        self.friendly_name_bytes()
            .map(|b| str::from_utf8(b).unwrap())
    }

    pub fn friendly_name_bytes(&self) -> Option<&[u8]> {
        unsafe { opt_bytes(self.get_ref().friendly_name) }
    }

    /// Two devices have the same group identifier if they belong to
    /// the same physical device; for example a headset and
    /// microphone.
    pub fn group_id(&self) -> Option<&str> {
        self.group_id_bytes().map(|b| str::from_utf8(b).unwrap())
    }

    pub fn group_id_bytes(&self) -> Option<&[u8]> {
        unsafe { opt_bytes(self.get_ref().group_id) }
    }

    /// Optional vendor name, may be None.
    pub fn vendor_name(&self) -> Option<&str> {
        self.vendor_name_bytes().map(|b| str::from_utf8(b).unwrap())
    }

    pub fn vendor_name_bytes(&self) -> Option<&[u8]> {
        unsafe { opt_bytes(self.get_ref().vendor_name) }
    }

    /// Type of device (Input/Output).
    pub fn device_type(&self) -> DeviceType {
        DeviceType::from_bits_truncate(self.get_ref().device_type)
    }

    /// State of device disabled/enabled/unplugged.
    pub fn state(&self) -> DeviceState {
        let state = self.get_ref().state;
        macro_rules! check( ($($raw:ident => $real:ident),*) => (
            $(if state == ffi::$raw {
                DeviceState::$real
            }) else *
            else {
                panic!("unknown device state: {}", state)
            }
        ));

        check!(CUBEB_DEVICE_STATE_DISABLED => Disabled,
               CUBEB_DEVICE_STATE_UNPLUGGED => Unplugged,
               CUBEB_DEVICE_STATE_ENABLED => Enabled)
    }

    /// Preferred device.
    pub fn preferred(&self) -> DevicePref {
        DevicePref::from_bits(self.get_ref().preferred).unwrap()
    }

    /// Sample format supported.
    pub fn format(&self) -> DeviceFormat {
        DeviceFormat::from_bits(self.get_ref().format).unwrap()
    }

    /// The default sample format for this device.
    pub fn default_format(&self) -> DeviceFormat {
        DeviceFormat::from_bits(self.get_ref().default_format).unwrap()
    }

    /// Channels.
    pub fn max_channels(&self) -> u32 {
        self.get_ref().max_channels
    }

    /// Default/Preferred sample rate.
    pub fn default_rate(&self) -> u32 {
        self.get_ref().default_rate
    }

    /// Maximum sample rate supported.
    pub fn max_rate(&self) -> u32 {
        self.get_ref().max_rate
    }

    /// Minimum sample rate supported.
    pub fn min_rate(&self) -> u32 {
        self.get_ref().min_rate
    }

    /// Lowest possible latency in frames.
    pub fn latency_lo(&self) -> u32 {
        self.get_ref().latency_lo
    }

    /// Higest possible latency in frames.
    pub fn latency_hi(&self) -> u32 {
        self.get_ref().latency_hi
    }
}

#[cfg(test)]
mod tests {
    use Device;
    use ffi::cubeb_device;

    #[test]
    fn device_device_ref_same_ptr() {
        let ptr: *mut cubeb_device = 0xDEAD_BEEF as *mut _;
        let device = unsafe { Device::from_ptr(ptr) };
        assert_eq!(device.as_ptr(), ptr);
        assert_eq!(device.as_ptr(), device.as_ref().as_ptr());
    }
}