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
#![feature(unique)]

extern crate libc;

pub mod error;
mod ffi;

use error::Error;

use std::ffi::CString;
use std::ptr;
use std::ptr::NonNull;
use libc::c_int;

/// Opaque struct for Ao handling. Make sure only one instance of this
/// type is created, and that initialization is done in the main thread.
pub struct Ao;
impl Ao {
    /// Initializes libao.
    pub fn new() -> Self {
        unsafe {
            ffi::ao_initialize();
        }
        Ao
    }

    /// Reloads libao.
    pub fn reload(&mut self) {
        unsafe {
            ffi::ao_shutdown();
            ffi::ao_initialize();
        }
    }
}

impl Drop for Ao {
    fn drop(&mut self) {
        unsafe {
            ffi::ao_shutdown();
        }
    }
}

/// Ao driver.
pub struct Driver {
    driver_id: i32,
}

impl Driver {
    /// Creates and returns (if-any) the default driver.
    pub fn new() -> Result<Self, Error> {
        let driver_id = unsafe { ffi::ao_default_driver_id() };
        if driver_id >= 0 {
            Ok(Driver { driver_id: driver_id })
        } else {
            Err(Error::from_errno())
        }
    }

    /// Tries to find a driver with the given name.
    ///
    /// # Panics
    /// If the given name contains inner zero bytes.
    pub fn with_name(short_name: &str) -> Result<Self, Error> {
        let short_name = CString::new(short_name).unwrap();
        let driver_id = unsafe { ffi::ao_driver_id(short_name.as_ptr()) };
        if driver_id >= 0 {
            Ok(Driver { driver_id: driver_id })
        } else {
            Err(Error::from_errno())
        }
    }

    /// Returns the driver id.
    pub fn driver_id(&self) -> i32 {
        self.driver_id
    }
}

/// Ao device.
pub struct Device {
    device: NonNull<ffi::AoDevice>,
}

impl Device {
    /// Creates a new device using the given driver, format, and settings.
    pub fn new(driver: &Driver,
               format: &Format,
               settings: Option<&Settings>)
               -> Result<Self, Error> {
        let options = match settings {
            Some(settings) => settings.as_ao_option(),
            None => ptr::null(),
        };
        let ao_device =
            unsafe { ffi::ao_open_live(driver.driver_id(), &format.to_ao_format(), options) };

        // unique new does a null-ptr check now
        let ao_device = match NonNull::new(ao_device) {
            Some(udev) => udev,
            None => {
                return Err(Error::from_errno())
            }
        };

        Ok(Device { device: ao_device })
    }

    /// Plays the given PCM data using the specified format.
    pub fn play(&self, buffer: &[i8]) {
        unsafe {
            ffi::ao_play(self.device.as_ref(), buffer.as_ptr(), buffer.len() as u32);
        }
    }
}

impl Drop for Device {
    fn drop(&mut self) {
        unsafe {
            if !self.device.as_ptr().is_null() {
                ffi::ao_close(self.device.as_mut());
            }
        }
    }
}

/// Ao settings.
pub struct Settings {
    options: *mut ffi::AoOption,
}

impl Settings {
    /// Creates empty settings.
    pub fn new() -> Self {
        Settings { options: ptr::null_mut() }
    }

    /// Appends a new setting to the list.
    ///
    /// # Panics
    /// If the passed string or value contain inner zero bytes.
    pub fn append(&mut self, key: &str, value: &str) {
        let key = CString::new(key).unwrap();
        let value = CString::new(value).unwrap();
        unsafe {
            // libao will create its own copies of the key and value.
            ffi::ao_append_option(&mut self.options, key.as_ptr(), value.as_ptr());
        }
    }

    /// Returns the contained AoOption pointer.
    pub fn as_ao_option(&self) -> *const ffi::AoOption {
        self.options
    }
}

impl Drop for Settings {
    fn drop(&mut self) {
        unsafe {
            ffi::ao_free_options(self.options);
        }
    }
}

/// Ao sample format.
pub struct Format {
    pub bits: u32,
    pub rate: u32,
    pub channels: u32,
    pub byte_format: ByteFormat,
    // TODO: Implement macros for creating channel formats (mono, stereo, etc).
    pub channel_format: Option<String>,
}

impl Format {
    /// Creates a new default format.
    pub fn new() -> Self {
        Format::default()
    }

    /// Returns a new AoFormat without consuming self.
    pub fn to_ao_format(&self) -> ffi::AoFormat {
        ffi::AoFormat {
            bits: self.bits.clone() as c_int,
            rate: self.rate.clone() as c_int,
            channels: self.channels.clone() as c_int,
            byte_format: self.byte_format.clone() as c_int,
            matrix: ptr::null_mut(),
        }
    }
}

impl Default for Format {
    fn default() -> Self {
        Format {
            bits: 16,
            rate: 44100,
            channels: 2,
            byte_format: ByteFormat::Little,
            channel_format: None,
        }
    }
}

/// Byte format, can either by little-endian, bit-endian, or native (inherits from system).
#[derive(Copy, Clone)]
pub enum ByteFormat {
    Little = 1,
    Big = 2,
    Native = 4,
}