Semi-working WASAPI example
This commit is contained in:
parent
4f96e54e31
commit
6b48a00758
|
@ -0,0 +1,5 @@
|
||||||
|
extern crate cpal;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
cpal::cpal_impl::create().unwrap();
|
||||||
|
}
|
|
@ -4,4 +4,4 @@ use this_platform_is_not_supported;
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
#[path="wasapi/mod.rs"]
|
#[path="wasapi/mod.rs"]
|
||||||
mod cpal_impl;
|
pub mod cpal_impl;
|
||||||
|
|
|
@ -1,24 +1,22 @@
|
||||||
extern crate libc;
|
extern crate libc;
|
||||||
extern crate winapi;
|
extern crate winapi;
|
||||||
|
|
||||||
#[link(name = "uuid")]
|
use std::{mem, ptr};
|
||||||
extern "C" {
|
use std::c_vec::CVec;
|
||||||
static CLSID_MMDeviceEnumerator: winapi::CLSID;
|
|
||||||
static IID_IMMDeviceEnumerator: winapi::IID;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create() -> Result<(), String> {
|
pub fn create() -> Result<(), String> {
|
||||||
|
// FIXME: release everything
|
||||||
unsafe {
|
unsafe {
|
||||||
|
|
||||||
try!(check_result(winapi::CoInitializeEx(::std::ptr::null_mut(), 0)));
|
try!(check_result(winapi::CoInitializeEx(::std::ptr::null_mut(), 0)));
|
||||||
|
|
||||||
|
// building the devices enumerator object
|
||||||
let enumerator = {
|
let enumerator = {
|
||||||
let mut enumerator: *mut winapi::IMMDeviceEnumerator = ::std::mem::uninitialized();
|
let mut enumerator: *mut winapi::IMMDeviceEnumerator = ::std::mem::uninitialized();
|
||||||
|
|
||||||
let hresult = winapi::CoCreateInstance(&CLSID_MMDeviceEnumerator,
|
let hresult = winapi::CoCreateInstance(&winapi::CLSID_MMDeviceEnumerator,
|
||||||
::std::ptr::null_mut(), winapi::CLSCTX_ALL,
|
ptr::null_mut(), winapi::CLSCTX_ALL,
|
||||||
&IID_IMMDeviceEnumerator,
|
&winapi::IID_IMMDeviceEnumerator,
|
||||||
::std::mem::transmute(&mut enumerator));
|
mem::transmute(&mut enumerator));
|
||||||
|
|
||||||
try!(check_result(hresult));
|
try!(check_result(hresult));
|
||||||
enumerator.as_mut().unwrap()
|
enumerator.as_mut().unwrap()
|
||||||
|
@ -26,28 +24,132 @@ fn create() -> Result<(), String> {
|
||||||
|
|
||||||
// getting the default end-point
|
// getting the default end-point
|
||||||
let device = {
|
let device = {
|
||||||
let mut device: *mut winapi::IMMDevice = ::std::mem::uninitialized();
|
let mut device: *mut winapi::IMMDevice = mem::uninitialized();
|
||||||
let f = enumerator.lpVtbl.as_ref().unwrap().GetDefaultAudioEndpoint;
|
let f = enumerator.lpVtbl.as_ref().unwrap().GetDefaultAudioEndpoint;
|
||||||
let hresult = f(enumerator, winapi::EDataFlow::eRender, winapi::ERole::eConsole,
|
let hresult = f(enumerator, winapi::EDataFlow::eRender, winapi::ERole::eConsole,
|
||||||
::std::mem::transmute(&mut device));
|
mem::transmute(&mut device));
|
||||||
try!(check_result(hresult));
|
try!(check_result(hresult));
|
||||||
device.as_mut().unwrap()
|
device.as_mut().unwrap()
|
||||||
};
|
};
|
||||||
|
|
||||||
// activating
|
// activating in order to get a `IAudioClient`
|
||||||
let audio_client = {
|
let audio_client = {
|
||||||
//let mut audio_client: *mut winapi::IAudioClient = ::std::mem::uninitialized();
|
let mut audio_client: *mut winapi::IAudioClient = mem::uninitialized();
|
||||||
let f = device.lpVtbl.as_ref().unwrap().Activate;
|
let f = device.lpVtbl.as_ref().unwrap().Activate;
|
||||||
//let hresult = f(IID_IAudioClient, winapi::CLSCTX_ALL, ::std::ptr::null_mut(),
|
let hresult = f(device, &winapi::IID_IAudioClient, winapi::CLSCTX_ALL,
|
||||||
// ::std::mem::transmute(&mut audio_client));
|
ptr::null_mut(), mem::transmute(&mut audio_client));
|
||||||
//try!(check_result(hresult));
|
try!(check_result(hresult));
|
||||||
|
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<u8> = {
|
||||||
|
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);
|
||||||
|
try!(check_result(hresult));
|
||||||
|
let format = format_ptr.as_ref().unwrap();
|
||||||
|
let mut format_copy = Vec::from_elem(mem::size_of::<winapi::WAVEFORMATEX>() +
|
||||||
|
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);
|
||||||
|
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 f = audio_client.lpVtbl.as_ref().unwrap().GetBufferSize;
|
||||||
|
let hresult = f(audio_client, &mut buffer_frame_count);
|
||||||
|
try!(check_result(hresult));
|
||||||
|
buffer_frame_count
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
let render_client = {
|
||||||
|
let mut render_client: *mut winapi::IAudioRenderClient = mem::uninitialized();
|
||||||
|
let f = audio_client.lpVtbl.as_ref().unwrap().GetService;
|
||||||
|
let hresult = f(audio_client, &winapi::IID_IAudioRenderClient,
|
||||||
|
mem::transmute(&mut render_client));
|
||||||
|
try!(check_result(hresult));
|
||||||
|
render_client.as_mut().unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut started = false;
|
||||||
|
loop {
|
||||||
|
//
|
||||||
|
let frames_available = if started {
|
||||||
|
let mut padding = mem::uninitialized();
|
||||||
|
let f = audio_client.lpVtbl.as_ref().unwrap().GetCurrentPadding;
|
||||||
|
let hresult = f(audio_client, &mut padding);
|
||||||
|
try!(check_result(hresult));
|
||||||
|
buffer_frame_count - padding
|
||||||
|
} else {
|
||||||
|
buffer_frame_count
|
||||||
|
};
|
||||||
|
|
||||||
|
if frames_available == 0 {
|
||||||
|
::std::io::timer::sleep(::std::time::duration::Duration::milliseconds((1000.0 * (44100 - frames_available) as f32 / 44100.0) as i64));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// loading buffer
|
||||||
|
let mut buffer: CVec<u16> = {
|
||||||
|
let mut buffer: *mut winapi::BYTE = mem::uninitialized();
|
||||||
|
let f = render_client.lpVtbl.as_ref().unwrap().GetBuffer;
|
||||||
|
let hresult = f(render_client, frames_available,
|
||||||
|
&mut buffer as *mut *mut libc::c_uchar);
|
||||||
|
try!(check_result(hresult));
|
||||||
|
assert!(!buffer.is_null());
|
||||||
|
CVec::new(buffer as *mut u16, frames_available as uint) // TODO: size of a frame?
|
||||||
|
};
|
||||||
|
|
||||||
|
// generating sinosoïd
|
||||||
|
let mut angle = 0.0f32;
|
||||||
|
for elem in buffer.as_mut_slice().iter_mut() {
|
||||||
|
use std::num::Int;
|
||||||
|
use std::num::FloatMath;
|
||||||
|
|
||||||
|
angle += 1.0;
|
||||||
|
let value = angle.sin();
|
||||||
|
let max: u16 = Int::max_value();
|
||||||
|
let value = (value * max as f32) as u16;
|
||||||
|
*elem = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// releasing buffer
|
||||||
|
{
|
||||||
|
let f = render_client.lpVtbl.as_ref().unwrap().ReleaseBuffer;
|
||||||
|
let hresult = f(render_client, frames_available, 0);
|
||||||
|
try!(check_result(hresult));
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
if !started {
|
||||||
|
let f = audio_client.lpVtbl.as_ref().unwrap().Start;
|
||||||
|
let hresult = f(audio_client);
|
||||||
|
try!(check_result(hresult));
|
||||||
|
started = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_result(result: winapi::HRESULT) -> Result<(), String> {
|
fn check_result(result: winapi::HRESULT) -> Result<(), String> {
|
||||||
// TODO:
|
if result < 0 {
|
||||||
|
return Err(::std::os::error_string(result as uint)); // TODO:
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue