soundio 0.2.1

Bindings to libsoundio for audio input and output.
Documentation
extern crate libsoundio_sys as raw;

use std::ffi::CStr;
use std::fmt;

/// Format defines the format of the samples. In 90% of cases you'll want `S16LE`, or maybe `Float64LE`.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum Format {
    /// Invalid format
    Invalid,
    /// Signed 8 bit
    S8,
    /// Unsigned 8 bit
    U8,
    /// Signed 16 bit Little Endian
    S16LE,
    /// Signed 16 bit Big Endian
    S16BE,
    /// Unsigned 16 bit Little Endian
    U16LE,
    /// Unsigned 16 bit Big Endian
    U16BE,
    /// Signed 24 bit Little Endian using low three bytes in 32-bit word
    S24LE,
    /// Signed 24 bit Big Endian using low three bytes in 32-bit word
    S24BE,
    /// Unsigned 24 bit Little Endian using low three bytes in 32-bit word
    U24LE,
    /// Unsigned 24 bit Big Endian using low three bytes in 32-bit word
    U24BE,
    /// Signed 32 bit Little Endian
    S32LE,
    /// Signed 32 bit Big Endian
    S32BE,
    /// Unsigned 32 bit Little Endian
    U32LE,
    /// Unsigned 32 bit Big Endian
    U32BE,
    /// Float 32 bit Little Endian, Range -1.0 to 1.0
    Float32LE,
    /// Float 32 bit Big Endian, Range -1.0 to 1.0
    Float32BE,
    /// Float 64 bit Little Endian, Range -1.0 to 1.0
    Float64LE,
    /// Float 64 bit Big Endian, Range -1.0 to 1.0
    Float64BE,
}

/// This is a small helper type to find the endianness of a sample format.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum Endian {
    Big,
    Little,
}

/// Return the endianness of a sample format. `Format::Invalid`,
/// `Format::S8` and `Format::U8` return `Endian::Little`.
///
/// # Examples
///
/// ```
/// use soundio::*;
/// assert_eq!(endianness(Format::S24LE), Endian::Little);
/// assert_eq!(endianness(Format::U8), Endian::Little);
/// assert_eq!(endianness(Format::Float64BE), Endian::Big);
/// ```
pub fn endianness(f: Format) -> Endian {
    match f {
        Format::Invalid
        | Format::U8
        | Format::S8
        | Format::U16LE
        | Format::S16LE
        | Format::U24LE
        | Format::S24LE
        | Format::U32LE
        | Format::S32LE
        | Format::Float32LE
        | Format::Float64LE => Endian::Little,
        Format::U16BE
        | Format::S16BE
        | Format::U24BE
        | Format::S24BE
        | Format::U32BE
        | Format::S32BE
        | Format::Float32BE
        | Format::Float64BE => Endian::Big,
    }
}

/// This module provides some aliases for native endian formats, and foreign endian formats.
#[cfg(target_endian = "big")]
#[allow(non_upper_case_globals)]
pub mod native {
    use super::Format;

    /// Signed 16 bit Foreign Endian
    pub const S16FE: Format = Format::S16LE;
    /// Signed 16 bit Native Endian
    pub const S16NE: Format = Format::S16BE;
    /// Unsigned 16 bit Foreign Endian
    pub const U16FE: Format = Format::U16LE;
    /// Unsigned 16 bit Native Endian
    pub const U16NE: Format = Format::U16BE;
    /// Signed 24 bit Foreign Endian using low three bytes in 32-bit word
    pub const S24FE: Format = Format::S24LE;
    /// Signed 24 bit Native Endian using low three bytes in 32-bit word
    pub const S24NE: Format = Format::S24BE;
    /// Unsigned 24 bit Foreign Endian using low three bytes in 32-bit word
    pub const U24FE: Format = Format::U24LE;
    /// Unsigned 24 bit Native Endian using low three bytes in 32-bit word
    pub const U24NE: Format = Format::U24BE;
    /// Signed 32 bit Foreign Endian
    pub const S32FE: Format = Format::S32LE;
    /// Signed 32 bit Native Endian
    pub const S32NE: Format = Format::S32BE;
    /// Unsigned 32 bit Foreign Endian
    pub const U32FE: Format = Format::U32LE;
    /// Unsigned 32 bit Native Endian
    pub const U32NE: Format = Format::U32BE;
    /// Float 32 bit Foreign Endian, Range -1.0 to 1.0
    pub const Float32FE: Format = Format::Float32LE;
    /// Float 32 bit Native Endian, Range -1.0 to 1.0
    pub const Float32NE: Format = Format::Float32BE;
    /// Float 64 bit Foreign Endian, Range -1.0 to 1.0
    pub const Float64FE: Format = Format::Float64LE;
    /// Float 64 bit Native Endian, Range -1.0 to 1.0
    pub const Float64NE: Format = Format::Float64BE;
}

/// This module provides some aliases for native endian formats, and foreign endian formats.
#[cfg(target_endian = "little")]
#[allow(non_upper_case_globals)]
pub mod native {
    use super::Format;

    /// Signed 16 bit Native Endian
    pub const S16NE: Format = Format::S16LE;
    /// Signed 16 bit Foreign Endian
    pub const S16FE: Format = Format::S16BE;
    /// Unsigned 16 bit Native Endian
    pub const U16NE: Format = Format::U16LE;
    /// Unsigned 16 bit Foreign Endian
    pub const U16FE: Format = Format::U16BE;
    /// Signed 24 bit Native Endian using low three bytes in 32-bit word
    pub const S24NE: Format = Format::S24LE;
    /// Signed 24 bit Foreign Endian using low three bytes in 32-bit word
    pub const S24FE: Format = Format::S24BE;
    /// Unsigned 24 bit Native Endian using low three bytes in 32-bit word
    pub const U24NE: Format = Format::U24LE;
    /// Unsigned 24 bit Foreign Endian using low three bytes in 32-bit word
    pub const U24FE: Format = Format::U24BE;
    /// Signed 32 bit Native Endian
    pub const S32NE: Format = Format::S32LE;
    /// Signed 32 bit Foreign Endian
    pub const S32FE: Format = Format::S32BE;
    /// Unsigned 32 bit Native Endian
    pub const U32NE: Format = Format::U32LE;
    /// Unsigned 32 bit Foreign Endian
    pub const U32FE: Format = Format::U32BE;
    /// Float 32 bit Native Endian, Range -1.0 to 1.0
    pub const Float32NE: Format = Format::Float32LE;
    /// Float 32 bit Foreign Endian, Range -1.0 to 1.0
    pub const Float32FE: Format = Format::Float32BE;
    /// Float 64 bit Native Endian, Range -1.0 to 1.0
    pub const Float64NE: Format = Format::Float64LE;
    /// Float 64 bit Foreign Endian, Range -1.0 to 1.0
    pub const Float64FE: Format = Format::Float64BE;
}

impl From<raw::SoundIoFormat> for Format {
    fn from(format: raw::SoundIoFormat) -> Format {
        match format {
            raw::SoundIoFormat::SoundIoFormatS8 => Format::S8,
            raw::SoundIoFormat::SoundIoFormatU8 => Format::U8,
            raw::SoundIoFormat::SoundIoFormatS16LE => Format::S16LE,
            raw::SoundIoFormat::SoundIoFormatS16BE => Format::S16BE,
            raw::SoundIoFormat::SoundIoFormatU16LE => Format::U16LE,
            raw::SoundIoFormat::SoundIoFormatU16BE => Format::U16BE,
            raw::SoundIoFormat::SoundIoFormatS24LE => Format::S24LE,
            raw::SoundIoFormat::SoundIoFormatS24BE => Format::S24BE,
            raw::SoundIoFormat::SoundIoFormatU24LE => Format::U24LE,
            raw::SoundIoFormat::SoundIoFormatU24BE => Format::U24BE,
            raw::SoundIoFormat::SoundIoFormatS32LE => Format::S32LE,
            raw::SoundIoFormat::SoundIoFormatS32BE => Format::S32BE,
            raw::SoundIoFormat::SoundIoFormatU32LE => Format::U32LE,
            raw::SoundIoFormat::SoundIoFormatU32BE => Format::U32BE,
            raw::SoundIoFormat::SoundIoFormatFloat32LE => Format::Float32LE,
            raw::SoundIoFormat::SoundIoFormatFloat32BE => Format::Float32BE,
            raw::SoundIoFormat::SoundIoFormatFloat64LE => Format::Float64LE,
            raw::SoundIoFormat::SoundIoFormatFloat64BE => Format::Float64BE,
            _ => Format::Invalid,
        }
    }
}

impl From<Format> for raw::SoundIoFormat {
    fn from(format: Format) -> raw::SoundIoFormat {
        match format {
            Format::S8 => raw::SoundIoFormat::SoundIoFormatS8,
            Format::U8 => raw::SoundIoFormat::SoundIoFormatU8,
            Format::S16LE => raw::SoundIoFormat::SoundIoFormatS16LE,
            Format::S16BE => raw::SoundIoFormat::SoundIoFormatS16BE,
            Format::U16LE => raw::SoundIoFormat::SoundIoFormatU16LE,
            Format::U16BE => raw::SoundIoFormat::SoundIoFormatU16BE,
            Format::S24LE => raw::SoundIoFormat::SoundIoFormatS24LE,
            Format::S24BE => raw::SoundIoFormat::SoundIoFormatS24BE,
            Format::U24LE => raw::SoundIoFormat::SoundIoFormatU24LE,
            Format::U24BE => raw::SoundIoFormat::SoundIoFormatU24BE,
            Format::S32LE => raw::SoundIoFormat::SoundIoFormatS32LE,
            Format::S32BE => raw::SoundIoFormat::SoundIoFormatS32BE,
            Format::U32LE => raw::SoundIoFormat::SoundIoFormatU32LE,
            Format::U32BE => raw::SoundIoFormat::SoundIoFormatU32BE,
            Format::Float32LE => raw::SoundIoFormat::SoundIoFormatFloat32LE,
            Format::Float32BE => raw::SoundIoFormat::SoundIoFormatFloat32BE,
            Format::Float64LE => raw::SoundIoFormat::SoundIoFormatFloat64LE,
            Format::Float64BE => raw::SoundIoFormat::SoundIoFormatFloat64BE,
            _ => raw::SoundIoFormat::SoundIoFormatInvalid,
        }
    }
}

impl Format {
    /// Returns the number of byte used per sample. Note that this
    /// is the size of the storage used for the sample, not the number of
    /// bits used. For example S24LE returns 4.
    ///
    /// The returned values are specifically:
    ///
    /// * S8: 1
    /// * U8: 1
    /// * S16LE: 2
    /// * S16BE: 2
    /// * U16LE: 2
    /// * U16BE: 2
    /// * S24LE: 4
    /// * S24BE: 4
    /// * U24LE: 4
    /// * U24BE: 4
    /// * S32LE: 4
    /// * S32BE: 4
    /// * U32LE: 4
    /// * U32BE: 4
    /// * Float32LE: 4
    /// * Float32BE: 4
    /// * Float64LE: 8
    /// * Float64BE: 8
    /// * Invalid: -1
    ///
    /// # Examples
    ///
    /// ```
    /// let a = soundio::Format::S8;
    /// assert_eq!(a.bytes_per_sample(), 1);
    /// let b = soundio::Format::Float64LE;
    /// assert_eq!(b.bytes_per_sample(), 8);
    /// ```
    pub fn bytes_per_sample(self) -> usize {
        unsafe { raw::soundio_get_bytes_per_sample(self.into()) as usize }
    }

    /// Returns the number of bytes per frame.
    /// A frame is one sample for all channels so this is simply the number
    /// of bytes for a sample `bytes_per_sample()` multiplied by the number of channels.
    ///
    /// # Examples
    ///
    /// ```
    /// let a = soundio::Format::S8;
    /// assert_eq!(a.bytes_per_frame(2), 2);
    /// let b = soundio::Format::Float64LE;
    /// assert_eq!(b.bytes_per_frame(4), 32);
    /// ```
    pub fn bytes_per_frame(self, channel_count: usize) -> usize {
        self.bytes_per_sample() * channel_count
    }

    /// Returns the number of bytes per second, which is the number of bytes
    /// per frame multiplied by the number of frames per second (the sample rate).
    ///
    /// # Examples
    ///
    /// ```
    /// let a = soundio::Format::S8;
    /// assert_eq!(a.bytes_per_second(2, 8000), 16000);
    /// let b = soundio::Format::Float64LE;
    /// assert_eq!(b.bytes_per_second(4, 4000), 128000);
    /// ```
    pub fn bytes_per_second(self, channel_count: usize, sample_rate: usize) -> usize {
        self.bytes_per_sample() * channel_count * sample_rate
    }
}

impl fmt::Display for Format {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let c_str: &CStr = unsafe { CStr::from_ptr(raw::soundio_format_string((*self).into())) };

        // These are all ASCII. See https://github.com/andrewrk/libsoundio/blob/323fb1aa277674e2eb126234e3e6edf10ee45461/src/soundio.c#L76
        f.write_str(c_str.to_str().unwrap())
    }
}