248 lines
8.4 KiB
Rust
248 lines
8.4 KiB
Rust
extern crate libc;
|
|
extern crate winapi;
|
|
|
|
use std::{mem, ptr};
|
|
use std::c_vec::CVec;
|
|
|
|
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,
|
|
samples_per_second: winapi::DWORD,
|
|
bits_per_sample: winapi::WORD,
|
|
started: bool,
|
|
}
|
|
|
|
pub struct Buffer<'a> {
|
|
audio_client: *mut winapi::IAudioClient,
|
|
render_client: *mut winapi::IAudioRenderClient,
|
|
buffer: CVec<u8>,
|
|
frames: winapi::UINT32,
|
|
start_on_drop: bool,
|
|
}
|
|
|
|
impl Channel {
|
|
pub fn new() -> Channel {
|
|
init().unwrap()
|
|
}
|
|
|
|
pub fn get_channels(&self) -> ::ChannelsCount {
|
|
self.num_channels as ::ChannelsCount
|
|
}
|
|
|
|
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!(),
|
|
}
|
|
}
|
|
|
|
pub fn append_data<'a>(&'a mut self) -> Buffer<'a> {
|
|
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<u8> = {
|
|
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 u8,
|
|
frames_available as uint * self.bytes_per_frame 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 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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a> Buffer<'a> {
|
|
pub fn get_buffer(&mut self) -> &mut [u8] {
|
|
self.buffer.as_mut_slice()
|
|
}
|
|
}
|
|
|
|
#[unsafe_destructor]
|
|
impl<'a> Drop for Buffer<'a> {
|
|
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> {
|
|
// FIXME: release everything
|
|
unsafe {
|
|
try!(check_result(winapi::CoInitializeEx(::std::ptr::null_mut(), 0)));
|
|
|
|
// building the devices enumerator object
|
|
let enumerator = {
|
|
let mut enumerator: *mut winapi::IMMDeviceEnumerator = ::std::mem::uninitialized();
|
|
|
|
let hresult = winapi::CoCreateInstance(&winapi::CLSID_MMDeviceEnumerator,
|
|
ptr::null_mut(), winapi::CLSCTX_ALL,
|
|
&winapi::IID_IMMDeviceEnumerator,
|
|
mem::transmute(&mut enumerator));
|
|
|
|
try!(check_result(hresult));
|
|
enumerator.as_mut().unwrap()
|
|
};
|
|
|
|
// getting the default end-point
|
|
let device = {
|
|
let mut device: *mut winapi::IMMDevice = mem::uninitialized();
|
|
let f = enumerator.lpVtbl.as_ref().unwrap().GetDefaultAudioEndpoint;
|
|
let hresult = f(enumerator, winapi::EDataFlow::eRender, winapi::ERole::eConsole,
|
|
mem::transmute(&mut device));
|
|
try!(check_result(hresult));
|
|
device.as_mut().unwrap()
|
|
};
|
|
|
|
// activating in order to get a `IAudioClient`
|
|
let audio_client = {
|
|
let mut audio_client: *mut winapi::IAudioClient = mem::uninitialized();
|
|
let f = device.lpVtbl.as_ref().unwrap().Activate;
|
|
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()
|
|
};
|
|
|
|
// 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().IsFormatSupported;
|
|
let hresult = f(audio_client, winapi::AUDCLNT_SHAREMODE::AUDCLNT_SHAREMODE_SHARED,
|
|
&format_attempt, &mut format_ptr);
|
|
try!(check_result(hresult));
|
|
|
|
let format = match format_ptr.as_ref() {
|
|
Some(f) => f,
|
|
None => &format_attempt,
|
|
};
|
|
|
|
let format_copy = ptr::read(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
|
|
};
|
|
|
|
//
|
|
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 max_frames_in_buffer);
|
|
try!(check_result(hresult));
|
|
max_frames_in_buffer
|
|
};
|
|
|
|
//
|
|
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()
|
|
};
|
|
|
|
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,
|
|
samples_per_second: format.nSamplesPerSec,
|
|
bits_per_sample: format.wBitsPerSample,
|
|
started: false,
|
|
})
|
|
}
|
|
}
|
|
|
|
fn check_result(result: winapi::HRESULT) -> Result<(), String> {
|
|
if result < 0 {
|
|
return Err(::std::os::error_string(result as uint)); // TODO:
|
|
}
|
|
|
|
Ok(())
|
|
}
|