From 91adc3e380cff0f07ce76dd70c1b9ff6f1039b4b Mon Sep 17 00:00:00 2001 From: tomaka Date: Mon, 23 Oct 2017 16:41:38 +0200 Subject: [PATCH] Docs and style improvements (#174) * Improve the crate root documentation * Add entry in CHANGELOG * Run rustfmt on the code * More improvements to documentation --- CHANGELOG.md | 1 + src/alsa/enumerate.rs | 2 +- src/alsa/mod.rs | 64 +++++++------ src/coreaudio/mod.rs | 28 +++--- src/emscripten/mod.rs | 67 ++++++++------ src/lib.rs | 200 ++++++++++++++++++++++++++++++----------- src/null/mod.rs | 8 +- src/wasapi/endpoint.rs | 6 +- src/wasapi/mod.rs | 2 +- src/wasapi/voice.rs | 38 ++++---- 10 files changed, 271 insertions(+), 145 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f3b503..2c0d8b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # Unreleased - Changed the emscripten backend to consume less CPU. +- Added improvements to the crate documentation. # Version 0.5.1 (2017-10-21) diff --git a/src/alsa/enumerate.rs b/src/alsa/enumerate.rs index e3e683e..e5c37e1 100644 --- a/src/alsa/enumerate.rs +++ b/src/alsa/enumerate.rs @@ -1,7 +1,7 @@ use super::Endpoint; use super::alsa; -use super::libc; use super::check_errors; +use super::libc; use std::ffi::CStr; use std::ffi::CString; diff --git a/src/alsa/mod.rs b/src/alsa/mod.rs index 17e0d2a..adbb8a6 100644 --- a/src/alsa/mod.rs +++ b/src/alsa/mod.rs @@ -67,9 +67,7 @@ impl Drop for Trigger { pub struct Endpoint(String); impl Endpoint { - pub fn supported_formats( - &self) - -> Result { + pub fn supported_formats(&self) -> Result { unsafe { let mut playback_handle = mem::uninitialized(); let device_name = ffi::CString::new(self.0.clone()).expect("Unable to get device name"); @@ -105,8 +103,7 @@ impl Endpoint { SND_PCM_FORMAT_S32_BE, SND_PCM_FORMAT_U32_LE, SND_PCM_FORMAT_U32_BE,*/ - (SampleFormat::F32, alsa::SND_PCM_FORMAT_FLOAT_LE) - /*SND_PCM_FORMAT_FLOAT_BE, + (SampleFormat::F32, alsa::SND_PCM_FORMAT_FLOAT_LE) /*SND_PCM_FORMAT_FLOAT_BE, SND_PCM_FORMAT_FLOAT64_LE, SND_PCM_FORMAT_FLOAT64_BE, SND_PCM_FORMAT_IEC958_SUBFRAME_LE, @@ -154,7 +151,11 @@ impl Endpoint { let samples_rates = if min_rate == max_rate { vec![(min_rate, max_rate)] - } else if alsa::snd_pcm_hw_params_test_rate(playback_handle, hw_params.0, min_rate + 1, 0) == 0 { + } else if alsa::snd_pcm_hw_params_test_rate(playback_handle, + hw_params.0, + min_rate + 1, + 0) == 0 + { vec![(min_rate, max_rate)] } else { const RATES: [libc::c_uint; 13] = [ @@ -252,7 +253,7 @@ impl Endpoint { pub struct EventLoop { // Each newly-created voice gets a new ID from this counter. The counter is then incremented. - next_voice_id: AtomicUsize, // TODO: use AtomicU64 when stable? + next_voice_id: AtomicUsize, // TODO: use AtomicU64 when stable? // A trigger that uses a `pipe()` as backend. Signalled whenever a new command is ready, so // that `poll()` can wake up and pick the changes. @@ -325,9 +326,9 @@ impl EventLoop { let pending_trigger = Trigger::new(); let run_context = Mutex::new(RunContext { - descriptors: Vec::new(), // TODO: clearify in doc initial value not necessary - voices: Vec::new(), - }); + descriptors: Vec::new(), // TODO: clearify in doc initial value not necessary + voices: Vec::new(), + }); EventLoop { next_voice_id: AtomicUsize::new(0), @@ -369,13 +370,14 @@ impl EventLoop { fd: self.pending_trigger.read_fd(), events: libc::POLLIN, revents: 0, - } + }, ]; for voice in run_context.voices.iter() { run_context.descriptors.reserve(voice.num_descriptors); let len = run_context.descriptors.len(); let filled = alsa::snd_pcm_poll_descriptors(voice.channel, - run_context.descriptors + run_context + .descriptors .as_mut_ptr() .offset(len as isize), voice.num_descriptors as @@ -413,9 +415,13 @@ impl EventLoop { { let num_descriptors = voice_inner.num_descriptors as libc::c_uint; - check_errors(alsa::snd_pcm_poll_descriptors_revents(voice_inner.channel, run_context.descriptors - .as_mut_ptr().offset(i_descriptor), - num_descriptors, &mut revent)).unwrap(); + let desc_ptr = + run_context.descriptors.as_mut_ptr().offset(i_descriptor); + let res = alsa::snd_pcm_poll_descriptors_revents(voice_inner.channel, + desc_ptr, + num_descriptors, + &mut revent); + check_errors(res).unwrap(); } if (revent as libc::c_short & libc::POLLOUT) == 0 { @@ -433,10 +439,12 @@ impl EventLoop { // buffer underrun voice_inner.buffer_len } else if available < 0 { - check_errors(available as libc::c_int).expect("buffer is not available"); + check_errors(available as libc::c_int) + .expect("buffer is not available"); unreachable!() } else { - (available * voice_inner.num_channels as alsa::snd_pcm_sframes_t) as usize + (available * voice_inner.num_channels as alsa::snd_pcm_sframes_t) as + usize } }; @@ -473,9 +481,8 @@ impl EventLoop { SampleFormat::F32 => { let buffer = Buffer { voice_inner: voice_inner, - buffer: iter::repeat(0.0) // we don't use mem::uninitialized in case of sNaN - .take(available) - .collect(), + // Note that we don't use `mem::uninitialized` because of sNaN. + buffer: iter::repeat(0.0).take(available).collect(), }; UnknownTypeBuffer::F32(::Buffer { target: Some(buffer) }) @@ -580,7 +587,7 @@ impl EventLoop { }; let new_voice_id = VoiceId(self.next_voice_id.fetch_add(1, Ordering::Relaxed)); - assert_ne!(new_voice_id.0, usize::max_value()); // check for overflows + assert_ne!(new_voice_id.0, usize::max_value()); // check for overflows let voice_inner = VoiceInner { id: new_voice_id.clone(), @@ -594,7 +601,10 @@ impl EventLoop { resume_trigger: Trigger::new(), }; - self.commands.lock().unwrap().push(Command::NewVoice(voice_inner)); + self.commands + .lock() + .unwrap() + .push(Command::NewVoice(voice_inner)); self.pending_trigger.wakeup(); Ok(new_voice_id) } @@ -602,7 +612,10 @@ impl EventLoop { #[inline] pub fn destroy_voice(&self, voice_id: VoiceId) { - self.commands.lock().unwrap().push(Command::DestroyVoice(voice_id)); + self.commands + .lock() + .unwrap() + .push(Command::DestroyVoice(voice_id)); self.pending_trigger.wakeup(); } @@ -670,8 +683,9 @@ impl<'a, T> Buffer<'a, T> { unsafe { loop { - let result = - alsa::snd_pcm_writei(self.voice_inner.channel, self.buffer.as_ptr() as *const _, to_write); + let result = alsa::snd_pcm_writei(self.voice_inner.channel, + self.buffer.as_ptr() as *const _, + to_write); if result == -32 { // buffer underrun diff --git a/src/coreaudio/mod.rs b/src/coreaudio/mod.rs index 2740e25..bdb4226 100644 --- a/src/coreaudio/mod.rs +++ b/src/coreaudio/mod.rs @@ -26,9 +26,7 @@ pub use self::enumerate::{EndpointsIterator, SupportedFormatsIterator, default_e pub struct Endpoint; impl Endpoint { - pub fn supported_formats( - &self) - -> Result { + pub fn supported_formats(&self) -> Result { Ok( vec![ SupportedFormat { @@ -70,9 +68,7 @@ impl EventLoop { #[inline] pub fn new() -> EventLoop { EventLoop { - active_callbacks: Arc::new(ActiveCallbacks { - callbacks: Mutex::new(Vec::new()), - }), + active_callbacks: Arc::new(ActiveCallbacks { callbacks: Mutex::new(Vec::new()) }), voices: Mutex::new(Vec::new()), } } @@ -82,7 +78,11 @@ impl EventLoop { where F: FnMut(VoiceId, UnknownTypeBuffer) { let callback: &mut FnMut(VoiceId, UnknownTypeBuffer) = &mut callback; - self.active_callbacks.callbacks.lock().unwrap().push(unsafe { mem::transmute(callback) }); + self.active_callbacks + .callbacks + .lock() + .unwrap() + .push(unsafe { mem::transmute(callback) }); loop { // So the loop does not get optimised out in --release @@ -95,8 +95,7 @@ impl EventLoop { #[inline] pub fn build_voice(&self, endpoint: &Endpoint, format: &Format) - -> Result - { + -> Result { fn convert_error(err: coreaudio::Error) -> CreationError { match err { coreaudio::Error::RenderCallbackBufferFormatDoesNotMatchAudioUnitStreamFormat | @@ -110,9 +109,9 @@ impl EventLoop { let mut audio_unit = { let au_type = if cfg!(target_os = "ios") { - // The DefaultOutput unit isn't available in iOS unfortunately. RemoteIO is a sensible replacement. - // See - // https://developer.apple.com/library/content/documentation/MusicAudio/Conceptual/AudioUnitHostingGuide_iOS/UsingSpecificAudioUnits/UsingSpecificAudioUnits.html + // The DefaultOutput unit isn't available in iOS unfortunately. + // RemoteIO is a sensible replacement. + // See https://goo.gl/CWwRTx coreaudio::audio_unit::IOType::RemoteIO } else { coreaudio::audio_unit::IOType::DefaultOutput @@ -123,7 +122,10 @@ impl EventLoop { // Determine the future ID of the voice. let mut voices_lock = self.voices.lock().unwrap(); - let voice_id = voices_lock.iter().position(|n| n.is_none()).unwrap_or(voices_lock.len()); + let voice_id = voices_lock + .iter() + .position(|n| n.is_none()) + .unwrap_or(voices_lock.len()); // TODO: iOS uses integer and fixed-point data diff --git a/src/emscripten/mod.rs b/src/emscripten/mod.rs index f096357..258b6df 100644 --- a/src/emscripten/mod.rs +++ b/src/emscripten/mod.rs @@ -5,8 +5,8 @@ use std::sync::Mutex; use stdweb; use stdweb::Reference; use stdweb::unstable::TryInto; -use stdweb::web::set_timeout; use stdweb::web::TypedArray; +use stdweb::web::set_timeout; use CreationError; use Format; @@ -33,9 +33,7 @@ impl EventLoop { pub fn new() -> EventLoop { stdweb::initialize(); - EventLoop { - voices: Mutex::new(Vec::new()), - } + EventLoop { voices: Mutex::new(Vec::new()) } } #[inline] @@ -68,7 +66,8 @@ impl EventLoop { voice: &voice, }; - user_cb(VoiceId(voice_id), ::UnknownTypeBuffer::F32(::Buffer { target: Some(buffer) })); + user_cb(VoiceId(voice_id), + ::UnknownTypeBuffer::F32(::Buffer { target: Some(buffer) })); } set_timeout(|| callback_fn::(user_data_ptr), 330); @@ -84,9 +83,7 @@ impl EventLoop { } #[inline] - pub fn build_voice(&self, _: &Endpoint, _format: &Format) - -> Result - { + pub fn build_voice(&self, _: &Endpoint, _format: &Format) -> Result { let voice = js!(return new AudioContext()).into_reference().unwrap(); let mut voices = self.voices.lock().unwrap(); @@ -110,14 +107,20 @@ impl EventLoop { #[inline] pub fn play(&self, voice_id: VoiceId) { let voices = self.voices.lock().unwrap(); - let voice = voices.get(voice_id.0).and_then(|v| v.as_ref()).expect("invalid voice ID"); + let voice = voices + .get(voice_id.0) + .and_then(|v| v.as_ref()) + .expect("invalid voice ID"); js!(@{voice}.resume()); } #[inline] pub fn pause(&self, voice_id: VoiceId) { let voices = self.voices.lock().unwrap(); - let voice = voices.get(voice_id.0).and_then(|v| v.as_ref()).expect("invalid voice ID"); + let voice = voices + .get(voice_id.0) + .and_then(|v| v.as_ref()) + .expect("invalid voice ID"); js!(@{voice}.suspend()); } } @@ -130,9 +133,12 @@ pub struct VoiceId(usize); fn is_webaudio_available() -> bool { stdweb::initialize(); - js!( - if (!AudioContext) { return false; } else { return true; } - ).try_into().unwrap() + js!(if (!AudioContext) { + return false; + } else { + return true; + }).try_into() + .unwrap() } // Content is false if the iterator is empty. @@ -170,18 +176,20 @@ pub struct Endpoint; impl Endpoint { #[inline] - pub fn supported_formats( - &self) - -> Result { + pub fn supported_formats(&self) -> Result { // TODO: right now cpal's API doesn't allow flexibility here // "44100" and "2" (channels) have also been hard-coded in the rest of the code ; if // this ever becomes more flexible, don't forget to change that - Ok(vec![SupportedFormat { - channels: vec![::ChannelPosition::BackLeft, ::ChannelPosition::BackRight], - min_samples_rate: ::SamplesRate(44100), - max_samples_rate: ::SamplesRate(44100), - data_type: ::SampleFormat::F32, - }].into_iter()) + Ok( + vec![ + SupportedFormat { + channels: vec![::ChannelPosition::BackLeft, ::ChannelPosition::BackRight], + min_samples_rate: ::SamplesRate(44100), + max_samples_rate: ::SamplesRate(44100), + data_type: ::SampleFormat::F32, + }, + ].into_iter(), + ) } #[inline] @@ -192,12 +200,16 @@ impl Endpoint { pub type SupportedFormatsIterator = ::std::vec::IntoIter; -pub struct Buffer<'a, T: 'a> where T: Sample { +pub struct Buffer<'a, T: 'a> + where T: Sample +{ temporary_buffer: Vec, voice: &'a Reference, } -impl<'a, T> Buffer<'a, T> where T: Sample { +impl<'a, T> Buffer<'a, T> + where T: Sample +{ #[inline] pub fn buffer(&mut self) -> &mut [T] { &mut self.temporary_buffer @@ -214,12 +226,15 @@ impl<'a, T> Buffer<'a, T> where T: Sample { let typed_array = { let t_slice: &[T] = self.temporary_buffer.as_slice(); - let u8_slice: &[u8] = unsafe { from_raw_parts(t_slice.as_ptr() as *const _, t_slice.len() * mem::size_of::()) }; + let u8_slice: &[u8] = unsafe { + from_raw_parts(t_slice.as_ptr() as *const _, + t_slice.len() * mem::size_of::()) + }; let typed_array: TypedArray = u8_slice.into(); typed_array }; - let num_channels = 2u32; // TODO: correct value + let num_channels = 2u32; // TODO: correct value debug_assert_eq!(self.temporary_buffer.len() % num_channels as usize, 0); js!( diff --git a/src/lib.rs b/src/lib.rs index de86f75..f5452b6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,42 +1,113 @@ -/*! -# How to use cpal - -In order to play a sound, first you need to create an `EventLoop` and a voice. - -```no_run -// getting the default sound output of the system (can return `None` if nothing is supported) -let endpoint = cpal::default_endpoint().unwrap(); - -// note that the user can at any moment disconnect the device, therefore all operations return -// a `Result` to handle this situation - -// getting a format for the PCM -let supported_formats_range = endpoint.supported_formats().unwrap().next().unwrap(); -let format = supported_formats_range.with_max_samples_rate(); - -let event_loop = cpal::EventLoop::new(); - -let voice_id = event_loop.build_voice(&endpoint, &format).unwrap(); -event_loop.play(voice_id); -``` - -`voice_id` is an identifier for the voice can be used to control the play/pause of the output. - -Once that's done, you can call `run()` on the `event_loop`. - -```no_run -# let event_loop = cpal::EventLoop::new(); -event_loop.run(move |_voice_id, _buffer| { - // write data to `buffer` here -}); -``` - -Calling `run()` will block the thread forever, so it's usually best done in a separate thread. - -While `run()` is running, the audio device of the user will call the callbacks you registered -from time to time. - -*/ +//! # How to use cpal +//! +//! Here are some concepts cpal exposes: +//! +//! - An endpoint is a target where the data of the audio channel will be played. +//! - A voice is an open audio channel which you can stream audio data to. You have to choose which +//! endpoint your voice targets before you create one. +//! - An event loop is a collection of voices. Each voice must belong to an event loop, and all the +//! voices that belong to an event loop are managed together. +//! +//! In order to play a sound, you first need to create an event loop: +//! +//! ``` +//! use cpal::EventLoop; +//! let event_loop = EventLoop::new(); +//! ``` +//! +//! Then choose an endpoint. You can either use the default endpoint with the `default_endpoint()` +//! function, or enumerate all the available endpoints with the `endpoints()` function. Beware that +//! `default_endpoint()` returns an `Option` in case no endpoint is available on the system. +//! +//! ``` +//! // Note: we call `unwrap()` because it is convenient, but you should avoid doing that in a real +//! // code. +//! let endpoint = cpal::default_endpoint().expect("no endpoint is available"); +//! ``` +//! +//! Before we can create a voice, we must decide what the format of the audio samples is going to +//! be. You can query all the supported formats with the `supported_formats()` method, which +//! produces a list of `SupportedFormat` structs which can later be turned into actual `Format` +//! structs. If you don't want to query the list of formats, you can also build your own `Format` +//! manually, but doing so could lead to an error when building the voice if the format ends up not +//! being supported. +//! +//! > **Note**: the `supported_formats()` method could return an error for example if the device +//! > has been disconnected. +//! +//! ```no_run +//! # let endpoint = cpal::default_endpoint().unwrap(); +//! let mut supported_formats_range = endpoint.supported_formats() +//! .expect("error while querying formats"); +//! let format = supported_formats_range.next().expect("no supported format?!") +//! .with_max_samples_rate(); +//! ``` +//! +//! Now that we have everything, we can create a voice from that event loop: +//! +//! ```no_run +//! # let endpoint = cpal::default_endpoint().unwrap(); +//! # let format = endpoint.supported_formats().unwrap().next().unwrap().with_max_samples_rate(); +//! # let event_loop = cpal::EventLoop::new(); +//! let voice_id = event_loop.build_voice(&endpoint, &format).unwrap(); +//! ``` +//! +//! The value returned by `build_voice()` is of type `VoiceId` and is an identifier that will +//! allow you to control the voice. +//! +//! There is a last step to perform before going forward, which is to start the voice. This is done +//! with the `play()` method on the event loop. +//! +//! ``` +//! # let event_loop: cpal::EventLoop = return; +//! # let voice_id: cpal::VoiceId = return; +//! event_loop.play(voice_id); +//! ``` +//! +//! Once everything is done, you must call `run()` on the `event_loop`. +//! +//! ```no_run +//! # let event_loop = cpal::EventLoop::new(); +//! event_loop.run(move |_voice_id, _buffer| { +//! // write data to `buffer` here +//! }); +//! ``` +//! +//! > **Note**: Calling `run()` will block the thread forever, so it's usually best done in a +//! > separate thread. +//! +//! While `run()` is running, the audio device of the user will from time to time call the callback +//! that you passed to this function. The callback gets passed the voice ID, and a struct of type +//! `UnknownTypeBuffer` that represents the buffer that must be filled with audio samples. The +//! `UnknownTypeBuffer` can be one of `I16`, `U16` or `F32` depending on the format that was passed +//! to `build_voice`. +//! +//! In this example, we simply simply fill the buffer with zeroes. +//! +//! ```no_run +//! use cpal::UnknownTypeBuffer; +//! +//! # let event_loop = cpal::EventLoop::new(); +//! event_loop.run(move |_voice_id, mut buffer| { +//! match buffer { +//! UnknownTypeBuffer::U16(mut buffer) => { +//! for elem in buffer.iter_mut() { +//! *elem = u16::max_value() / 2; +//! } +//! }, +//! UnknownTypeBuffer::I16(mut buffer) => { +//! for elem in buffer.iter_mut() { +//! *elem = 0; +//! } +//! }, +//! UnknownTypeBuffer::F32(mut buffer) => { +//! for elem in buffer.iter_mut() { +//! *elem = 0.0; +//! } +//! }, +//! } +//! }); +//! ``` #![recursion_limit = "512"] @@ -78,6 +149,8 @@ mod cpal_impl; mod cpal_impl; /// An iterator for the list of formats that are supported by the backend. +/// +/// See [`endpoints()`](fn.endpoints.html). pub struct EndpointsIterator(cpal_impl::EndpointsIterator); impl Iterator for EndpointsIterator { @@ -95,6 +168,8 @@ impl Iterator for EndpointsIterator { } /// Return an iterator to the list of formats that are supported by the system. +/// +/// Can be empty if the system doesn't support audio in general. #[inline] pub fn endpoints() -> EndpointsIterator { EndpointsIterator(Default::default()) @@ -120,12 +195,18 @@ pub fn get_default_endpoint() -> Option { default_endpoint() } -/// An opaque type that identifies an end point. +/// An opaque type that identifies an endpoint that is capable of playing audio. +/// +/// Please note that endpoints may become invalid if they get disconnected. Therefore all the +/// methods that involve an endpoint return a `Result`. #[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. + /// + /// Can return an error if the endpoint is no longer valid (eg. it has been disconnected). + /// The returned iterator should never be empty. #[inline] pub fn supported_formats(&self) -> Result { Ok(SupportedFormatsIterator(self.0.supported_formats()?)) @@ -141,6 +222,7 @@ impl Endpoint { } /// Returns the name of the endpoint. + // TODO: human-readable or system name? #[inline] pub fn name(&self) -> String { self.0.name() @@ -193,6 +275,8 @@ pub struct Format { } /// An iterator that produces a list of formats supported by the endpoint. +/// +/// See [`Endpoint::supported_formats()`](struct.Endpoint.html#method.supported_formats). pub struct SupportedFormatsIterator(cpal_impl::SupportedFormatsIterator); impl Iterator for SupportedFormatsIterator { @@ -209,17 +293,20 @@ impl Iterator for SupportedFormatsIterator { } } -/// Describes a format. +/// Describes a range of supported formats. #[derive(Debug, Clone, PartialEq, Eq)] pub struct SupportedFormat { pub channels: Vec, + /// Minimum value for the samples rate of the supported formats. pub min_samples_rate: SamplesRate, + /// Maximum value for the samples rate of the supported formats. pub max_samples_rate: SamplesRate, + /// Type of data expected by the endpoint. pub data_type: SampleFormat, } impl SupportedFormat { - /// Builds a corresponding `Format` corresponding to the maximum samples rate. + /// Turns this `SupportedFormat` into a `Format` corresponding to the maximum samples rate. #[inline] pub fn with_max_samples_rate(self) -> Format { Format { @@ -242,6 +329,9 @@ impl From for SupportedFormat { } } +/// Collection of voices managed together. +/// +/// Created with the [`new`](struct.EventLoop.html#method.new) method. pub struct EventLoop(cpal_impl::EventLoop); impl EventLoop { @@ -254,10 +344,12 @@ impl EventLoop { /// Creates a new voice that will play on the given endpoint and with the given format. /// /// On success, returns an identifier for the voice. + /// + /// Can return an error if the endpoint is no longer valid, or if the format is not supported + /// by the endpoint. #[inline] pub fn build_voice(&self, endpoint: &Endpoint, format: &Format) - -> Result - { + -> Result { self.0.build_voice(&endpoint.0, format).map(VoiceId) } @@ -274,9 +366,11 @@ impl EventLoop { /// Takes control of the current thread and processes the sounds. /// + /// > **Note**: Since it takes control of the thread, this method is best called on a separate + /// > thread. + /// /// Whenever a voice needs to be fed some data, the closure passed as parameter is called. - /// **Note**: Calling other methods of the events loop from the callback will most likely - /// deadlock. Don't do that. Maybe this will change in the future. + /// You can call the other methods of `EventLoop` without getting a deadlock. #[inline] pub fn run(&self, mut callback: F) -> ! where F: FnMut(VoiceId, UnknownTypeBuffer) @@ -284,7 +378,7 @@ impl EventLoop { self.0.run(move |id, buf| callback(VoiceId(id), buf)) } - /// Sends a command to the audio device that it should start playing. + /// Instructs the audio device that it should start playing. /// /// Has no effect is the voice was already playing. /// @@ -300,11 +394,11 @@ impl EventLoop { self.0.play(voice.0) } - /// Sends a command to the audio device that it should stop playing. + /// Instructs the audio device that it should stop playing. /// /// 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 where it was. /// /// # Panic /// @@ -405,8 +499,12 @@ impl Error for CreationError { /// 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 -/// is destroyed. +/// You should destroy this object as soon as possible. Data is only sent to the audio device when +/// this object is destroyed. +/// +/// This struct implements the `Deref` and `DerefMut` traits to `[T]`. Therefore writing to this +/// buffer is done in the same way as writing to a `Vec` or any other kind of Rust array. +// TODO: explain audio stuff in general #[must_use] pub struct Buffer<'a, T: 'a> where T: Sample diff --git a/src/null/mod.rs b/src/null/mod.rs index faa6977..8095d77 100644 --- a/src/null/mod.rs +++ b/src/null/mod.rs @@ -23,9 +23,7 @@ impl EventLoop { } #[inline] - pub fn build_voice(&self, _: &Endpoint, _: &Format) - -> Result - { + pub fn build_voice(&self, _: &Endpoint, _: &Format) -> Result { Err(CreationError::DeviceNotAvailable) } @@ -70,9 +68,7 @@ pub struct Endpoint; impl Endpoint { #[inline] - pub fn supported_formats( - &self) - -> Result { + pub fn supported_formats(&self) -> Result { unreachable!() } diff --git a/src/wasapi/endpoint.rs b/src/wasapi/endpoint.rs index 15d143a..52e1f76 100644 --- a/src/wasapi/endpoint.rs +++ b/src/wasapi/endpoint.rs @@ -110,9 +110,7 @@ impl Endpoint { Ok(client) } - pub fn supported_formats( - &self) - -> Result { + pub fn supported_formats(&self) -> Result { // 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 @@ -299,7 +297,7 @@ lazy_static! { // building the devices enumerator object unsafe { let mut enumerator: *mut winapi::IMMDeviceEnumerator = mem::uninitialized(); - + let hresult = ole32::CoCreateInstance(&winapi::CLSID_MMDeviceEnumerator, ptr::null_mut(), winapi::CLSCTX_ALL, &winapi::IID_IMMDeviceEnumerator, diff --git a/src/wasapi/mod.rs b/src/wasapi/mod.rs index 136708b..3d511b2 100644 --- a/src/wasapi/mod.rs +++ b/src/wasapi/mod.rs @@ -4,7 +4,7 @@ extern crate kernel32; use std::io::Error as IoError; -pub use self::endpoint::{Endpoint, EndpointsIterator, default_endpoint, SupportedFormatsIterator}; +pub use self::endpoint::{Endpoint, EndpointsIterator, SupportedFormatsIterator, default_endpoint}; pub use self::voice::{Buffer, EventLoop, VoiceId}; mod com; diff --git a/src/wasapi/voice.rs b/src/wasapi/voice.rs index 8ebd772..b547784 100644 --- a/src/wasapi/voice.rs +++ b/src/wasapi/voice.rs @@ -1,4 +1,3 @@ - use super::Endpoint; use super::check_result; use super::com; @@ -10,9 +9,9 @@ use std::marker::PhantomData; use std::mem; use std::ptr; use std::slice; +use std::sync::Mutex; use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering; -use std::sync::Mutex; use ChannelPosition; use CreationError; @@ -81,9 +80,9 @@ impl EventLoop { EventLoop { pending_scheduled_event: pending_scheduled_event, run_context: Mutex::new(RunContext { - voices: Vec::new(), - handles: vec![pending_scheduled_event], - }), + voices: Vec::new(), + handles: vec![pending_scheduled_event], + }), next_voice_id: AtomicUsize::new(0), commands: Mutex::new(Vec::new()), } @@ -226,20 +225,20 @@ impl EventLoop { }; let new_voice_id = VoiceId(self.next_voice_id.fetch_add(1, Ordering::Relaxed)); - assert_ne!(new_voice_id.0, usize::max_value()); // check for overflows + assert_ne!(new_voice_id.0, usize::max_value()); // check for overflows // Once we built the `VoiceInner`, we add a command that will be picked up by the // `run()` method and added to the `RunContext`. { let inner = VoiceInner { - id: new_voice_id.clone(), - audio_client: audio_client, - render_client: render_client, - event: event, - playing: false, - max_frames_in_buffer: max_frames_in_buffer, - bytes_per_frame: format.nBlockAlign, - }; + id: new_voice_id.clone(), + audio_client: audio_client, + render_client: render_client, + event: event, + playing: false, + max_frames_in_buffer: max_frames_in_buffer, + bytes_per_frame: format.nBlockAlign, + }; self.commands.lock().unwrap().push(Command::NewVoice(inner)); @@ -254,7 +253,10 @@ impl EventLoop { #[inline] pub fn destroy_voice(&self, voice_id: VoiceId) { unsafe { - self.commands.lock().unwrap().push(Command::DestroyVoice(voice_id)); + self.commands + .lock() + .unwrap() + .push(Command::DestroyVoice(voice_id)); let result = kernel32::SetEvent(self.pending_scheduled_event); assert!(result != 0); } @@ -356,8 +358,8 @@ impl EventLoop { debug_assert!(!buffer.is_null()); (buffer as *mut _, - frames_available as usize * voice.bytes_per_frame as usize / - mem::size_of::()) // FIXME: correct size when not f32 + frames_available as usize * voice.bytes_per_frame as usize / + mem::size_of::()) // FIXME: correct size when not f32 }; let buffer = Buffer { @@ -368,7 +370,7 @@ impl EventLoop { marker: PhantomData, }; - let buffer = UnknownTypeBuffer::F32(::Buffer { target: Some(buffer) }); // FIXME: not always f32 + let buffer = UnknownTypeBuffer::F32(::Buffer { target: Some(buffer) }); // FIXME: not always f32 callback(voice_id, buffer); } }