Add supported formats enumeration
This commit is contained in:
parent
47f966bf75
commit
1985c346ac
|
@ -1,7 +1,9 @@
|
||||||
extern crate cpal;
|
extern crate cpal;
|
||||||
|
|
||||||
fn main() {
|
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.
|
// Produce a sinusoid of maximum amplitude.
|
||||||
let mut data_source = (0u64..).map(|t| t as f32 * 0.03)
|
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"]
|
#[path="null/mod.rs"]
|
||||||
mod cpal_impl;
|
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.
|
/// An iterator for the list of formats that are supported by the backend.
|
||||||
pub struct EndpointsIterator(cpal_impl::EndpointsIterator);
|
pub struct EndpointsIterator(cpal_impl::EndpointsIterator);
|
||||||
|
|
||||||
impl Iterator for EndpointsIterator {
|
impl Iterator for EndpointsIterator {
|
||||||
type Item = Endpoint;
|
type Item = Endpoint;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn next(&mut self) -> Option<Endpoint> {
|
fn next(&mut self) -> Option<Endpoint> {
|
||||||
self.0.next().map(Endpoint)
|
self.0.next().map(Endpoint)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||||
self.0.size_hint()
|
self.0.size_hint()
|
||||||
}
|
}
|
||||||
|
@ -98,6 +85,45 @@ pub fn get_default_endpoint() -> Option<Endpoint> {
|
||||||
#[derive(Clone, PartialEq, Eq)]
|
#[derive(Clone, PartialEq, Eq)]
|
||||||
pub struct Endpoint(cpal_impl::Endpoint);
|
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.
|
/// 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
|
/// 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 {
|
impl Voice {
|
||||||
/// Builds a new channel.
|
/// Builds a new channel.
|
||||||
pub fn new(endpoint: &Endpoint) -> Result<Voice, Box<Error>> {
|
#[inline]
|
||||||
let channel = try!(cpal_impl::Voice::new(&endpoint.0));
|
pub fn new(endpoint: &Endpoint, format: &Format) -> Result<Voice, Box<Error>> {
|
||||||
|
let channel = try!(cpal_impl::Voice::new(&endpoint.0, format));
|
||||||
Ok(Voice(channel))
|
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
|
/// You can add data with any number of channels, but matching the voice's native format
|
||||||
/// will lead to better performances.
|
/// will lead to better performances.
|
||||||
|
#[inline]
|
||||||
pub fn get_channels(&self) -> ChannelsCount {
|
pub fn get_channels(&self) -> ChannelsCount {
|
||||||
self.0.get_channels()
|
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
|
/// You can add data with any samples rate, but matching the voice's native format
|
||||||
/// will lead to better performances.
|
/// will lead to better performances.
|
||||||
|
#[inline]
|
||||||
pub fn get_samples_rate(&self) -> SamplesRate {
|
pub fn get_samples_rate(&self) -> SamplesRate {
|
||||||
self.0.get_samples_rate()
|
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
|
/// You can add data of any format, but matching the voice's native format
|
||||||
/// will lead to better performances.
|
/// will lead to better performances.
|
||||||
|
#[inline]
|
||||||
pub fn get_samples_format(&self) -> SampleFormat {
|
pub fn get_samples_format(&self) -> SampleFormat {
|
||||||
self.0.get_samples_format()
|
self.0.get_samples_format()
|
||||||
}
|
}
|
||||||
|
@ -205,6 +235,7 @@ impl Voice {
|
||||||
///
|
///
|
||||||
/// Only call this after you have submitted some data, otherwise you may hear
|
/// Only call this after you have submitted some data, otherwise you may hear
|
||||||
/// some glitches.
|
/// some glitches.
|
||||||
|
#[inline]
|
||||||
pub fn play(&mut self) {
|
pub fn play(&mut self) {
|
||||||
self.0.play()
|
self.0.play()
|
||||||
}
|
}
|
||||||
|
@ -214,6 +245,7 @@ impl Voice {
|
||||||
/// Has no effect is the voice was already paused.
|
/// Has no effect is the voice was already paused.
|
||||||
///
|
///
|
||||||
/// If you call `play` afterwards, the playback will resume exactly where it was.
|
/// If you call `play` afterwards, the playback will resume exactly where it was.
|
||||||
|
#[inline]
|
||||||
pub fn pause(&mut self) {
|
pub fn pause(&mut self) {
|
||||||
self.0.pause()
|
self.0.pause()
|
||||||
}
|
}
|
||||||
|
@ -222,18 +254,21 @@ impl Voice {
|
||||||
impl<'a, T> Deref for Buffer<'a, T> where T: Sample {
|
impl<'a, T> Deref for Buffer<'a, T> where T: Sample {
|
||||||
type Target = [T];
|
type Target = [T];
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn deref(&self) -> &[T] {
|
fn deref(&self) -> &[T] {
|
||||||
panic!("It is forbidden to read from the audio buffer");
|
panic!("It is forbidden to read from the audio buffer");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T> DerefMut for Buffer<'a, T> where T: Sample {
|
impl<'a, T> DerefMut for Buffer<'a, T> where T: Sample {
|
||||||
|
#[inline]
|
||||||
fn deref_mut(&mut self) -> &mut [T] {
|
fn deref_mut(&mut self) -> &mut [T] {
|
||||||
self.target.as_mut().unwrap().get_buffer()
|
self.target.as_mut().unwrap().get_buffer()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T> Drop for Buffer<'a, T> where T: Sample {
|
impl<'a, T> Drop for Buffer<'a, T> where T: Sample {
|
||||||
|
#[inline]
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
self.target.take().unwrap().finish();
|
self.target.take().unwrap().finish();
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,7 +99,7 @@ impl Iterator for EndpointsIterator {
|
||||||
check_result((*self.collection).Item(self.next_item, &mut device)).unwrap();
|
check_result((*self.collection).Item(self.next_item, &mut device)).unwrap();
|
||||||
|
|
||||||
self.next_item += 1;
|
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
|
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;
|
extern crate ole32;
|
||||||
|
|
||||||
use std::io::Error as IoError;
|
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::enumerate::{EndpointsIterator, get_default_endpoint};
|
||||||
pub use self::voice::{Voice, Buffer};
|
pub use self::voice::{Voice, Buffer};
|
||||||
|
|
||||||
|
pub type SupportedFormatsIterator = OptionIntoIter<Format>;
|
||||||
|
|
||||||
mod com;
|
mod com;
|
||||||
mod enumerate;
|
mod enumerate;
|
||||||
mod voice;
|
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> {
|
fn check_result(result: winapi::HRESULT) -> Result<(), IoError> {
|
||||||
if result < 0 {
|
if result < 0 {
|
||||||
Err(IoError::from_raw_os_error(result))
|
Err(IoError::from_raw_os_error(result))
|
||||||
|
@ -39,3 +28,116 @@ fn check_result(result: winapi::HRESULT) -> Result<(), IoError> {
|
||||||
Ok(())
|
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::ptr;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
|
use Format;
|
||||||
|
|
||||||
pub struct Voice {
|
pub struct Voice {
|
||||||
audio_client: *mut winapi::IAudioClient,
|
audio_client: *mut winapi::IAudioClient,
|
||||||
render_client: *mut winapi::IAudioRenderClient,
|
render_client: *mut winapi::IAudioRenderClient,
|
||||||
|
@ -26,7 +28,7 @@ unsafe impl Send for Voice {}
|
||||||
unsafe impl Sync for Voice {}
|
unsafe impl Sync for Voice {}
|
||||||
|
|
||||||
impl 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
|
// FIXME: release everything
|
||||||
unsafe {
|
unsafe {
|
||||||
// making sure that COM is initialized
|
// making sure that COM is initialized
|
||||||
|
@ -36,7 +38,7 @@ impl Voice {
|
||||||
// activating the end point in order to get a `IAudioClient`
|
// activating the end point in order to get a `IAudioClient`
|
||||||
let audio_client: *mut winapi::IAudioClient = {
|
let audio_client: *mut winapi::IAudioClient = {
|
||||||
let mut audio_client = mem::uninitialized();
|
let mut audio_client = mem::uninitialized();
|
||||||
let hresult = (*end_point.0).Activate(&winapi::IID_IAudioClient, winapi::CLSCTX_ALL,
|
let hresult = (*end_point.device).Activate(&winapi::IID_IAudioClient, winapi::CLSCTX_ALL,
|
||||||
ptr::null_mut(), &mut audio_client);
|
ptr::null_mut(), &mut audio_client);
|
||||||
// can fail if the device has been disconnected since we enumerated it, or if
|
// can fail if the device has been disconnected since we enumerated it, or if
|
||||||
// the device doesn't support playback for some reason
|
// the device doesn't support playback for some reason
|
||||||
|
|
Loading…
Reference in New Issue