cpal/src/wasapi/mod.rs

248 lines
8.4 KiB
Rust
Raw Normal View History

2014-12-11 14:22:55 +01:00
extern crate libc;
extern crate winapi;
2014-12-11 16:28:26 +01:00
use std::{mem, ptr};
use std::c_vec::CVec;
2014-12-11 14:22:55 +01:00
2014-12-11 17:23:33 +01:00
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,
2014-12-11 19:07:58 +01:00
samples_per_second: winapi::DWORD,
bits_per_sample: winapi::WORD,
2014-12-11 17:23:33 +01:00
started: bool,
}
2014-12-11 19:02:04 +01:00
pub struct Buffer<'a> {
2014-12-11 17:23:33 +01:00
audio_client: *mut winapi::IAudioClient,
render_client: *mut winapi::IAudioRenderClient,
2014-12-11 19:02:04 +01:00
buffer: CVec<u8>,
2014-12-11 17:23:33 +01:00
frames: winapi::UINT32,
start_on_drop: bool,
}
impl Channel {
pub fn new() -> Channel {
init().unwrap()
}
2014-12-11 19:02:04 +01:00
pub fn get_channels(&self) -> ::ChannelsCount {
self.num_channels as ::ChannelsCount
2014-12-11 17:23:33 +01:00
}
2014-12-11 19:07:58 +01:00
pub fn get_samples_per_second(&self) -> u32 {
self.samples_per_second as u32
}
pub fn get_samples_format(&self) -> ::SampleFormat {
match self.bits_per_sample {
16 => ::SampleFormat::U16,
_ => unimplemented!(),
}
}
2014-12-11 19:02:04 +01:00
pub fn append_data<'a>(&'a mut self) -> Buffer<'a> {
2014-12-11 17:23:33 +01:00
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
2014-12-11 19:02:04 +01:00
let buffer: CVec<u8> = {
2014-12-11 17:23:33 +01:00
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());
2014-12-11 19:02:04 +01:00
CVec::new(buffer as *mut u8,
frames_available as uint * self.bytes_per_frame as uint)
2014-12-11 17:23:33 +01:00
};
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;
}
}
}
}
2014-12-11 19:42:04 +01:00
impl Drop for Channel {
fn drop(&mut self) {
unsafe {
{
let f = self.render_client.as_mut().unwrap().lpVtbl.as_ref().unwrap().Release;
f(self.render_client);
}
{
let f = self.audio_client.as_mut().unwrap().lpVtbl.as_ref().unwrap().Release;
f(self.audio_client);
}
}
}
}
2014-12-11 19:02:04 +01:00
impl<'a> Buffer<'a> {
pub fn get_buffer(&mut self) -> &mut [u8] {
2014-12-11 17:23:33 +01:00
self.buffer.as_mut_slice()
}
}
#[unsafe_destructor]
2014-12-11 19:02:04 +01:00
impl<'a> Drop for Buffer<'a> {
2014-12-11 17:23:33 +01:00
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<Channel, String> {
2014-12-11 16:28:26 +01:00
// FIXME: release everything
2014-12-11 14:22:55 +01:00
unsafe {
try!(check_result(winapi::CoInitializeEx(::std::ptr::null_mut(), 0)));
2014-12-11 16:28:26 +01:00
// building the devices enumerator object
2014-12-11 14:22:55 +01:00
let enumerator = {
let mut enumerator: *mut winapi::IMMDeviceEnumerator = ::std::mem::uninitialized();
2014-12-11 16:28:26 +01:00
let hresult = winapi::CoCreateInstance(&winapi::CLSID_MMDeviceEnumerator,
ptr::null_mut(), winapi::CLSCTX_ALL,
&winapi::IID_IMMDeviceEnumerator,
mem::transmute(&mut enumerator));
2014-12-11 14:22:55 +01:00
try!(check_result(hresult));
enumerator.as_mut().unwrap()
};
// getting the default end-point
let device = {
2014-12-11 16:28:26 +01:00
let mut device: *mut winapi::IMMDevice = mem::uninitialized();
2014-12-11 14:22:55 +01:00
let f = enumerator.lpVtbl.as_ref().unwrap().GetDefaultAudioEndpoint;
let hresult = f(enumerator, winapi::EDataFlow::eRender, winapi::ERole::eConsole,
2014-12-11 16:28:26 +01:00
mem::transmute(&mut device));
2014-12-11 14:22:55 +01:00
try!(check_result(hresult));
device.as_mut().unwrap()
};
2014-12-11 16:28:26 +01:00
// activating in order to get a `IAudioClient`
2014-12-11 14:22:55 +01:00
let audio_client = {
2014-12-11 16:28:26 +01:00
let mut audio_client: *mut winapi::IAudioClient = mem::uninitialized();
2014-12-11 14:22:55 +01:00
let f = device.lpVtbl.as_ref().unwrap().Activate;
2014-12-11 16:28:26 +01:00
let hresult = f(device, &winapi::IID_IAudioClient, winapi::CLSCTX_ALL,
ptr::null_mut(), mem::transmute(&mut audio_client));
try!(check_result(hresult));
audio_client.as_mut().unwrap()
};
2014-12-11 17:23:33 +01:00
// 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,
};
2014-12-11 16:28:26 +01:00
let mut format_ptr: *mut winapi::WAVEFORMATEX = mem::uninitialized();
2014-12-11 17:23:33 +01:00
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);
2014-12-11 16:28:26 +01:00
try!(check_result(hresult));
2014-12-11 17:23:33 +01:00
let format = match format_ptr.as_ref() {
Some(f) => f,
None => &format_attempt,
};
let format_copy = *format;
2014-12-11 16:28:26 +01:00
let f = audio_client.lpVtbl.as_ref().unwrap().Initialize;
2014-12-11 17:23:33 +01:00
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);
}
2014-12-11 16:28:26 +01:00
try!(check_result(hresult));
2014-12-11 17:23:33 +01:00
format_copy
2014-12-11 16:28:26 +01:00
};
//
2014-12-11 17:23:33 +01:00
let max_frames_in_buffer = {
let mut max_frames_in_buffer = mem::uninitialized();
2014-12-11 16:28:26 +01:00
let f = audio_client.lpVtbl.as_ref().unwrap().GetBufferSize;
2014-12-11 17:23:33 +01:00
let hresult = f(audio_client, &mut max_frames_in_buffer);
2014-12-11 16:28:26 +01:00
try!(check_result(hresult));
2014-12-11 17:23:33 +01:00
max_frames_in_buffer
2014-12-11 14:22:55 +01:00
};
2014-12-11 16:28:26 +01:00
//
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()
};
2014-12-11 17:23:33 +01:00
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,
2014-12-11 19:07:58 +01:00
samples_per_second: format.nSamplesPerSec,
bits_per_sample: format.wBitsPerSample,
2014-12-11 17:23:33 +01:00
started: false,
})
}
}
2014-12-11 14:22:55 +01:00
fn check_result(result: winapi::HRESULT) -> Result<(), String> {
2014-12-11 16:28:26 +01:00
if result < 0 {
return Err(::std::os::error_string(result as uint)); // TODO:
}
2014-12-11 14:22:55 +01:00
Ok(())
}