From 873779aaa85d54c0825081f66695507b9a6408fa Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Thu, 11 Dec 2014 17:23:33 +0100 Subject: [PATCH] Add basic API --- Cargo.toml | 7 +- examples/basic.rs | 7 +- src/lib.rs | 32 +++++++++ src/wasapi/mod.rs | 171 +++++++++++++++++++++++++++++++++++++++------- 4 files changed, 191 insertions(+), 26 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index dcecce0..1c44faa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,12 @@ version = "0.0.1" authors = ["Pierre Krieger "] description = "Cross-platform audio playing library in pure Rust." -[dependencies.winapi] +[target.i686-pc-windows-gnu.dependencies.winapi] +git = "https://github.com/tomaka/winapi-rs" +branch = "mmdeviceapi" +features = ["ole32"] + +[target.x86_64-pc-windows-gnu.dependencies.winapi] git = "https://github.com/tomaka/winapi-rs" branch = "mmdeviceapi" features = ["ole32"] diff --git a/examples/basic.rs b/examples/basic.rs index 5b1da41..2db9af4 100644 --- a/examples/basic.rs +++ b/examples/basic.rs @@ -1,5 +1,10 @@ extern crate cpal; fn main() { - cpal::cpal_impl::create().unwrap(); + let mut channel = cpal::Channel::new(); + + loop { + let mut buffer = channel.append_data::<(u16, u16)>(); + buffer[0] = std::rand::random(); + } } diff --git a/src/lib.rs b/src/lib.rs index 6c1bc9a..f5af99c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,4 @@ +#![feature(unsafe_destructor)] #[cfg(all(not(windows)))] use this_platform_is_not_supported; @@ -5,3 +6,34 @@ use this_platform_is_not_supported; #[cfg(windows)] #[path="wasapi/mod.rs"] pub mod cpal_impl; + +pub struct Channel(cpal_impl::Channel); + +pub struct Buffer<'a, T>(cpal_impl::Buffer<'a, T>); + +impl Channel { + pub fn new() -> Channel { + let channel = cpal_impl::Channel::new(); + Channel(channel) + } + + pub fn get_channels(&self) -> u16 { + self.0.get_channels() + } + + pub fn append_data<'a, T>(&'a mut self) -> Buffer<'a, T> { + Buffer(self.0.append_data()) + } +} + +impl<'a, T> Deref<[T]> for Buffer<'a, T> { + fn deref(&self) -> &[T] { + panic!() + } +} + +impl<'a, T> DerefMut<[T]> for Buffer<'a, T> { + fn deref_mut(&mut self) -> &mut [T] { + self.0.get_buffer() + } +} diff --git a/src/wasapi/mod.rs b/src/wasapi/mod.rs index b272d5d..053f692 100644 --- a/src/wasapi/mod.rs +++ b/src/wasapi/mod.rs @@ -4,7 +4,104 @@ extern crate winapi; use std::{mem, ptr}; use std::c_vec::CVec; -pub fn create() -> Result<(), String> { +pub struct Channel { + audio_client: *mut winapi::IAudioClient, + render_client: *mut winapi::IAudioRenderClient, + max_frames_in_buffer: winapi::UINT32, + num_channels: winapi::WORD, + bytes_per_frame: winapi::WORD, + started: bool, +} + +pub struct Buffer<'a, T> { + audio_client: *mut winapi::IAudioClient, + render_client: *mut winapi::IAudioRenderClient, + buffer: CVec, + frames: winapi::UINT32, + start_on_drop: bool, +} + +impl Channel { + pub fn new() -> Channel { + init().unwrap() + } + + pub fn get_channels(&self) -> u16 { + self.num_channels as u16 + } + + pub fn append_data<'a, T>(&'a mut self) -> Buffer<'a, T> { + assert!(mem::size_of::() == self.bytes_per_frame as uint); + + unsafe { + loop { + // + let frames_available = { + let mut padding = mem::uninitialized(); + let f = self.audio_client.as_mut().unwrap().lpVtbl + .as_ref().unwrap().GetCurrentPadding; + let hresult = f(self.audio_client, &mut padding); + check_result(hresult).unwrap(); + self.max_frames_in_buffer - padding + }; + + if frames_available == 0 { + // TODO: + ::std::io::timer::sleep(::std::time::duration::Duration::milliseconds(5)); + continue; + } + + // loading buffer + let buffer: CVec = { + let mut buffer: *mut winapi::BYTE = mem::uninitialized(); + let f = self.render_client.as_mut().unwrap().lpVtbl.as_ref().unwrap().GetBuffer; + let hresult = f(self.render_client, frames_available, + &mut buffer as *mut *mut libc::c_uchar); + check_result(hresult).unwrap(); + assert!(!buffer.is_null()); + CVec::new(buffer as *mut T, frames_available as uint) + }; + + let buffer = Buffer { + audio_client: self.audio_client, + render_client: self.render_client, + buffer: buffer, + frames: frames_available, + start_on_drop: !self.started, + }; + + self.started = true; + return buffer; + } + } + } +} + +impl<'a, T> Buffer<'a, T> { + pub fn get_buffer(&mut self) -> &mut [T] { + self.buffer.as_mut_slice() + } +} + +#[unsafe_destructor] +impl<'a, T> Drop for Buffer<'a, T> { + fn drop(&mut self) { + // releasing buffer + unsafe { + let f = self.render_client.as_mut().unwrap().lpVtbl.as_ref().unwrap().ReleaseBuffer; + let hresult = f(self.render_client, self.frames, 0); + check_result(hresult).unwrap(); + + if self.start_on_drop { + let f = self.audio_client.as_mut().unwrap().lpVtbl.as_ref().unwrap().Start; + let hresult = f(self.audio_client); + check_result(hresult).unwrap(); + } + }; + } +} + +fn init() -> Result { // FIXME: release everything unsafe { try!(check_result(winapi::CoInitializeEx(::std::ptr::null_mut(), 0))); @@ -42,36 +139,51 @@ pub fn create() -> Result<(), String> { audio_client.as_mut().unwrap() }; - // format is a WAVEFORMATEX - // but we store it as an array of bytes because of the weird format inheritance system - let format: Vec = { + // computing the format and initializing the device + let format = { + let format_attempt = winapi::WAVEFORMATEX { + wFormatTag: 1, // WAVE_FORMAT_PCM ; TODO: replace by constant + nChannels: 2, + nSamplesPerSec: 44100, + nAvgBytesPerSec: 2 * 44100 * 2, + nBlockAlign: (2 * 16) / 8, + wBitsPerSample: 16, + cbSize: 0, + }; + let mut format_ptr: *mut winapi::WAVEFORMATEX = mem::uninitialized(); - let f = audio_client.lpVtbl.as_ref().unwrap().GetMixFormat; - let hresult = f(audio_client, &mut format_ptr); + let f = audio_client.lpVtbl.as_ref().unwrap().IsFormatSupported; + let hresult = f(audio_client, winapi::AUDCLNT_SHAREMODE::AUDCLNT_SHAREMODE_SHARED, + &format_attempt, &mut format_ptr); try!(check_result(hresult)); - let format = format_ptr.as_ref().unwrap(); - let mut format_copy = Vec::from_elem(mem::size_of::() + - format.cbSize as uint, 0u8); - ptr::copy_nonoverlapping_memory(format_copy.as_mut_ptr(), mem::transmute(format), - format_copy.len()); - winapi::CoTaskMemFree(format_ptr as *mut libc::c_void); + + let format = match format_ptr.as_ref() { + Some(f) => f, + None => &format_attempt, + }; + + let format_copy = *format; + + let f = audio_client.lpVtbl.as_ref().unwrap().Initialize; + let hresult = f(audio_client, winapi::AUDCLNT_SHAREMODE::AUDCLNT_SHAREMODE_SHARED, + 0, 10000000, 0, format, ptr::null()); + + if !format_ptr.is_null() { + winapi::CoTaskMemFree(format_ptr as *mut libc::c_void); + } + + try!(check_result(hresult)); + format_copy }; - // initializing - { - let f = audio_client.lpVtbl.as_ref().unwrap().Initialize; - let hresult = f(audio_client, winapi::AUDCLNT_SHAREMODE::AUDCLNT_SHAREMODE_SHARED, 0, 10000000, 0, format.as_ptr() as *const winapi::WAVEFORMATEX, ptr::null()); - try!(check_result(hresult)); - }; - // - let buffer_frame_count = { - let mut buffer_frame_count = mem::uninitialized(); + let max_frames_in_buffer = { + let mut max_frames_in_buffer = mem::uninitialized(); let f = audio_client.lpVtbl.as_ref().unwrap().GetBufferSize; - let hresult = f(audio_client, &mut buffer_frame_count); + let hresult = f(audio_client, &mut max_frames_in_buffer); try!(check_result(hresult)); - buffer_frame_count + max_frames_in_buffer }; // @@ -84,6 +196,17 @@ pub fn create() -> Result<(), String> { render_client.as_mut().unwrap() }; + Ok(Channel { + audio_client: audio_client, + render_client: render_client, + max_frames_in_buffer: max_frames_in_buffer, + num_channels: format.nChannels, + bytes_per_frame: format.nBlockAlign, + started: false, + }) + } +} +/* let mut started = false; loop { // @@ -144,7 +267,7 @@ pub fn create() -> Result<(), String> { }; Ok(()) -} +}*/ fn check_result(result: winapi::HRESULT) -> Result<(), String> { if result < 0 {