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
extern crate libc;
mod bindgen;
use bindgen::{SF_INFO, SNDFILE, sf_count_t, sf_open, sf_readf_float, sf_close, sf_error, sf_error_number, sf_strerror, OpenMode, SfError};

use std::ffi::{CString, NulError, CStr};
use libc::{c_int, c_float};

/// Information about a sound file.
///
/// Currently a raw copy of the `SF_INFO` struct - better API tbc.
/// Check out [the libsndfile docs](http://www.mega-nerd.com/libsndfile/api.html#open)
/// for more info on this struct.
#[derive(Debug)]
pub struct SndFileInfo {
    /// The number of frames in the file
    pub frames: sf_count_t,
    /// The file's sample rate
    pub samplerate: c_int,
    /// The number of channels in the file
    pub channels: c_int,
    /// The file's format, which is currently gibberish
    format: c_int,
    /// I have no idea what this is
    sections: c_int,
    /// Whether the file is seekable, but I don't know what's true and what's false
    seekable: c_int
}
impl From<SF_INFO> for SndFileInfo {
    fn from(info: SF_INFO) -> Self {
        SndFileInfo {
            frames: info.frames,
            samplerate: info.samplerate,
            channels: info.channels,
            format: info.format,
            sections: info.sections,
            seekable: info.seekable
        }
    }
}
/// A `libsndfile` sound file object.
#[derive(Debug)]
pub struct SndFile {
    ptr: *mut SNDFILE,
    pub info: SndFileInfo
}
impl Drop for SndFile {
    fn drop(&mut self) {
        unsafe { sf_close(self.ptr) };
    }
}
impl SndFile {
    /// Opens a sound file for reading.
    ///
    /// Takes a `path` to the sound file to open. Please note that opening for
    /// writing is not supported yet.

    pub fn open(path: &str) -> Result<Self, SndError> {
        let cstr = try!(CString::new(path));
        let mut sfi = SF_INFO {
            frames: 0,
            samplerate: 0,
            channels: 0,
            format: 0,
            sections: 0,
            seekable: 0
        };
        let sndfile_ptr: *mut SNDFILE;
        unsafe {
            sndfile_ptr = sf_open(cstr.as_ptr(), OpenMode::SFM_READ as c_int, &mut sfi);
        }
        if sndfile_ptr.is_null() {
            Err(Self::get_error(sndfile_ptr))
        }
        else {
            Ok(SndFile {
                ptr: sndfile_ptr,
                info: SndFileInfo::from(sfi)
            })
        }
    }
    fn get_error(sf: *mut SNDFILE) -> SndError {
        let strptr: &CStr;
        let mut errno: c_int;
        unsafe {
            errno = sf_error(sf);
            if errno >= SfError::SF_ERR_UNLISTED as c_int {
                strptr = CStr::from_ptr(sf_error_number(errno));
                errno = SfError::SF_ERR_UNLISTED as c_int;
            }
            else {
                strptr = CStr::from_ptr(sf_strerror(sf));
            }
        }
        let expl = strptr.to_string_lossy().into_owned();
        SndError {
            err: errno,
            expl: expl
        }
    }

    /// Reads sound data from a sound file into a buffer of `libc::c_float`s.
    ///
    /// Returns either the amount of frames actually read (which may be less than
    /// the `frames` requested if there are less frames available).
    /// A `c_float` is usually `f32` on most platforms.
    ///
    /// # Panics
    ///
    /// Panics if the amount of frames read exceeds `usize::MAX` or is less than 0.

    pub fn into_slice_float(&mut self, buf: &mut [c_float], frames: usize) -> Result<usize, SndError> {
        let units_reqd = self.info.channels as usize * frames;
        if buf.len() < units_reqd {
            return Err(SndError {
                err: SfError::SF_ERR_UNLISTED as c_int,
                expl: "Binding: Buffer provided not big enough".to_string()
            });
        }
        let mut written: sf_count_t = 0;
        let ptr = buf.as_mut_ptr();
        unsafe {
            written = sf_readf_float(self.ptr, ptr, frames as i64);
        }
        assert!(written >= 0);
        assert!((written as usize) < ::std::usize::MAX);
        Ok(written as usize)
    }
}

/// An error type.
///
/// This one sucks. A better one is coming.
#[derive(Debug)]
pub struct SndError {
    err: c_int,
    pub expl: String
}
impl From<NulError> for SndError {
    fn from(_: NulError) -> SndError {
        SndError {
            err: SfError::SF_ERR_UNLISTED as c_int,
            expl: "Binding: Encountered a NUL byte in path argument".to_string()
        }
    }
}