cpal-0.10.0/.circleci/config.yml010066400037200003720000000027041350771351400145640ustar0000000000000000version: 2 jobs: android-test: working_directory: ~/cpal docker: - image: tomaka/cargo-apk steps: - run: apt-get -qq update && apt-get install -y git - checkout - restore_cache: key: android-test-cache-{{ checksum "Cargo.toml" }} - run: cargo apk build --example beep - save_cache: key: android-test-cache-{{ checksum "Cargo.toml" }} paths: - target asmjs-test: working_directory: ~/cpal docker: - image: tomaka/rustc-emscripten steps: - run: apt-get -qq update && apt-get install -y git - checkout - restore_cache: key: asmjs-test-cache-{{ checksum "Cargo.toml" }} - run: cargo build --example beep --target asmjs-unknown-emscripten - save_cache: key: asmjs-test-cache-{{ checksum "Cargo.toml" }} paths: - target wasm-test: working_directory: ~/cpal docker: - image: tomaka/rustc-emscripten steps: - run: apt-get -qq update && apt-get install -y git - checkout - restore_cache: key: wasm-test-cache-{{ checksum "Cargo.toml" }} - run: cargo build --example beep --target wasm32-unknown-emscripten - save_cache: key: wasm-test-cache-{{ checksum "Cargo.toml" }} paths: - target workflows: version: 2 build-test-and-deploy: jobs: - android-test - asmjs-test - wasm-test cpal-0.10.0/.gitignore010066400037200003720000000000661350771351400127300ustar0000000000000000/target /Cargo.lock .cargo/ .DS_Store recorded.wavcpal-0.10.0/.rustfmt.toml010066400037200003720000000007531350771351400134220ustar0000000000000000fn_args_density = "Compressed" fn_args_layout = "Visual" fn_brace_style = "SameLineWhere" fn_call_style = "Visual" fn_empty_single_line = false format_strings = true generics_indent = "Visual" impl_empty_single_line = false match_block_trailing_comma = true reorder_imported_names = true reorder_imports = true reorder_imports_in_group = true spaces_around_ranges = true use_try_shorthand = true where_density = "Tall" where_style = "Legacy" wrap_match_arms = false write_mode = "Overwrite" cpal-0.10.0/.travis.yml010066400037200003720000000014711350771351400130520ustar0000000000000000language: rust sudo: false addons: apt: packages: - libasound2-dev os: - linux - osx env: global: - secure: "ZGNi7bKoxNIziWzdNP1QbH6ZbeWDInq42yq1rzWY71I/pi8yA4zjVAYdgXKElc0saB0d2TxJCwpeGVyXwuXXeNoud8HWsqQxhNVMM8K27a2jeezza8UCCJ7kAS6E/+gQj7w+HFtgWiUHZ0KEjGONqXJM+cWqH5oprTEuNBOPqDA=" - secure: "H7JRHaCkm6NvmMU76pNUyK5fj8d0kEb1snGCTcC4ZdW98P5qSEDbMBOKZgSW0DNhKmzYS1qHHAXlmm9c9JCVYfeErL9JeTrRk4DvIO8sH3ImhHIB/q4U+h+hxZ6W+dGpTi0kjQ5ZS3z+Mlr/9CB4UT83HmF78Zfyf6KA15esUIQ=" after_success: - | [ $TRAVIS_BRANCH = master ] && [ $TRAVIS_PULL_REQUEST = false ] && cargo publish --token ${CRATESIO_TOKEN} --manifest-path alsa-sys/Cargo.toml - | [ $TRAVIS_BRANCH = master ] && [ $TRAVIS_PULL_REQUEST = false ] && cargo publish --token ${CRATESIO_TOKEN} cpal-0.10.0/CHANGELOG.md010066400037200003720000000064221350771351400125530ustar0000000000000000# Unreleased # Version 0.10.0 (2019-07-05) - core-foundation-sys and coreaudio-rs version bumps. - Add an ASIO host, available under Windows. - Introduce a new Host API, adding support for alternative audio APIs. - Remove sleep loop on macOS in favour of using a `Condvar`. - Allow users to handle stream callback errors with a new `StreamEvent` type. - Overhaul error handling throughout the crate. - Remove unnecessary Mutex from ALSA and WASAPI backends in favour of channels. - Remove `panic!` from OutputBuffer Deref impl as it is no longer necessary. # Version 0.9.0 (2019-06-06) - Better buffer handling - Fix logic error in frame/sample size - Added error handling for unknown ALSA device errors - Fix resuming a paused stream on Windows (wasapi). - Implement `default_output_format` for emscripten backend. # Version 0.8.1 (2018-03-18) - Fix the handling of non-default sample rates for coreaudio input streams. # Version 0.8.0 (2018-02-15) - Add `record_wav.rs` example. Records 3 seconds to `$CARGO_MANIFEST_DIR/recorded.wav` using default input device. - Update `enumerate.rs` example to display default input/output devices and formats. - Add input stream support to coreaudio, alsa and windows backends. - Introduce `StreamData` type for handling either input or output streams in `EventLoop::run` callback. - Add `Device::supported_{input/output}_formats` methods. - Add `Device::default_{input/output}_format` methods. - Add `default_{input/output}_device` functions. - Replace usage of `Voice` with `Stream` throughout the crate. - Remove `Endpoint` in favour of `Device` for supporting both input and output streams. # Version 0.7.0 (2018-02-04) - Rename `ChannelsCount` to `ChannelCount`. - Rename `SamplesRate` to `SampleRate`. - Rename the `min_samples_rate` field of `SupportedFormat` to `min_sample_rate` - Rename the `with_max_samples_rate()` method of`SupportedFormat` to `with_max_sample_rate()` - Rename the `samples_rate` field of `Format` to `sample_rate` - Changed the type of the `channels` field of the `SupportedFormat` struct from `Vec` to `ChannelCount` (an alias to `u16`) - Remove unused ChannelPosition API. - Implement `Endpoint` and `Format` Enumeration for macos. - Implement format handling for macos `build_voice` method. # Version 0.6.0 (2017-12-11) - Changed the emscripten backend to consume less CPU. - Added improvements to the crate documentation. - Implement `pause` and `play` for ALSA backend. - Reduced the number of allocations in the CoreAudio backend. - Fixes for macos build (#186, #189). # Version 0.5.1 (2017-10-21) - Added `Sample::to_i16()`, `Sample::to_u16()` and `Sample::from`. # Version 0.5.0 (2017-10-21) - Removed the dependency on the `futures` library. - Removed the `Voice` and `SamplesStream` types. - Added `EventLoop::build_voice`, `EventLoop::destroy_voice`, `EventLoop::play`, and `EventLoop::pause` that can be used to create, destroy, play and pause voices. - Added a `VoiceId` struct that is now used to identify a voice owned by an `EventLoop`. - Changed `EventLoop::run()` to take a callback that is called whenever a voice requires sound data. - Changed `supported_formats()` to produce a list of `SupportedFormat` instead of `Format`. A `SupportedFormat` must then be turned into a `Format` in order to build a voice. cpal-0.10.0/Cargo.toml.orig010066400037200003720000000026601350771351400136310ustar0000000000000000[package] name = "cpal" version = "0.10.0" authors = ["The CPAL contributors", "Pierre Krieger "] description = "Low-level cross-platform audio I/O library in pure Rust." repository = "https://github.com/tomaka/cpal" documentation = "https://docs.rs/cpal" license = "Apache-2.0" keywords = ["audio", "sound"] [features] asio = ["asio-sys"] # Only available on Windows. See README for setup instructions. [dependencies] failure = "0.1.5" lazy_static = "1.3" num-traits = "0.2.6" [dev-dependencies] hound = "3.4" [target.'cfg(target_os = "windows")'.dependencies] winapi = { version = "0.3", features = ["audiosessiontypes", "audioclient", "coml2api", "combaseapi", "debug", "devpkey", "handleapi", "ksmedia", "mmdeviceapi", "objbase", "std", "synchapi", "winuser"] } asio-sys = { version = "0.1", path = "asio-sys", optional = true } [target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))'.dependencies] alsa-sys = { version = "0.1", path = "alsa-sys" } libc = "0.2" [target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies] coreaudio-rs = { version = "0.9.1", default-features = false, features = ["audio_unit", "core_audio"] } core-foundation-sys = "0.6.2" # For linking to CoreFoundation.framework and handling device name `CFString`s. [target.'cfg(target_os = "emscripten")'.dependencies] stdweb = { version = "0.1.3", default-features = false } cpal-0.10.0/Cargo.toml0000644000000041100000000000000100620ustar00# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] name = "cpal" version = "0.10.0" authors = ["The CPAL contributors", "Pierre Krieger "] description = "Low-level cross-platform audio I/O library in pure Rust." documentation = "https://docs.rs/cpal" keywords = ["audio", "sound"] license = "Apache-2.0" repository = "https://github.com/tomaka/cpal" [dependencies.failure] version = "0.1.5" [dependencies.lazy_static] version = "1.3" [dependencies.num-traits] version = "0.2.6" [dev-dependencies.hound] version = "3.4" [features] asio = ["asio-sys"] [target."cfg(any(target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\"))".dependencies.alsa-sys] version = "0.1" [target."cfg(any(target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\"))".dependencies.libc] version = "0.2" [target."cfg(any(target_os = \"macos\", target_os = \"ios\"))".dependencies.core-foundation-sys] version = "0.6.2" [target."cfg(any(target_os = \"macos\", target_os = \"ios\"))".dependencies.coreaudio-rs] version = "0.9.1" features = ["audio_unit", "core_audio"] default-features = false [target."cfg(target_os = \"emscripten\")".dependencies.stdweb] version = "0.1.3" default-features = false [target."cfg(target_os = \"windows\")".dependencies.asio-sys] version = "0.1" optional = true [target."cfg(target_os = \"windows\")".dependencies.winapi] version = "0.3" features = ["audiosessiontypes", "audioclient", "coml2api", "combaseapi", "debug", "devpkey", "handleapi", "ksmedia", "mmdeviceapi", "objbase", "std", "synchapi", "winuser"] cpal-0.10.0/README.md010066400037200003720000000102761350771351400122230ustar0000000000000000# CPAL - Cross-Platform Audio Library [![Build Status](https://travis-ci.org/tomaka/cpal.svg?branch=master)](https://travis-ci.org/tomaka/cpal) [![Crates.io](https://img.shields.io/crates/v/cpal.svg)](https://crates.io/crates/cpal) [![docs.rs](https://docs.rs/cpal/badge.svg)](https://docs.rs/cpal/) Low-level library for audio input and output in pure Rust. This library currently supports the following: - Enumerate supported audio hosts. - Enumerate all available audio devices. - Get the current default input and output devices. - Enumerate known supported input and output stream formats for a device. - Get the current default input and output stream formats for a device. - Build and run input and output PCM streams on a chosen device with a given stream format. Currently supported hosts include: - Linux (via ALSA) - Windows (via WASAPI by default, see ASIO instructions below) - macOS (via CoreAudio) - iOS (via CoreAudio) - Emscripten Note that on Linux, the ALSA development files are required. These are provided as part of the `libasound2-dev` package on Debian and Ubuntu distributions and `alsa-lib-devel` on Fedora. ## ASIO on Windows [ASIO](https://en.wikipedia.org/wiki/Audio_Stream_Input/Output) is an audio driver protocol by Steinberg. While it is available on multiple operating systems, it is most commonly used on Windows to work around limitations of WASAPI including access to large numbers of channels and lower-latency audio processing. CPAL allows for using the ASIO SDK as the audio host on Windows instead of WASAPI. To do so, follow these steps: 1. **Download the ASIO SDK** `.zip` from [this link](https://www.steinberg.net/en/company/developers.html). The version as of writing this is 2.3.1. 2. Extract the files and place the directory somewhere you are happy for it to stay (e.g. `~/.asio`). 3. Assign the full path of the directory (that contains the `readme`, `changes`, `ASIO SDK 2.3` pdf, etc) to the `CPAL_ASIO_DIR` environment variable. This is necessary for the `asio-sys` build script to build and bind to the SDK. 4. `bindgen`, the library used to generate bindings to the C++ SDK, requires clang. **Download and install LLVM** from [here](http://releases.llvm.org/download.html) under the "Pre-Built Binaries" section. The version as of writing this is 7.0.0. 5. Add the LLVM `bin` directory to a `LIBCLANG_PATH` environment variable. If you installed LLVM to the default directory, this should work in the command prompt: ``` setx LIBCLANG_PATH "C:\Program Files\LLVM\bin" ``` 6. If you don't have any ASIO devices or drivers available, you can [**download and install ASIO4ALL**](http://www.asio4all.org/). Be sure to enable the "offline" feature during installation despite what the installer says about it being useless. 7. **Loading VCVARS**. `rust-bindgen` uses the C++ tool-chain when generating bindings to the ASIO SDK. As a result, it is necessary to load some environment variables in the command prompt that we use to build our project. On 64-bit machines run: ``` "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" amd64 ``` On 32-bit machines run: ``` "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86 ``` Note that, depending on your version of Visual Studio, this script might be in a slightly different location. 8. Select the ASIO host at the start of our program with the following code: ```rust let host; #[cfg(target_os = "windows")] { host = cpal::host_from_id(cpal::HostId::Asio).expect("failed to initialise ASIO host"); } ``` If you run into compilations errors produced by `asio-sys` or `bindgen`, make sure that `CPAL_ASIO_DIR` is set correctly and try `cargo clean`. 9. Make sure to enable the `asio` feature when building CPAL: ``` cargo build --features "asio" ``` or if you are using CPAL as a dependency in a downstream project, enable the feature like this: ```toml cpal = { version = "*", features = ["asio"] } ``` In the future we would like to work on automating this process to make it easier, but we are not familiar enough with the ASIO license to do so yet. *Updated as of ASIO version 2.3.3.* cpal-0.10.0/appveyor.yml010066400037200003720000000010441350771351400133250ustar0000000000000000os: Visual Studio 2015 environment: matrix: # MSVC - channel: stable target: x86_64-pc-windows-msvc - channel: nightly target: x86_64-pc-windows-msvc matrix: allow_failures: - channel: nightly install: - appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe - rustup-init -yv --default-toolchain %channel% --default-host %target% - set PATH=%PATH%;%USERPROFILE%\.cargo\bin - rustc -vV - cargo -vV build: false test_script: - cargo test --verbose %cargoflags% cpal-0.10.0/build.rs010066400037200003720000000004561350771351400124100ustar0000000000000000use std::env; const CPAL_ASIO_DIR: &'static str = "CPAL_ASIO_DIR"; fn main() { // If ASIO directory isn't set silently return early // otherwise set the asio config flag match env::var(CPAL_ASIO_DIR) { Err(_) => return, Ok(_) => println!("cargo:rustc-cfg=asio"), }; }cpal-0.10.0/examples/beep.rs010066400037200003720000000043521350771351400140410ustar0000000000000000extern crate cpal; extern crate failure; use cpal::traits::{DeviceTrait, EventLoopTrait, HostTrait}; fn main() -> Result<(), failure::Error> { let host = cpal::default_host(); let device = host.default_output_device().expect("failed to find a default output device"); let format = device.default_output_format()?; let event_loop = host.event_loop(); let stream_id = event_loop.build_output_stream(&device, &format)?; event_loop.play_stream(stream_id.clone())?; let sample_rate = format.sample_rate.0 as f32; let mut sample_clock = 0f32; // Produce a sinusoid of maximum amplitude. let mut next_value = || { sample_clock = (sample_clock + 1.0) % sample_rate; (sample_clock * 440.0 * 2.0 * 3.141592 / sample_rate).sin() }; event_loop.run(move |id, result| { let data = match result { Ok(data) => data, Err(err) => { eprintln!("an error occurred on stream {:?}: {}", id, err); return; } }; match data { cpal::StreamData::Output { buffer: cpal::UnknownTypeOutputBuffer::U16(mut buffer) } => { for sample in buffer.chunks_mut(format.channels as usize) { let value = ((next_value() * 0.5 + 0.5) * std::u16::MAX as f32) as u16; for out in sample.iter_mut() { *out = value; } } }, cpal::StreamData::Output { buffer: cpal::UnknownTypeOutputBuffer::I16(mut buffer) } => { for sample in buffer.chunks_mut(format.channels as usize) { let value = (next_value() * std::i16::MAX as f32) as i16; for out in sample.iter_mut() { *out = value; } } }, cpal::StreamData::Output { buffer: cpal::UnknownTypeOutputBuffer::F32(mut buffer) } => { for sample in buffer.chunks_mut(format.channels as usize) { let value = next_value(); for out in sample.iter_mut() { *out = value; } } }, _ => (), } }); } cpal-0.10.0/examples/enumerate.rs010066400037200003720000000046571350771351400151230ustar0000000000000000extern crate cpal; extern crate failure; use cpal::traits::{DeviceTrait, HostTrait}; fn main() -> Result<(), failure::Error> { println!("Supported hosts:\n {:?}", cpal::ALL_HOSTS); let available_hosts = cpal::available_hosts(); println!("Available hosts:\n {:?}", available_hosts); for host_id in available_hosts { println!("{:?}", host_id); let host = cpal::host_from_id(host_id)?; let default_in = host.default_input_device().map(|e| e.name().unwrap()); let default_out = host.default_output_device().map(|e| e.name().unwrap()); println!(" Default Input Device:\n {:?}", default_in); println!(" Default Output Device:\n {:?}", default_out); let devices = host.devices()?; println!(" Devices: "); for (device_index, device) in devices.enumerate() { println!(" {}. \"{}\"", device_index + 1, device.name()?); // Input formats if let Ok(fmt) = device.default_input_format() { println!(" Default input stream format:\n {:?}", fmt); } let mut input_formats = match device.supported_input_formats() { Ok(f) => f.peekable(), Err(e) => { println!("Error: {:?}", e); continue; }, }; if input_formats.peek().is_some() { println!(" All supported input stream formats:"); for (format_index, format) in input_formats.enumerate() { println!(" {}.{}. {:?}", device_index + 1, format_index + 1, format); } } // Output formats if let Ok(fmt) = device.default_output_format() { println!(" Default output stream format:\n {:?}", fmt); } let mut output_formats = match device.supported_output_formats() { Ok(f) => f.peekable(), Err(e) => { println!("Error: {:?}", e); continue; }, }; if output_formats.peek().is_some() { println!(" All supported output stream formats:"); for (format_index, format) in output_formats.enumerate() { println!(" {}.{}. {:?}", device_index + 1, format_index + 1, format); } } } } Ok(()) } cpal-0.10.0/examples/feedback.rs010066400037200003720000000101351350771351400146460ustar0000000000000000//! Feeds back the input stream directly into the output stream. //! //! Assumes that the input and output devices can use the same stream format and that they support //! the f32 sample format. //! //! Uses a delay of `LATENCY_MS` milliseconds in case the default input and output streams are not //! precisely synchronised. extern crate cpal; extern crate failure; use cpal::traits::{DeviceTrait, EventLoopTrait, HostTrait}; const LATENCY_MS: f32 = 150.0; fn main() -> Result<(), failure::Error> { let host = cpal::default_host(); let event_loop = host.event_loop(); // Default devices. let input_device = host.default_input_device().expect("failed to get default input device"); let output_device = host.default_output_device().expect("failed to get default output device"); println!("Using default input device: \"{}\"", input_device.name()?); println!("Using default output device: \"{}\"", output_device.name()?); // We'll try and use the same format between streams to keep it simple let mut format = input_device.default_input_format()?; format.data_type = cpal::SampleFormat::F32; // Build streams. println!("Attempting to build both streams with `{:?}`.", format); let input_stream_id = event_loop.build_input_stream(&input_device, &format)?; let output_stream_id = event_loop.build_output_stream(&output_device, &format)?; println!("Successfully built streams."); // Create a delay in case the input and output devices aren't synced. let latency_frames = (LATENCY_MS / 1_000.0) * format.sample_rate.0 as f32; let latency_samples = latency_frames as usize * format.channels as usize; // The channel to share samples. let (tx, rx) = std::sync::mpsc::sync_channel(latency_samples * 2); // Fill the samples with 0.0 equal to the length of the delay. for _ in 0..latency_samples { tx.send(0.0)?; } // Play the streams. println!("Starting the input and output streams with `{}` milliseconds of latency.", LATENCY_MS); event_loop.play_stream(input_stream_id.clone())?; event_loop.play_stream(output_stream_id.clone())?; // Run the event loop on a separate thread. std::thread::spawn(move || { event_loop.run(move |id, result| { let data = match result { Ok(data) => data, Err(err) => { eprintln!("an error occurred on stream {:?}: {}", id, err); return; } }; match data { cpal::StreamData::Input { buffer: cpal::UnknownTypeInputBuffer::F32(buffer) } => { assert_eq!(id, input_stream_id); let mut output_fell_behind = false; for &sample in buffer.iter() { if tx.try_send(sample).is_err() { output_fell_behind = true; } } if output_fell_behind { eprintln!("output stream fell behind: try increasing latency"); } }, cpal::StreamData::Output { buffer: cpal::UnknownTypeOutputBuffer::F32(mut buffer) } => { assert_eq!(id, output_stream_id); let mut input_fell_behind = None; for sample in buffer.iter_mut() { *sample = match rx.try_recv() { Ok(s) => s, Err(err) => { input_fell_behind = Some(err); 0.0 }, }; } if let Some(err) = input_fell_behind { eprintln!("input stream fell behind: {}: try increasing latency", err); } }, _ => panic!("we're expecting f32 data"), } }); }); // Run for 3 seconds before closing. println!("Playing for 3 seconds... "); std::thread::sleep(std::time::Duration::from_secs(3)); println!("Done!"); Ok(()) } cpal-0.10.0/examples/record_wav.rs010066400037200003720000000106021350771351400152540ustar0000000000000000//! Records a WAV file (roughly 3 seconds long) using the default input device and format. //! //! The input data is recorded to "$CARGO_MANIFEST_DIR/recorded.wav". extern crate cpal; extern crate failure; extern crate hound; use cpal::traits::{DeviceTrait, EventLoopTrait, HostTrait}; fn main() -> Result<(), failure::Error> { // Use the default host for working with audio devices. let host = cpal::default_host(); // Setup the default input device and stream with the default input format. let device = host.default_input_device().expect("Failed to get default input device"); println!("Default input device: {}", device.name()?); let format = device.default_input_format().expect("Failed to get default input format"); println!("Default input format: {:?}", format); let event_loop = host.event_loop(); let stream_id = event_loop.build_input_stream(&device, &format)?; event_loop.play_stream(stream_id)?; // The WAV file we're recording to. const PATH: &'static str = concat!(env!("CARGO_MANIFEST_DIR"), "/recorded.wav"); let spec = wav_spec_from_format(&format); let writer = hound::WavWriter::create(PATH, spec)?; let writer = std::sync::Arc::new(std::sync::Mutex::new(Some(writer))); // A flag to indicate that recording is in progress. println!("Begin recording..."); let recording = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(true)); // Run the input stream on a separate thread. let writer_2 = writer.clone(); let recording_2 = recording.clone(); std::thread::spawn(move || { event_loop.run(move |id, event| { let data = match event { Ok(data) => data, Err(err) => { eprintln!("an error occurred on stream {:?}: {}", id, err); return; } }; // If we're done recording, return early. if !recording_2.load(std::sync::atomic::Ordering::Relaxed) { return; } // Otherwise write to the wav writer. match data { cpal::StreamData::Input { buffer: cpal::UnknownTypeInputBuffer::U16(buffer) } => { if let Ok(mut guard) = writer_2.try_lock() { if let Some(writer) = guard.as_mut() { for sample in buffer.iter() { let sample = cpal::Sample::to_i16(sample); writer.write_sample(sample).ok(); } } } }, cpal::StreamData::Input { buffer: cpal::UnknownTypeInputBuffer::I16(buffer) } => { if let Ok(mut guard) = writer_2.try_lock() { if let Some(writer) = guard.as_mut() { for &sample in buffer.iter() { writer.write_sample(sample).ok(); } } } }, cpal::StreamData::Input { buffer: cpal::UnknownTypeInputBuffer::F32(buffer) } => { if let Ok(mut guard) = writer_2.try_lock() { if let Some(writer) = guard.as_mut() { for &sample in buffer.iter() { writer.write_sample(sample).ok(); } } } }, _ => (), } }); }); // Let recording go for roughly three seconds. std::thread::sleep(std::time::Duration::from_secs(3)); recording.store(false, std::sync::atomic::Ordering::Relaxed); writer.lock().unwrap().take().unwrap().finalize()?; println!("Recording {} complete!", PATH); Ok(()) } fn sample_format(format: cpal::SampleFormat) -> hound::SampleFormat { match format { cpal::SampleFormat::U16 => hound::SampleFormat::Int, cpal::SampleFormat::I16 => hound::SampleFormat::Int, cpal::SampleFormat::F32 => hound::SampleFormat::Float, } } fn wav_spec_from_format(format: &cpal::Format) -> hound::WavSpec { hound::WavSpec { channels: format.channels as _, sample_rate: format.sample_rate.0 as _, bits_per_sample: (format.data_type.sample_size() * 8) as _, sample_format: sample_format(format.data_type), } } cpal-0.10.0/src/host/alsa/enumerate.rs010066400037200003720000000114621350771351400157610ustar0000000000000000use {BackendSpecificError, DevicesError}; use super::Device; use super::alsa; use super::check_errors; use std::ffi::CString; use std::mem; /// ALSA implementation for `Devices`. pub struct Devices { // we keep the original list so that we can pass it to the free function global_list: *const *const u8, // pointer to the next string ; contained within `global_list` next_str: *const *const u8, } impl Devices { pub fn new() -> Result { unsafe { // TODO: check in which situation this can fail. let card = -1; // -1 means all cards. let iface = b"pcm\0"; // Interface identification. let mut hints = mem::uninitialized(); // Array of device name hints. let res = alsa::snd_device_name_hint(card, iface.as_ptr() as *const _, &mut hints); if let Err(description) = check_errors(res) { let err = BackendSpecificError { description }; return Err(err.into()); } let hints = hints as *const *const u8; let devices = Devices { global_list: hints, next_str: hints, }; Ok(devices) } } } unsafe impl Send for Devices { } unsafe impl Sync for Devices { } impl Drop for Devices { #[inline] fn drop(&mut self) { unsafe { alsa::snd_device_name_free_hint(self.global_list as *mut _); } } } impl Iterator for Devices { type Item = Device; fn next(&mut self) -> Option { loop { unsafe { if (*self.next_str).is_null() { return None; } let name = { let n_ptr = alsa::snd_device_name_get_hint(*self.next_str as *const _, b"NAME\0".as_ptr() as *const _); if !n_ptr.is_null() { let bytes = CString::from_raw(n_ptr).into_bytes(); let string = String::from_utf8(bytes).unwrap(); Some(string) } else { None } }; let io = { let n_ptr = alsa::snd_device_name_get_hint(*self.next_str as *const _, b"IOID\0".as_ptr() as *const _); if !n_ptr.is_null() { let bytes = CString::from_raw(n_ptr).into_bytes(); let string = String::from_utf8(bytes).unwrap(); Some(string) } else { None } }; self.next_str = self.next_str.offset(1); if let Some(io) = io { if io != "Output" { continue; } } let name = match name { Some(name) => { // Ignoring the `null` device. if name == "null" { continue; } name }, _ => continue, }; // trying to open the PCM device to see if it can be opened let name_zeroed = CString::new(&name[..]).unwrap(); // See if the device has an available output stream. let mut playback_handle = mem::uninitialized(); let has_available_output = alsa::snd_pcm_open( &mut playback_handle, name_zeroed.as_ptr() as *const _, alsa::SND_PCM_STREAM_PLAYBACK, alsa::SND_PCM_NONBLOCK, ) == 0; if has_available_output { alsa::snd_pcm_close(playback_handle); } // See if the device has an available input stream. let mut capture_handle = mem::uninitialized(); let has_available_input = alsa::snd_pcm_open( &mut capture_handle, name_zeroed.as_ptr() as *const _, alsa::SND_PCM_STREAM_CAPTURE, alsa::SND_PCM_NONBLOCK, ) == 0; if has_available_input { alsa::snd_pcm_close(capture_handle); } if has_available_output || has_available_input { return Some(Device(name)); } } } } } #[inline] pub fn default_input_device() -> Option { Some(Device("default".to_owned())) } #[inline] pub fn default_output_device() -> Option { Some(Device("default".to_owned())) } cpal-0.10.0/src/host/alsa/mod.rs010066400037200003720000001314731350771351400145600ustar0000000000000000extern crate alsa_sys as alsa; extern crate libc; pub use self::enumerate::{Devices, default_input_device, default_output_device}; use ChannelCount; use BackendSpecificError; use BuildStreamError; use DefaultFormatError; use DeviceNameError; use DevicesError; use Format; use PauseStreamError; use PlayStreamError; use SampleFormat; use SampleRate; use SupportedFormatsError; use StreamData; use StreamDataResult; use StreamError; use SupportedFormat; use UnknownTypeInputBuffer; use UnknownTypeOutputBuffer; use traits::{DeviceTrait, EventLoopTrait, HostTrait, StreamIdTrait}; use std::{cmp, ffi, mem, ptr}; use std::sync::Mutex; use std::sync::mpsc::{channel, Sender, Receiver}; use std::sync::atomic::{AtomicUsize, Ordering}; use std::vec::IntoIter as VecIntoIter; pub type SupportedInputFormats = VecIntoIter; pub type SupportedOutputFormats = VecIntoIter; mod enumerate; /// The default linux and freebsd host type. #[derive(Debug)] pub struct Host; impl Host { pub fn new() -> Result { Ok(Host) } } impl HostTrait for Host { type Devices = Devices; type Device = Device; type EventLoop = EventLoop; fn is_available() -> bool { // Assume ALSA is always available on linux/freebsd. true } fn devices(&self) -> Result { Devices::new() } fn default_input_device(&self) -> Option { default_input_device() } fn default_output_device(&self) -> Option { default_output_device() } fn event_loop(&self) -> Self::EventLoop { EventLoop::new() } } impl DeviceTrait for Device { type SupportedInputFormats = SupportedInputFormats; type SupportedOutputFormats = SupportedOutputFormats; fn name(&self) -> Result { Device::name(self) } fn supported_input_formats(&self) -> Result { Device::supported_input_formats(self) } fn supported_output_formats(&self) -> Result { Device::supported_output_formats(self) } fn default_input_format(&self) -> Result { Device::default_input_format(self) } fn default_output_format(&self) -> Result { Device::default_output_format(self) } } impl EventLoopTrait for EventLoop { type Device = Device; type StreamId = StreamId; fn build_input_stream( &self, device: &Self::Device, format: &Format, ) -> Result { EventLoop::build_input_stream(self, device, format) } fn build_output_stream( &self, device: &Self::Device, format: &Format, ) -> Result { EventLoop::build_output_stream(self, device, format) } fn play_stream(&self, stream: Self::StreamId) -> Result<(), PlayStreamError> { EventLoop::play_stream(self, stream) } fn pause_stream(&self, stream: Self::StreamId) -> Result<(), PauseStreamError> { EventLoop::pause_stream(self, stream) } fn destroy_stream(&self, stream: Self::StreamId) { EventLoop::destroy_stream(self, stream) } fn run(&self, callback: F) -> ! where F: FnMut(Self::StreamId, StreamDataResult) + Send, { EventLoop::run(self, callback) } } impl StreamIdTrait for StreamId {} struct Trigger { // [read fd, write fd] fds: [libc::c_int; 2], } impl Trigger { fn new() -> Self { let mut fds = [0, 0]; match unsafe { libc::pipe(fds.as_mut_ptr()) } { 0 => Trigger { fds: fds }, _ => panic!("Could not create pipe"), } } fn read_fd(&self) -> libc::c_int { self.fds[0] } fn write_fd(&self) -> libc::c_int { self.fds[1] } fn wakeup(&self) { let buf = 1u64; let ret = unsafe { libc::write(self.write_fd(), &buf as *const u64 as *const _, 8) }; assert!(ret == 8); } fn clear_pipe(&self) { let mut out = 0u64; let ret = unsafe { libc::read(self.read_fd(), &mut out as *mut u64 as *mut _, 8) }; assert_eq!(ret, 8); } } impl Drop for Trigger { fn drop(&mut self) { unsafe { libc::close(self.fds[0]); libc::close(self.fds[1]); } } } #[derive(Clone, Debug, PartialEq, Eq)] pub struct Device(String); impl Device { #[inline] fn name(&self) -> Result { Ok(self.0.clone()) } unsafe fn supported_formats( &self, stream_t: alsa::snd_pcm_stream_t, ) -> Result, SupportedFormatsError> { let mut handle = mem::uninitialized(); let device_name = match ffi::CString::new(&self.0[..]) { Ok(name) => name, Err(err) => { let description = format!("failed to retrieve device name: {}", err); let err = BackendSpecificError { description }; return Err(err.into()); } }; match alsa::snd_pcm_open( &mut handle, device_name.as_ptr() as *const _, stream_t, alsa::SND_PCM_NONBLOCK, ) { -2 | -16 /* determined empirically */ => return Err(SupportedFormatsError::DeviceNotAvailable), -22 => return Err(SupportedFormatsError::InvalidArgument), e => if let Err(description) = check_errors(e) { let err = BackendSpecificError { description }; return Err(err.into()) } } let hw_params = HwParams::alloc(); match check_errors(alsa::snd_pcm_hw_params_any(handle, hw_params.0)) { Err(description) => { let err = BackendSpecificError { description }; return Err(err.into()); } Ok(_) => (), }; // TODO: check endianess const FORMATS: [(SampleFormat, alsa::snd_pcm_format_t); 3] = [ //SND_PCM_FORMAT_S8, //SND_PCM_FORMAT_U8, (SampleFormat::I16, alsa::SND_PCM_FORMAT_S16_LE), //SND_PCM_FORMAT_S16_BE, (SampleFormat::U16, alsa::SND_PCM_FORMAT_U16_LE), //SND_PCM_FORMAT_U16_BE, /*SND_PCM_FORMAT_S24_LE, SND_PCM_FORMAT_S24_BE, SND_PCM_FORMAT_U24_LE, SND_PCM_FORMAT_U24_BE, SND_PCM_FORMAT_S32_LE, 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, SND_PCM_FORMAT_FLOAT64_LE, SND_PCM_FORMAT_FLOAT64_BE, SND_PCM_FORMAT_IEC958_SUBFRAME_LE, SND_PCM_FORMAT_IEC958_SUBFRAME_BE, SND_PCM_FORMAT_MU_LAW, SND_PCM_FORMAT_A_LAW, SND_PCM_FORMAT_IMA_ADPCM, SND_PCM_FORMAT_MPEG, SND_PCM_FORMAT_GSM, SND_PCM_FORMAT_SPECIAL, SND_PCM_FORMAT_S24_3LE, SND_PCM_FORMAT_S24_3BE, SND_PCM_FORMAT_U24_3LE, SND_PCM_FORMAT_U24_3BE, SND_PCM_FORMAT_S20_3LE, SND_PCM_FORMAT_S20_3BE, SND_PCM_FORMAT_U20_3LE, SND_PCM_FORMAT_U20_3BE, SND_PCM_FORMAT_S18_3LE, SND_PCM_FORMAT_S18_3BE, SND_PCM_FORMAT_U18_3LE, SND_PCM_FORMAT_U18_3BE,*/, ]; let mut supported_formats = Vec::new(); for &(sample_format, alsa_format) in FORMATS.iter() { if alsa::snd_pcm_hw_params_test_format(handle, hw_params.0, alsa_format) == 0 { supported_formats.push(sample_format); } } let mut min_rate = mem::uninitialized(); if let Err(desc) = check_errors(alsa::snd_pcm_hw_params_get_rate_min( hw_params.0, &mut min_rate, ptr::null_mut(), )) { let description = format!("unable to get minimum supported rate: {}", desc); let err = BackendSpecificError { description }; return Err(err.into()); } let mut max_rate = mem::uninitialized(); if let Err(desc) = check_errors(alsa::snd_pcm_hw_params_get_rate_max( hw_params.0, &mut max_rate, ptr::null_mut(), )) { let description = format!("unable to get maximum supported rate: {}", desc); let err = BackendSpecificError { description }; return Err(err.into()); } let sample_rates = if min_rate == max_rate { vec![(min_rate, max_rate)] } else if alsa::snd_pcm_hw_params_test_rate(handle, hw_params.0, min_rate + 1, 0) == 0 { vec![(min_rate, max_rate)] } else { const RATES: [libc::c_uint; 13] = [ 5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000, 88200, 96000, 176400, 192000, ]; let mut rates = Vec::new(); for &rate in RATES.iter() { if alsa::snd_pcm_hw_params_test_rate(handle, hw_params.0, rate, 0) == 0 { rates.push((rate, rate)); } } if rates.len() == 0 { vec![(min_rate, max_rate)] } else { rates } }; let mut min_channels = mem::uninitialized(); if let Err(desc) = check_errors(alsa::snd_pcm_hw_params_get_channels_min(hw_params.0, &mut min_channels)) { let description = format!("unable to get minimum supported channel count: {}", desc); let err = BackendSpecificError { description }; return Err(err.into()); } let mut max_channels = mem::uninitialized(); if let Err(desc) = check_errors(alsa::snd_pcm_hw_params_get_channels_max(hw_params.0, &mut max_channels)) { let description = format!("unable to get maximum supported channel count: {}", desc); let err = BackendSpecificError { description }; return Err(err.into()); } let max_channels = cmp::min(max_channels, 32); // TODO: limiting to 32 channels or too much stuff is returned let supported_channels = (min_channels .. max_channels + 1) .filter_map(|num| if alsa::snd_pcm_hw_params_test_channels( handle, hw_params.0, num, ) == 0 { Some(num as ChannelCount) } else { None }) .collect::>(); let mut output = Vec::with_capacity(supported_formats.len() * supported_channels.len() * sample_rates.len()); for &data_type in supported_formats.iter() { for channels in supported_channels.iter() { for &(min_rate, max_rate) in sample_rates.iter() { output.push(SupportedFormat { channels: channels.clone(), min_sample_rate: SampleRate(min_rate as u32), max_sample_rate: SampleRate(max_rate as u32), data_type: data_type, }); } } } // TODO: RAII alsa::snd_pcm_close(handle); Ok(output.into_iter()) } fn supported_input_formats(&self) -> Result { unsafe { self.supported_formats(alsa::SND_PCM_STREAM_CAPTURE) } } fn supported_output_formats(&self) -> Result { unsafe { self.supported_formats(alsa::SND_PCM_STREAM_PLAYBACK) } } // ALSA does not offer default stream formats, so instead we compare all supported formats by // the `SupportedFormat::cmp_default_heuristics` order and select the greatest. fn default_format( &self, stream_t: alsa::snd_pcm_stream_t, ) -> Result { let mut formats: Vec<_> = unsafe { match self.supported_formats(stream_t) { Err(SupportedFormatsError::DeviceNotAvailable) => { return Err(DefaultFormatError::DeviceNotAvailable); }, Err(SupportedFormatsError::InvalidArgument) => { // this happens sometimes when querying for input and output capabilities but // the device supports only one return Err(DefaultFormatError::StreamTypeNotSupported); } Err(SupportedFormatsError::BackendSpecific { err }) => { return Err(err.into()); } Ok(fmts) => fmts.collect(), } }; formats.sort_by(|a, b| a.cmp_default_heuristics(b)); match formats.into_iter().last() { Some(f) => { let min_r = f.min_sample_rate; let max_r = f.max_sample_rate; let mut format = f.with_max_sample_rate(); const HZ_44100: SampleRate = SampleRate(44_100); if min_r <= HZ_44100 && HZ_44100 <= max_r { format.sample_rate = HZ_44100; } Ok(format) }, None => Err(DefaultFormatError::StreamTypeNotSupported) } } fn default_input_format(&self) -> Result { self.default_format(alsa::SND_PCM_STREAM_CAPTURE) } fn default_output_format(&self) -> Result { self.default_format(alsa::SND_PCM_STREAM_PLAYBACK) } } pub struct EventLoop { // Each newly-created stream gets a new ID from this counter. The counter is then incremented. next_stream_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. pending_command_trigger: Trigger, // This field is locked by the `run()` method. // The mutex also ensures that only one thread at a time has `run()` running. run_context: Mutex, // Commands processed by the `run()` method that is currently running. commands: Sender, } unsafe impl Send for EventLoop { } unsafe impl Sync for EventLoop { } enum Command { NewStream(StreamInner), PlayStream(StreamId), PauseStream(StreamId), DestroyStream(StreamId), } struct RunContext { // Descriptors to wait for. Always contains `pending_command_trigger.read_fd()` as first element. descriptors: Vec, // List of streams that are written in `descriptors`. streams: Vec, commands: Receiver, } struct StreamInner { // The id of the stream. id: StreamId, // The ALSA channel. channel: *mut alsa::snd_pcm_t, // When converting between file descriptors and `snd_pcm_t`, this is the number of // file descriptors that this `snd_pcm_t` uses. num_descriptors: usize, // Format of the samples. sample_format: SampleFormat, // Number of channels, ie. number of samples per frame. num_channels: u16, // Number of samples that can fit in the buffer. buffer_len: usize, // Minimum number of samples to put in the buffer. period_len: usize, // Whether or not the hardware supports pausing the stream. can_pause: bool, // Whether or not the sample stream is currently paused. is_paused: bool, // A file descriptor opened with `eventfd`. // It is used to wait for resume signal. resume_trigger: Trigger, // Lazily allocated buffer that is reused inside the loop. // Zero-allocate a new buffer (the fastest way to have zeroed memory) at the first time this is // used. buffer: Vec, } #[derive(Copy, Debug, Clone, PartialEq, Eq, Hash)] pub struct StreamId(usize); enum StreamType { Input, Output } impl EventLoop { #[inline] fn new() -> EventLoop { let pending_command_trigger = Trigger::new(); let mut initial_descriptors = vec![]; reset_descriptors_with_pending_command_trigger( &mut initial_descriptors, &pending_command_trigger, ); let (tx, rx) = channel(); let run_context = Mutex::new(RunContext { descriptors: initial_descriptors, streams: Vec::new(), commands: rx, }); EventLoop { next_stream_id: AtomicUsize::new(0), pending_command_trigger: pending_command_trigger, run_context, commands: tx, } } #[inline] fn run(&self, mut callback: F) -> ! where F: FnMut(StreamId, StreamDataResult) { self.run_inner(&mut callback) } fn run_inner(&self, callback: &mut dyn FnMut(StreamId, StreamDataResult)) -> ! { unsafe { let mut run_context = self.run_context.lock().unwrap(); let run_context = &mut *run_context; 'stream_loop: loop { process_commands(run_context); reset_descriptors_with_pending_command_trigger( &mut run_context.descriptors, &self.pending_command_trigger, ); append_stream_poll_descriptors(run_context); // At this point, this should include the command `pending_commands_trigger` along // with the poll descriptors for each stream. match poll_all_descriptors(&mut run_context.descriptors) { Ok(true) => (), Ok(false) => continue, Err(err) => { for stream in run_context.streams.iter() { let result = Err(err.clone().into()); callback(stream.id, result); } run_context.streams.clear(); break 'stream_loop; } } // If the `pending_command_trigger` was signaled, we need to process the comands. if run_context.descriptors[0].revents != 0 { run_context.descriptors[0].revents = 0; self.pending_command_trigger.clear_pipe(); } // The set of streams that error within the following loop and should be removed. let mut streams_to_remove: Vec<(StreamId, StreamError)> = vec![]; // Iterate over each individual stream/descriptor. let mut i_stream = 0; let mut i_descriptor = 1; while (i_descriptor as usize) < run_context.descriptors.len() { let stream = &mut run_context.streams[i_stream]; let stream_descriptor_ptr = run_context.descriptors.as_mut_ptr().offset(i_descriptor); // Only go on if this event was a pollout or pollin event. let stream_type = match check_for_pollout_or_pollin(stream, stream_descriptor_ptr) { Ok(Some(ty)) => ty, Ok(None) => { i_descriptor += stream.num_descriptors as isize; i_stream += 1; continue; }, Err(err) => { streams_to_remove.push((stream.id, err.into())); i_descriptor += stream.num_descriptors as isize; i_stream += 1; continue; } }; // Get the number of available samples for reading/writing. let available_samples = match get_available_samples(stream) { Ok(n) => n, Err(err) => { streams_to_remove.push((stream.id, err.into())); i_descriptor += stream.num_descriptors as isize; i_stream += 1; continue; } }; // Only go on if there is at least `stream.period_len` samples. if available_samples < stream.period_len { i_descriptor += stream.num_descriptors as isize; i_stream += 1; continue; } // Prepare the data buffer. let buffer_size = stream.sample_format.sample_size() * available_samples; stream.buffer.resize(buffer_size, 0u8); let available_frames = available_samples / stream.num_channels as usize; match stream_type { StreamType::Input => { let result = alsa::snd_pcm_readi( stream.channel, stream.buffer.as_mut_ptr() as *mut _, available_frames as alsa::snd_pcm_uframes_t, ); if let Err(err) = check_errors(result as _) { let description = format!("`snd_pcm_readi` failed: {}", err); let err = BackendSpecificError { description }; streams_to_remove.push((stream.id, err.into())); continue; } let input_buffer = match stream.sample_format { SampleFormat::I16 => UnknownTypeInputBuffer::I16(::InputBuffer { buffer: cast_input_buffer(&mut stream.buffer), }), SampleFormat::U16 => UnknownTypeInputBuffer::U16(::InputBuffer { buffer: cast_input_buffer(&mut stream.buffer), }), SampleFormat::F32 => UnknownTypeInputBuffer::F32(::InputBuffer { buffer: cast_input_buffer(&mut stream.buffer), }), }; let stream_data = StreamData::Input { buffer: input_buffer, }; callback(stream.id, Ok(stream_data)); }, StreamType::Output => { { // We're now sure that we're ready to write data. let output_buffer = match stream.sample_format { SampleFormat::I16 => UnknownTypeOutputBuffer::I16(::OutputBuffer { buffer: cast_output_buffer(&mut stream.buffer), }), SampleFormat::U16 => UnknownTypeOutputBuffer::U16(::OutputBuffer { buffer: cast_output_buffer(&mut stream.buffer), }), SampleFormat::F32 => UnknownTypeOutputBuffer::F32(::OutputBuffer { buffer: cast_output_buffer(&mut stream.buffer), }), }; let stream_data = StreamData::Output { buffer: output_buffer, }; callback(stream.id, Ok(stream_data)); } loop { let result = alsa::snd_pcm_writei( stream.channel, stream.buffer.as_ptr() as *const _, available_frames as alsa::snd_pcm_uframes_t, ); if result == -32 { // buffer underrun // TODO: Notify the user of this. alsa::snd_pcm_prepare(stream.channel); } else if let Err(err) = check_errors(result as _) { let description = format!("`snd_pcm_writei` failed: {}", err); let err = BackendSpecificError { description }; streams_to_remove.push((stream.id, err.into())); continue; } else if result as usize != available_frames { let description = format!( "unexpected number of frames written: expected {}, \ result {} (this should never happen)", available_frames, result, ); let err = BackendSpecificError { description }; streams_to_remove.push((stream.id, err.into())); continue; } else { break; } } }, } } // Remove any streams that have errored and notify the user. for (stream_id, err) in streams_to_remove { run_context.streams.retain(|s| s.id != stream_id); callback(stream_id, Err(err.into())); } } } panic!("`cpal::EventLoop::run` API currently disallows returning"); } fn build_input_stream( &self, device: &Device, format: &Format, ) -> Result { unsafe { let name = ffi::CString::new(device.0.clone()).expect("unable to clone device"); let mut capture_handle = mem::uninitialized(); match alsa::snd_pcm_open( &mut capture_handle, name.as_ptr(), alsa::SND_PCM_STREAM_CAPTURE, alsa::SND_PCM_NONBLOCK, ) { -16 /* determined empirically */ => return Err(BuildStreamError::DeviceNotAvailable), -22 => return Err(BuildStreamError::InvalidArgument), e => if let Err(description) = check_errors(e) { let err = BackendSpecificError { description }; return Err(err.into()); } } let hw_params = HwParams::alloc(); set_hw_params_from_format(capture_handle, &hw_params, format) .map_err(|description| BackendSpecificError { description })?; let can_pause = alsa::snd_pcm_hw_params_can_pause(hw_params.0) == 1; let (buffer_len, period_len) = set_sw_params_from_format(capture_handle, format) .map_err(|description| BackendSpecificError { description })?; if let Err(desc) = check_errors(alsa::snd_pcm_prepare(capture_handle)) { let description = format!("could not get capture handle: {}", desc); let err = BackendSpecificError { description }; return Err(err.into()); } let num_descriptors = { let num_descriptors = alsa::snd_pcm_poll_descriptors_count(capture_handle); if num_descriptors == 0 { let description = "poll descriptor count for capture stream was 0".to_string(); let err = BackendSpecificError { description }; return Err(err.into()); } num_descriptors as usize }; let new_stream_id = StreamId(self.next_stream_id.fetch_add(1, Ordering::Relaxed)); if new_stream_id.0 == usize::max_value() { return Err(BuildStreamError::StreamIdOverflow); } let stream_inner = StreamInner { id: new_stream_id.clone(), channel: capture_handle, sample_format: format.data_type, num_descriptors: num_descriptors, num_channels: format.channels as u16, buffer_len: buffer_len, period_len: period_len, can_pause: can_pause, is_paused: false, resume_trigger: Trigger::new(), buffer: vec![], }; if let Err(desc) = check_errors(alsa::snd_pcm_start(capture_handle)) { let description = format!("could not start capture stream: {}", desc); let err = BackendSpecificError { description }; return Err(err.into()); } self.push_command(Command::NewStream(stream_inner)); Ok(new_stream_id) } } fn build_output_stream( &self, device: &Device, format: &Format, ) -> Result { unsafe { let name = ffi::CString::new(device.0.clone()).expect("unable to clone device"); let mut playback_handle = mem::uninitialized(); match alsa::snd_pcm_open( &mut playback_handle, name.as_ptr(), alsa::SND_PCM_STREAM_PLAYBACK, alsa::SND_PCM_NONBLOCK, ) { -16 /* determined empirically */ => return Err(BuildStreamError::DeviceNotAvailable), -22 => return Err(BuildStreamError::InvalidArgument), e => if let Err(description) = check_errors(e) { let err = BackendSpecificError { description }; return Err(err.into()) } } let hw_params = HwParams::alloc(); set_hw_params_from_format(playback_handle, &hw_params, format) .map_err(|description| BackendSpecificError { description })?; let can_pause = alsa::snd_pcm_hw_params_can_pause(hw_params.0) == 1; let (buffer_len, period_len) = set_sw_params_from_format(playback_handle, format) .map_err(|description| BackendSpecificError { description })?; if let Err(desc) = check_errors(alsa::snd_pcm_prepare(playback_handle)) { let description = format!("could not get playback handle: {}", desc); let err = BackendSpecificError { description }; return Err(err.into()); } let num_descriptors = { let num_descriptors = alsa::snd_pcm_poll_descriptors_count(playback_handle); if num_descriptors == 0 { let description = "poll descriptor count for playback stream was 0".to_string(); let err = BackendSpecificError { description }; return Err(err.into()); } num_descriptors as usize }; let new_stream_id = StreamId(self.next_stream_id.fetch_add(1, Ordering::Relaxed)); if new_stream_id.0 == usize::max_value() { return Err(BuildStreamError::StreamIdOverflow); } let stream_inner = StreamInner { id: new_stream_id.clone(), channel: playback_handle, sample_format: format.data_type, num_descriptors: num_descriptors, num_channels: format.channels as u16, buffer_len: buffer_len, period_len: period_len, can_pause: can_pause, is_paused: false, resume_trigger: Trigger::new(), buffer: vec![], }; self.push_command(Command::NewStream(stream_inner)); Ok(new_stream_id) } } #[inline] fn push_command(&self, command: Command) { // Safe to unwrap: sender outlives receiver. self.commands.send(command).unwrap(); self.pending_command_trigger.wakeup(); } #[inline] fn destroy_stream(&self, stream_id: StreamId) { self.push_command(Command::DestroyStream(stream_id)); } #[inline] fn play_stream(&self, stream_id: StreamId) -> Result<(), PlayStreamError> { self.push_command(Command::PlayStream(stream_id)); Ok(()) } #[inline] fn pause_stream(&self, stream_id: StreamId) -> Result<(), PauseStreamError> { self.push_command(Command::PauseStream(stream_id)); Ok(()) } } // Process any pending `Command`s within the `RunContext`'s queue. fn process_commands(run_context: &mut RunContext) { for command in run_context.commands.try_iter() { match command { Command::DestroyStream(stream_id) => { run_context.streams.retain(|s| s.id != stream_id); }, Command::PlayStream(stream_id) => { if let Some(stream) = run_context.streams.iter_mut() .find(|stream| stream.can_pause && stream.id == stream_id) { unsafe { alsa::snd_pcm_pause(stream.channel, 0); } stream.is_paused = false; } }, Command::PauseStream(stream_id) => { if let Some(stream) = run_context.streams.iter_mut() .find(|stream| stream.can_pause && stream.id == stream_id) { unsafe { alsa::snd_pcm_pause(stream.channel, 1); } stream.is_paused = true; } }, Command::NewStream(stream_inner) => { run_context.streams.push(stream_inner); }, } } } // Resets the descriptors so that only `pending_command_trigger.read_fd()` is contained. fn reset_descriptors_with_pending_command_trigger( descriptors: &mut Vec, pending_command_trigger: &Trigger, ) { descriptors.clear(); descriptors.push(libc::pollfd { fd: pending_command_trigger.read_fd(), events: libc::POLLIN, revents: 0, }); } // Appends the `poll` descriptors for each stream onto the `RunContext`'s descriptor slice, ready // for a call to `libc::poll`. fn append_stream_poll_descriptors(run_context: &mut RunContext) { for stream in run_context.streams.iter() { run_context.descriptors.reserve(stream.num_descriptors); let len = run_context.descriptors.len(); let filled = unsafe { alsa::snd_pcm_poll_descriptors( stream.channel, run_context.descriptors.as_mut_ptr().offset(len as isize), stream.num_descriptors as libc::c_uint, ) }; debug_assert_eq!(filled, stream.num_descriptors as libc::c_int); unsafe { run_context.descriptors.set_len(len + stream.num_descriptors); } } } // Poll all descriptors within the given set. // // Returns `Ok(true)` if some event has occurred or `Ok(false)` if no events have // occurred. // // Returns an `Err` if `libc::poll` returns a negative value for some reason. fn poll_all_descriptors(descriptors: &mut [libc::pollfd]) -> Result { let res = unsafe { // Don't timeout, wait forever. libc::poll(descriptors.as_mut_ptr(), descriptors.len() as libc::nfds_t, -1) }; if res < 0 { let description = format!("`libc::poll()` failed: {}", res); Err(BackendSpecificError { description }) } else if res == 0 { Ok(false) } else { Ok(true) } } // Check whether the event is `POLLOUT` or `POLLIN`. // // If so, return the stream type associated with the event. // // Otherwise, returns `Ok(None)`. // // Returns an `Err` if the `snd_pcm_poll_descriptors_revents` call fails. fn check_for_pollout_or_pollin( stream: &StreamInner, stream_descriptor_ptr: *mut libc::pollfd, ) -> Result, BackendSpecificError> { let (revent, res) = unsafe { let mut revent = mem::uninitialized(); let res = alsa::snd_pcm_poll_descriptors_revents( stream.channel, stream_descriptor_ptr, stream.num_descriptors as libc::c_uint, &mut revent, ); (revent, res) }; if let Err(desc) = check_errors(res) { let description = format!("`snd_pcm_poll_descriptors_revents` failed: {}",desc); let err = BackendSpecificError { description }; return Err(err); } if revent as i16 == libc::POLLOUT { Ok(Some(StreamType::Output)) } else if revent as i16 == libc::POLLIN { Ok(Some(StreamType::Input)) } else { Ok(None) } } // Determine the number of samples that are available to read/write. fn get_available_samples(stream: &StreamInner) -> Result { // TODO: what about snd_pcm_avail_update? let available = unsafe { alsa::snd_pcm_avail(stream.channel) }; if available == -32 { // buffer underrun // TODO: Notify the user some how. Ok(stream.buffer_len) } else if let Err(desc) = check_errors(available as libc::c_int) { let description = format!("failed to get available samples: {}", desc); let err = BackendSpecificError { description }; Err(err) } else { Ok((available * stream.num_channels as alsa::snd_pcm_sframes_t) as usize) } } unsafe fn set_hw_params_from_format( pcm_handle: *mut alsa::snd_pcm_t, hw_params: &HwParams, format: &Format, ) -> Result<(), String> { if let Err(e) = check_errors(alsa::snd_pcm_hw_params_any(pcm_handle, hw_params.0)) { return Err(format!("errors on pcm handle: {}", e)); } if let Err(e) = check_errors(alsa::snd_pcm_hw_params_set_access(pcm_handle, hw_params.0, alsa::SND_PCM_ACCESS_RW_INTERLEAVED)) { return Err(format!("handle not acessible: {}", e)); } let data_type = if cfg!(target_endian = "big") { match format.data_type { SampleFormat::I16 => alsa::SND_PCM_FORMAT_S16_BE, SampleFormat::U16 => alsa::SND_PCM_FORMAT_U16_BE, SampleFormat::F32 => alsa::SND_PCM_FORMAT_FLOAT_BE, } } else { match format.data_type { SampleFormat::I16 => alsa::SND_PCM_FORMAT_S16_LE, SampleFormat::U16 => alsa::SND_PCM_FORMAT_U16_LE, SampleFormat::F32 => alsa::SND_PCM_FORMAT_FLOAT_LE, } }; if let Err(e) = check_errors(alsa::snd_pcm_hw_params_set_format(pcm_handle, hw_params.0, data_type)) { return Err(format!("format could not be set: {}", e)); } if let Err(e) = check_errors(alsa::snd_pcm_hw_params_set_rate(pcm_handle, hw_params.0, format.sample_rate.0 as libc::c_uint, 0)) { return Err(format!("sample rate could not be set: {}", e)); } if let Err(e) = check_errors(alsa::snd_pcm_hw_params_set_channels(pcm_handle, hw_params.0, format.channels as libc::c_uint)) { return Err(format!("channel count could not be set: {}", e)); } // TODO: Review this. 200ms seems arbitrary... let mut max_buffer_size = format.sample_rate.0 as alsa::snd_pcm_uframes_t / format.channels as alsa::snd_pcm_uframes_t / 5; // 200ms of buffer if let Err(e) = check_errors(alsa::snd_pcm_hw_params_set_buffer_size_max(pcm_handle, hw_params.0, &mut max_buffer_size)) { return Err(format!("max buffer size could not be set: {}", e)); } if let Err(e) = check_errors(alsa::snd_pcm_hw_params(pcm_handle, hw_params.0)) { return Err(format!("hardware params could not be set: {}", e)); } Ok(()) } unsafe fn set_sw_params_from_format( pcm_handle: *mut alsa::snd_pcm_t, format: &Format, ) -> Result<(usize, usize), String> { let mut sw_params = mem::uninitialized(); // TODO: RAII if let Err(e) = check_errors(alsa::snd_pcm_sw_params_malloc(&mut sw_params)) { return Err(format!("snd_pcm_sw_params_malloc failed: {}", e)); } if let Err(e) = check_errors(alsa::snd_pcm_sw_params_current(pcm_handle, sw_params)) { return Err(format!("snd_pcm_sw_params_current failed: {}", e)); } if let Err(e) = check_errors(alsa::snd_pcm_sw_params_set_start_threshold(pcm_handle, sw_params, 0)) { return Err(format!("snd_pcm_sw_params_set_start_threshold failed: {}", e)); } let (buffer_len, period_len) = { let mut buffer = mem::uninitialized(); let mut period = mem::uninitialized(); if let Err(e) = check_errors(alsa::snd_pcm_get_params(pcm_handle, &mut buffer, &mut period)) { return Err(format!("failed to initialize buffer: {}", e)); } if buffer == 0 { return Err(format!("initialization resulted in a null buffer")); } if let Err(e) = check_errors(alsa::snd_pcm_sw_params_set_avail_min(pcm_handle, sw_params, period)) { return Err(format!("snd_pcm_sw_params_set_avail_min failed: {}", e)); } let buffer = buffer as usize * format.channels as usize; let period = period as usize * format.channels as usize; (buffer, period) }; if let Err(e) = check_errors(alsa::snd_pcm_sw_params(pcm_handle, sw_params)) { return Err(format!("snd_pcm_sw_params failed: {}", e)); } alsa::snd_pcm_sw_params_free(sw_params); Ok((buffer_len, period_len)) } /// Wrapper around `hw_params`. struct HwParams(*mut alsa::snd_pcm_hw_params_t); impl HwParams { pub fn alloc() -> HwParams { unsafe { let mut hw_params = mem::uninitialized(); check_errors(alsa::snd_pcm_hw_params_malloc(&mut hw_params)) .expect("unable to get hardware parameters"); HwParams(hw_params) } } } impl Drop for HwParams { fn drop(&mut self) { unsafe { alsa::snd_pcm_hw_params_free(self.0); } } } impl Drop for StreamInner { #[inline] fn drop(&mut self) { unsafe { alsa::snd_pcm_close(self.channel); } } } #[inline] fn check_errors(err: libc::c_int) -> Result<(), String> { if err < 0 { unsafe { let s = ffi::CStr::from_ptr(alsa::snd_strerror(err)) .to_bytes() .to_vec(); let s = String::from_utf8(s).expect("Streaming error occured"); return Err(s); } } Ok(()) } /// Cast a byte slice into a (immutable) slice of desired type. /// Safety: it's up to the caller to ensure that the input slice has valid bit representations. unsafe fn cast_input_buffer(v: &[u8]) -> &[T] { debug_assert!(v.len() % std::mem::size_of::() == 0); std::slice::from_raw_parts(v.as_ptr() as *const T, v.len() / std::mem::size_of::()) } /// Cast a byte slice into a mutable slice of desired type. /// Safety: it's up to the caller to ensure that the input slice has valid bit representations. unsafe fn cast_output_buffer(v: &mut [u8]) -> &mut [T] { debug_assert!(v.len() % std::mem::size_of::() == 0); std::slice::from_raw_parts_mut(v.as_mut_ptr() as *mut T, v.len() / std::mem::size_of::()) } cpal-0.10.0/src/host/asio/device.rs010066400037200003720000000150741350771351400152510ustar0000000000000000use std; pub type SupportedInputFormats = std::vec::IntoIter; pub type SupportedOutputFormats = std::vec::IntoIter; use std::hash::{Hash, Hasher}; use std::sync::Arc; use BackendSpecificError; use DefaultFormatError; use DeviceNameError; use DevicesError; use Format; use SampleFormat; use SampleRate; use SupportedFormat; use SupportedFormatsError; use super::sys; /// A ASIO Device #[derive(Debug)] pub struct Device { /// The driver represented by this device. pub driver: Arc, } /// All available devices. pub struct Devices { asio: Arc, drivers: std::vec::IntoIter, } impl PartialEq for Device { fn eq(&self, other: &Self) -> bool { self.driver.name() == other.driver.name() } } impl Eq for Device {} impl Hash for Device { fn hash(&self, state: &mut H) { self.driver.name().hash(state); } } impl Device { pub fn name(&self) -> Result { Ok(self.driver.name().to_string()) } /// Gets the supported input formats. /// TODO currently only supports the default. /// Need to find all possible formats. pub fn supported_input_formats( &self, ) -> Result { // Retrieve the default format for the total supported channels and supported sample // format. let mut f = match self.default_input_format() { Err(_) => return Err(SupportedFormatsError::DeviceNotAvailable), Ok(f) => f, }; // Collect a format for every combination of supported sample rate and number of channels. let mut supported_formats = vec![]; for &rate in ::COMMON_SAMPLE_RATES { if !self.driver.can_sample_rate(rate.0.into()).ok().unwrap_or(false) { continue; } for channels in 1..f.channels + 1 { f.channels = channels; f.sample_rate = rate; supported_formats.push(SupportedFormat::from(f.clone())); } } Ok(supported_formats.into_iter()) } /// Gets the supported output formats. /// TODO currently only supports the default. /// Need to find all possible formats. pub fn supported_output_formats( &self, ) -> Result { // Retrieve the default format for the total supported channels and supported sample // format. let mut f = match self.default_output_format() { Err(_) => return Err(SupportedFormatsError::DeviceNotAvailable), Ok(f) => f, }; // Collect a format for every combination of supported sample rate and number of channels. let mut supported_formats = vec![]; for &rate in ::COMMON_SAMPLE_RATES { if !self.driver.can_sample_rate(rate.0.into()).ok().unwrap_or(false) { continue; } for channels in 1..f.channels + 1 { f.channels = channels; f.sample_rate = rate; supported_formats.push(SupportedFormat::from(f.clone())); } } Ok(supported_formats.into_iter()) } /// Returns the default input format pub fn default_input_format(&self) -> Result { let channels = self.driver.channels().map_err(default_format_err)?.ins as u16; let sample_rate = SampleRate(self.driver.sample_rate().map_err(default_format_err)? as _); // Map th ASIO sample type to a CPAL sample type let data_type = self.driver.input_data_type().map_err(default_format_err)?; let data_type = convert_data_type(&data_type) .ok_or(DefaultFormatError::StreamTypeNotSupported)?; Ok(Format { channels, sample_rate, data_type, }) } /// Returns the default output format pub fn default_output_format(&self) -> Result { let channels = self.driver.channels().map_err(default_format_err)?.outs as u16; let sample_rate = SampleRate(self.driver.sample_rate().map_err(default_format_err)? as _); let data_type = self.driver.output_data_type().map_err(default_format_err)?; let data_type = convert_data_type(&data_type) .ok_or(DefaultFormatError::StreamTypeNotSupported)?; Ok(Format { channels, sample_rate, data_type, }) } } impl Devices { pub fn new(asio: Arc) -> Result { let drivers = asio.driver_names().into_iter(); Ok(Devices { asio, drivers }) } } impl Iterator for Devices { type Item = Device; /// Load drivers and return device fn next(&mut self) -> Option { loop { match self.drivers.next() { Some(name) => match self.asio.load_driver(&name) { Ok(driver) => return Some(Device { driver: Arc::new(driver) }), Err(_) => continue, } None => return None, } } } fn size_hint(&self) -> (usize, Option) { unimplemented!() } } pub(crate) fn convert_data_type(ty: &sys::AsioSampleType) -> Option { let fmt = match *ty { sys::AsioSampleType::ASIOSTInt16MSB => SampleFormat::I16, sys::AsioSampleType::ASIOSTInt16LSB => SampleFormat::I16, sys::AsioSampleType::ASIOSTFloat32MSB => SampleFormat::F32, sys::AsioSampleType::ASIOSTFloat32LSB => SampleFormat::F32, // NOTE: While ASIO does not support these formats directly, the stream callback created by // CPAL supports converting back and forth between the following. This is because many ASIO // drivers only support `Int32` formats, while CPAL does not support this format at all. We // allow for this implicit conversion temporarily until CPAL gets support for an `I32` // format. sys::AsioSampleType::ASIOSTInt32MSB => SampleFormat::I16, sys::AsioSampleType::ASIOSTInt32LSB => SampleFormat::I16, _ => return None, }; Some(fmt) } fn default_format_err(e: sys::AsioError) -> DefaultFormatError { match e { sys::AsioError::NoDrivers | sys::AsioError::HardwareMalfunction => DefaultFormatError::DeviceNotAvailable, sys::AsioError::NoRate => DefaultFormatError::StreamTypeNotSupported, err => { let description = format!("{}", err); BackendSpecificError { description }.into() } } } cpal-0.10.0/src/host/asio/mod.rs010066400037200003720000000066621350771351400145740ustar0000000000000000extern crate asio_sys as sys; use { BuildStreamError, DefaultFormatError, DeviceNameError, DevicesError, Format, PauseStreamError, PlayStreamError, StreamDataResult, SupportedFormatsError, }; use traits::{ DeviceTrait, EventLoopTrait, HostTrait, StreamIdTrait, }; pub use self::device::{Device, Devices, SupportedInputFormats, SupportedOutputFormats}; pub use self::stream::{EventLoop, StreamId}; use std::sync::Arc; mod device; mod stream; /// The host for ASIO. #[derive(Debug)] pub struct Host { asio: Arc, } impl Host { pub fn new() -> Result { let asio = Arc::new(sys::Asio::new()); let host = Host { asio }; Ok(host) } } impl HostTrait for Host { type Devices = Devices; type Device = Device; type EventLoop = EventLoop; fn is_available() -> bool { true //unimplemented!("check how to do this using asio-sys") } fn devices(&self) -> Result { Devices::new(self.asio.clone()) } fn default_input_device(&self) -> Option { // ASIO has no concept of a default device, so just use the first. self.input_devices().ok().and_then(|mut ds| ds.next()) } fn default_output_device(&self) -> Option { // ASIO has no concept of a default device, so just use the first. self.output_devices().ok().and_then(|mut ds| ds.next()) } fn event_loop(&self) -> Self::EventLoop { EventLoop::new() } } impl DeviceTrait for Device { type SupportedInputFormats = SupportedInputFormats; type SupportedOutputFormats = SupportedOutputFormats; fn name(&self) -> Result { Device::name(self) } fn supported_input_formats(&self) -> Result { Device::supported_input_formats(self) } fn supported_output_formats(&self) -> Result { Device::supported_output_formats(self) } fn default_input_format(&self) -> Result { Device::default_input_format(self) } fn default_output_format(&self) -> Result { Device::default_output_format(self) } } impl EventLoopTrait for EventLoop { type Device = Device; type StreamId = StreamId; fn build_input_stream( &self, device: &Self::Device, format: &Format, ) -> Result { EventLoop::build_input_stream(self, device, format) } fn build_output_stream( &self, device: &Self::Device, format: &Format, ) -> Result { EventLoop::build_output_stream(self, device, format) } fn play_stream(&self, stream: Self::StreamId) -> Result<(), PlayStreamError> { EventLoop::play_stream(self, stream) } fn pause_stream(&self, stream: Self::StreamId) -> Result<(), PauseStreamError> { EventLoop::pause_stream(self, stream) } fn destroy_stream(&self, stream: Self::StreamId) { EventLoop::destroy_stream(self, stream) } fn run(&self, callback: F) -> ! where F: FnMut(Self::StreamId, StreamDataResult) + Send, { EventLoop::run(self, callback) } } impl StreamIdTrait for StreamId {} cpal-0.10.0/src/host/asio/stream.rs010066400037200003720000000750511350771351400153060ustar0000000000000000extern crate asio_sys as sys; extern crate num_traits; use self::num_traits::PrimInt; use super::Device; use std; use std::mem; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, Mutex}; use std::thread; use std::time::Duration; use BackendSpecificError; use BuildStreamError; use Format; use PauseStreamError; use PlayStreamError; use SampleFormat; use StreamData; use StreamDataResult; use UnknownTypeInputBuffer; use UnknownTypeOutputBuffer; /// Sample types whose constant silent value is known. trait Silence { const SILENCE: Self; } /// Constraints on the interleaved sample buffer format required by the CPAL API. trait InterleavedSample: Clone + Copy + Silence { fn unknown_type_input_buffer(&[Self]) -> UnknownTypeInputBuffer; fn unknown_type_output_buffer(&mut [Self]) -> UnknownTypeOutputBuffer; } /// Constraints on the ASIO sample types. trait AsioSample: Clone + Copy + Silence + std::ops::Add {} /// Controls all streams pub struct EventLoop { /// The input and output ASIO streams asio_streams: Arc>, /// List of all CPAL streams cpal_streams: Arc>>>, /// Total stream count. stream_count: AtomicUsize, /// The CPAL callback that the user gives to fill the buffers. callbacks: Arc>>, } /// Id for each stream. /// Created depending on the number they are created. /// Starting at one! not zero. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct StreamId(usize); /// CPAL stream. /// This decouples the many cpal streams /// from the single input and single output /// ASIO streams. /// Each stream can be playing or paused. struct Stream { playing: bool, // The driver associated with this stream. driver: Arc, } // Used to keep track of whether or not the current current asio stream buffer requires // being silencing before summing audio. #[derive(Default)] struct SilenceAsioBuffer { first: bool, second: bool, } impl EventLoop { pub fn new() -> EventLoop { EventLoop { asio_streams: Arc::new(Mutex::new(sys::AsioStreams { input: None, output: None, })), cpal_streams: Arc::new(Mutex::new(Vec::new())), // This is why the Id's count from one not zero // because at this point there is no streams stream_count: AtomicUsize::new(0), callbacks: Arc::new(Mutex::new(None)), } } /// Create a new CPAL Input Stream. /// /// If there is no existing ASIO Input Stream it will be created. /// /// On success, the buffer size of the stream is returned. fn get_or_create_input_stream( &self, driver: &sys::Driver, format: &Format, device: &Device, ) -> Result { match device.default_input_format() { Ok(f) => { let num_asio_channels = f.channels; check_format(driver, format, num_asio_channels) }, Err(_) => Err(BuildStreamError::FormatNotSupported), }?; let num_channels = format.channels as usize; let ref mut streams = *self.asio_streams.lock().unwrap(); // Either create a stream if thers none or had back the // size of the current one. match streams.input { Some(ref input) => Ok(input.buffer_size as usize), None => { let output = streams.output.take(); driver .prepare_input_stream(output, num_channels) .map(|new_streams| { let bs = match new_streams.input { Some(ref inp) => inp.buffer_size as usize, None => unreachable!(), }; *streams = new_streams; bs }).map_err(|ref e| { println!("Error preparing stream: {}", e); BuildStreamError::DeviceNotAvailable }) } } } /// Create a new CPAL Output Stream. /// /// If there is no existing ASIO Output Stream it will be created. /// /// On success, the buffer size of the stream is returned. fn get_or_create_output_stream( &self, driver: &sys::Driver, format: &Format, device: &Device, ) -> Result { match device.default_output_format() { Ok(f) => { let num_asio_channels = f.channels; check_format(driver, format, num_asio_channels) }, Err(_) => Err(BuildStreamError::FormatNotSupported), }?; let num_channels = format.channels as usize; let ref mut streams = *self.asio_streams.lock().unwrap(); // Either create a stream if there's none or return the size of the current one. match streams.output { Some(ref output) => Ok(output.buffer_size as usize), None => { let input = streams.input.take(); driver .prepare_output_stream(input, num_channels) .map(|new_streams| { let bs = match new_streams.output { Some(ref out) => out.buffer_size as usize, None => unreachable!(), }; *streams = new_streams; bs }).map_err(|ref e| { println!("Error preparing stream: {}", e); BuildStreamError::DeviceNotAvailable }) } } } /// Builds a new cpal input stream pub fn build_input_stream( &self, device: &Device, format: &Format, ) -> Result { let Device { driver, .. } = device; let stream_type = driver.input_data_type().map_err(build_stream_err)?; // Ensure that the desired sample type is supported. let data_type = super::device::convert_data_type(&stream_type) .ok_or(BuildStreamError::FormatNotSupported)?; if format.data_type != data_type { return Err(BuildStreamError::FormatNotSupported); } let num_channels = format.channels.clone(); let stream_buffer_size = self.get_or_create_input_stream(&driver, format, device)?; let cpal_num_samples = stream_buffer_size * num_channels as usize; let count = self.stream_count.fetch_add(1, Ordering::SeqCst); let asio_streams = self.asio_streams.clone(); let cpal_streams = self.cpal_streams.clone(); let callbacks = self.callbacks.clone(); // Create the buffer depending on the size of the data type. let stream_id = StreamId(count); let len_bytes = cpal_num_samples * data_type.sample_size(); let mut interleaved = vec![0u8; len_bytes]; // Set the input callback. // This is most performance critical part of the ASIO bindings. driver.set_callback(move |buffer_index| unsafe { // If not playing return early. // TODO: Don't assume `count` is valid - we should search for the matching `StreamId`. if let Some(s) = cpal_streams.lock().unwrap().get(count) { if let Some(s) = s { if !s.playing { return; } } } // Acquire the stream and callback. let stream_lock = asio_streams.lock().unwrap(); let ref asio_stream = match stream_lock.input { Some(ref asio_stream) => asio_stream, None => return, }; let mut callbacks = callbacks.lock().unwrap(); let callback = match callbacks.as_mut() { Some(callback) => callback, None => return, }; /// 1. Write from the ASIO buffer to the interleaved CPAL buffer. /// 2. Deliver the CPAL buffer to the user callback. unsafe fn process_input_callback( stream_id: StreamId, callback: &mut (dyn FnMut(StreamId, StreamDataResult) + Send), interleaved: &mut [u8], asio_stream: &sys::AsioStream, buffer_index: usize, from_endianness: F, to_cpal_sample: G, ) where A: AsioSample, B: InterleavedSample, F: Fn(A) -> A, G: Fn(A) -> B, { // 1. Write the ASIO channels to the CPAL buffer. let interleaved: &mut [B] = cast_slice_mut(interleaved); let n_channels = interleaved.len() / asio_stream.buffer_size as usize; for ch_ix in 0..n_channels { let asio_channel = asio_channel_slice::(asio_stream, buffer_index, ch_ix); for (frame, s_asio) in interleaved.chunks_mut(n_channels).zip(asio_channel) { frame[ch_ix] = to_cpal_sample(from_endianness(*s_asio)); } } // 2. Deliver the interleaved buffer to the callback. callback( stream_id, Ok(StreamData::Input { buffer: B::unknown_type_input_buffer(interleaved) }), ); } match (&stream_type, data_type) { (&sys::AsioSampleType::ASIOSTInt16LSB, SampleFormat::I16) => { process_input_callback::( stream_id, callback, &mut interleaved, asio_stream, buffer_index as usize, from_le, std::convert::identity::, ); } (&sys::AsioSampleType::ASIOSTInt16MSB, SampleFormat::I16) => { process_input_callback::( stream_id, callback, &mut interleaved, asio_stream, buffer_index as usize, from_be, std::convert::identity::, ); } // TODO: Handle endianness conversion for floats? We currently use the `PrimInt` // trait for the `to_le` and `to_be` methods, but this does not support floats. (&sys::AsioSampleType::ASIOSTFloat32LSB, SampleFormat::F32) | (&sys::AsioSampleType::ASIOSTFloat32MSB, SampleFormat::F32) => { process_input_callback::( stream_id, callback, &mut interleaved, asio_stream, buffer_index as usize, std::convert::identity::, std::convert::identity::, ); } // TODO: Add support for the following sample formats to CPAL and simplify the // `process_output_callback` function above by removing the unnecessary sample // conversion function. (&sys::AsioSampleType::ASIOSTInt32LSB, SampleFormat::I16) => { process_input_callback::( stream_id, callback, &mut interleaved, asio_stream, buffer_index as usize, from_le, |s| (s >> 16) as i16, ); } (&sys::AsioSampleType::ASIOSTInt32MSB, SampleFormat::I16) => { process_input_callback::( stream_id, callback, &mut interleaved, asio_stream, buffer_index as usize, from_be, |s| (s >> 16) as i16, ); } // TODO: Handle endianness conversion for floats? We currently use the `PrimInt` // trait for the `to_le` and `to_be` methods, but this does not support floats. (&sys::AsioSampleType::ASIOSTFloat64LSB, SampleFormat::F32) | (&sys::AsioSampleType::ASIOSTFloat64MSB, SampleFormat::F32) => { process_input_callback::( stream_id, callback, &mut interleaved, asio_stream, buffer_index as usize, std::convert::identity::, |s| s as f32, ); } unsupported_format_pair => { unreachable!("`build_input_stream` should have returned with unsupported \ format {:?}", unsupported_format_pair) } } }); // Create stream and set to paused self.cpal_streams .lock() .unwrap() .push(Some(Stream { driver: driver.clone(), playing: false })); Ok(StreamId(count)) } /// Create the an output cpal stream. pub fn build_output_stream( &self, device: &Device, format: &Format, ) -> Result { let Device { driver, .. } = device; let stream_type = driver.output_data_type().map_err(build_stream_err)?; // Ensure that the desired sample type is supported. let data_type = super::device::convert_data_type(&stream_type) .ok_or(BuildStreamError::FormatNotSupported)?; if format.data_type != data_type { return Err(BuildStreamError::FormatNotSupported); } let num_channels = format.channels.clone(); let stream_buffer_size = self.get_or_create_output_stream(&driver, format, device)?; let cpal_num_samples = stream_buffer_size * num_channels as usize; let count = self.stream_count.fetch_add(1, Ordering::SeqCst); let asio_streams = self.asio_streams.clone(); let cpal_streams = self.cpal_streams.clone(); let callbacks = self.callbacks.clone(); // Create buffers depending on data type. let stream_id = StreamId(count); let len_bytes = cpal_num_samples * data_type.sample_size(); let mut interleaved = vec![0u8; len_bytes]; let mut silence_asio_buffer = SilenceAsioBuffer::default(); driver.set_callback(move |buffer_index| unsafe { // If not playing, return early. // TODO: Don't assume `count` is valid - we should search for the matching `StreamId`. if let Some(s) = cpal_streams.lock().unwrap().get(count) { if let Some(s) = s { if !s.playing { return (); } } } // Acquire the stream and callback. let stream_lock = asio_streams.lock().unwrap(); let ref asio_stream = match stream_lock.output { Some(ref asio_stream) => asio_stream, None => return, }; let mut callbacks = callbacks.lock().unwrap(); let callback = callbacks.as_mut(); // Silence the ASIO buffer that is about to be used. // // This checks if any other callbacks have already silenced the buffer associated with // the current `buffer_index`. // // If not, we will silence it and set the opposite buffer half to unsilenced. let silence = match buffer_index { 0 if !silence_asio_buffer.first => { silence_asio_buffer.first = true; silence_asio_buffer.second = false; true } 0 => false, 1 if !silence_asio_buffer.second => { silence_asio_buffer.second = true; silence_asio_buffer.first = false; true } 1 => false, _ => unreachable!("ASIO uses a double-buffer so there should only be 2"), }; /// 1. Render the given callback to the given buffer of interleaved samples. /// 2. If required, silence the ASIO buffer. /// 3. Finally, write the interleaved data to the non-interleaved ASIO buffer, /// performing endianness conversions as necessary. unsafe fn process_output_callback( stream_id: StreamId, callback: Option<&mut &mut (dyn FnMut(StreamId, StreamDataResult) + Send)>, interleaved: &mut [u8], silence_asio_buffer: bool, asio_stream: &sys::AsioStream, buffer_index: usize, to_asio_sample: F, to_endianness: G, ) where A: InterleavedSample, B: AsioSample, F: Fn(A) -> B, G: Fn(B) -> B, { // 1. Render interleaved buffer from callback. let interleaved: &mut [A] = cast_slice_mut(interleaved); match callback { None => interleaved.iter_mut().for_each(|s| *s = A::SILENCE), Some(callback) => { let buffer = A::unknown_type_output_buffer(interleaved); callback(stream_id, Ok(StreamData::Output { buffer })); } } // 2. Silence ASIO channels if necessary. let n_channels = interleaved.len() / asio_stream.buffer_size as usize; if silence_asio_buffer { for ch_ix in 0..n_channels { let asio_channel = asio_channel_slice_mut::(asio_stream, buffer_index, ch_ix); asio_channel.iter_mut().for_each(|s| *s = to_endianness(B::SILENCE)); } } // 3. Write interleaved samples to ASIO channels, one channel at a time. for ch_ix in 0..n_channels { let asio_channel = asio_channel_slice_mut::(asio_stream, buffer_index, ch_ix); for (frame, s_asio) in interleaved.chunks(n_channels).zip(asio_channel) { *s_asio = *s_asio + to_endianness(to_asio_sample(frame[ch_ix])); } } } match (data_type, &stream_type) { (SampleFormat::I16, &sys::AsioSampleType::ASIOSTInt16LSB) => { process_output_callback::( stream_id, callback, &mut interleaved, silence, asio_stream, buffer_index as usize, std::convert::identity::, to_le, ); } (SampleFormat::I16, &sys::AsioSampleType::ASIOSTInt16MSB) => { process_output_callback::( stream_id, callback, &mut interleaved, silence, asio_stream, buffer_index as usize, std::convert::identity::, to_be, ); } // TODO: Handle endianness conversion for floats? We currently use the `PrimInt` // trait for the `to_le` and `to_be` methods, but this does not support floats. (SampleFormat::F32, &sys::AsioSampleType::ASIOSTFloat32LSB) | (SampleFormat::F32, &sys::AsioSampleType::ASIOSTFloat32MSB) => { process_output_callback::( stream_id, callback, &mut interleaved, silence, asio_stream, buffer_index as usize, std::convert::identity::, std::convert::identity::, ); } // TODO: Add support for the following sample formats to CPAL and simplify the // `process_output_callback` function above by removing the unnecessary sample // conversion function. (SampleFormat::I16, &sys::AsioSampleType::ASIOSTInt32LSB) => { process_output_callback::( stream_id, callback, &mut interleaved, silence, asio_stream, buffer_index as usize, |s| (s as i32) << 16, to_le, ); } (SampleFormat::I16, &sys::AsioSampleType::ASIOSTInt32MSB) => { process_output_callback::( stream_id, callback, &mut interleaved, silence, asio_stream, buffer_index as usize, |s| (s as i32) << 16, to_be, ); } // TODO: Handle endianness conversion for floats? We currently use the `PrimInt` // trait for the `to_le` and `to_be` methods, but this does not support floats. (SampleFormat::F32, &sys::AsioSampleType::ASIOSTFloat64LSB) | (SampleFormat::F32, &sys::AsioSampleType::ASIOSTFloat64MSB) => { process_output_callback::( stream_id, callback, &mut interleaved, silence, asio_stream, buffer_index as usize, |s| s as f64, std::convert::identity::, ); } unsupported_format_pair => { unreachable!("`build_output_stream` should have returned with unsupported \ format {:?}", unsupported_format_pair) } } }); // Create the stream paused self.cpal_streams .lock() .unwrap() .push(Some(Stream { driver: driver.clone(), playing: false })); // Give the ID based on the stream count Ok(StreamId(count)) } /// Play the cpal stream for the given ID. pub fn play_stream(&self, stream_id: StreamId) -> Result<(), PlayStreamError> { let mut streams = self.cpal_streams.lock().unwrap(); if let Some(s) = streams.get_mut(stream_id.0).expect("Bad play stream index") { s.playing = true; // Calling play when already playing is a no-op s.driver.start().map_err(play_stream_err)?; } Ok(()) } /// Pause the cpal stream for the given ID. /// /// Pause the ASIO streams if there are no other CPAL streams playing, as ASIO only allows /// stopping the entire driver. pub fn pause_stream(&self, stream_id: StreamId) -> Result<(), PauseStreamError> { let mut streams = self.cpal_streams.lock().unwrap(); let streams_playing = streams.iter() .filter(|s| s.as_ref().map(|s| s.playing).unwrap_or(false)) .count(); if let Some(s) = streams.get_mut(stream_id.0).expect("Bad pause stream index") { if streams_playing <= 1 { s.driver.stop().map_err(pause_stream_err)?; } s.playing = false; } Ok(()) } /// Destroy the cpal stream based on the ID. pub fn destroy_stream(&self, stream_id: StreamId) { // TODO: Should we not also remove an ASIO stream here? // Yes, and we should update the logic in the callbacks to search for the stream with // the matching ID, rather than assuming the index associated with the ID is valid. let mut streams = self.cpal_streams.lock().unwrap(); streams.get_mut(stream_id.0).take(); } /// Run the cpal callbacks pub fn run(&self, mut callback: F) -> ! where F: FnMut(StreamId, StreamDataResult) + Send, { let callback: &mut (FnMut(StreamId, StreamDataResult) + Send) = &mut callback; // Transmute needed to convince the compiler that the callback has a static lifetime *self.callbacks.lock().unwrap() = Some(unsafe { mem::transmute(callback) }); loop { // A sleep here to prevent the loop being // removed in --release thread::sleep(Duration::new(1u64, 0u32)); } } } /// Clean up if event loop is dropped. /// Currently event loop is never dropped. impl Drop for EventLoop { fn drop(&mut self) { *self.asio_streams.lock().unwrap() = sys::AsioStreams { output: None, input: None, }; } } impl Silence for i16 { const SILENCE: Self = 0; } impl Silence for i32 { const SILENCE: Self = 0; } impl Silence for f32 { const SILENCE: Self = 0.0; } impl Silence for f64 { const SILENCE: Self = 0.0; } impl InterleavedSample for i16 { fn unknown_type_input_buffer(buffer: &[Self]) -> UnknownTypeInputBuffer { UnknownTypeInputBuffer::I16(::InputBuffer { buffer }) } fn unknown_type_output_buffer(buffer: &mut [Self]) -> UnknownTypeOutputBuffer { UnknownTypeOutputBuffer::I16(::OutputBuffer { buffer }) } } impl InterleavedSample for f32 { fn unknown_type_input_buffer(buffer: &[Self]) -> UnknownTypeInputBuffer { UnknownTypeInputBuffer::F32(::InputBuffer { buffer }) } fn unknown_type_output_buffer(buffer: &mut [Self]) -> UnknownTypeOutputBuffer { UnknownTypeOutputBuffer::F32(::OutputBuffer { buffer }) } } impl AsioSample for i16 {} impl AsioSample for i32 {} impl AsioSample for f32 {} impl AsioSample for f64 {} /// Check whether or not the desired format is supported by the stream. /// /// Checks sample rate, data type and then finally the number of channels. fn check_format( driver: &sys::Driver, format: &Format, num_asio_channels: u16, ) -> Result<(), BuildStreamError> { let Format { channels, sample_rate, data_type, } = format; // Try and set the sample rate to what the user selected. let sample_rate = sample_rate.0.into(); if sample_rate != driver.sample_rate().map_err(build_stream_err)? { if driver.can_sample_rate(sample_rate).map_err(build_stream_err)? { driver .set_sample_rate(sample_rate) .map_err(build_stream_err)?; } else { return Err(BuildStreamError::FormatNotSupported); } } // unsigned formats are not supported by asio match data_type { SampleFormat::I16 | SampleFormat::F32 => (), SampleFormat::U16 => return Err(BuildStreamError::FormatNotSupported), } if *channels > num_asio_channels { return Err(BuildStreamError::FormatNotSupported); } Ok(()) } /// Cast a byte slice into a mutable slice of desired type. /// /// Safety: it's up to the caller to ensure that the input slice has valid bit representations. unsafe fn cast_slice_mut(v: &mut [u8]) -> &mut [T] { debug_assert!(v.len() % std::mem::size_of::() == 0); std::slice::from_raw_parts_mut(v.as_mut_ptr() as *mut T, v.len() / std::mem::size_of::()) } /// Helper function to convert to little endianness. fn to_le(t: T) -> T { t.to_le() } /// Helper function to convert to big endianness. fn to_be(t: T) -> T { t.to_be() } /// Helper function to convert from little endianness. fn from_le(t: T) -> T { T::from_le(t) } /// Helper function to convert from little endianness. fn from_be(t: T) -> T { T::from_be(t) } /// Shorthand for retrieving the asio buffer slice associated with a channel. /// /// Safety: it's up to the user to ensure that this function is not called multiple times for the /// same channel. unsafe fn asio_channel_slice( asio_stream: &sys::AsioStream, buffer_index: usize, channel_index: usize, ) -> &[T] { asio_channel_slice_mut(asio_stream, buffer_index, channel_index) } /// Shorthand for retrieving the asio buffer slice associated with a channel. /// /// Safety: it's up to the user to ensure that this function is not called multiple times for the /// same channel. unsafe fn asio_channel_slice_mut( asio_stream: &sys::AsioStream, buffer_index: usize, channel_index: usize, ) -> &mut [T] { let buff_ptr: *mut T = asio_stream .buffer_infos[channel_index] .buffers[buffer_index as usize] as *mut _; std::slice::from_raw_parts_mut(buff_ptr, asio_stream.buffer_size as usize) } fn build_stream_err(e: sys::AsioError) -> BuildStreamError { match e { sys::AsioError::NoDrivers | sys::AsioError::HardwareMalfunction => BuildStreamError::DeviceNotAvailable, sys::AsioError::InvalidInput | sys::AsioError::BadMode => BuildStreamError::InvalidArgument, err => { let description = format!("{}", err); BackendSpecificError { description }.into() } } } fn pause_stream_err(e: sys::AsioError) -> PauseStreamError { match e { sys::AsioError::NoDrivers | sys::AsioError::HardwareMalfunction => PauseStreamError::DeviceNotAvailable, err => { let description = format!("{}", err); BackendSpecificError { description }.into() } } } fn play_stream_err(e: sys::AsioError) -> PlayStreamError { match e { sys::AsioError::NoDrivers | sys::AsioError::HardwareMalfunction => PlayStreamError::DeviceNotAvailable, err => { let description = format!("{}", err); BackendSpecificError { description }.into() } } } cpal-0.10.0/src/host/coreaudio/enumerate.rs010066400037200003720000000104621350771351400170120ustar0000000000000000use {BackendSpecificError, DevicesError, SupportedFormat}; use std::mem; use std::ptr::null; use std::vec::IntoIter as VecIntoIter; use super::coreaudio::sys::{ AudioDeviceID, AudioObjectPropertyAddress, AudioObjectGetPropertyData, AudioObjectGetPropertyDataSize, kAudioHardwareNoError, kAudioHardwarePropertyDefaultInputDevice, kAudioHardwarePropertyDefaultOutputDevice, kAudioHardwarePropertyDevices, kAudioObjectPropertyElementMaster, kAudioObjectPropertyScopeGlobal, kAudioObjectSystemObject, OSStatus, }; use super::Device; unsafe fn audio_devices() -> Result, OSStatus> { let property_address = AudioObjectPropertyAddress { mSelector: kAudioHardwarePropertyDevices, mScope: kAudioObjectPropertyScopeGlobal, mElement: kAudioObjectPropertyElementMaster, }; macro_rules! try_status_or_return { ($status:expr) => { if $status != kAudioHardwareNoError as i32 { return Err($status); } }; } let data_size = 0u32; let status = AudioObjectGetPropertyDataSize( kAudioObjectSystemObject, &property_address as *const _, 0, null(), &data_size as *const _ as *mut _, ); try_status_or_return!(status); let device_count = data_size / mem::size_of::() as u32; let mut audio_devices = vec![]; audio_devices.reserve_exact(device_count as usize); let status = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property_address as *const _, 0, null(), &data_size as *const _ as *mut _, audio_devices.as_mut_ptr() as *mut _, ); try_status_or_return!(status); audio_devices.set_len(device_count as usize); Ok(audio_devices) } pub struct Devices(VecIntoIter); impl Devices { pub fn new() -> Result { let devices = unsafe { match audio_devices() { Ok(devices) => devices, Err(os_status) => { let description = format!("{}", os_status); let err = BackendSpecificError { description }; return Err(err.into()); } } }; Ok(Devices(devices.into_iter())) } } unsafe impl Send for Devices { } unsafe impl Sync for Devices { } impl Iterator for Devices { type Item = Device; fn next(&mut self) -> Option { self.0.next().map(|id| Device { audio_device_id: id }) } } pub fn default_input_device() -> Option { let property_address = AudioObjectPropertyAddress { mSelector: kAudioHardwarePropertyDefaultInputDevice, mScope: kAudioObjectPropertyScopeGlobal, mElement: kAudioObjectPropertyElementMaster, }; let audio_device_id: AudioDeviceID = 0; let data_size = mem::size_of::();; let status = unsafe { AudioObjectGetPropertyData( kAudioObjectSystemObject, &property_address as *const _, 0, null(), &data_size as *const _ as *mut _, &audio_device_id as *const _ as *mut _, ) }; if status != kAudioHardwareNoError as i32 { return None; } let device = Device { audio_device_id: audio_device_id, }; Some(device) } pub fn default_output_device() -> Option { let property_address = AudioObjectPropertyAddress { mSelector: kAudioHardwarePropertyDefaultOutputDevice, mScope: kAudioObjectPropertyScopeGlobal, mElement: kAudioObjectPropertyElementMaster, }; let audio_device_id: AudioDeviceID = 0; let data_size = mem::size_of::();; let status = unsafe { AudioObjectGetPropertyData( kAudioObjectSystemObject, &property_address as *const _, 0, null(), &data_size as *const _ as *mut _, &audio_device_id as *const _ as *mut _, ) }; if status != kAudioHardwareNoError as i32 { return None; } let device = Device { audio_device_id: audio_device_id, }; Some(device) } pub type SupportedInputFormats = VecIntoIter; pub type SupportedOutputFormats = VecIntoIter; cpal-0.10.0/src/host/coreaudio/mod.rs010066400037200003720000001060721350771351400156070ustar0000000000000000extern crate coreaudio; extern crate core_foundation_sys; use ChannelCount; use BackendSpecificError; use BuildStreamError; use DefaultFormatError; use DeviceNameError; use DevicesError; use Format; use PauseStreamError; use PlayStreamError; use SupportedFormatsError; use SampleFormat; use SampleRate; use StreamData; use StreamDataResult; use SupportedFormat; use UnknownTypeInputBuffer; use UnknownTypeOutputBuffer; use traits::{DeviceTrait, EventLoopTrait, HostTrait, StreamIdTrait}; use std::ffi::CStr; use std::fmt; use std::mem; use std::os::raw::c_char; use std::ptr::null; use std::sync::{Arc, Condvar, Mutex}; use std::thread; use std::time::Duration; use std::slice; use self::coreaudio::audio_unit::{AudioUnit, Scope, Element}; use self::coreaudio::audio_unit::render_callback::{self, data}; use self::coreaudio::sys::{ AudioBuffer, AudioBufferList, AudioDeviceID, AudioObjectAddPropertyListener, AudioObjectGetPropertyData, AudioObjectGetPropertyDataSize, AudioObjectID, AudioObjectPropertyAddress, AudioObjectPropertyScope, AudioObjectRemovePropertyListener, AudioObjectSetPropertyData, AudioStreamBasicDescription, AudioValueRange, kAudioDevicePropertyAvailableNominalSampleRates, kAudioDevicePropertyDeviceNameCFString, kAudioDevicePropertyNominalSampleRate, kAudioObjectPropertyScopeInput, kAudioObjectPropertyScopeGlobal, kAudioDevicePropertyScopeOutput, kAudioDevicePropertyStreamConfiguration, kAudioDevicePropertyStreamFormat, kAudioFormatFlagIsFloat, kAudioFormatFlagIsPacked, kAudioFormatLinearPCM, kAudioObjectPropertyElementMaster, kAudioObjectPropertyScopeOutput, kAudioOutputUnitProperty_CurrentDevice, kAudioOutputUnitProperty_EnableIO, kAudioUnitProperty_StreamFormat, kCFStringEncodingUTF8, OSStatus, }; use self::core_foundation_sys::string::{ CFStringRef, CFStringGetCStringPtr, }; mod enumerate; pub use self::enumerate::{Devices, SupportedInputFormats, SupportedOutputFormats, default_input_device, default_output_device}; /// Coreaudio host, the default host on macOS and iOS. #[derive(Debug)] pub struct Host; impl Host { pub fn new() -> Result { Ok(Host) } } impl HostTrait for Host { type Devices = Devices; type Device = Device; type EventLoop = EventLoop; fn is_available() -> bool { // Assume coreaudio is always available on macOS and iOS. true } fn devices(&self) -> Result { Devices::new() } fn default_input_device(&self) -> Option { default_input_device() } fn default_output_device(&self) -> Option { default_output_device() } fn event_loop(&self) -> Self::EventLoop { EventLoop::new() } } impl DeviceTrait for Device { type SupportedInputFormats = SupportedInputFormats; type SupportedOutputFormats = SupportedOutputFormats; fn name(&self) -> Result { Device::name(self) } fn supported_input_formats(&self) -> Result { Device::supported_input_formats(self) } fn supported_output_formats(&self) -> Result { Device::supported_output_formats(self) } fn default_input_format(&self) -> Result { Device::default_input_format(self) } fn default_output_format(&self) -> Result { Device::default_output_format(self) } } impl EventLoopTrait for EventLoop { type Device = Device; type StreamId = StreamId; fn build_input_stream( &self, device: &Self::Device, format: &Format, ) -> Result { EventLoop::build_input_stream(self, device, format) } fn build_output_stream( &self, device: &Self::Device, format: &Format, ) -> Result { EventLoop::build_output_stream(self, device, format) } fn play_stream(&self, stream: Self::StreamId) -> Result<(), PlayStreamError> { EventLoop::play_stream(self, stream) } fn pause_stream(&self, stream: Self::StreamId) -> Result<(), PauseStreamError> { EventLoop::pause_stream(self, stream) } fn destroy_stream(&self, stream: Self::StreamId) { EventLoop::destroy_stream(self, stream) } fn run(&self, callback: F) -> ! where F: FnMut(Self::StreamId, StreamDataResult) + Send, { EventLoop::run(self, callback) } } impl StreamIdTrait for StreamId {} #[derive(Clone, PartialEq, Eq)] pub struct Device { audio_device_id: AudioDeviceID, } impl Device { fn name(&self) -> Result { let property_address = AudioObjectPropertyAddress { mSelector: kAudioDevicePropertyDeviceNameCFString, mScope: kAudioDevicePropertyScopeOutput, mElement: kAudioObjectPropertyElementMaster, }; let device_name: CFStringRef = null(); let data_size = mem::size_of::(); let c_str = unsafe { let status = AudioObjectGetPropertyData( self.audio_device_id, &property_address as *const _, 0, null(), &data_size as *const _ as *mut _, &device_name as *const _ as *mut _, ); check_os_status(status)?; let c_string: *const c_char = CFStringGetCStringPtr(device_name, kCFStringEncodingUTF8); if c_string == null() { let description = "core foundation unexpectedly returned null string".to_string(); let err = BackendSpecificError { description }; return Err(err.into()); } CStr::from_ptr(c_string as *mut _) }; Ok(c_str.to_string_lossy().into_owned()) } // Logic re-used between `supported_input_formats` and `supported_output_formats`. fn supported_formats( &self, scope: AudioObjectPropertyScope, ) -> Result { let mut property_address = AudioObjectPropertyAddress { mSelector: kAudioDevicePropertyStreamConfiguration, mScope: scope, mElement: kAudioObjectPropertyElementMaster, }; unsafe { // Retrieve the devices audio buffer list. let data_size = 0u32; let status = AudioObjectGetPropertyDataSize( self.audio_device_id, &property_address as *const _, 0, null(), &data_size as *const _ as *mut _, ); check_os_status(status)?; let mut audio_buffer_list: Vec = vec![]; audio_buffer_list.reserve_exact(data_size as usize); let status = AudioObjectGetPropertyData( self.audio_device_id, &property_address as *const _, 0, null(), &data_size as *const _ as *mut _, audio_buffer_list.as_mut_ptr() as *mut _, ); check_os_status(status)?; let audio_buffer_list = audio_buffer_list.as_mut_ptr() as *mut AudioBufferList; // If there's no buffers, skip. if (*audio_buffer_list).mNumberBuffers == 0 { return Ok(vec![].into_iter()); } // Count the number of channels as the sum of all channels in all output buffers. let n_buffers = (*audio_buffer_list).mNumberBuffers as usize; let first: *const AudioBuffer = (*audio_buffer_list).mBuffers.as_ptr(); let buffers: &'static [AudioBuffer] = slice::from_raw_parts(first, n_buffers); let mut n_channels = 0; for buffer in buffers { n_channels += buffer.mNumberChannels as usize; } // AFAIK the sample format should always be f32 on macos and i16 on iOS? Feel free to // fix this if more pcm formats are supported. let sample_format = if cfg!(target_os = "ios") { SampleFormat::I16 } else { SampleFormat::F32 }; // Get available sample rate ranges. property_address.mSelector = kAudioDevicePropertyAvailableNominalSampleRates; let data_size = 0u32; let status = AudioObjectGetPropertyDataSize( self.audio_device_id, &property_address as *const _, 0, null(), &data_size as *const _ as *mut _, ); check_os_status(status)?; let n_ranges = data_size as usize / mem::size_of::(); let mut ranges: Vec = vec![]; ranges.reserve_exact(data_size as usize); let status = AudioObjectGetPropertyData( self.audio_device_id, &property_address as *const _, 0, null(), &data_size as *const _ as *mut _, ranges.as_mut_ptr() as *mut _, ); check_os_status(status)?; let ranges: *mut AudioValueRange = ranges.as_mut_ptr() as *mut _; let ranges: &'static [AudioValueRange] = slice::from_raw_parts(ranges, n_ranges); // Collect the supported formats for the device. let mut fmts = vec![]; for range in ranges { let fmt = SupportedFormat { channels: n_channels as ChannelCount, min_sample_rate: SampleRate(range.mMinimum as _), max_sample_rate: SampleRate(range.mMaximum as _), data_type: sample_format, }; fmts.push(fmt); } Ok(fmts.into_iter()) } } fn supported_input_formats(&self) -> Result { self.supported_formats(kAudioObjectPropertyScopeInput) } fn supported_output_formats(&self) -> Result { self.supported_formats(kAudioObjectPropertyScopeOutput) } fn default_format( &self, scope: AudioObjectPropertyScope, ) -> Result { fn default_format_error_from_os_status(status: OSStatus) -> Result<(), DefaultFormatError> { let err = match coreaudio::Error::from_os_status(status) { Err(err) => err, Ok(_) => return Ok(()), }; match err { coreaudio::Error::AudioUnit(coreaudio::error::AudioUnitError::FormatNotSupported) | coreaudio::Error::AudioCodec(_) | coreaudio::Error::AudioFormat(_) => { Err(DefaultFormatError::StreamTypeNotSupported) } coreaudio::Error::AudioUnit(coreaudio::error::AudioUnitError::NoConnection) => { Err(DefaultFormatError::DeviceNotAvailable) } err => { let description = format!("{}", std::error::Error::description(&err)); let err = BackendSpecificError { description }; Err(err.into()) } } } let property_address = AudioObjectPropertyAddress { mSelector: kAudioDevicePropertyStreamFormat, mScope: scope, mElement: kAudioObjectPropertyElementMaster, }; unsafe { let asbd: AudioStreamBasicDescription = mem::uninitialized(); let data_size = mem::size_of::() as u32; let status = AudioObjectGetPropertyData( self.audio_device_id, &property_address as *const _, 0, null(), &data_size as *const _ as *mut _, &asbd as *const _ as *mut _, ); default_format_error_from_os_status(status)?; let sample_format = { let audio_format = coreaudio::audio_unit::AudioFormat::from_format_and_flag( asbd.mFormatID, Some(asbd.mFormatFlags), ); let flags = match audio_format { Some(coreaudio::audio_unit::AudioFormat::LinearPCM(flags)) => flags, _ => return Err(DefaultFormatError::StreamTypeNotSupported), }; let maybe_sample_format = coreaudio::audio_unit::SampleFormat::from_flags_and_bytes_per_frame( flags, asbd.mBytesPerFrame, ); match maybe_sample_format { Some(coreaudio::audio_unit::SampleFormat::F32) => SampleFormat::F32, Some(coreaudio::audio_unit::SampleFormat::I16) => SampleFormat::I16, _ => return Err(DefaultFormatError::StreamTypeNotSupported), } }; let format = Format { sample_rate: SampleRate(asbd.mSampleRate as _), channels: asbd.mChannelsPerFrame as _, data_type: sample_format, }; Ok(format) } } fn default_input_format(&self) -> Result { self.default_format(kAudioObjectPropertyScopeInput) } fn default_output_format(&self) -> Result { self.default_format(kAudioObjectPropertyScopeOutput) } } impl fmt::Debug for Device { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("Device") .field("audio_device_id", &self.audio_device_id) .field("name", &self.name()) .finish() } } // The ID of a stream is its index within the `streams` array of the events loop. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct StreamId(usize); pub struct EventLoop { // This `Arc` is shared with all the callbacks of coreaudio. // // TODO: Eventually, CPAL's API should be changed to allow for submitting a unique callback per // stream to avoid streams blocking one another. user_callback: Arc>, streams: Mutex>>, loop_cond: Arc<(Mutex, Condvar)>, } enum UserCallback { // When `run` is called with a callback, that callback will be stored here. // // It is essential for the safety of the program that this callback is removed before `run` // returns (not possible with the current CPAL API). Active(&'static mut (FnMut(StreamId, StreamDataResult) + Send)), // A queue of events that have occurred but that have not yet been emitted to the user as we // don't yet have a callback to do so. Inactive, } struct StreamInner { playing: bool, audio_unit: AudioUnit, // Track the device with which the audio unit was spawned. // // We must do this so that we can avoid changing the device sample rate if there is already // a stream associated with the device. device_id: AudioDeviceID, } // TODO need stronger error identification impl From for BuildStreamError { fn from(err: coreaudio::Error) -> BuildStreamError { match err { coreaudio::Error::RenderCallbackBufferFormatDoesNotMatchAudioUnitStreamFormat | coreaudio::Error::NoKnownSubtype | coreaudio::Error::AudioUnit(coreaudio::error::AudioUnitError::FormatNotSupported) | coreaudio::Error::AudioCodec(_) | coreaudio::Error::AudioFormat(_) => BuildStreamError::FormatNotSupported, _ => BuildStreamError::DeviceNotAvailable, } } } // Create a coreaudio AudioStreamBasicDescription from a CPAL Format. fn asbd_from_format(format: &Format) -> AudioStreamBasicDescription { let n_channels = format.channels as usize; let sample_rate = format.sample_rate.0; let bytes_per_channel = format.data_type.sample_size(); let bits_per_channel = bytes_per_channel * 8; let bytes_per_frame = n_channels * bytes_per_channel; let frames_per_packet = 1; let bytes_per_packet = frames_per_packet * bytes_per_frame; let sample_format = format.data_type; let format_flags = match sample_format { SampleFormat::F32 => (kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked) as u32, _ => kAudioFormatFlagIsPacked as u32, }; let asbd = AudioStreamBasicDescription { mBitsPerChannel: bits_per_channel as _, mBytesPerFrame: bytes_per_frame as _, mChannelsPerFrame: n_channels as _, mBytesPerPacket: bytes_per_packet as _, mFramesPerPacket: frames_per_packet as _, mFormatFlags: format_flags, mFormatID: kAudioFormatLinearPCM, mSampleRate: sample_rate as _, ..Default::default() }; asbd } fn audio_unit_from_device(device: &Device, input: bool) -> Result { let mut audio_unit = { let au_type = if cfg!(target_os = "ios") { // The HalOutput 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::HalOutput }; AudioUnit::new(au_type)? }; if input { // Enable input processing. let enable_input = 1u32; audio_unit.set_property( kAudioOutputUnitProperty_EnableIO, Scope::Input, Element::Input, Some(&enable_input), )?; // Disable output processing. let disable_output = 0u32; audio_unit.set_property( kAudioOutputUnitProperty_EnableIO, Scope::Output, Element::Output, Some(&disable_output), )?; } audio_unit.set_property( kAudioOutputUnitProperty_CurrentDevice, Scope::Global, Element::Output, Some(&device.audio_device_id), )?; Ok(audio_unit) } impl EventLoop { #[inline] fn new() -> EventLoop { EventLoop { user_callback: Arc::new(Mutex::new(UserCallback::Inactive)), streams: Mutex::new(Vec::new()), loop_cond: Arc::new((Mutex::new(false), Condvar::new())), } } #[inline] fn run(&self, mut callback: F) -> ! where F: FnMut(StreamId, StreamDataResult) + Send { { let mut guard = self.user_callback.lock().unwrap(); if let UserCallback::Active(_) = *guard { panic!("`EventLoop::run` was called when the event loop was already running"); } let callback: &mut (FnMut(StreamId, StreamDataResult) + Send) = &mut callback; *guard = UserCallback::Active(unsafe { mem::transmute(callback) }); } // Wait on a condvar to notify, which should never happen. let &(ref lock, ref cvar) = &*self.loop_cond; let mut running = lock.lock().unwrap(); *running = true; while *running { running = cvar.wait(running).unwrap(); } unreachable!("current `EventLoop` API requires that `run` may not return"); // It is critical that we remove the callback before returning (currently not possible). // *self.user_callback.lock().unwrap() = UserCallback::Inactive; } fn next_stream_id(&self) -> usize { let streams_lock = self.streams.lock().unwrap(); let stream_id = streams_lock .iter() .position(|n| n.is_none()) .unwrap_or(streams_lock.len()); stream_id } // Add the stream to the list of streams within `self`. fn add_stream(&self, stream_id: usize, au: AudioUnit, device_id: AudioDeviceID) { let inner = StreamInner { playing: true, audio_unit: au, device_id: device_id, }; let mut streams_lock = self.streams.lock().unwrap(); if stream_id == streams_lock.len() { streams_lock.push(Some(inner)); } else { streams_lock[stream_id] = Some(inner); } } #[inline] fn build_input_stream( &self, device: &Device, format: &Format, ) -> Result { // The scope and element for working with a device's input stream. let scope = Scope::Output; let element = Element::Input; // Check whether or not we need to change the device sample rate to suit the one specified for the stream. unsafe { // Get the current sample rate. let mut property_address = AudioObjectPropertyAddress { mSelector: kAudioDevicePropertyNominalSampleRate, mScope: kAudioObjectPropertyScopeGlobal, mElement: kAudioObjectPropertyElementMaster, }; let sample_rate: f64 = 0.0; let data_size = mem::size_of::() as u32; let status = AudioObjectGetPropertyData( device.audio_device_id, &property_address as *const _, 0, null(), &data_size as *const _ as *mut _, &sample_rate as *const _ as *mut _, ); coreaudio::Error::from_os_status(status)?; // If the requested sample rate is different to the device sample rate, update the device. if sample_rate as u32 != format.sample_rate.0 { // In order to avoid breaking existing input streams we return an error if there is // already an active input stream for this device with the actual sample rate. for stream in &*self.streams.lock().unwrap() { if let Some(stream) = stream.as_ref() { if stream.device_id == device.audio_device_id { let description = "cannot change device sample rate for stream as an \ existing stream is already running at the current sample rate" .into(); let err = BackendSpecificError { description }; return Err(err.into()); } } } // Get available sample rate ranges. property_address.mSelector = kAudioDevicePropertyAvailableNominalSampleRates; let data_size = 0u32; let status = AudioObjectGetPropertyDataSize( device.audio_device_id, &property_address as *const _, 0, null(), &data_size as *const _ as *mut _, ); coreaudio::Error::from_os_status(status)?; let n_ranges = data_size as usize / mem::size_of::(); let mut ranges: Vec = vec![]; ranges.reserve_exact(data_size as usize); let status = AudioObjectGetPropertyData( device.audio_device_id, &property_address as *const _, 0, null(), &data_size as *const _ as *mut _, ranges.as_mut_ptr() as *mut _, ); coreaudio::Error::from_os_status(status)?; let ranges: *mut AudioValueRange = ranges.as_mut_ptr() as *mut _; let ranges: &'static [AudioValueRange] = slice::from_raw_parts(ranges, n_ranges); // Now that we have the available ranges, pick the one matching the desired rate. let sample_rate = format.sample_rate.0; let maybe_index = ranges .iter() .position(|r| r.mMinimum as u32 == sample_rate && r.mMaximum as u32 == sample_rate); let range_index = match maybe_index { None => return Err(BuildStreamError::FormatNotSupported), Some(i) => i, }; // Update the property selector to specify the nominal sample rate. property_address.mSelector = kAudioDevicePropertyNominalSampleRate; // Setting the sample rate of a device is an asynchronous process in coreaudio. // // Thus we are required to set a `listener` so that we may be notified when the // change occurs. unsafe extern "C" fn rate_listener( device_id: AudioObjectID, _n_addresses: u32, _properties: *const AudioObjectPropertyAddress, rate_ptr: *mut ::std::os::raw::c_void, ) -> OSStatus { let rate_ptr: *const f64 = rate_ptr as *const _; let data_size = mem::size_of::(); let property_address = AudioObjectPropertyAddress { mSelector: kAudioDevicePropertyNominalSampleRate, mScope: kAudioObjectPropertyScopeGlobal, mElement: kAudioObjectPropertyElementMaster, }; AudioObjectGetPropertyData( device_id, &property_address as *const _, 0, null(), &data_size as *const _ as *mut _, rate_ptr as *const _ as *mut _, ) } // Add our sample rate change listener callback. let reported_rate: f64 = 0.0; let status = AudioObjectAddPropertyListener( device.audio_device_id, &property_address as *const _, Some(rate_listener), &reported_rate as *const _ as *mut _, ); coreaudio::Error::from_os_status(status)?; // Finally, set the sample rate. let sample_rate = sample_rate as f64; let status = AudioObjectSetPropertyData( device.audio_device_id, &property_address as *const _, 0, null(), data_size, &ranges[range_index] as *const _ as *const _, ); coreaudio::Error::from_os_status(status)?; // Wait for the reported_rate to change. // // This should not take longer than a few ms, but we timeout after 1 sec just in case. let timer = ::std::time::Instant::now(); while sample_rate != reported_rate { if timer.elapsed() > ::std::time::Duration::from_secs(1) { let description = "timeout waiting for sample rate update for device".into(); let err = BackendSpecificError { description }; return Err(err.into()); } ::std::thread::sleep(::std::time::Duration::from_millis(5)); } // Remove the `rate_listener` callback. let status = AudioObjectRemovePropertyListener( device.audio_device_id, &property_address as *const _, Some(rate_listener), &reported_rate as *const _ as *mut _, ); coreaudio::Error::from_os_status(status)?; } } let mut audio_unit = audio_unit_from_device(device, true)?; // Set the stream in interleaved mode. let asbd = asbd_from_format(format); audio_unit.set_property(kAudioUnitProperty_StreamFormat, scope, element, Some(&asbd))?; // Determine the future ID of the stream. let stream_id = self.next_stream_id(); // Register the callback that is being called by coreaudio whenever it needs data to be // fed to the audio buffer. let user_callback = self.user_callback.clone(); let sample_format = format.data_type; let bytes_per_channel = format.data_type.sample_size(); type Args = render_callback::Args; audio_unit.set_input_callback(move |args: Args| unsafe { let ptr = (*args.data.data).mBuffers.as_ptr() as *const AudioBuffer; let len = (*args.data.data).mNumberBuffers as usize; let buffers: &[AudioBuffer] = slice::from_raw_parts(ptr, len); // TODO: Perhaps loop over all buffers instead? let AudioBuffer { mNumberChannels: _num_channels, mDataByteSize: data_byte_size, mData: data } = buffers[0]; let mut user_callback = user_callback.lock().unwrap(); // A small macro to simplify handling the callback for different sample types. macro_rules! try_callback { ($SampleFormat:ident, $SampleType:ty) => {{ let data_len = (data_byte_size as usize / bytes_per_channel) as usize; let data_slice = slice::from_raw_parts(data as *const $SampleType, data_len); let callback = match *user_callback { UserCallback::Active(ref mut cb) => cb, UserCallback::Inactive => return Ok(()), }; let unknown_type_buffer = UnknownTypeInputBuffer::$SampleFormat(::InputBuffer { buffer: data_slice }); let stream_data = StreamData::Input { buffer: unknown_type_buffer }; callback(StreamId(stream_id), Ok(stream_data)); }}; } match sample_format { SampleFormat::F32 => try_callback!(F32, f32), SampleFormat::I16 => try_callback!(I16, i16), SampleFormat::U16 => try_callback!(U16, u16), } Ok(()) })?; // TODO: start playing now? is that consistent with the other backends? audio_unit.start()?; // Add the stream to the list of streams within `self`. self.add_stream(stream_id, audio_unit, device.audio_device_id); Ok(StreamId(stream_id)) } #[inline] fn build_output_stream( &self, device: &Device, format: &Format, ) -> Result { let mut audio_unit = audio_unit_from_device(device, false)?; // The scope and element for working with a device's output stream. let scope = Scope::Input; let element = Element::Output; // Set the stream in interleaved mode. let asbd = asbd_from_format(format); audio_unit.set_property(kAudioUnitProperty_StreamFormat, scope, element, Some(&asbd))?; // Determine the future ID of the stream. let stream_id = self.next_stream_id(); // Register the callback that is being called by coreaudio whenever it needs data to be // fed to the audio buffer. let user_callback = self.user_callback.clone(); let sample_format = format.data_type; let bytes_per_channel = format.data_type.sample_size(); type Args = render_callback::Args; audio_unit.set_render_callback(move |args: Args| unsafe { // If `run()` is currently running, then a callback will be available from this list. // Otherwise, we just fill the buffer with zeroes and return. let AudioBuffer { mNumberChannels: _num_channels, mDataByteSize: data_byte_size, mData: data } = (*args.data.data).mBuffers[0]; let mut user_callback = user_callback.lock().unwrap(); // A small macro to simplify handling the callback for different sample types. macro_rules! try_callback { ($SampleFormat:ident, $SampleType:ty, $equilibrium:expr) => {{ let data_len = (data_byte_size as usize / bytes_per_channel) as usize; let data_slice = slice::from_raw_parts_mut(data as *mut $SampleType, data_len); let callback = match *user_callback { UserCallback::Active(ref mut cb) => cb, UserCallback::Inactive => { for sample in data_slice.iter_mut() { *sample = $equilibrium; } return Ok(()); } }; let unknown_type_buffer = UnknownTypeOutputBuffer::$SampleFormat(::OutputBuffer { buffer: data_slice }); let stream_data = StreamData::Output { buffer: unknown_type_buffer }; callback(StreamId(stream_id), Ok(stream_data)); }}; } match sample_format { SampleFormat::F32 => try_callback!(F32, f32, 0.0), SampleFormat::I16 => try_callback!(I16, i16, 0), SampleFormat::U16 => try_callback!(U16, u16, ::std::u16::MAX / 2), } Ok(()) })?; // TODO: start playing now? is that consistent with the other backends? audio_unit.start()?; // Add the stream to the list of streams within `self`. self.add_stream(stream_id, audio_unit, device.audio_device_id); Ok(StreamId(stream_id)) } fn destroy_stream(&self, stream_id: StreamId) { { let mut streams = self.streams.lock().unwrap(); streams[stream_id.0] = None; } } fn play_stream(&self, stream_id: StreamId) -> Result<(), PlayStreamError> { let mut streams = self.streams.lock().unwrap(); let stream = streams[stream_id.0].as_mut().unwrap(); if !stream.playing { if let Err(e) = stream.audio_unit.start() { let description = format!("{}", std::error::Error::description(&e)); let err = BackendSpecificError { description }; return Err(err.into()); } stream.playing = true; } Ok(()) } fn pause_stream(&self, stream_id: StreamId) -> Result<(), PauseStreamError> { let mut streams = self.streams.lock().unwrap(); let stream = streams[stream_id.0].as_mut().unwrap(); if stream.playing { if let Err(e) = stream.audio_unit.stop() { let description = format!("{}", std::error::Error::description(&e)); let err = BackendSpecificError { description }; return Err(err.into()); } stream.playing = false; } Ok(()) } } fn check_os_status(os_status: OSStatus) -> Result<(), BackendSpecificError> { match coreaudio::Error::from_os_status(os_status) { Ok(()) => Ok(()), Err(err) => { let description = std::error::Error::description(&err).to_string(); Err(BackendSpecificError { description }) } } } cpal-0.10.0/src/host/emscripten/mod.rs010066400037200003720000000273751350771351400160160ustar0000000000000000use std::mem; use std::os::raw::c_void; use std::slice::from_raw_parts; use std::sync::Mutex; use stdweb; use stdweb::Reference; use stdweb::unstable::TryInto; use stdweb::web::TypedArray; use stdweb::web::set_timeout; use BuildStreamError; use DefaultFormatError; use DeviceNameError; use DevicesError; use Format; use PauseStreamError; use PlayStreamError; use SupportedFormatsError; use StreamData; use StreamDataResult; use SupportedFormat; use UnknownTypeOutputBuffer; use traits::{DeviceTrait, EventLoopTrait, HostTrait, StreamIdTrait}; /// The default emscripten host type. #[derive(Debug)] pub struct Host; impl Host { pub fn new() -> Result { Ok(Host) } } impl HostTrait for Host { type Devices = Devices; type Device = Device; type EventLoop = EventLoop; fn is_available() -> bool { // Assume this host is always available on emscripten. true } fn devices(&self) -> Result { Devices::new() } fn default_input_device(&self) -> Option { default_input_device() } fn default_output_device(&self) -> Option { default_output_device() } fn event_loop(&self) -> Self::EventLoop { EventLoop::new() } } impl DeviceTrait for Device { type SupportedInputFormats = SupportedInputFormats; type SupportedOutputFormats = SupportedOutputFormats; fn name(&self) -> Result { Device::name(self) } fn supported_input_formats(&self) -> Result { Device::supported_input_formats(self) } fn supported_output_formats(&self) -> Result { Device::supported_output_formats(self) } fn default_input_format(&self) -> Result { Device::default_input_format(self) } fn default_output_format(&self) -> Result { Device::default_output_format(self) } } impl EventLoopTrait for EventLoop { type Device = Device; type StreamId = StreamId; fn build_input_stream( &self, device: &Self::Device, format: &Format, ) -> Result { EventLoop::build_input_stream(self, device, format) } fn build_output_stream( &self, device: &Self::Device, format: &Format, ) -> Result { EventLoop::build_output_stream(self, device, format) } fn play_stream(&self, stream: Self::StreamId) -> Result<(), PlayStreamError> { EventLoop::play_stream(self, stream) } fn pause_stream(&self, stream: Self::StreamId) -> Result<(), PauseStreamError> { EventLoop::pause_stream(self, stream) } fn destroy_stream(&self, stream: Self::StreamId) { EventLoop::destroy_stream(self, stream) } fn run(&self, callback: F) -> ! where F: FnMut(Self::StreamId, StreamDataResult) + Send, { EventLoop::run(self, callback) } } impl StreamIdTrait for StreamId {} // The emscripten backend works by having a global variable named `_cpal_audio_contexts`, which // is an array of `AudioContext` objects. A stream ID corresponds to an entry in this array. // // Creating a stream creates a new `AudioContext`. Destroying a stream destroys it. // TODO: handle latency better ; right now we just use setInterval with the amount of sound data // that is in each buffer ; this is obviously bad, and also the schedule is too tight and there may // be underflows pub struct EventLoop { streams: Mutex>>, } impl EventLoop { #[inline] pub fn new() -> EventLoop { stdweb::initialize(); EventLoop { streams: Mutex::new(Vec::new()), } } #[inline] fn run(&self, callback: F) -> ! where F: FnMut(StreamId, StreamDataResult), { // The `run` function uses `set_timeout` to invoke a Rust callback repeatidely. The job // of this callback is to fill the content of the audio buffers. // The first argument of the callback function (a `void*`) is a casted pointer to `self` // and to the `callback` parameter that was passed to `run`. fn callback_fn(user_data_ptr: *mut c_void) where F: FnMut(StreamId, StreamDataResult) { unsafe { let user_data_ptr2 = user_data_ptr as *mut (&EventLoop, F); let user_data = &mut *user_data_ptr2; let user_cb = &mut user_data.1; let streams = user_data.0.streams.lock().unwrap().clone(); for (stream_id, stream) in streams.iter().enumerate() { let stream = match stream.as_ref() { Some(v) => v, None => continue, }; let mut temporary_buffer = vec![0.0; 44100 * 2 / 3]; { let buffer = UnknownTypeOutputBuffer::F32(::OutputBuffer { buffer: &mut temporary_buffer }); let data = StreamData::Output { buffer: buffer }; user_cb(StreamId(stream_id), Ok(data)); // TODO: directly use a TypedArray once this is supported by stdweb } let typed_array = { let f32_slice = temporary_buffer.as_slice(); let u8_slice: &[u8] = from_raw_parts( f32_slice.as_ptr() as *const _, f32_slice.len() * mem::size_of::(), ); let typed_array: TypedArray = u8_slice.into(); typed_array }; let num_channels = 2u32; // TODO: correct value debug_assert_eq!(temporary_buffer.len() % num_channels as usize, 0); js!( var src_buffer = new Float32Array(@{typed_array}.buffer); var context = @{stream}; var buf_len = @{temporary_buffer.len() as u32}; var num_channels = @{num_channels}; var buffer = context.createBuffer(num_channels, buf_len / num_channels, 44100); for (var channel = 0; channel < num_channels; ++channel) { var buffer_content = buffer.getChannelData(channel); for (var i = 0; i < buf_len / num_channels; ++i) { buffer_content[i] = src_buffer[i * num_channels + channel]; } } var node = context.createBufferSource(); node.buffer = buffer; node.connect(context.destination); node.start(); ); } set_timeout(|| callback_fn::(user_data_ptr), 330); } } let mut user_data = (self, callback); let user_data_ptr = &mut user_data as *mut (_, _); set_timeout(|| callback_fn::(user_data_ptr as *mut _), 10); stdweb::event_loop(); } #[inline] fn build_input_stream(&self, _: &Device, _format: &Format) -> Result { unimplemented!(); } #[inline] fn build_output_stream(&self, _: &Device, _format: &Format) -> Result { let stream = js!(return new AudioContext()).into_reference().unwrap(); let mut streams = self.streams.lock().unwrap(); let stream_id = if let Some(pos) = streams.iter().position(|v| v.is_none()) { streams[pos] = Some(stream); pos } else { let l = streams.len(); streams.push(Some(stream)); l }; Ok(StreamId(stream_id)) } #[inline] fn destroy_stream(&self, stream_id: StreamId) { self.streams.lock().unwrap()[stream_id.0] = None; } #[inline] fn play_stream(&self, stream_id: StreamId) -> Result<(), PlayStreamError> { let streams = self.streams.lock().unwrap(); let stream = streams .get(stream_id.0) .and_then(|v| v.as_ref()) .expect("invalid stream ID"); js!(@{stream}.resume()); Ok(()) } #[inline] fn pause_stream(&self, stream_id: StreamId) -> Result<(), PauseStreamError> { let streams = self.streams.lock().unwrap(); let stream = streams .get(stream_id.0) .and_then(|v| v.as_ref()) .expect("invalid stream ID"); js!(@{stream}.suspend()); Ok(()) } } // Index within the `streams` array of the events loop. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct StreamId(usize); // Detects whether the `AudioContext` global variable is available. fn is_webaudio_available() -> bool { stdweb::initialize(); js!(if (!AudioContext) { return false; } else { return true; }).try_into() .unwrap() } // Content is false if the iterator is empty. pub struct Devices(bool); impl Devices { fn new() -> Result { Ok(Self::default()) } } impl Default for Devices { fn default() -> Devices { // We produce an empty iterator if the WebAudio API isn't available. Devices(is_webaudio_available()) } } impl Iterator for Devices { type Item = Device; #[inline] fn next(&mut self) -> Option { if self.0 { self.0 = false; Some(Device) } else { None } } } #[inline] fn default_input_device() -> Option { unimplemented!(); } #[inline] fn default_output_device() -> Option { if is_webaudio_available() { Some(Device) } else { None } } #[derive(Clone, Debug, PartialEq, Eq)] pub struct Device; impl Device { #[inline] fn name(&self) -> Result { Ok("Default Device".to_owned()) } #[inline] fn supported_input_formats(&self) -> Result { unimplemented!(); } #[inline] fn supported_output_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 // According to https://developer.mozilla.org/en-US/docs/Web/API/BaseAudioContext/createBuffer // browsers must support 1 to 32 channels at leats and 8,000 Hz to 96,000 Hz. Ok( vec![ SupportedFormat { channels: 2, min_sample_rate: ::SampleRate(44100), max_sample_rate: ::SampleRate(44100), data_type: ::SampleFormat::F32, }, ].into_iter(), ) } fn default_input_format(&self) -> Result { unimplemented!(); } fn default_output_format(&self) -> Result { // TODO: because it is hard coded, see supported_output_formats. Ok( Format { channels: 2, sample_rate: ::SampleRate(44100), data_type: ::SampleFormat::F32, }, ) } } pub type SupportedInputFormats = ::std::vec::IntoIter; pub type SupportedOutputFormats = ::std::vec::IntoIter; cpal-0.10.0/src/host/mod.rs010066400037200003720000000005171350771351400136320ustar0000000000000000#[cfg(any(target_os = "linux", target_os = "freebsd"))] pub(crate) mod alsa; #[cfg(all(windows, feature = "asio"))] pub(crate) mod asio; #[cfg(any(target_os = "macos", target_os = "ios"))] pub(crate) mod coreaudio; #[cfg(target_os = "emscripten")] pub(crate) mod emscripten; pub(crate) mod null; #[cfg(windows)] pub(crate) mod wasapi; cpal-0.10.0/src/host/null/mod.rs010066400037200003720000000066741350771351400146160ustar0000000000000000#![allow(dead_code)] use BuildStreamError; use DefaultFormatError; use DevicesError; use DeviceNameError; use Format; use PauseStreamError; use PlayStreamError; use StreamDataResult; use SupportedFormatsError; use SupportedFormat; use traits::{DeviceTrait, EventLoopTrait, HostTrait, StreamIdTrait}; #[derive(Default)] pub struct Devices; #[derive(Clone, Debug, PartialEq, Eq)] pub struct Device; pub struct EventLoop; pub struct Host; #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct StreamId; pub struct SupportedInputFormats; pub struct SupportedOutputFormats; impl Host { pub fn new() -> Result { Ok(Host) } } impl Devices { pub fn new() -> Result { Ok(Devices) } } impl EventLoop { pub fn new() -> EventLoop { EventLoop } } impl DeviceTrait for Device { type SupportedInputFormats = SupportedInputFormats; type SupportedOutputFormats = SupportedOutputFormats; #[inline] fn name(&self) -> Result { Ok("null".to_owned()) } #[inline] fn supported_input_formats(&self) -> Result { unimplemented!() } #[inline] fn supported_output_formats(&self) -> Result { unimplemented!() } #[inline] fn default_input_format(&self) -> Result { unimplemented!() } #[inline] fn default_output_format(&self) -> Result { unimplemented!() } } impl EventLoopTrait for EventLoop { type Device = Device; type StreamId = StreamId; #[inline] fn run(&self, _callback: F) -> ! where F: FnMut(StreamId, StreamDataResult) { loop { /* TODO: don't spin */ } } #[inline] fn build_input_stream(&self, _: &Device, _: &Format) -> Result { Err(BuildStreamError::DeviceNotAvailable) } #[inline] fn build_output_stream(&self, _: &Device, _: &Format) -> Result { Err(BuildStreamError::DeviceNotAvailable) } #[inline] fn destroy_stream(&self, _: StreamId) { unimplemented!() } #[inline] fn play_stream(&self, _: StreamId) -> Result<(), PlayStreamError> { panic!() } #[inline] fn pause_stream(&self, _: StreamId) -> Result<(), PauseStreamError> { panic!() } } impl HostTrait for Host { type Device = Device; type Devices = Devices; type EventLoop = EventLoop; fn is_available() -> bool { false } fn devices(&self) -> Result { Devices::new() } fn default_input_device(&self) -> Option { None } fn default_output_device(&self) -> Option { None } fn event_loop(&self) -> Self::EventLoop { EventLoop::new() } } impl StreamIdTrait for StreamId {} impl Iterator for Devices { type Item = Device; #[inline] fn next(&mut self) -> Option { None } } impl Iterator for SupportedInputFormats { type Item = SupportedFormat; #[inline] fn next(&mut self) -> Option { None } } impl Iterator for SupportedOutputFormats { type Item = SupportedFormat; #[inline] fn next(&mut self) -> Option { None } } cpal-0.10.0/src/host/wasapi/com.rs010066400037200003720000000020411350771351400151070ustar0000000000000000//! Handles COM initialization and cleanup. use super::check_result; use std::ptr; use super::winapi::um::objbase::{COINIT_MULTITHREADED}; use super::winapi::um::combaseapi::{CoInitializeEx, CoUninitialize}; thread_local!(static COM_INITIALIZED: ComInitialized = { unsafe { // this call can fail if another library initialized COM in single-threaded mode // handling this situation properly would make the API more annoying, so we just don't care check_result(CoInitializeEx(ptr::null_mut(), COINIT_MULTITHREADED)).unwrap(); ComInitialized(ptr::null_mut()) } }); /// RAII object that guards the fact that COM is initialized. /// // We store a raw pointer because it's the only way at the moment to remove `Send`/`Sync` from the // object. struct ComInitialized(*mut ()); impl Drop for ComInitialized { #[inline] fn drop(&mut self) { unsafe { CoUninitialize() }; } } /// Ensures that COM is initialized in this thread. #[inline] pub fn com_initialized() { COM_INITIALIZED.with(|_| {}); } cpal-0.10.0/src/host/wasapi/device.rs010066400037200003720000000670011350771351400155770ustar0000000000000000use std; use std::ffi::OsString; use std::fmt; use std::io::Error as IoError; use std::mem; use std::ops::{Deref, DerefMut}; use std::os::windows::ffi::OsStringExt; use std::ptr; use std::slice; use std::sync::{Arc, Mutex, MutexGuard}; use BackendSpecificError; use DefaultFormatError; use DeviceNameError; use DevicesError; use Format; use SupportedFormatsError; use SampleFormat; use SampleRate; use SupportedFormat; use COMMON_SAMPLE_RATES; use super::check_result; use super::check_result_backend_specific; use super::com; use super::winapi::Interface; use super::winapi::ctypes::c_void; use super::winapi::shared::devpkey; use super::winapi::shared::ksmedia; use super::winapi::shared::guiddef::{ GUID, }; use super::winapi::shared::winerror; use super::winapi::shared::minwindef::{ DWORD, }; use super::winapi::shared::mmreg; use super::winapi::shared::wtypes; // https://msdn.microsoft.com/en-us/library/cc230355.aspx use super::winapi::um::winnt::LPWSTR; use super::winapi::um::winnt::WCHAR; use super::winapi::um::coml2api; use super::winapi::um::audioclient::{ IAudioClient, IID_IAudioClient, AUDCLNT_E_DEVICE_INVALIDATED, }; use super::winapi::um::audiosessiontypes::{ AUDCLNT_SHAREMODE_SHARED, }; use super::winapi::um::combaseapi::{ CoCreateInstance, CoTaskMemFree, CLSCTX_ALL, PropVariantClear, }; use super::winapi::um::mmdeviceapi::{ eAll, eCapture, eConsole, eRender, CLSID_MMDeviceEnumerator, DEVICE_STATE_ACTIVE, EDataFlow, IMMDevice, IMMDeviceCollection, IMMDeviceEnumerator, IMMEndpoint, }; pub type SupportedInputFormats = std::vec::IntoIter; pub type SupportedOutputFormats = std::vec::IntoIter; /// Wrapper because of that stupid decision to remove `Send` and `Sync` from raw pointers. #[derive(Copy, Clone)] struct IAudioClientWrapper(*mut IAudioClient); unsafe impl Send for IAudioClientWrapper { } unsafe impl Sync for IAudioClientWrapper { } /// An opaque type that identifies an end point. pub struct Device { device: *mut 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>>, // TODO: add NonZero around the ptr } struct Endpoint { endpoint: *mut IMMEndpoint, } enum WaveFormat { Ex(mmreg::WAVEFORMATEX), Extensible(mmreg::WAVEFORMATEXTENSIBLE), } // Use RAII to make sure CoTaskMemFree is called when we are responsible for freeing. struct WaveFormatExPtr(*mut mmreg::WAVEFORMATEX); impl Drop for WaveFormatExPtr { fn drop(&mut self) { unsafe { CoTaskMemFree(self.0 as *mut _); } } } impl WaveFormat { // Given a pointer to some format, returns a valid copy of the format. pub fn copy_from_waveformatex_ptr(ptr: *const mmreg::WAVEFORMATEX) -> Option { unsafe { match (*ptr).wFormatTag { mmreg::WAVE_FORMAT_PCM | mmreg::WAVE_FORMAT_IEEE_FLOAT => { Some(WaveFormat::Ex(*ptr)) }, mmreg::WAVE_FORMAT_EXTENSIBLE => { let extensible_ptr = ptr as *const mmreg::WAVEFORMATEXTENSIBLE; Some(WaveFormat::Extensible(*extensible_ptr)) }, _ => None, } } } // Get the pointer to the WAVEFORMATEX struct. pub fn as_ptr(&self) -> *const mmreg::WAVEFORMATEX { self.deref() as *const _ } } impl Deref for WaveFormat { type Target = mmreg::WAVEFORMATEX; fn deref(&self) -> &Self::Target { match *self { WaveFormat::Ex(ref f) => f, WaveFormat::Extensible(ref f) => &f.Format, } } } impl DerefMut for WaveFormat { fn deref_mut(&mut self) -> &mut Self::Target { match *self { WaveFormat::Ex(ref mut f) => f, WaveFormat::Extensible(ref mut f) => &mut f.Format, } } } unsafe fn immendpoint_from_immdevice(device: *const IMMDevice) -> *mut IMMEndpoint { let mut endpoint: *mut IMMEndpoint = mem::uninitialized(); check_result((*device).QueryInterface(&IMMEndpoint::uuidof(), &mut endpoint as *mut _ as *mut _)) .expect("could not query IMMDevice interface for IMMEndpoint"); endpoint } unsafe fn data_flow_from_immendpoint(endpoint: *const IMMEndpoint) -> EDataFlow { let mut data_flow = mem::uninitialized(); check_result((*endpoint).GetDataFlow(&mut data_flow)) .expect("could not get endpoint data_flow"); data_flow } // Given the audio client and format, returns whether or not the format is supported. pub unsafe fn is_format_supported( client: *const IAudioClient, waveformatex_ptr: *const mmreg::WAVEFORMATEX, ) -> Result { /* // `IsFormatSupported` checks whether the format is supported and fills // a `WAVEFORMATEX` let mut dummy_fmt_ptr: *mut mmreg::WAVEFORMATEX = mem::uninitialized(); let hresult = (*audio_client) .IsFormatSupported(share_mode, &format_attempt.Format, &mut dummy_fmt_ptr); // we free that `WAVEFORMATEX` immediately after because we don't need it if !dummy_fmt_ptr.is_null() { CoTaskMemFree(dummy_fmt_ptr as *mut _); } // `IsFormatSupported` can return `S_FALSE` (which means that a compatible format // has been found) but we also treat this as an error match (hresult, check_result(hresult)) { (_, Err(ref e)) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => { (*audio_client).Release(); return Err(BuildStreamError::DeviceNotAvailable); }, (_, Err(e)) => { (*audio_client).Release(); panic!("{:?}", e); }, (winerror::S_FALSE, _) => { (*audio_client).Release(); return Err(BuildStreamError::FormatNotSupported); }, (_, Ok(())) => (), }; */ // Check if the given format is supported. let is_supported = |waveformatex_ptr, mut closest_waveformatex_ptr| { let result = (*client).IsFormatSupported( AUDCLNT_SHAREMODE_SHARED, waveformatex_ptr, &mut closest_waveformatex_ptr, ); // `IsFormatSupported` can return `S_FALSE` (which means that a compatible format // has been found, but not an exact match) so we also treat this as unsupported. match (result, check_result(result)) { (_, Err(ref e)) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => { return Err(SupportedFormatsError::DeviceNotAvailable); }, (_, Err(_)) => { Ok(false) }, (winerror::S_FALSE, _) => { Ok(false) }, (_, Ok(())) => { Ok(true) }, } }; // First we want to retrieve a pointer to the `WAVEFORMATEX`. // Although `GetMixFormat` writes the format to a given `WAVEFORMATEX` pointer, // the pointer itself may actually point to a `WAVEFORMATEXTENSIBLE` structure. // We check the wFormatTag to determine this and get a pointer to the correct type. match (*waveformatex_ptr).wFormatTag { mmreg::WAVE_FORMAT_PCM | mmreg::WAVE_FORMAT_IEEE_FLOAT => { let mut closest_waveformatex = *waveformatex_ptr; let closest_waveformatex_ptr = &mut closest_waveformatex as *mut _; is_supported(waveformatex_ptr, closest_waveformatex_ptr) }, mmreg::WAVE_FORMAT_EXTENSIBLE => { let waveformatextensible_ptr = waveformatex_ptr as *const mmreg::WAVEFORMATEXTENSIBLE; let mut closest_waveformatextensible = *waveformatextensible_ptr; let closest_waveformatextensible_ptr = &mut closest_waveformatextensible as *mut _; let closest_waveformatex_ptr = closest_waveformatextensible_ptr as *mut mmreg::WAVEFORMATEX; is_supported(waveformatex_ptr, closest_waveformatex_ptr) }, _ => Ok(false), } } // Get a cpal Format from a WAVEFORMATEX. unsafe fn format_from_waveformatex_ptr( waveformatex_ptr: *const mmreg::WAVEFORMATEX, ) -> Option { fn cmp_guid(a: &GUID, b: &GUID) -> bool { a.Data1 == b.Data1 && a.Data2 == b.Data2 && a.Data3 == b.Data3 && a.Data4 == b.Data4 } let data_type = match ((*waveformatex_ptr).wBitsPerSample, (*waveformatex_ptr).wFormatTag) { (16, mmreg::WAVE_FORMAT_PCM) => SampleFormat::I16, (32, mmreg::WAVE_FORMAT_IEEE_FLOAT) => SampleFormat::F32, (n_bits, mmreg::WAVE_FORMAT_EXTENSIBLE) => { let waveformatextensible_ptr = waveformatex_ptr as *const mmreg::WAVEFORMATEXTENSIBLE; let sub = (*waveformatextensible_ptr).SubFormat; if n_bits == 16 && cmp_guid(&sub, &ksmedia::KSDATAFORMAT_SUBTYPE_PCM) { SampleFormat::I16 } else if n_bits == 32 && cmp_guid(&sub, &ksmedia::KSDATAFORMAT_SUBTYPE_IEEE_FLOAT) { SampleFormat::F32 } else { return None; } }, // Unknown data format returned by GetMixFormat. _ => return None, }; let format = Format { channels: (*waveformatex_ptr).nChannels as _, sample_rate: SampleRate((*waveformatex_ptr).nSamplesPerSec), data_type: data_type, }; Some(format) } unsafe impl Send for Device { } unsafe impl Sync for Device { } impl Device { pub fn name(&self) -> Result { unsafe { // Open the device's property store. let mut property_store = ptr::null_mut(); (*self.device).OpenPropertyStore(coml2api::STGM_READ, &mut property_store); // Get the endpoint's friendly-name property. let mut property_value = mem::zeroed(); if let Err(err) = check_result( (*property_store).GetValue( &devpkey::DEVPKEY_Device_FriendlyName as *const _ as *const _, &mut property_value ) ) { let description = format!("failed to retrieve name from property store: {}", err); let err = BackendSpecificError { description }; return Err(err.into()); } // Read the friendly-name from the union data field, expecting a *const u16. if property_value.vt != wtypes::VT_LPWSTR as _ { let description = format!("property store produced invalid data: {:?}", property_value.vt); let err = BackendSpecificError { description }; return Err(err.into()); } let ptr_usize: usize = *(&property_value.data as *const _ as *const usize); let ptr_utf16 = ptr_usize as *const u16; // Find the length of the friendly name. let mut len = 0; while *ptr_utf16.offset(len) != 0 { len += 1; } // Create the utf16 slice and covert it into a string. let name_slice = slice::from_raw_parts(ptr_utf16, len as usize); let name_os_string: OsString = OsStringExt::from_wide(name_slice); let name_string = match name_os_string.into_string() { Ok(string) => string, Err(os_string) => os_string.to_string_lossy().into(), }; // Clean up the property. PropVariantClear(&mut property_value); Ok(name_string) } } #[inline] fn from_immdevice(device: *mut IMMDevice) -> Self { Device { 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) -> Result>, IoError> { let mut lock = self.future_audio_client.lock().unwrap(); if lock.is_some() { return Ok(lock); } let audio_client: *mut IAudioClient = unsafe { let mut audio_client = mem::uninitialized(); let hresult = (*self.device).Activate(&IID_IAudioClient, 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)?; assert!(!audio_client.is_null()); audio_client as *mut _ }; *lock = Some(IAudioClientWrapper(audio_client)); Ok(lock) } /// Returns an uninitialized `IAudioClient`. #[inline] pub(crate) fn build_audioclient(&self) -> Result<*mut IAudioClient, IoError> { let mut lock = self.ensure_future_audio_client()?; let client = lock.unwrap().0; *lock = None; Ok(client) } // There is no way to query the list of all formats that are supported by the // audio processor, so instead we just trial some commonly supported formats. // // Common formats are trialed by first getting the default format (returned via // `GetMixFormat`) and then mutating that format with common sample rates and // querying them via `IsFormatSupported`. // // When calling `IsFormatSupported` with the shared-mode audio engine, only the default // number of channels seems to be supported. Any more or less returns an invalid // parameter error. Thus we just assume that the default number of channels is the only // number supported. fn supported_formats(&self) -> Result { // initializing COM because we call `CoTaskMemFree` to release the format. com::com_initialized(); // Retrieve the `IAudioClient`. let lock = match self.ensure_future_audio_client() { Ok(lock) => lock, Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => { return Err(SupportedFormatsError::DeviceNotAvailable) } Err(e) => { let description = format!("{}", e); let err = BackendSpecificError { description }; return Err(err.into()); }, }; let client = lock.unwrap().0; unsafe { // Retrieve the pointer to the default WAVEFORMATEX. let mut default_waveformatex_ptr = WaveFormatExPtr(mem::uninitialized()); match check_result((*client).GetMixFormat(&mut default_waveformatex_ptr.0)) { Ok(()) => (), Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => { return Err(SupportedFormatsError::DeviceNotAvailable); }, Err(e) => { let description = format!("{}", e); let err = BackendSpecificError { description }; return Err(err.into()); }, }; // If the default format can't succeed we have no hope of finding other formats. assert_eq!(try!(is_format_supported(client, default_waveformatex_ptr.0)), true); // Copy the format to use as a test format (as to avoid mutating the original format). let mut test_format = { match WaveFormat::copy_from_waveformatex_ptr(default_waveformatex_ptr.0) { Some(f) => f, // If the format is neither EX or EXTENSIBLE we don't know how to work with it. None => return Ok(vec![].into_iter()), } }; // Begin testing common sample rates. // // NOTE: We should really be testing for whole ranges here, but it is infeasible to // test every sample rate up to the overflow limit as the `IsFormatSupported` method is // quite slow. let mut supported_sample_rates: Vec = Vec::new(); for &rate in COMMON_SAMPLE_RATES { let rate = rate.0 as DWORD; test_format.nSamplesPerSec = rate; test_format.nAvgBytesPerSec = rate * (*default_waveformatex_ptr.0).nBlockAlign as DWORD; if try!(is_format_supported(client, test_format.as_ptr())) { supported_sample_rates.push(rate); } } // If the common rates don't include the default one, add the default. let default_sr = (*default_waveformatex_ptr.0).nSamplesPerSec as _; if !supported_sample_rates.iter().any(|&r| r == default_sr) { supported_sample_rates.push(default_sr); } // Reset the sample rate on the test format now that we're done. test_format.nSamplesPerSec = (*default_waveformatex_ptr.0).nSamplesPerSec; test_format.nAvgBytesPerSec = (*default_waveformatex_ptr.0).nAvgBytesPerSec; // TODO: Test the different sample formats? // Create the supported formats. let mut format = match format_from_waveformatex_ptr(default_waveformatex_ptr.0) { Some(fmt) => fmt, None => { let description = "could not create a `cpal::Format` from a `WAVEFORMATEX`".to_string(); let err = BackendSpecificError { description }; return Err(err.into()); } }; let mut supported_formats = Vec::with_capacity(supported_sample_rates.len()); for rate in supported_sample_rates { format.sample_rate = SampleRate(rate as _); supported_formats.push(SupportedFormat::from(format.clone())); } Ok(supported_formats.into_iter()) } } pub fn supported_input_formats(&self) -> Result { if self.data_flow() == eCapture { self.supported_formats() // If it's an output device, assume no input formats. } else { Ok(vec![].into_iter()) } } pub fn supported_output_formats(&self) -> Result { if self.data_flow() == eRender { self.supported_formats() // If it's an input device, assume no output formats. } else { Ok(vec![].into_iter()) } } // We always create voices in shared mode, therefore all samples go through an audio // processor to mix them together. // // One format is guaranteed to be supported, the one returned by `GetMixFormat`. fn default_format(&self) -> Result { // initializing COM because we call `CoTaskMemFree` com::com_initialized(); let lock = match self.ensure_future_audio_client() { Ok(lock) => lock, Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => { return Err(DefaultFormatError::DeviceNotAvailable) } Err(e) => { let description = format!("{}", e); let err = BackendSpecificError { description }; return Err(err.into()); } }; let client = lock.unwrap().0; unsafe { let mut format_ptr = WaveFormatExPtr(mem::uninitialized()); match check_result((*client).GetMixFormat(&mut format_ptr.0)) { Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => { return Err(DefaultFormatError::DeviceNotAvailable); }, Err(e) => { let description = format!("{}", e); let err = BackendSpecificError { description }; return Err(err.into()); }, Ok(()) => (), }; format_from_waveformatex_ptr(format_ptr.0) .ok_or(DefaultFormatError::StreamTypeNotSupported) } } fn data_flow(&self) -> EDataFlow { let endpoint = Endpoint::from(self.device as *const _); endpoint.data_flow() } pub fn default_input_format(&self) -> Result { if self.data_flow() == eCapture { self.default_format() } else { Err(DefaultFormatError::StreamTypeNotSupported) } } pub fn default_output_format(&self) -> Result { let data_flow = self.data_flow(); if data_flow == eRender { self.default_format() } else { Err(DefaultFormatError::StreamTypeNotSupported) } } } impl PartialEq for Device { #[inline] fn eq(&self, other: &Device) -> bool { // Use case: In oder to check whether the default device has changed // the client code might need to compare the previous default device with the current one. // The pointer comparison (`self.device == other.device`) don't work there, // because the pointers are different even when the default device stays the same. // // In this code section we're trying to use the GetId method for the device comparison, cf. // https://docs.microsoft.com/en-us/windows/desktop/api/mmdeviceapi/nf-mmdeviceapi-immdevice-getid unsafe { struct IdRAII (LPWSTR); /// RAII for device IDs. impl Drop for IdRAII { fn drop(&mut self) { unsafe {CoTaskMemFree(self.0 as *mut c_void)} } } let mut id1: LPWSTR = ptr::null_mut(); let rc1 = (*self.device).GetId(&mut id1); // GetId only fails with E_OUTOFMEMORY and if it does, we're probably dead already. // Plus it won't do to change the device comparison logic unexpectedly. if rc1 != winerror::S_OK {panic! ("cpal: GetId failure: {}", rc1)} let id1 = IdRAII(id1); let mut id2: LPWSTR = ptr::null_mut(); let rc2 = (*other.device).GetId(&mut id2); if rc2 != winerror::S_OK {panic! ("cpal: GetId failure: {}", rc1)} let id2 = IdRAII(id2); // 16-bit null-terminated comparison. let mut offset = 0; loop { let w1: WCHAR = *id1.0.offset(offset); let w2: WCHAR = *id2.0.offset(offset); if w1 == 0 && w2 == 0 {return true} if w1 != w2 {return false} offset += 1; } } } } impl Eq for Device { } impl Clone for Device { #[inline] fn clone(&self) -> Device { unsafe { (*self.device).AddRef(); } Device { device: self.device, future_audio_client: self.future_audio_client.clone(), } } } impl fmt::Debug for Device { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("Device") .field("device", &self.device) .field("name", &self.name()) .finish() } } impl Drop for Device { #[inline] fn drop(&mut self) { unsafe { (*self.device).Release(); } if let Some(client) = self.future_audio_client.lock().unwrap().take() { unsafe { (*client.0).Release(); } } } } impl Drop for Endpoint { fn drop(&mut self) { unsafe { (*self.endpoint).Release(); } } } impl From<*const IMMDevice> for Endpoint { fn from(device: *const IMMDevice) -> Self { unsafe { let endpoint = immendpoint_from_immdevice(device); Endpoint { endpoint: endpoint } } } } impl Endpoint { fn data_flow(&self) -> EDataFlow { unsafe { data_flow_from_immendpoint(self.endpoint) } } } lazy_static! { static ref ENUMERATOR: Enumerator = { // COM initialization is thread local, but we only need to have COM initialized in the // thread we create the objects in com::com_initialized(); // building the devices enumerator object unsafe { let mut enumerator: *mut IMMDeviceEnumerator = mem::uninitialized(); let hresult = CoCreateInstance( &CLSID_MMDeviceEnumerator, ptr::null_mut(), CLSCTX_ALL, &IMMDeviceEnumerator::uuidof(), &mut enumerator as *mut *mut IMMDeviceEnumerator as *mut _, ); check_result(hresult).unwrap(); Enumerator(enumerator) } }; } /// RAII object around `IMMDeviceEnumerator`. struct Enumerator(*mut IMMDeviceEnumerator); unsafe impl Send for Enumerator { } unsafe impl Sync for Enumerator { } impl Drop for Enumerator { #[inline] fn drop(&mut self) { unsafe { (*self.0).Release(); } } } /// WASAPI implementation for `Devices`. pub struct Devices { collection: *mut IMMDeviceCollection, total_count: u32, next_item: u32, } impl Devices { pub fn new() -> Result { unsafe { let mut collection: *mut IMMDeviceCollection = mem::uninitialized(); // can fail because of wrong parameters (should never happen) or out of memory check_result_backend_specific( (*ENUMERATOR.0).EnumAudioEndpoints( eAll, DEVICE_STATE_ACTIVE, &mut collection, ) )?; let mut count = mem::uninitialized(); // can fail if the parameter is null, which should never happen check_result_backend_specific((*collection).GetCount(&mut count))?; Ok(Devices { collection: collection, total_count: count, next_item: 0, }) } } } unsafe impl Send for Devices { } unsafe impl Sync for Devices { } impl Drop for Devices { #[inline] fn drop(&mut self) { unsafe { (*self.collection).Release(); } } } impl Iterator for Devices { type Item = Device; fn next(&mut self) -> Option { if self.next_item >= self.total_count { return None; } unsafe { let mut device = mem::uninitialized(); // can fail if out of range, which we just checked above check_result((*self.collection).Item(self.next_item, &mut device)).unwrap(); self.next_item += 1; Some(Device::from_immdevice(device)) } } #[inline] fn size_hint(&self) -> (usize, Option) { let num = self.total_count - self.next_item; let num = num as usize; (num, Some(num)) } } fn default_device(data_flow: EDataFlow) -> Option { unsafe { let mut device = mem::uninitialized(); let hres = (*ENUMERATOR.0) .GetDefaultAudioEndpoint(data_flow, eConsole, &mut device); if let Err(_err) = check_result(hres) { return None; // TODO: check specifically for `E_NOTFOUND`, and panic otherwise } Some(Device::from_immdevice(device)) } } pub fn default_input_device() -> Option { default_device(eCapture) } pub fn default_output_device() -> Option { default_device(eRender) } cpal-0.10.0/src/host/wasapi/mod.rs010066400037200003720000000072471350771351400151250ustar0000000000000000extern crate winapi; use BackendSpecificError; use BuildStreamError; use DefaultFormatError; use DeviceNameError; use DevicesError; use Format; use PlayStreamError; use PauseStreamError; use StreamDataResult; use SupportedFormatsError; use self::winapi::um::winnt::HRESULT; use std::io::Error as IoError; use traits::{DeviceTrait, EventLoopTrait, HostTrait, StreamIdTrait}; pub use self::device::{Device, Devices, SupportedInputFormats, SupportedOutputFormats, default_input_device, default_output_device}; pub use self::stream::{EventLoop, StreamId}; mod com; mod device; mod stream; /// The WASAPI host, the default windows host type. #[derive(Debug)] pub struct Host; impl Host { pub fn new() -> Result { Ok(Host) } } impl HostTrait for Host { type Devices = Devices; type Device = Device; type EventLoop = EventLoop; fn is_available() -> bool { // Assume WASAPI is always available on windows. true } fn devices(&self) -> Result { Devices::new() } fn default_input_device(&self) -> Option { default_input_device() } fn default_output_device(&self) -> Option { default_output_device() } fn event_loop(&self) -> Self::EventLoop { EventLoop::new() } } impl DeviceTrait for Device { type SupportedInputFormats = SupportedInputFormats; type SupportedOutputFormats = SupportedOutputFormats; fn name(&self) -> Result { Device::name(self) } fn supported_input_formats(&self) -> Result { Device::supported_input_formats(self) } fn supported_output_formats(&self) -> Result { Device::supported_output_formats(self) } fn default_input_format(&self) -> Result { Device::default_input_format(self) } fn default_output_format(&self) -> Result { Device::default_output_format(self) } } impl EventLoopTrait for EventLoop { type Device = Device; type StreamId = StreamId; fn build_input_stream( &self, device: &Self::Device, format: &Format, ) -> Result { EventLoop::build_input_stream(self, device, format) } fn build_output_stream( &self, device: &Self::Device, format: &Format, ) -> Result { EventLoop::build_output_stream(self, device, format) } fn play_stream(&self, stream: Self::StreamId) -> Result<(), PlayStreamError> { EventLoop::play_stream(self, stream) } fn pause_stream(&self, stream: Self::StreamId) -> Result<(), PauseStreamError> { EventLoop::pause_stream(self, stream) } fn destroy_stream(&self, stream: Self::StreamId) { EventLoop::destroy_stream(self, stream) } fn run(&self, callback: F) -> ! where F: FnMut(Self::StreamId, StreamDataResult) + Send, { EventLoop::run(self, callback) } } impl StreamIdTrait for StreamId {} #[inline] fn check_result(result: HRESULT) -> Result<(), IoError> { if result < 0 { Err(IoError::from_raw_os_error(result)) } else { Ok(()) } } fn check_result_backend_specific(result: HRESULT) -> Result<(), BackendSpecificError> { match check_result(result) { Ok(()) => Ok(()), Err(err) => { let description = format!("{}", err); return Err(BackendSpecificError { description }); } } } cpal-0.10.0/src/host/wasapi/stream.rs010066400037200003720000001046771350771351400156460ustar0000000000000000use super::Device; use super::check_result; use super::com; use super::winapi::shared::basetsd::UINT32; use super::winapi::shared::ksmedia; use super::winapi::shared::minwindef::{BYTE, DWORD, FALSE, WORD}; use super::winapi::shared::mmreg; use super::winapi::um::audioclient::{self, AUDCLNT_E_DEVICE_INVALIDATED, AUDCLNT_S_BUFFER_EMPTY}; use super::winapi::um::audiosessiontypes::{AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK}; use super::winapi::um::handleapi; use super::winapi::um::synchapi; use super::winapi::um::winbase; use super::winapi::um::winnt; use std::mem; use std::ptr; use std::slice; use std::sync::Mutex; use std::sync::mpsc::{channel, Sender, Receiver}; use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering; use BackendSpecificError; use BuildStreamError; use Format; use PauseStreamError; use PlayStreamError; use SampleFormat; use StreamData; use StreamDataResult; use StreamError; use UnknownTypeOutputBuffer; use UnknownTypeInputBuffer; pub struct EventLoop { // Data used by the `run()` function implementation. The mutex is kept lock permanently by // `run()`. This ensures that two `run()` invocations can't run at the same time, and also // means that we shouldn't try to lock this field from anywhere else but `run()`. run_context: Mutex, // Identifier of the next stream to create. Each new stream increases this counter. If the // counter overflows, there's a panic. // TODO: use AtomicU64 instead next_stream_id: AtomicUsize, // Commands processed by the `run()` method that is currently running. // `pending_scheduled_event` must be signalled whenever a command is added here, so that it // will get picked up. commands: Sender, // This event is signalled after a new entry is added to `commands`, so that the `run()` // method can be notified. pending_scheduled_event: winnt::HANDLE, } struct RunContext { // Streams that have been created in this event loop. streams: Vec, // Handles corresponding to the `event` field of each element of `voices`. Must always be in // sync with `voices`, except that the first element is always `pending_scheduled_event`. handles: Vec, commands: Receiver, } enum Command { NewStream(StreamInner), DestroyStream(StreamId), PlayStream(StreamId), PauseStream(StreamId), } enum AudioClientFlow { Render { render_client: *mut audioclient::IAudioRenderClient, }, Capture { capture_client: *mut audioclient::IAudioCaptureClient, }, } struct StreamInner { id: StreamId, audio_client: *mut audioclient::IAudioClient, client_flow: AudioClientFlow, // Event that is signalled by WASAPI whenever audio data must be written. event: winnt::HANDLE, // True if the stream is currently playing. False if paused. playing: bool, // Number of frames of audio data in the underlying buffer allocated by WASAPI. max_frames_in_buffer: UINT32, // Number of bytes that each frame occupies. bytes_per_frame: WORD, // The sample format with which the stream was created. sample_format: SampleFormat, } impl EventLoop { pub fn new() -> EventLoop { let pending_scheduled_event = unsafe { synchapi::CreateEventA(ptr::null_mut(), 0, 0, ptr::null()) }; let (tx, rx) = channel(); EventLoop { pending_scheduled_event: pending_scheduled_event, run_context: Mutex::new(RunContext { streams: Vec::new(), handles: vec![pending_scheduled_event], commands: rx, }), next_stream_id: AtomicUsize::new(0), commands: tx, } } pub(crate) fn build_input_stream( &self, device: &Device, format: &Format, ) -> Result { unsafe { // Making sure that COM is initialized. // It's not actually sure that this is required, but when in doubt do it. com::com_initialized(); // Obtaining a `IAudioClient`. let audio_client = match device.build_audioclient() { Ok(client) => client, Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => return Err(BuildStreamError::DeviceNotAvailable), Err(e) => { let description = format!("{}", e); let err = BackendSpecificError { description }; return Err(err.into()); } }; // Computing the format and initializing the device. let waveformatex = { let format_attempt = format_to_waveformatextensible(format) .ok_or(BuildStreamError::FormatNotSupported)?; let share_mode = AUDCLNT_SHAREMODE_SHARED; // Ensure the format is supported. match super::device::is_format_supported(audio_client, &format_attempt.Format) { Ok(false) => return Err(BuildStreamError::FormatNotSupported), Err(_) => return Err(BuildStreamError::DeviceNotAvailable), _ => (), } // finally initializing the audio client let hresult = (*audio_client).Initialize( share_mode, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, 0, 0, &format_attempt.Format, ptr::null(), ); match check_result(hresult) { Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => { (*audio_client).Release(); return Err(BuildStreamError::DeviceNotAvailable); }, Err(e) => { (*audio_client).Release(); let description = format!("{}", e); let err = BackendSpecificError { description }; return Err(err.into()); }, Ok(()) => (), }; format_attempt.Format }; // obtaining the size of the samples buffer in number of frames let max_frames_in_buffer = { let mut max_frames_in_buffer = mem::uninitialized(); let hresult = (*audio_client).GetBufferSize(&mut max_frames_in_buffer); match check_result(hresult) { Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => { (*audio_client).Release(); return Err(BuildStreamError::DeviceNotAvailable); }, Err(e) => { (*audio_client).Release(); let description = format!("{}", e); let err = BackendSpecificError { description }; return Err(err.into()); }, Ok(()) => (), }; max_frames_in_buffer }; // Creating the event that will be signalled whenever we need to submit some samples. let event = { let event = synchapi::CreateEventA(ptr::null_mut(), 0, 0, ptr::null()); if event == ptr::null_mut() { (*audio_client).Release(); let description = format!("failed to create event"); let err = BackendSpecificError { description }; return Err(err.into()); } if let Err(e) = check_result((*audio_client).SetEventHandle(event)) { (*audio_client).Release(); let description = format!("failed to call SetEventHandle: {}", e); let err = BackendSpecificError { description }; return Err(err.into()); } event }; // Building a `IAudioCaptureClient` that will be used to read captured samples. let capture_client = { let mut capture_client: *mut audioclient::IAudioCaptureClient = mem::uninitialized(); let hresult = (*audio_client).GetService( &audioclient::IID_IAudioCaptureClient, &mut capture_client as *mut *mut audioclient::IAudioCaptureClient as *mut _, ); match check_result(hresult) { Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => { (*audio_client).Release(); return Err(BuildStreamError::DeviceNotAvailable); }, Err(e) => { (*audio_client).Release(); let description = format!("failed to build capture client: {}", e); let err = BackendSpecificError { description }; return Err(err.into()); }, Ok(()) => (), }; &mut *capture_client }; let new_stream_id = StreamId(self.next_stream_id.fetch_add(1, Ordering::Relaxed)); if new_stream_id.0 == usize::max_value() { return Err(BuildStreamError::StreamIdOverflow); } // Once we built the `StreamInner`, we add a command that will be picked up by the // `run()` method and added to the `RunContext`. { let client_flow = AudioClientFlow::Capture { capture_client: capture_client, }; let inner = StreamInner { id: new_stream_id.clone(), audio_client: audio_client, client_flow: client_flow, event: event, playing: false, max_frames_in_buffer: max_frames_in_buffer, bytes_per_frame: waveformatex.nBlockAlign, sample_format: format.data_type, }; self.push_command(Command::NewStream(inner)); }; Ok(new_stream_id) } } pub(crate) fn build_output_stream( &self, device: &Device, format: &Format, ) -> Result { unsafe { // Making sure that COM is initialized. // It's not actually sure that this is required, but when in doubt do it. com::com_initialized(); // Obtaining a `IAudioClient`. let audio_client = match device.build_audioclient() { Ok(client) => client, Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => return Err(BuildStreamError::DeviceNotAvailable), Err(e) => { let description = format!("{}", e); let err = BackendSpecificError { description }; return Err(err.into()); } }; // Computing the format and initializing the device. let waveformatex = { let format_attempt = format_to_waveformatextensible(format) .ok_or(BuildStreamError::FormatNotSupported)?; let share_mode = AUDCLNT_SHAREMODE_SHARED; // Ensure the format is supported. match super::device::is_format_supported(audio_client, &format_attempt.Format) { Ok(false) => return Err(BuildStreamError::FormatNotSupported), Err(_) => return Err(BuildStreamError::DeviceNotAvailable), _ => (), } // finally initializing the audio client let hresult = (*audio_client).Initialize(share_mode, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, 0, 0, &format_attempt.Format, ptr::null()); match check_result(hresult) { Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => { (*audio_client).Release(); return Err(BuildStreamError::DeviceNotAvailable); }, Err(e) => { (*audio_client).Release(); let description = format!("{}", e); let err = BackendSpecificError { description }; return Err(err.into()); }, Ok(()) => (), }; format_attempt.Format }; // Creating the event that will be signalled whenever we need to submit some samples. let event = { let event = synchapi::CreateEventA(ptr::null_mut(), 0, 0, ptr::null()); if event == ptr::null_mut() { (*audio_client).Release(); let description = format!("failed to create event"); let err = BackendSpecificError { description }; return Err(err.into()); } match check_result((*audio_client).SetEventHandle(event)) { Err(e) => { (*audio_client).Release(); let description = format!("failed to call SetEventHandle: {}", e); let err = BackendSpecificError { description }; return Err(err.into()); }, Ok(_) => (), }; event }; // obtaining the size of the samples buffer in number of frames let max_frames_in_buffer = { let mut max_frames_in_buffer = mem::uninitialized(); let hresult = (*audio_client).GetBufferSize(&mut max_frames_in_buffer); match check_result(hresult) { Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => { (*audio_client).Release(); return Err(BuildStreamError::DeviceNotAvailable); }, Err(e) => { (*audio_client).Release(); let description = format!("failed to obtain buffer size: {}", e); let err = BackendSpecificError { description }; return Err(err.into()); }, Ok(()) => (), }; max_frames_in_buffer }; // Building a `IAudioRenderClient` that will be used to fill the samples buffer. let render_client = { let mut render_client: *mut audioclient::IAudioRenderClient = mem::uninitialized(); let hresult = (*audio_client).GetService(&audioclient::IID_IAudioRenderClient, &mut render_client as *mut *mut audioclient::IAudioRenderClient as *mut _); match check_result(hresult) { Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => { (*audio_client).Release(); return Err(BuildStreamError::DeviceNotAvailable); }, Err(e) => { (*audio_client).Release(); let description = format!("failed to build render client: {}", e); let err = BackendSpecificError { description }; return Err(err.into()); }, Ok(()) => (), }; &mut *render_client }; let new_stream_id = StreamId(self.next_stream_id.fetch_add(1, Ordering::Relaxed)); if new_stream_id.0 == usize::max_value() { return Err(BuildStreamError::StreamIdOverflow); } // Once we built the `StreamInner`, we add a command that will be picked up by the // `run()` method and added to the `RunContext`. { let client_flow = AudioClientFlow::Render { render_client: render_client, }; let inner = StreamInner { id: new_stream_id.clone(), audio_client: audio_client, client_flow: client_flow, event: event, playing: false, max_frames_in_buffer: max_frames_in_buffer, bytes_per_frame: waveformatex.nBlockAlign, sample_format: format.data_type, }; self.push_command(Command::NewStream(inner)); }; Ok(new_stream_id) } } #[inline] pub(crate) fn destroy_stream(&self, stream_id: StreamId) { self.push_command(Command::DestroyStream(stream_id)); } #[inline] pub(crate) fn run(&self, mut callback: F) -> ! where F: FnMut(StreamId, StreamDataResult) { self.run_inner(&mut callback); } fn run_inner(&self, callback: &mut dyn FnMut(StreamId, StreamDataResult)) -> ! { unsafe { // We keep `run_context` locked forever, which guarantees that two invocations of // `run()` cannot run simultaneously. let mut run_context = self.run_context.lock().unwrap(); // Force a deref so that borrow checker can operate on each field independently. // Shadow the name because we don't use (or drop) it otherwise. let run_context = &mut *run_context; // Keep track of the set of streams that should be removed due to some error occurring. // // Checked at the start of each loop. let mut streams_to_remove: Vec<(StreamId, StreamError)> = vec![]; 'stream_loop: loop { // Remove any failed streams. for (stream_id, err) in streams_to_remove.drain(..) { match run_context.streams.iter().position(|s| s.id == stream_id) { None => continue, Some(p) => { run_context.handles.remove(p + 1); run_context.streams.remove(p); callback(stream_id, Err(err.into())); }, } } // Process queued commands. process_commands(run_context, callback); // Wait for any of the handles to be signalled. let handle_idx = match wait_for_handle_signal(&run_context.handles) { Ok(idx) => idx, Err(err) => { for stream in &run_context.streams { callback(stream.id.clone(), Err(err.clone().into())); } run_context.streams.clear(); run_context.handles.truncate(1); break 'stream_loop; } }; // If `handle_idx` is 0, then it's `pending_scheduled_event` that was signalled in // order for us to pick up the pending commands. Otherwise, a stream needs data. if handle_idx == 0 { continue; } let stream_idx = handle_idx - 1; let stream = &mut run_context.streams[stream_idx]; // The number of frames available for reading/writing. let mut frames_available = match get_available_frames(stream) { Ok(0) => continue, // TODO: Can this happen? Ok(n) => n, Err(err) => { streams_to_remove.push((stream.id.clone(), err)); continue; } }; let sample_size = stream.sample_format.sample_size(); // Obtaining a pointer to the buffer. match stream.client_flow { AudioClientFlow::Capture { capture_client } => { // Get the available data in the shared buffer. let mut buffer: *mut BYTE = mem::uninitialized(); let mut flags = mem::uninitialized(); let hresult = (*capture_client).GetBuffer( &mut buffer, &mut frames_available, &mut flags, ptr::null_mut(), ptr::null_mut(), ); // TODO: Can this happen? if hresult == AUDCLNT_S_BUFFER_EMPTY { continue; } else if let Err(err) = stream_error_from_hresult(hresult) { streams_to_remove.push((stream.id.clone(), err)); continue; } debug_assert!(!buffer.is_null()); let buffer_len = frames_available as usize * stream.bytes_per_frame as usize / sample_size; // Simplify the capture callback sample format branches. macro_rules! capture_callback { ($T:ty, $Variant:ident) => {{ let buffer_data = buffer as *mut _ as *const $T; let slice = slice::from_raw_parts(buffer_data, buffer_len); let unknown_buffer = UnknownTypeInputBuffer::$Variant(::InputBuffer { buffer: slice, }); let data = StreamData::Input { buffer: unknown_buffer }; callback(stream.id.clone(), Ok(data)); // Release the buffer. let hresult = (*capture_client).ReleaseBuffer(frames_available); if let Err(err) = stream_error_from_hresult(hresult) { streams_to_remove.push((stream.id.clone(), err)); continue; } }}; } match stream.sample_format { SampleFormat::F32 => capture_callback!(f32, F32), SampleFormat::I16 => capture_callback!(i16, I16), SampleFormat::U16 => capture_callback!(u16, U16), } }, AudioClientFlow::Render { render_client } => { let mut buffer: *mut BYTE = mem::uninitialized(); let hresult = (*render_client).GetBuffer( frames_available, &mut buffer as *mut *mut _, ); if let Err(err) = stream_error_from_hresult(hresult) { streams_to_remove.push((stream.id.clone(), err)); continue; } debug_assert!(!buffer.is_null()); let buffer_len = frames_available as usize * stream.bytes_per_frame as usize / sample_size; // Simplify the render callback sample format branches. macro_rules! render_callback { ($T:ty, $Variant:ident) => {{ let buffer_data = buffer as *mut $T; let slice = slice::from_raw_parts_mut(buffer_data, buffer_len); let unknown_buffer = UnknownTypeOutputBuffer::$Variant(::OutputBuffer { buffer: slice }); let data = StreamData::Output { buffer: unknown_buffer }; callback(stream.id.clone(), Ok(data)); let hresult = (*render_client) .ReleaseBuffer(frames_available as u32, 0); if let Err(err) = stream_error_from_hresult(hresult) { streams_to_remove.push((stream.id.clone(), err)); continue; } }} } match stream.sample_format { SampleFormat::F32 => render_callback!(f32, F32), SampleFormat::I16 => render_callback!(i16, I16), SampleFormat::U16 => render_callback!(u16, U16), } }, } } } panic!("`cpal::EventLoop::run` API currently disallows returning"); } #[inline] pub(crate) fn play_stream(&self, stream: StreamId) -> Result<(), PlayStreamError> { self.push_command(Command::PlayStream(stream)); Ok(()) } #[inline] pub(crate) fn pause_stream(&self, stream: StreamId) -> Result<(), PauseStreamError> { self.push_command(Command::PauseStream(stream)); Ok(()) } #[inline] fn push_command(&self, command: Command) { // Safe to unwrap: sender outlives receiver. self.commands.send(command).unwrap(); unsafe { let result = synchapi::SetEvent(self.pending_scheduled_event); assert!(result != 0); } } } impl Drop for EventLoop { #[inline] fn drop(&mut self) { unsafe { handleapi::CloseHandle(self.pending_scheduled_event); } } } unsafe impl Send for EventLoop { } unsafe impl Sync for EventLoop { } // The content of a stream ID is a number that was fetched from `next_stream_id`. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct StreamId(usize); impl Drop for AudioClientFlow { fn drop(&mut self) { unsafe { match *self { AudioClientFlow::Capture { capture_client } => (*capture_client).Release(), AudioClientFlow::Render { render_client } => (*render_client).Release(), }; } } } impl Drop for StreamInner { #[inline] fn drop(&mut self) { unsafe { (*self.audio_client).Release(); handleapi::CloseHandle(self.event); } } } // Turns a `Format` into a `WAVEFORMATEXTENSIBLE`. // // Returns `None` if the WAVEFORMATEXTENSIBLE does not support the given format. fn format_to_waveformatextensible(format: &Format) -> Option { let format_tag = match format.data_type { SampleFormat::I16 => mmreg::WAVE_FORMAT_PCM, SampleFormat::F32 => mmreg::WAVE_FORMAT_EXTENSIBLE, SampleFormat::U16 => return None, }; let channels = format.channels as WORD; let sample_rate = format.sample_rate.0 as DWORD; let sample_bytes = format.data_type.sample_size() as WORD; let avg_bytes_per_sec = channels as DWORD * sample_rate * sample_bytes as DWORD; let block_align = channels * sample_bytes; let bits_per_sample = 8 * sample_bytes; let cb_size = match format.data_type { SampleFormat::I16 => 0, SampleFormat::F32 => { let extensible_size = mem::size_of::(); let ex_size = mem::size_of::(); (extensible_size - ex_size) as WORD }, SampleFormat::U16 => return None, }; let waveformatex = mmreg::WAVEFORMATEX { wFormatTag: format_tag, nChannels: channels, nSamplesPerSec: sample_rate, nAvgBytesPerSec: avg_bytes_per_sec, nBlockAlign: block_align, wBitsPerSample: bits_per_sample, cbSize: cb_size, }; // CPAL does not care about speaker positions, so pass audio straight through. // TODO: This constant should be defined in winapi but is missing. const KSAUDIO_SPEAKER_DIRECTOUT: DWORD = 0; let channel_mask = KSAUDIO_SPEAKER_DIRECTOUT; let sub_format = match format.data_type { SampleFormat::I16 => ksmedia::KSDATAFORMAT_SUBTYPE_PCM, SampleFormat::F32 => ksmedia::KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, SampleFormat::U16 => return None, }; let waveformatextensible = mmreg::WAVEFORMATEXTENSIBLE { Format: waveformatex, Samples: bits_per_sample as WORD, dwChannelMask: channel_mask, SubFormat: sub_format, }; Some(waveformatextensible) } // Process any pending commands that are queued within the `RunContext`. fn process_commands( run_context: &mut RunContext, callback: &mut dyn FnMut(StreamId, StreamDataResult), ) { // Process the pending commands. for command in run_context.commands.try_iter() { match command { Command::NewStream(stream_inner) => { let event = stream_inner.event; run_context.streams.push(stream_inner); run_context.handles.push(event); }, Command::DestroyStream(stream_id) => { match run_context.streams.iter().position(|s| s.id == stream_id) { None => continue, Some(p) => { run_context.handles.remove(p + 1); run_context.streams.remove(p); }, } }, Command::PlayStream(stream_id) => { match run_context.streams.iter().position(|s| s.id == stream_id) { None => continue, Some(p) => { if !run_context.streams[p].playing { let hresult = unsafe { (*run_context.streams[p].audio_client).Start() }; match stream_error_from_hresult(hresult) { Ok(()) => { run_context.streams[p].playing = true; } Err(err) => { callback(stream_id, Err(err.into())); run_context.handles.remove(p + 1); run_context.streams.remove(p); } } } } } }, Command::PauseStream(stream_id) => { match run_context.streams.iter().position(|s| s.id == stream_id) { None => continue, Some(p) => { if run_context.streams[p].playing { let hresult = unsafe { (*run_context.streams[p].audio_client).Stop() }; match stream_error_from_hresult(hresult) { Ok(()) => { run_context.streams[p].playing = false; } Err(err) => { callback(stream_id, Err(err.into())); run_context.handles.remove(p + 1); run_context.streams.remove(p); } } } }, } }, } } } // Wait for any of the given handles to be signalled. // // Returns the index of the `handle` that was signalled, or an `Err` if // `WaitForMultipleObjectsEx` fails. // // This is called when the `run` thread is ready to wait for the next event. The // next event might be some command submitted by the user (the first handle) or // might indicate that one of the streams is ready to deliver or receive audio. fn wait_for_handle_signal(handles: &[winnt::HANDLE]) -> Result { debug_assert!(handles.len() <= winnt::MAXIMUM_WAIT_OBJECTS as usize); let result = unsafe { synchapi::WaitForMultipleObjectsEx( handles.len() as u32, handles.as_ptr(), FALSE, // Don't wait for all, just wait for the first winbase::INFINITE, // TODO: allow setting a timeout FALSE, // irrelevant parameter here ) }; if result == winbase::WAIT_FAILED { let err = unsafe { winapi::um::errhandlingapi::GetLastError() }; let description = format!("`WaitForMultipleObjectsEx failed: {}", err); let err = BackendSpecificError { description }; return Err(err); } // Notifying the corresponding task handler. debug_assert!(result >= winbase::WAIT_OBJECT_0); let handle_idx = (result - winbase::WAIT_OBJECT_0) as usize; Ok(handle_idx) } // Get the number of available frames that are available for writing/reading. fn get_available_frames(stream: &StreamInner) -> Result { unsafe { let mut padding = mem::uninitialized(); let hresult = (*stream.audio_client).GetCurrentPadding(&mut padding); stream_error_from_hresult(hresult)?; Ok(stream.max_frames_in_buffer - padding) } } // Convert the given `HRESULT` into a `StreamError` if it does indicate an error. fn stream_error_from_hresult(hresult: winnt::HRESULT) -> Result<(), StreamError> { if hresult == AUDCLNT_E_DEVICE_INVALIDATED { return Err(StreamError::DeviceNotAvailable); } if let Err(err) = check_result(hresult) { let description = format!("{}", err); let err = BackendSpecificError { description }; return Err(err.into()); } Ok(()) } cpal-0.10.0/src/lib.rs010066400037200003720000000551551350771351400126540ustar0000000000000000//! # How to use cpal //! //! Here are some concepts cpal exposes: //! //! - A [**Host**](./trait.Host.html) provides access to the available audio devices on the system. //! Some platforms have more than one host available, but every platform supported by CPAL has at //! least one [**DefaultHost**](./trait.Host.html) that is guaranteed to be available. //! - A [**Device**](./trait.Device.html) is an audio device that may have any number of input and //! output streams. //! - A stream is an open flow of audio data. Input streams allow you to receive audio data, output //! streams allow you to play audio data. You must choose which **Device** will run your stream //! before you can create one. Often, a default device can be retrieved via the **Host**. //! - An [**EventLoop**](./trait.EventLoop.html) is a collection of streams being run by one or //! more **Device**s under a single **Host**. Each stream must belong to an **EventLoop**, and //! all the streams that belong to an **EventLoop** are managed together. //! //! The first step is to initialise the `Host` (for accessing audio devices) and create an //! `EventLoop`: //! //! ``` //! use cpal::traits::HostTrait; //! let host = cpal::default_host(); //! let event_loop = host.event_loop(); //! ``` //! //! Then choose a `Device`. The easiest way is to use the default input or output `Device` via the //! `default_input_device()` or `default_output_device()` functions. Alternatively you can //! enumerate all the available devices with the `devices()` function. Beware that the //! `default_*_device()` functions return an `Option` in case no device is available for that //! stream type on the system. //! //! ```no_run //! # use cpal::traits::HostTrait; //! # let host = cpal::default_host(); //! let device = host.default_output_device().expect("no output device available"); //! ``` //! //! Before we can create a stream, we must decide what the format of the audio samples is going to //! be. You can query all the supported formats with the `supported_input_formats()` and //! `supported_output_formats()` methods. These produce 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 stream if the format is not supported by the device. //! //! > **Note**: the `supported_formats()` method could return an error for example if the device //! > has been disconnected. //! //! ```no_run //! use cpal::traits::{DeviceTrait, HostTrait}; //! # let host = cpal::default_host(); //! # let device = host.default_output_device().unwrap(); //! let mut supported_formats_range = device.supported_output_formats() //! .expect("error while querying formats"); //! let format = supported_formats_range.next() //! .expect("no supported format?!") //! .with_max_sample_rate(); //! ``` //! //! Now that we have everything for the stream, we can create it from our event loop: //! //! ```no_run //! use cpal::traits::{DeviceTrait, EventLoopTrait, HostTrait}; //! # let host = cpal::default_host(); //! # let event_loop = host.event_loop(); //! # let device = host.default_output_device().unwrap(); //! # let format = device.supported_output_formats().unwrap().next().unwrap().with_max_sample_rate(); //! let stream_id = event_loop.build_output_stream(&device, &format).unwrap(); //! ``` //! //! The value returned by `build_output_stream()` is of type `StreamId` and is an identifier that //! will allow you to control the stream. //! //! Now we must start the stream. This is done with the `play_stream()` method on the event loop. //! //! ```no_run //! # use cpal::traits::{EventLoopTrait, HostTrait}; //! # let host = cpal::default_host(); //! # let event_loop = host.event_loop(); //! # let stream_id = unimplemented!(); //! event_loop.play_stream(stream_id).expect("failed to play_stream"); //! ``` //! //! Now everything is ready! We call `run()` on the `event_loop` to begin processing. //! //! ```no_run //! # use cpal::traits::{EventLoopTrait, HostTrait}; //! # let host = cpal::default_host(); //! # let event_loop = host.event_loop(); //! event_loop.run(move |_stream_id, _stream_result| { //! // react to stream events and read or write stream data 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 stream ID and an instance of type //! `StreamData` that represents the data that must be read from or written to. The inner //! `UnknownTypeOutputBuffer` can be one of `I16`, `U16` or `F32` depending on the format that was //! passed to `build_output_stream`. //! //! In this example, we simply fill the given output buffer with zeroes. //! //! ```no_run //! use cpal::{StreamData, UnknownTypeOutputBuffer}; //! use cpal::traits::{EventLoopTrait, HostTrait}; //! # let host = cpal::default_host(); //! # let event_loop = host.event_loop(); //! event_loop.run(move |stream_id, stream_result| { //! let stream_data = match stream_result { //! Ok(data) => data, //! Err(err) => { //! eprintln!("an error occurred on stream {:?}: {}", stream_id, err); //! return; //! } //! _ => return, //! }; //! //! match stream_data { //! StreamData::Output { buffer: UnknownTypeOutputBuffer::U16(mut buffer) } => { //! for elem in buffer.iter_mut() { //! *elem = u16::max_value() / 2; //! } //! }, //! StreamData::Output { buffer: UnknownTypeOutputBuffer::I16(mut buffer) } => { //! for elem in buffer.iter_mut() { //! *elem = 0; //! } //! }, //! StreamData::Output { buffer: UnknownTypeOutputBuffer::F32(mut buffer) } => { //! for elem in buffer.iter_mut() { //! *elem = 0.0; //! } //! }, //! _ => (), //! } //! }); //! ``` #![recursion_limit = "512"] extern crate failure; #[cfg(target_os = "windows")] #[macro_use] extern crate lazy_static; // Extern crate declarations with `#[macro_use]` must unfortunately be at crate root. #[cfg(target_os = "emscripten")] #[macro_use] extern crate stdweb; pub use platform::{ ALL_HOSTS, Device, Devices, EventLoop, Host, HostId, SupportedInputFormats, SupportedOutputFormats, StreamId, available_hosts, default_host, host_from_id, }; pub use samples_formats::{Sample, SampleFormat}; use failure::Fail; use std::ops::{Deref, DerefMut}; mod host; pub mod platform; mod samples_formats; pub mod traits; /// A host's device iterator yielding only *input* devices. pub type InputDevices = std::iter::Filter::Item) -> bool>; /// A host's device iterator yielding only *output* devices. pub type OutputDevices = std::iter::Filter::Item) -> bool>; /// Number of channels. pub type ChannelCount = u16; /// The number of samples processed per second for a single channel of audio. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub struct SampleRate(pub u32); /// The format of an input or output audio stream. #[derive(Debug, Clone, PartialEq, Eq)] pub struct Format { pub channels: ChannelCount, pub sample_rate: SampleRate, pub data_type: SampleFormat, } /// Describes a range of supported stream formats. #[derive(Debug, Clone, PartialEq, Eq)] pub struct SupportedFormat { pub channels: ChannelCount, /// Minimum value for the samples rate of the supported formats. pub min_sample_rate: SampleRate, /// Maximum value for the samples rate of the supported formats. pub max_sample_rate: SampleRate, /// Type of data expected by the device. pub data_type: SampleFormat, } /// Stream data passed to the `EventLoop::run` callback. pub enum StreamData<'a> { Input { buffer: UnknownTypeInputBuffer<'a>, }, Output { buffer: UnknownTypeOutputBuffer<'a>, }, } /// Stream data passed to the `EventLoop::run` callback, or an error in the case that the device /// was invalidated or some backend-specific error occurred. pub type StreamDataResult<'a> = Result, StreamError>; /// Represents a buffer containing audio data that may be read. /// /// This struct implements the `Deref` trait targeting `[T]`. Therefore this buffer can be read the /// same way as reading from a `Vec` or any other kind of Rust array. // TODO: explain audio stuff in general // TODO: remove the wrapper and just use slices in next major version pub struct InputBuffer<'a, T: 'a> where T: Sample, { buffer: &'a [T], } /// Represents a buffer that must be filled with audio data. The buffer in unfilled state may /// contain garbage values. /// /// 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 // TODO: remove the wrapper and just use slices #[must_use] pub struct OutputBuffer<'a, T: 'a> where T: Sample, { buffer: &'a mut [T], } /// This is the struct that is provided to you by cpal when you want to read samples from a buffer. /// /// Since the type of data is only known at runtime, you have to read the right buffer. pub enum UnknownTypeInputBuffer<'a> { /// Samples whose format is `u16`. U16(InputBuffer<'a, u16>), /// Samples whose format is `i16`. I16(InputBuffer<'a, i16>), /// Samples whose format is `f32`. F32(InputBuffer<'a, f32>), } /// This is the struct that is provided to you by cpal when you want to write samples to a buffer. /// /// Since the type of data is only known at runtime, you have to fill the right buffer. pub enum UnknownTypeOutputBuffer<'a> { /// Samples whose format is `u16`. U16(OutputBuffer<'a, u16>), /// Samples whose format is `i16`. I16(OutputBuffer<'a, i16>), /// Samples whose format is `f32`. F32(OutputBuffer<'a, f32>), } /// The requested host, although supported on this platform, is unavailable. #[derive(Clone, Debug, Fail)] #[fail(display = "the requested host is unavailable")] pub struct HostUnavailable; /// Some error has occurred that is specific to the backend from which it was produced. /// /// This error is often used as a catch-all in cases where: /// /// - It is unclear exactly what error might be produced by the backend API. /// - It does not make sense to add a variant to the enclosing error type. /// - No error was expected to occur at all, but we return an error to avoid the possibility of a /// `panic!` caused by some unforseen or unknown reason. /// /// **Note:** If you notice a `BackendSpecificError` that you believe could be better handled in a /// cross-platform manner, please create an issue or submit a pull request with a patch that adds /// the necessary error variant to the appropriate error enum. #[derive(Clone, Debug, Fail)] #[fail(display = "A backend-specific error has occurred: {}", description)] pub struct BackendSpecificError { pub description: String } /// An error that might occur while attempting to enumerate the available devices on a system. #[derive(Debug, Fail)] pub enum DevicesError { /// See the `BackendSpecificError` docs for more information about this error variant. #[fail(display = "{}", err)] BackendSpecific { #[fail(cause)] err: BackendSpecificError, } } /// An error that may occur while attempting to retrieve a device name. #[derive(Debug, Fail)] pub enum DeviceNameError { /// See the `BackendSpecificError` docs for more information about this error variant. #[fail(display = "{}", err)] BackendSpecific { #[fail(cause)] err: BackendSpecificError, } } /// Error that can happen when enumerating the list of supported formats. #[derive(Debug, Fail)] pub enum SupportedFormatsError { /// The device no longer exists. This can happen if the device is disconnected while the /// program is running. #[fail(display = "The requested device is no longer available. For example, it has been unplugged.")] DeviceNotAvailable, /// We called something the C-Layer did not understand #[fail(display = "Invalid argument passed to the backend. For example, this happens when trying to read capture capabilities when the device does not support it.")] InvalidArgument, /// See the `BackendSpecificError` docs for more information about this error variant. #[fail(display = "{}", err)] BackendSpecific { #[fail(cause)] err: BackendSpecificError, } } /// May occur when attempting to request the default input or output stream format from a `Device`. #[derive(Debug, Fail)] pub enum DefaultFormatError { /// The device no longer exists. This can happen if the device is disconnected while the /// program is running. #[fail(display = "The requested device is no longer available. For example, it has been unplugged.")] DeviceNotAvailable, /// Returned if e.g. the default input format was requested on an output-only audio device. #[fail(display = "The requested stream type is not supported by the device.")] StreamTypeNotSupported, /// See the `BackendSpecificError` docs for more information about this error variant. #[fail(display = "{}", err)] BackendSpecific { #[fail(cause)] err: BackendSpecificError, } } /// Error that can happen when creating a `Stream`. #[derive(Debug, Fail)] pub enum BuildStreamError { /// The device no longer exists. This can happen if the device is disconnected while the /// program is running. #[fail(display = "The requested device is no longer available. For example, it has been unplugged.")] DeviceNotAvailable, /// The required format is not supported. #[fail(display = "The requested stream format is not supported by the device.")] FormatNotSupported, /// We called something the C-Layer did not understand /// /// On ALSA device functions called with a feature they do not support will yield this. E.g. /// Trying to use capture capabilities on an output only format yields this. #[fail(display = "The requested device does not support this capability (invalid argument)")] InvalidArgument, /// Occurs if adding a new Stream ID would cause an integer overflow. #[fail(display = "Adding a new stream ID would cause an overflow")] StreamIdOverflow, /// See the `BackendSpecificError` docs for more information about this error variant. #[fail(display = "{}", err)] BackendSpecific { #[fail(cause)] err: BackendSpecificError, } } /// Errors that might occur when calling `play_stream`. /// /// As of writing this, only macOS may immediately return an error while calling this method. This /// is because both the alsa and wasapi backends only enqueue these commands and do not process /// them immediately. #[derive(Debug, Fail)] pub enum PlayStreamError { /// The device associated with the stream is no longer available. #[fail(display = "the device associated with the stream is no longer available")] DeviceNotAvailable, /// See the `BackendSpecificError` docs for more information about this error variant. #[fail(display = "{}", err)] BackendSpecific { #[fail(cause)] err: BackendSpecificError, } } /// Errors that might occur when calling `pause_stream`. /// /// As of writing this, only macOS may immediately return an error while calling this method. This /// is because both the alsa and wasapi backends only enqueue these commands and do not process /// them immediately. #[derive(Debug, Fail)] pub enum PauseStreamError { /// The device associated with the stream is no longer available. #[fail(display = "the device associated with the stream is no longer available")] DeviceNotAvailable, /// See the `BackendSpecificError` docs for more information about this error variant. #[fail(display = "{}", err)] BackendSpecific { #[fail(cause)] err: BackendSpecificError, } } /// Errors that might occur while a stream is running. #[derive(Debug, Fail)] pub enum StreamError { /// The device no longer exists. This can happen if the device is disconnected while the /// program is running. #[fail(display = "The requested device is no longer available. For example, it has been unplugged.")] DeviceNotAvailable, /// See the `BackendSpecificError` docs for more information about this error variant. #[fail(display = "{}", err)] BackendSpecific { #[fail(cause)] err: BackendSpecificError, } } impl SupportedFormat { /// Turns this `SupportedFormat` into a `Format` corresponding to the maximum samples rate. #[inline] pub fn with_max_sample_rate(self) -> Format { Format { channels: self.channels, sample_rate: self.max_sample_rate, data_type: self.data_type, } } /// A comparison function which compares two `SupportedFormat`s in terms of their priority of /// use as a default stream format. /// /// Some backends do not provide a default stream format for their audio devices. In these /// cases, CPAL attempts to decide on a reasonable default format for the user. To do this we /// use the "greatest" of all supported stream formats when compared with this method. /// /// Formats are prioritised by the following heuristics: /// /// **Channels**: /// /// - Stereo /// - Mono /// - Max available channels /// /// **Sample format**: /// - f32 /// - i16 /// - u16 /// /// **Sample rate**: /// /// - 44100 (cd quality) /// - Max sample rate pub fn cmp_default_heuristics(&self, other: &Self) -> std::cmp::Ordering { use std::cmp::Ordering::Equal; use SampleFormat::{F32, I16, U16}; let cmp_stereo = (self.channels == 2).cmp(&(other.channels == 2)); if cmp_stereo != Equal { return cmp_stereo; } let cmp_mono = (self.channels == 1).cmp(&(other.channels == 1)); if cmp_mono != Equal { return cmp_mono; } let cmp_channels = self.channels.cmp(&other.channels); if cmp_channels != Equal { return cmp_channels; } let cmp_f32 = (self.data_type == F32).cmp(&(other.data_type == F32)); if cmp_f32 != Equal { return cmp_f32; } let cmp_i16 = (self.data_type == I16).cmp(&(other.data_type == I16)); if cmp_i16 != Equal { return cmp_i16; } let cmp_u16 = (self.data_type == U16).cmp(&(other.data_type == U16)); if cmp_u16 != Equal { return cmp_u16; } const HZ_44100: SampleRate = SampleRate(44_100); let r44100_in_self = self.min_sample_rate <= HZ_44100 && HZ_44100 <= self.max_sample_rate; let r44100_in_other = other.min_sample_rate <= HZ_44100 && HZ_44100 <= other.max_sample_rate; let cmp_r44100 = r44100_in_self.cmp(&r44100_in_other); if cmp_r44100 != Equal { return cmp_r44100; } self.max_sample_rate.cmp(&other.max_sample_rate) } } impl<'a, T> Deref for InputBuffer<'a, T> where T: Sample { type Target = [T]; #[inline] fn deref(&self) -> &[T] { self.buffer } } impl<'a, T> Deref for OutputBuffer<'a, T> where T: Sample { type Target = [T]; #[inline] fn deref(&self) -> &[T] { self.buffer } } impl<'a, T> DerefMut for OutputBuffer<'a, T> where T: Sample { #[inline] fn deref_mut(&mut self) -> &mut [T] { self.buffer } } impl<'a> UnknownTypeInputBuffer<'a> { /// Returns the length of the buffer in number of samples. #[inline] pub fn len(&self) -> usize { match self { &UnknownTypeInputBuffer::U16(ref buf) => buf.len(), &UnknownTypeInputBuffer::I16(ref buf) => buf.len(), &UnknownTypeInputBuffer::F32(ref buf) => buf.len(), } } } impl<'a> UnknownTypeOutputBuffer<'a> { /// Returns the length of the buffer in number of samples. #[inline] pub fn len(&self) -> usize { match self { &UnknownTypeOutputBuffer::U16(ref buf) => buf.len(), &UnknownTypeOutputBuffer::I16(ref buf) => buf.len(), &UnknownTypeOutputBuffer::F32(ref buf) => buf.len(), } } } impl From for SupportedFormat { #[inline] fn from(format: Format) -> SupportedFormat { SupportedFormat { channels: format.channels, min_sample_rate: format.sample_rate, max_sample_rate: format.sample_rate, data_type: format.data_type, } } } impl From for DevicesError { fn from(err: BackendSpecificError) -> Self { DevicesError::BackendSpecific { err } } } impl From for DeviceNameError { fn from(err: BackendSpecificError) -> Self { DeviceNameError::BackendSpecific { err } } } impl From for SupportedFormatsError { fn from(err: BackendSpecificError) -> Self { SupportedFormatsError::BackendSpecific { err } } } impl From for DefaultFormatError { fn from(err: BackendSpecificError) -> Self { DefaultFormatError::BackendSpecific { err } } } impl From for BuildStreamError { fn from(err: BackendSpecificError) -> Self { BuildStreamError::BackendSpecific { err } } } impl From for PlayStreamError { fn from(err: BackendSpecificError) -> Self { PlayStreamError::BackendSpecific { err } } } impl From for PauseStreamError { fn from(err: BackendSpecificError) -> Self { PauseStreamError::BackendSpecific { err } } } impl From for StreamError { fn from(err: BackendSpecificError) -> Self { StreamError::BackendSpecific { err } } } // If a backend does not provide an API for retrieving supported formats, we query it with a bunch // of commonly used rates. This is always the case for wasapi and is sometimes the case for alsa. // // If a rate you desire is missing from this list, feel free to add it! #[cfg(target_os = "windows")] const COMMON_SAMPLE_RATES: &'static [SampleRate] = &[ SampleRate(5512), SampleRate(8000), SampleRate(11025), SampleRate(16000), SampleRate(22050), SampleRate(32000), SampleRate(44100), SampleRate(48000), SampleRate(64000), SampleRate(88200), SampleRate(96000), SampleRate(176400), SampleRate(192000), ]; cpal-0.10.0/src/platform/mod.rs010066400037200003720000000474001350771351400145030ustar0000000000000000//! Platform-specific items. //! //! This module also contains the implementation of the platform's dynamically dispatched `Host` //! type and its associated `EventLoop`, `Device`, `StreamId` and other associated types. These //! types are useful in the case that users require switching between audio host APIs at runtime. #[doc(inline)] pub use self::platform_impl::*; // A macro to assist with implementing a platform's dynamically dispatched `Host` type. // // These dynamically dispatched types are necessary to allow for users to switch between hosts at // runtime. // // For example the invocation `impl_platform_host(Wasapi wasapi, Asio asio)`, this macro should // expand to: // // ``` // pub enum HostId { // Wasapi, // Asio, // } // // pub enum Host { // Wasapi(crate::host::wasapi::Host), // Asio(crate::host::asio::Host), // } // ``` // // And so on for Device, Devices, EventLoop, Host, StreamId, SupportedInputFormats, // SupportedOutputFormats and all their necessary trait implementations. // ``` macro_rules! impl_platform_host { ($($HostVariant:ident $host_mod:ident),*) => { /// All hosts supported by CPAL on this platform. pub const ALL_HOSTS: &'static [HostId] = &[ $( HostId::$HostVariant, )* ]; /// The platform's dynamically dispatched **Host** type. /// /// An instance of this **Host** type may represent one of any of the **Host**s available /// on the platform. /// /// Use this type if you require switching between available hosts at runtime. /// /// This type may be constructed via the **host_from_id** function. **HostId**s may /// be acquired via the **ALL_HOSTS** const and the **available_hosts** function. pub struct Host(HostInner); /// The **Device** implementation associated with the platform's dynamically dispatched /// **Host** type. pub struct Device(DeviceInner); /// The **Devices** iterator associated with the platform's dynamically dispatched **Host** /// type. pub struct Devices(DevicesInner); /// The **EventLoop** implementation associated with the platform's dynamically dispatched /// **Host** type. pub struct EventLoop(EventLoopInner); /// The **StreamId** implementation associated with the platform's dynamically dispatched /// **Host** type. #[derive(Clone, Debug, Eq, Hash, PartialEq)] pub struct StreamId(StreamIdInner); /// The **SupportedInputFormats** iterator associated with the platform's dynamically /// dispatched **Host** type. pub struct SupportedInputFormats(SupportedInputFormatsInner); /// The **SupportedOutputFormats** iterator associated with the platform's dynamically /// dispatched **Host** type. pub struct SupportedOutputFormats(SupportedOutputFormatsInner); /// Unique identifier for available hosts on the platform. #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] pub enum HostId { $( $HostVariant, )* } enum DeviceInner { $( $HostVariant(crate::host::$host_mod::Device), )* } enum DevicesInner { $( $HostVariant(crate::host::$host_mod::Devices), )* } enum EventLoopInner { $( $HostVariant(crate::host::$host_mod::EventLoop), )* } enum HostInner { $( $HostVariant(crate::host::$host_mod::Host), )* } #[derive(Clone, Debug, Eq, Hash, PartialEq)] enum StreamIdInner { $( $HostVariant(crate::host::$host_mod::StreamId), )* } enum SupportedInputFormatsInner { $( $HostVariant(crate::host::$host_mod::SupportedInputFormats), )* } enum SupportedOutputFormatsInner { $( $HostVariant(crate::host::$host_mod::SupportedOutputFormats), )* } impl Host { /// The unique identifier associated with this host. pub fn id(&self) -> HostId { match self.0 { $( HostInner::$HostVariant(_) => HostId::$HostVariant, )* } } } impl Iterator for Devices { type Item = Device; fn next(&mut self) -> Option { match self.0 { $( DevicesInner::$HostVariant(ref mut d) => { d.next().map(DeviceInner::$HostVariant).map(Device) } )* } } fn size_hint(&self) -> (usize, Option) { match self.0 { $( DevicesInner::$HostVariant(ref d) => d.size_hint(), )* } } } impl Iterator for SupportedInputFormats { type Item = crate::SupportedFormat; fn next(&mut self) -> Option { match self.0 { $( SupportedInputFormatsInner::$HostVariant(ref mut s) => s.next(), )* } } fn size_hint(&self) -> (usize, Option) { match self.0 { $( SupportedInputFormatsInner::$HostVariant(ref d) => d.size_hint(), )* } } } impl Iterator for SupportedOutputFormats { type Item = crate::SupportedFormat; fn next(&mut self) -> Option { match self.0 { $( SupportedOutputFormatsInner::$HostVariant(ref mut s) => s.next(), )* } } fn size_hint(&self) -> (usize, Option) { match self.0 { $( SupportedOutputFormatsInner::$HostVariant(ref d) => d.size_hint(), )* } } } impl crate::traits::DeviceTrait for Device { type SupportedInputFormats = SupportedInputFormats; type SupportedOutputFormats = SupportedOutputFormats; fn name(&self) -> Result { match self.0 { $( DeviceInner::$HostVariant(ref d) => d.name(), )* } } fn supported_input_formats(&self) -> Result { match self.0 { $( DeviceInner::$HostVariant(ref d) => { d.supported_input_formats() .map(SupportedInputFormatsInner::$HostVariant) .map(SupportedInputFormats) } )* } } fn supported_output_formats(&self) -> Result { match self.0 { $( DeviceInner::$HostVariant(ref d) => { d.supported_output_formats() .map(SupportedOutputFormatsInner::$HostVariant) .map(SupportedOutputFormats) } )* } } fn default_input_format(&self) -> Result { match self.0 { $( DeviceInner::$HostVariant(ref d) => d.default_input_format(), )* } } fn default_output_format(&self) -> Result { match self.0 { $( DeviceInner::$HostVariant(ref d) => d.default_output_format(), )* } } } impl crate::traits::EventLoopTrait for EventLoop { type StreamId = StreamId; type Device = Device; #[allow(unreachable_patterns)] fn build_input_stream( &self, device: &Self::Device, format: &crate::Format, ) -> Result { match (&self.0, &device.0) { $( (&EventLoopInner::$HostVariant(ref e), &DeviceInner::$HostVariant(ref d)) => { e.build_input_stream(d, format) .map(StreamIdInner::$HostVariant) .map(StreamId) } )* _ => panic!("tried to build a stream with a device from another host"), } } #[allow(unreachable_patterns)] fn build_output_stream( &self, device: &Self::Device, format: &crate::Format, ) -> Result { match (&self.0, &device.0) { $( (&EventLoopInner::$HostVariant(ref e), &DeviceInner::$HostVariant(ref d)) => { e.build_output_stream(d, format) .map(StreamIdInner::$HostVariant) .map(StreamId) } )* _ => panic!("tried to build a stream with a device from another host"), } } #[allow(unreachable_patterns)] fn play_stream(&self, stream: Self::StreamId) -> Result<(), crate::PlayStreamError> { match (&self.0, stream.0) { $( (&EventLoopInner::$HostVariant(ref e), StreamIdInner::$HostVariant(ref s)) => { e.play_stream(s.clone()) } )* _ => panic!("tried to play a stream with an ID associated with another host"), } } #[allow(unreachable_patterns)] fn pause_stream(&self, stream: Self::StreamId) -> Result<(), crate::PauseStreamError> { match (&self.0, stream.0) { $( (&EventLoopInner::$HostVariant(ref e), StreamIdInner::$HostVariant(ref s)) => { e.pause_stream(s.clone()) } )* _ => panic!("tried to pause a stream with an ID associated with another host"), } } #[allow(unreachable_patterns)] fn destroy_stream(&self, stream: Self::StreamId) { match (&self.0, stream.0) { $( (&EventLoopInner::$HostVariant(ref e), StreamIdInner::$HostVariant(ref s)) => { e.destroy_stream(s.clone()) } )* _ => panic!("tried to destroy a stream with an ID associated with another host"), } } fn run(&self, mut callback: F) -> ! where F: FnMut(Self::StreamId, crate::StreamDataResult) + Send { match self.0 { $( EventLoopInner::$HostVariant(ref e) => { e.run(|id, result| { let result = result; callback(StreamId(StreamIdInner::$HostVariant(id)), result); }); }, )* } } } impl crate::traits::HostTrait for Host { type Devices = Devices; type Device = Device; type EventLoop = EventLoop; fn is_available() -> bool { $( crate::host::$host_mod::Host::is_available() ||)* false } fn devices(&self) -> Result { match self.0 { $( HostInner::$HostVariant(ref h) => { h.devices().map(DevicesInner::$HostVariant).map(Devices) } )* } } fn default_input_device(&self) -> Option { match self.0 { $( HostInner::$HostVariant(ref h) => { h.default_input_device().map(DeviceInner::$HostVariant).map(Device) } )* } } fn default_output_device(&self) -> Option { match self.0 { $( HostInner::$HostVariant(ref h) => { h.default_output_device().map(DeviceInner::$HostVariant).map(Device) } )* } } fn event_loop(&self) -> Self::EventLoop { match self.0 { $( HostInner::$HostVariant(ref h) => { EventLoop(EventLoopInner::$HostVariant(h.event_loop())) } )* } } } impl crate::traits::StreamIdTrait for StreamId {} $( impl From for Device { fn from(h: crate::host::$host_mod::Device) -> Self { Device(DeviceInner::$HostVariant(h)) } } impl From for Devices { fn from(h: crate::host::$host_mod::Devices) -> Self { Devices(DevicesInner::$HostVariant(h)) } } impl From for EventLoop { fn from(h: crate::host::$host_mod::EventLoop) -> Self { EventLoop(EventLoopInner::$HostVariant(h)) } } impl From for Host { fn from(h: crate::host::$host_mod::Host) -> Self { Host(HostInner::$HostVariant(h)) } } impl From for StreamId { fn from(h: crate::host::$host_mod::StreamId) -> Self { StreamId(StreamIdInner::$HostVariant(h)) } } )* /// Produces a list of hosts that are currently available on the system. pub fn available_hosts() -> Vec { let mut host_ids = vec![]; $( if ::is_available() { host_ids.push(HostId::$HostVariant); } )* host_ids } /// Given a unique host identifier, initialise and produce the host if it is available. pub fn host_from_id(id: HostId) -> Result { match id { $( HostId::$HostVariant => { crate::host::$host_mod::Host::new() .map(HostInner::$HostVariant) .map(Host) } )* } } }; } // TODO: Add pulseaudio and jack here eventually. #[cfg(any(target_os = "linux", target_os = "freebsd"))] mod platform_impl { pub use crate::host::alsa::{ Device as AlsaDevice, Devices as AlsaDevices, EventLoop as AlsaEventLoop, Host as AlsaHost, StreamId as AlsaStreamId, SupportedInputFormats as AlsaSupportedInputFormats, SupportedOutputFormats as AlsaSupportedOutputFormats, }; impl_platform_host!(Alsa alsa); /// The default host for the current compilation target platform. pub fn default_host() -> Host { AlsaHost::new() .expect("the default host should always be available") .into() } } #[cfg(any(target_os = "macos", target_os = "ios"))] mod platform_impl { pub use crate::host::coreaudio::{ Device as CoreAudioDevice, Devices as CoreAudioDevices, EventLoop as CoreAudioEventLoop, Host as CoreAudioHost, StreamId as CoreAudioStreamId, SupportedInputFormats as CoreAudioSupportedInputFormats, SupportedOutputFormats as CoreAudioSupportedOutputFormats, }; impl_platform_host!(CoreAudio coreaudio); /// The default host for the current compilation target platform. pub fn default_host() -> Host { CoreAudioHost::new() .expect("the default host should always be available") .into() } } #[cfg(target_os = "emscripten")] mod platform_impl { pub use crate::host::emscripten::{ Device as EmscriptenDevice, Devices as EmscriptenDevices, EventLoop as EmscriptenEventLoop, Host as EmscriptenHost, StreamId as EmscriptenStreamId, SupportedInputFormats as EmscriptenSupportedInputFormats, SupportedOutputFormats as EmscriptenSupportedOutputFormats, }; impl_platform_host!(Emscripten emscripten); /// The default host for the current compilation target platform. pub fn default_host() -> Host { EmscriptenHost::new() .expect("the default host should always be available") .into() } } #[cfg(windows)] mod platform_impl { #[cfg(feature = "asio")] pub use crate::host::asio::{ Device as AsioDevice, Devices as AsioDevices, EventLoop as AsioEventLoop, Host as AsioHost, StreamId as AsioStreamId, SupportedInputFormats as AsioSupportedInputFormats, SupportedOutputFormats as AsioSupportedOutputFormats, }; pub use crate::host::wasapi::{ Device as WasapiDevice, Devices as WasapiDevices, EventLoop as WasapiEventLoop, Host as WasapiHost, StreamId as WasapiStreamId, SupportedInputFormats as WasapiSupportedInputFormats, SupportedOutputFormats as WasapiSupportedOutputFormats, }; #[cfg(feature = "asio")] impl_platform_host!(Asio asio, Wasapi wasapi); #[cfg(not(feature = "asio"))] impl_platform_host!(Wasapi wasapi); /// The default host for the current compilation target platform. pub fn default_host() -> Host { WasapiHost::new() .expect("the default host should always be available") .into() } } #[cfg(not(any(windows, target_os = "linux", target_os = "freebsd", target_os = "macos", target_os = "ios", target_os = "emscripten")))] mod platform_impl { pub use crate::host::null::{ Device as NullDevice, Devices as NullDevices, EventLoop as NullEventLoop, Host as NullHost, StreamId as NullStreamId, SupportedInputFormats as NullSupportedInputFormats, SupportedOutputFormats as NullSupportedOutputFormats, }; impl_platform_host!(Null null); /// The default host for the current compilation target platform. pub fn default_host() -> Host { NullHost::new() .expect("the default host should always be available") .into() } } cpal-0.10.0/src/samples_formats.rs010066400037200003720000000121251350771351400152730ustar0000000000000000use std::mem; /// Format that each sample has. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum SampleFormat { /// The value 0 corresponds to 0. I16, /// The value 0 corresponds to 32768. U16, /// The boundaries are (-1.0, 1.0). F32, } impl SampleFormat { /// Returns the size in bytes of a sample of this format. #[inline] pub fn sample_size(&self) -> usize { match self { &SampleFormat::I16 => mem::size_of::(), &SampleFormat::U16 => mem::size_of::(), &SampleFormat::F32 => mem::size_of::(), } } /// Deprecated. Use `sample_size` instead. #[inline] #[deprecated] pub fn get_sample_size(&self) -> usize { self.sample_size() } } /// Trait for containers that contain PCM data. pub unsafe trait Sample: Copy + Clone { /// Returns the `SampleFormat` corresponding to this data type. // TODO: rename to `format()`. Requires a breaking change. fn get_format() -> SampleFormat; /// Turns the sample into its equivalent as a floating-point. fn to_f32(&self) -> f32; /// Converts this sample into a standard i16 sample. fn to_i16(&self) -> i16; /// Converts this sample into a standard u16 sample. fn to_u16(&self) -> u16; /// Converts any sample type to this one by calling `to_i16`, `to_u16` or `to_f32`. fn from(&S) -> Self where S: Sample; } unsafe impl Sample for u16 { #[inline] fn get_format() -> SampleFormat { SampleFormat::U16 } #[inline] fn to_f32(&self) -> f32 { self.to_i16().to_f32() } #[inline] fn to_i16(&self) -> i16 { if *self >= 32768 { (*self - 32768) as i16 } else { (*self as i16) - 32767 - 1 } } #[inline] fn to_u16(&self) -> u16 { *self } #[inline] fn from(sample: &S) -> Self where S: Sample { sample.to_u16() } } unsafe impl Sample for i16 { #[inline] fn get_format() -> SampleFormat { SampleFormat::I16 } #[inline] fn to_f32(&self) -> f32 { if *self < 0 { *self as f32 / -(::std::i16::MIN as f32) } else { *self as f32 / ::std::i16::MAX as f32 } } #[inline] fn to_i16(&self) -> i16 { *self } #[inline] fn to_u16(&self) -> u16 { if *self < 0 { (*self - ::std::i16::MIN) as u16 } else { (*self as u16) + 32768 } } #[inline] fn from(sample: &S) -> Self where S: Sample { sample.to_i16() } } unsafe impl Sample for f32 { #[inline] fn get_format() -> SampleFormat { SampleFormat::F32 } #[inline] fn to_f32(&self) -> f32 { *self } #[inline] fn to_i16(&self) -> i16 { if *self >= 0.0 { (*self * ::std::i16::MAX as f32) as i16 } else { (-*self * ::std::i16::MIN as f32) as i16 } } #[inline] fn to_u16(&self) -> u16 { (((*self + 1.0) * 0.5) * ::std::u16::MAX as f32).round() as u16 } #[inline] fn from(sample: &S) -> Self where S: Sample { sample.to_f32() } } #[cfg(test)] mod test { use super::Sample; #[test] fn i16_to_i16() { assert_eq!(0i16.to_i16(), 0); assert_eq!((-467i16).to_i16(), -467); assert_eq!(32767i16.to_i16(), 32767); assert_eq!((-32768i16).to_i16(), -32768); } #[test] fn i16_to_u16() { assert_eq!(0i16.to_u16(), 32768); assert_eq!((-16384i16).to_u16(), 16384); assert_eq!(32767i16.to_u16(), 65535); assert_eq!((-32768i16).to_u16(), 0); } #[test] fn i16_to_f32() { assert_eq!(0i16.to_f32(), 0.0); assert_eq!((-16384i16).to_f32(), -0.5); assert_eq!(32767i16.to_f32(), 1.0); assert_eq!((-32768i16).to_f32(), -1.0); } #[test] fn u16_to_i16() { assert_eq!(32768u16.to_i16(), 0); assert_eq!(16384u16.to_i16(), -16384); assert_eq!(65535u16.to_i16(), 32767); assert_eq!(0u16.to_i16(), -32768); } #[test] fn u16_to_u16() { assert_eq!(0u16.to_u16(), 0); assert_eq!(467u16.to_u16(), 467); assert_eq!(32767u16.to_u16(), 32767); assert_eq!(65535u16.to_u16(), 65535); } #[test] fn u16_to_f32() { assert_eq!(0u16.to_f32(), -1.0); assert_eq!(32768u16.to_f32(), 0.0); assert_eq!(65535u16.to_f32(), 1.0); } #[test] fn f32_to_i16() { assert_eq!(0.0f32.to_i16(), 0); assert_eq!((-0.5f32).to_i16(), ::std::i16::MIN / 2); assert_eq!(1.0f32.to_i16(), ::std::i16::MAX); assert_eq!((-1.0f32).to_i16(), ::std::i16::MIN); } #[test] fn f32_to_u16() { assert_eq!((-1.0f32).to_u16(), 0); assert_eq!(0.0f32.to_u16(), 32768); assert_eq!(1.0f32.to_u16(), 65535); } #[test] fn f32_to_f32() { assert_eq!(0.1f32.to_f32(), 0.1); assert_eq!((-0.7f32).to_f32(), -0.7); assert_eq!(1.0f32.to_f32(), 1.0); } } cpal-0.10.0/src/traits.rs010066400037200003720000000203531350771351400134040ustar0000000000000000//! The suite of traits allowing CPAL to abstract over hosts, devices, event loops and stream IDs. use { BuildStreamError, DefaultFormatError, DeviceNameError, DevicesError, Format, InputDevices, OutputDevices, PauseStreamError, PlayStreamError, StreamDataResult, SupportedFormat, SupportedFormatsError, }; /// A **Host** provides access to the available audio devices on the system. /// /// Each platform may have a number of available hosts depending on the system, each with their own /// pros and cons. /// /// For example, WASAPI is the standard audio host API that ships with the Windows operating /// system. However, due to historical limitations with respect to performance and flexibility, /// Steinberg created the ASIO API providing better audio device support for pro audio and /// low-latency applications. As a result, it is common for some devices and device capabilities to /// only be available via ASIO, while others are only available via WASAPI. /// /// Another great example is the Linux platform. While the ALSA host API is the lowest-level API /// available to almost all distributions of Linux, its flexibility is limited as it requires that /// each process have exclusive access to the devices with which they establish streams. PulseAudio /// is another popular host API that aims to solve this issue by providing user-space mixing, /// however it has its own limitations w.r.t. low-latency and high-performance audio applications. /// JACK is yet another host API that is more suitable to pro-audio applications, however it is /// less readily available by default in many Linux distributions and is known to be tricky to /// setup. pub trait HostTrait { /// The type used for enumerating available devices by the host. type Devices: Iterator; /// The `Device` type yielded by the host. type Device: DeviceTrait; /// The event loop type used by the `Host` type EventLoop: EventLoopTrait; /// Whether or not the host is available on the system. fn is_available() -> bool; /// An iterator yielding all `Device`s currently available to the host on the system. /// /// Can be empty if the system does not support audio in general. fn devices(&self) -> Result; /// The default input audio device on the system. /// /// Returns `None` if no input device is available. fn default_input_device(&self) -> Option; /// The default output audio device on the system. /// /// Returns `None` if no output device is available. fn default_output_device(&self) -> Option; /// Initialise the event loop, ready for managing audio streams. fn event_loop(&self) -> Self::EventLoop; /// An iterator yielding all `Device`s currently available to the system that support one or more /// input stream formats. /// /// Can be empty if the system does not support audio input. fn input_devices(&self) -> Result, DevicesError> { fn supports_input(device: &D) -> bool { device.supported_input_formats() .map(|mut iter| iter.next().is_some()) .unwrap_or(false) } Ok(self.devices()?.filter(supports_input::)) } /// An iterator yielding all `Device`s currently available to the system that support one or more /// output stream formats. /// /// Can be empty if the system does not support audio output. fn output_devices(&self) -> Result, DevicesError> { fn supports_output(device: &D) -> bool { device.supported_output_formats() .map(|mut iter| iter.next().is_some()) .unwrap_or(false) } Ok(self.devices()?.filter(supports_output::)) } } /// A device that is capable of audio input and/or output. /// /// Please note that `Device`s may become invalid if they get disconnected. Therefore all the /// methods that involve a device return a `Result` allowing the user to handle this case. pub trait DeviceTrait { /// The iterator type yielding supported input stream formats. type SupportedInputFormats: Iterator; /// The iterator type yielding supported output stream formats. type SupportedOutputFormats: Iterator; /// The human-readable name of the device. fn name(&self) -> Result; /// An iterator yielding formats that are supported by the backend. /// /// Can return an error if the device is no longer valid (eg. it has been disconnected). fn supported_input_formats(&self) -> Result; /// An iterator yielding output stream formats that are supported by the device. /// /// Can return an error if the device is no longer valid (eg. it has been disconnected). fn supported_output_formats(&self) -> Result; /// The default input stream format for the device. fn default_input_format(&self) -> Result; /// The default output stream format for the device. fn default_output_format(&self) -> Result; } /// Collection of streams managed together. /// /// Created with the `Host::event_loop` method. pub trait EventLoopTrait { /// The `Device` type yielded by the host. type Device: DeviceTrait; /// The type used to uniquely distinguish between streams. type StreamId: StreamIdTrait; /// Creates a new input stream that will run from the given device and with the given format. /// /// On success, returns an identifier for the stream. /// /// Can return an error if the device is no longer valid, or if the input stream format is not /// supported by the device. fn build_input_stream( &self, device: &Self::Device, format: &Format, ) -> Result; /// Creates a new output stream that will play on the given device and with the given format. /// /// On success, returns an identifier for the stream. /// /// Can return an error if the device is no longer valid, or if the output stream format is not /// supported by the device. fn build_output_stream( &self, device: &Self::Device, format: &Format, ) -> Result; /// Instructs the audio device that it should start playing the stream with the given ID. /// /// Has no effect is the stream was already playing. /// /// Only call this after you have submitted some data, otherwise you may hear some glitches. /// /// # Panic /// /// If the stream does not exist, this function can either panic or be a no-op. fn play_stream(&self, stream: Self::StreamId) -> Result<(), PlayStreamError>; /// Instructs the audio device that it should stop playing the stream with the given ID. /// /// Has no effect is the stream was already paused. /// /// If you call `play` afterwards, the playback will resume where it was. /// /// # Panic /// /// If the stream does not exist, this function can either panic or be a no-op. fn pause_stream(&self, stream: Self::StreamId) -> Result<(), PauseStreamError>; /// Destroys an existing stream. /// /// # Panic /// /// If the stream does not exist, this function can either panic or be a no-op. fn destroy_stream(&self, stream: Self::StreamId); /// Takes control of the current thread and begins the stream processing. /// /// > **Note**: Since it takes control of the thread, this method is best called on a separate /// > thread. /// /// Whenever a stream needs to be fed some data, the closure passed as parameter is called. /// You can call the other methods of `EventLoop` without getting a deadlock. fn run(&self, callback: F) -> ! where F: FnMut(Self::StreamId, StreamDataResult) + Send; } /// The set of required bounds for host `StreamId` types. pub trait StreamIdTrait: Clone + std::fmt::Debug + std::hash::Hash + PartialEq + Eq {} cpal-0.10.0/.cargo_vcs_info.json0000644000000001120000000000000120620ustar00{ "git": { "sha1": "0f393f48a53ec012c449c88ffb6953f6006062cc" } }