Add supported formats enumeration
This commit is contained in:
parent
47f966bf75
commit
1985c346ac
|
@ -1,7 +1,9 @@
|
|||
extern crate cpal;
|
||||
|
||||
fn main() {
|
||||
let mut channel = cpal::Voice::new(&cpal::get_default_endpoint().unwrap()).unwrap();
|
||||
let endpoint = cpal::get_default_endpoint().unwrap();
|
||||
let format = endpoint.get_supported_formats_list().next().unwrap();
|
||||
let mut channel = cpal::Voice::new(&endpoint, &format).unwrap();
|
||||
|
||||
// Produce a sinusoid of maximum amplitude.
|
||||
let mut data_source = (0u64..).map(|t| t as f32 * 0.03)
|
||||
|
|
69
src/lib.rs
69
src/lib.rs
|
@ -54,31 +54,18 @@ mod cpal_impl;
|
|||
#[path="null/mod.rs"]
|
||||
mod cpal_impl;
|
||||
|
||||
/// Number of channels.
|
||||
pub type ChannelsCount = u16;
|
||||
|
||||
///
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct SamplesRate(pub u32);
|
||||
|
||||
/// Describes a format.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub struct Format {
|
||||
pub channels: ChannelsCount,
|
||||
pub samples_rate: SamplesRate,
|
||||
pub data_type: SampleFormat,
|
||||
}
|
||||
|
||||
/// An iterator for the list of formats that are supported by the backend.
|
||||
pub struct EndpointsIterator(cpal_impl::EndpointsIterator);
|
||||
|
||||
impl Iterator for EndpointsIterator {
|
||||
type Item = Endpoint;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<Endpoint> {
|
||||
self.0.next().map(Endpoint)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.0.size_hint()
|
||||
}
|
||||
|
@ -98,6 +85,45 @@ pub fn get_default_endpoint() -> Option<Endpoint> {
|
|||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub struct Endpoint(cpal_impl::Endpoint);
|
||||
|
||||
impl Endpoint {
|
||||
/// Returns an iterator that produces the list of formats that are supported by the backend.
|
||||
pub fn get_supported_formats_list(&self) -> SupportedFormatsIterator {
|
||||
SupportedFormatsIterator(self.0.get_supported_formats_list())
|
||||
}
|
||||
}
|
||||
|
||||
/// Number of channels.
|
||||
pub type ChannelsCount = u16;
|
||||
|
||||
///
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct SamplesRate(pub u32);
|
||||
|
||||
/// Describes a format.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub struct Format {
|
||||
pub channels: ChannelsCount,
|
||||
pub samples_rate: SamplesRate,
|
||||
pub data_type: SampleFormat,
|
||||
}
|
||||
|
||||
/// An iterator that produces a list of formats supported by the endpoint.
|
||||
pub struct SupportedFormatsIterator(cpal_impl::SupportedFormatsIterator);
|
||||
|
||||
impl Iterator for SupportedFormatsIterator {
|
||||
type Item = Format;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<Format> {
|
||||
self.0.next()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.0.size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a buffer that must be filled with audio data.
|
||||
///
|
||||
/// You should destroy this object as soon as possible. Data is only committed when it
|
||||
|
@ -136,8 +162,9 @@ pub struct Voice(cpal_impl::Voice);
|
|||
|
||||
impl Voice {
|
||||
/// Builds a new channel.
|
||||
pub fn new(endpoint: &Endpoint) -> Result<Voice, Box<Error>> {
|
||||
let channel = try!(cpal_impl::Voice::new(&endpoint.0));
|
||||
#[inline]
|
||||
pub fn new(endpoint: &Endpoint, format: &Format) -> Result<Voice, Box<Error>> {
|
||||
let channel = try!(cpal_impl::Voice::new(&endpoint.0, format));
|
||||
Ok(Voice(channel))
|
||||
}
|
||||
|
||||
|
@ -145,6 +172,7 @@ impl Voice {
|
|||
///
|
||||
/// You can add data with any number of channels, but matching the voice's native format
|
||||
/// will lead to better performances.
|
||||
#[inline]
|
||||
pub fn get_channels(&self) -> ChannelsCount {
|
||||
self.0.get_channels()
|
||||
}
|
||||
|
@ -153,6 +181,7 @@ impl Voice {
|
|||
///
|
||||
/// You can add data with any samples rate, but matching the voice's native format
|
||||
/// will lead to better performances.
|
||||
#[inline]
|
||||
pub fn get_samples_rate(&self) -> SamplesRate {
|
||||
self.0.get_samples_rate()
|
||||
}
|
||||
|
@ -161,6 +190,7 @@ impl Voice {
|
|||
///
|
||||
/// You can add data of any format, but matching the voice's native format
|
||||
/// will lead to better performances.
|
||||
#[inline]
|
||||
pub fn get_samples_format(&self) -> SampleFormat {
|
||||
self.0.get_samples_format()
|
||||
}
|
||||
|
@ -205,6 +235,7 @@ impl Voice {
|
|||
///
|
||||
/// Only call this after you have submitted some data, otherwise you may hear
|
||||
/// some glitches.
|
||||
#[inline]
|
||||
pub fn play(&mut self) {
|
||||
self.0.play()
|
||||
}
|
||||
|
@ -214,6 +245,7 @@ impl Voice {
|
|||
/// Has no effect is the voice was already paused.
|
||||
///
|
||||
/// If you call `play` afterwards, the playback will resume exactly where it was.
|
||||
#[inline]
|
||||
pub fn pause(&mut self) {
|
||||
self.0.pause()
|
||||
}
|
||||
|
@ -222,18 +254,21 @@ impl Voice {
|
|||
impl<'a, T> Deref for Buffer<'a, T> where T: Sample {
|
||||
type Target = [T];
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &[T] {
|
||||
panic!("It is forbidden to read from the audio buffer");
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> DerefMut for Buffer<'a, T> where T: Sample {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut [T] {
|
||||
self.target.as_mut().unwrap().get_buffer()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Drop for Buffer<'a, T> where T: Sample {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
self.target.take().unwrap().finish();
|
||||
}
|
||||
|
|
|
@ -99,7 +99,7 @@ impl Iterator for EndpointsIterator {
|
|||
check_result((*self.collection).Item(self.next_item, &mut device)).unwrap();
|
||||
|
||||
self.next_item += 1;
|
||||
Some(Endpoint(device))
|
||||
Some(Endpoint::from_immdevice(device))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -121,6 +121,6 @@ pub fn get_default_endpoint() -> Option<Endpoint> {
|
|||
return None; // TODO: check specifically for `E_NOTFOUND`, and panic otherwise
|
||||
}
|
||||
|
||||
Some(Endpoint(device))
|
||||
Some(Endpoint::from_immdevice(device))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,35 +3,24 @@ extern crate winapi;
|
|||
extern crate ole32;
|
||||
|
||||
use std::io::Error as IoError;
|
||||
use std::sync::{Arc, Mutex, MutexGuard};
|
||||
use std::ptr;
|
||||
use std::mem;
|
||||
|
||||
use Format;
|
||||
use SamplesRate;
|
||||
use SampleFormat;
|
||||
|
||||
pub use std::option::IntoIter as OptionIntoIter;
|
||||
pub use self::enumerate::{EndpointsIterator, get_default_endpoint};
|
||||
pub use self::voice::{Voice, Buffer};
|
||||
|
||||
pub type SupportedFormatsIterator = OptionIntoIter<Format>;
|
||||
|
||||
mod com;
|
||||
mod enumerate;
|
||||
mod voice;
|
||||
|
||||
/// An opaque type that identifies an end point.
|
||||
#[derive(PartialEq, Eq)]
|
||||
#[allow(raw_pointer_derive)]
|
||||
pub struct Endpoint(*mut winapi::IMMDevice);
|
||||
|
||||
unsafe impl Send for Endpoint {}
|
||||
unsafe impl Sync for Endpoint {}
|
||||
|
||||
impl Clone for Endpoint {
|
||||
fn clone(&self) -> Endpoint {
|
||||
unsafe { (*self.0).AddRef(); }
|
||||
Endpoint(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Endpoint {
|
||||
fn drop(&mut self) {
|
||||
unsafe { (*self.0).Release(); }
|
||||
}
|
||||
}
|
||||
|
||||
fn check_result(result: winapi::HRESULT) -> Result<(), IoError> {
|
||||
if result < 0 {
|
||||
Err(IoError::from_raw_os_error(result))
|
||||
|
@ -39,3 +28,116 @@ fn check_result(result: winapi::HRESULT) -> Result<(), IoError> {
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrapper because of that stupid decision to remove `Send` and `Sync` from raw pointers.
|
||||
#[derive(Copy, Clone)]
|
||||
#[allow(raw_pointer_derive)]
|
||||
struct IAudioClientWrapper(*mut winapi::IAudioClient);
|
||||
unsafe impl Send for IAudioClientWrapper {}
|
||||
unsafe impl Sync for IAudioClientWrapper {}
|
||||
|
||||
/// An opaque type that identifies an end point.
|
||||
pub struct Endpoint {
|
||||
device: *mut winapi::IMMDevice,
|
||||
|
||||
/// We cache an uninitialized `IAudioClient` so that we can call functions from it without
|
||||
/// having to create/destroy audio clients all the time.
|
||||
future_audio_client: Arc<Mutex<Option<IAudioClientWrapper>>>, // TODO: add NonZero around the ptr
|
||||
}
|
||||
|
||||
unsafe impl Send for Endpoint {}
|
||||
unsafe impl Sync for Endpoint {}
|
||||
|
||||
impl Endpoint {
|
||||
#[inline]
|
||||
fn from_immdevice(device: *mut winapi::IMMDevice) -> Endpoint {
|
||||
Endpoint {
|
||||
device: device,
|
||||
future_audio_client: Arc::new(Mutex::new(None)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Ensures that `future_audio_client` contains a `Some` and returns a locked mutex to it.
|
||||
fn ensure_future_audio_client(&self) -> MutexGuard<Option<IAudioClientWrapper>> {
|
||||
let mut lock = self.future_audio_client.lock().unwrap();
|
||||
if lock.is_some() {
|
||||
return lock;
|
||||
}
|
||||
|
||||
let audio_client: *mut winapi::IAudioClient = unsafe {
|
||||
let mut audio_client = mem::uninitialized();
|
||||
let hresult = (*self.device).Activate(&winapi::IID_IAudioClient, winapi::CLSCTX_ALL,
|
||||
ptr::null_mut(), &mut audio_client);
|
||||
// can fail if the device has been disconnected since we enumerated it, or if
|
||||
// the device doesn't support playback for some reason
|
||||
check_result(hresult).unwrap(); // FIXME: don't unwrap
|
||||
audio_client as *mut _
|
||||
};
|
||||
|
||||
*lock = Some(IAudioClientWrapper(audio_client));
|
||||
lock
|
||||
}
|
||||
|
||||
pub fn get_supported_formats_list(&self) -> SupportedFormatsIterator {
|
||||
// We always create voices in shared mode, therefore all samples go through an audio
|
||||
// processor to mix them together.
|
||||
// However there is no way to query the list of all formats that are supported by the
|
||||
// audio processor, but one format is guaranteed to be supported, the one returned by
|
||||
// `GetMixFormat`.
|
||||
|
||||
// initializing COM because we call `CoTaskMemFree`
|
||||
com::com_initialized();
|
||||
|
||||
let lock = self.ensure_future_audio_client();
|
||||
let client = lock.unwrap().0;
|
||||
|
||||
unsafe {
|
||||
let mut format_ptr = mem::uninitialized();
|
||||
check_result((*client).GetMixFormat(&mut format_ptr)).unwrap(); // FIXME: don't unwrap
|
||||
|
||||
let format = {
|
||||
assert!((*format_ptr).wFormatTag == winapi::WAVE_FORMAT_EXTENSIBLE);
|
||||
|
||||
// FIXME: decode from the format
|
||||
Format {
|
||||
channels: 2,
|
||||
samples_rate: SamplesRate(44100),
|
||||
data_type: SampleFormat::U16,
|
||||
}
|
||||
};
|
||||
|
||||
ole32::CoTaskMemFree(format_ptr as *mut _);
|
||||
|
||||
Some(format).into_iter()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Endpoint {
|
||||
fn eq(&self, other: &Endpoint) -> bool {
|
||||
self.device == other.device
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Endpoint {}
|
||||
|
||||
impl Clone for Endpoint {
|
||||
fn clone(&self) -> Endpoint {
|
||||
unsafe { (*self.device).AddRef(); }
|
||||
|
||||
Endpoint {
|
||||
device: self.device,
|
||||
future_audio_client: self.future_audio_client.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Endpoint {
|
||||
fn drop(&mut self) {
|
||||
unsafe { (*self.device).Release(); }
|
||||
|
||||
if let Some(client) = self.future_audio_client.lock().unwrap().take() {
|
||||
unsafe { (*client.0).Release(); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,8 @@ use std::mem;
|
|||
use std::ptr;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use Format;
|
||||
|
||||
pub struct Voice {
|
||||
audio_client: *mut winapi::IAudioClient,
|
||||
render_client: *mut winapi::IAudioRenderClient,
|
||||
|
@ -26,7 +28,7 @@ unsafe impl Send for Voice {}
|
|||
unsafe impl Sync for Voice {}
|
||||
|
||||
impl Voice {
|
||||
pub fn new(end_point: &Endpoint) -> Result<Voice, IoError> {
|
||||
pub fn new(end_point: &Endpoint, format: &Format) -> Result<Voice, IoError> {
|
||||
// FIXME: release everything
|
||||
unsafe {
|
||||
// making sure that COM is initialized
|
||||
|
@ -36,8 +38,8 @@ impl Voice {
|
|||
// activating the end point in order to get a `IAudioClient`
|
||||
let audio_client: *mut winapi::IAudioClient = {
|
||||
let mut audio_client = mem::uninitialized();
|
||||
let hresult = (*end_point.0).Activate(&winapi::IID_IAudioClient, winapi::CLSCTX_ALL,
|
||||
ptr::null_mut(), &mut audio_client);
|
||||
let hresult = (*end_point.device).Activate(&winapi::IID_IAudioClient, winapi::CLSCTX_ALL,
|
||||
ptr::null_mut(), &mut audio_client);
|
||||
// can fail if the device has been disconnected since we enumerated it, or if
|
||||
// the device doesn't support playback for some reason
|
||||
try!(check_result(hresult));
|
||||
|
|
Loading…
Reference in New Issue