Add basic API
This commit is contained in:
parent
6b48a00758
commit
873779aaa8
|
@ -5,7 +5,12 @@ version = "0.0.1"
|
||||||
authors = ["Pierre Krieger <pierre.krieger1708@gmail.com>"]
|
authors = ["Pierre Krieger <pierre.krieger1708@gmail.com>"]
|
||||||
description = "Cross-platform audio playing library in pure Rust."
|
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"
|
git = "https://github.com/tomaka/winapi-rs"
|
||||||
branch = "mmdeviceapi"
|
branch = "mmdeviceapi"
|
||||||
features = ["ole32"]
|
features = ["ole32"]
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
extern crate cpal;
|
extern crate cpal;
|
||||||
|
|
||||||
fn main() {
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
32
src/lib.rs
32
src/lib.rs
|
@ -1,3 +1,4 @@
|
||||||
|
#![feature(unsafe_destructor)]
|
||||||
|
|
||||||
#[cfg(all(not(windows)))]
|
#[cfg(all(not(windows)))]
|
||||||
use this_platform_is_not_supported;
|
use this_platform_is_not_supported;
|
||||||
|
@ -5,3 +6,34 @@ use this_platform_is_not_supported;
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
#[path="wasapi/mod.rs"]
|
#[path="wasapi/mod.rs"]
|
||||||
pub mod cpal_impl;
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -4,7 +4,104 @@ extern crate winapi;
|
||||||
use std::{mem, ptr};
|
use std::{mem, ptr};
|
||||||
use std::c_vec::CVec;
|
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<T>,
|
||||||
|
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::<T>() == 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<T> = {
|
||||||
|
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<Channel, String> {
|
||||||
// FIXME: release everything
|
// 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)));
|
||||||
|
@ -42,36 +139,51 @@ pub fn create() -> Result<(), String> {
|
||||||
audio_client.as_mut().unwrap()
|
audio_client.as_mut().unwrap()
|
||||||
};
|
};
|
||||||
|
|
||||||
// format is a WAVEFORMATEX
|
// computing the format and initializing the device
|
||||||
// but we store it as an array of bytes because of the weird format inheritance system
|
let format = {
|
||||||
let format: Vec<u8> = {
|
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 mut format_ptr: *mut winapi::WAVEFORMATEX = mem::uninitialized();
|
||||||
let f = audio_client.lpVtbl.as_ref().unwrap().GetMixFormat;
|
let f = audio_client.lpVtbl.as_ref().unwrap().IsFormatSupported;
|
||||||
let hresult = f(audio_client, &mut format_ptr);
|
let hresult = f(audio_client, winapi::AUDCLNT_SHAREMODE::AUDCLNT_SHAREMODE_SHARED,
|
||||||
|
&format_attempt, &mut format_ptr);
|
||||||
try!(check_result(hresult));
|
try!(check_result(hresult));
|
||||||
let format = format_ptr.as_ref().unwrap();
|
|
||||||
let mut format_copy = Vec::from_elem(mem::size_of::<winapi::WAVEFORMATEX>() +
|
let format = match format_ptr.as_ref() {
|
||||||
format.cbSize as uint, 0u8);
|
Some(f) => f,
|
||||||
ptr::copy_nonoverlapping_memory(format_copy.as_mut_ptr(), mem::transmute(format),
|
None => &format_attempt,
|
||||||
format_copy.len());
|
};
|
||||||
winapi::CoTaskMemFree(format_ptr as *mut libc::c_void);
|
|
||||||
|
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
|
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 max_frames_in_buffer = {
|
||||||
let mut buffer_frame_count = mem::uninitialized();
|
let mut max_frames_in_buffer = mem::uninitialized();
|
||||||
let f = audio_client.lpVtbl.as_ref().unwrap().GetBufferSize;
|
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));
|
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()
|
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;
|
let mut started = false;
|
||||||
loop {
|
loop {
|
||||||
//
|
//
|
||||||
|
@ -144,7 +267,7 @@ pub fn create() -> Result<(), String> {
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}*/
|
||||||
|
|
||||||
fn check_result(result: winapi::HRESULT) -> Result<(), String> {
|
fn check_result(result: winapi::HRESULT) -> Result<(), String> {
|
||||||
if result < 0 {
|
if result < 0 {
|
||||||
|
|
Loading…
Reference in New Issue