#audio #sound


Low-level cross-platform audio playing library in pure Rust

54 releases (8 breaking)

0.8.2 Jul 3, 2018
0.8.0 Feb 15, 2018
0.6.0 Dec 11, 2017
0.5.1 Oct 21, 2017
0.0.5 Dec 30, 2014

#11 in Audio

Download history 1440/week @ 2019-01-21 1296/week @ 2019-01-28 1221/week @ 2019-02-04 1393/week @ 2019-02-11 1382/week @ 2019-02-18 1157/week @ 2019-02-25 1387/week @ 2019-03-04 1343/week @ 2019-03-11 1220/week @ 2019-03-18 1548/week @ 2019-03-25 1364/week @ 2019-04-01 1290/week @ 2019-04-08 1797/week @ 2019-04-15 1730/week @ 2019-04-22 1591/week @ 2019-04-29

5,791 downloads per month
Used in 52 crates (14 directly)



CPAL - Cross-Platform Audio Library

Build Status Crates.io docs.rs

Low-level library for audio input and output in pure Rust.

This library currently supports the following:

  • 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 backends include:

  • Linux (via ALSA)
  • Windows
  • macOS (via CoreAudio)
  • iOS (via CoreAudio)
  • Emscripten


How to use cpal

Here are some concepts cpal exposes:

  • A Device is an audio device that may have any number of input and output streams.
  • A stream is an open audio channel. Input streams allow you to receive audio data, output streams allow you to play audio data. You must choose which Device runs your stream before you create one.
  • An EventLoop is a collection of streams being run by one or more Device. Each stream must belong to an EventLoop, and all the streams that belong to an EventLoop are managed together.

The first step is to create an EventLoop:

use cpal::EventLoop;
let event_loop = EventLoop::new();

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.

let device = cpal::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.

# let device = cpal::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?!")

Now that we have everything, we can create a stream from our event loop:

# let device = cpal::default_output_device().unwrap();
# let format = device.supported_output_formats().unwrap().next().unwrap().with_max_sample_rate();
# let event_loop = cpal::EventLoop::new();
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.

# let event_loop: cpal::EventLoop = return;
# let stream_id: cpal::StreamId = return;

Once everything is ready! Now we call run() on the event_loop to begin processing.

# let event_loop = cpal::EventLoop::new();
event_loop.run(move |_stream_id, _stream_data| {
    // 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 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 simply fill the given output buffer with zeroes.

use cpal::{StreamData, UnknownTypeOutputBuffer};

# let event_loop = cpal::EventLoop::new();
event_loop.run(move |_stream_id, mut stream_data| {
    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;
        _ => (),


~11K SLoC

  • linux dragonfly freebsd openbsd alsa-sys 0.1
  • macos ios core-foundation-sys 0.5.1
  • macos ios coreaudio-rs 0.9+audio_unit+core_audio
  • linux dragonfly freebsd openbsd libc 0.2
  • emscripten stdweb 0.1.3
  • windows winapi 0.3+audiosessiontypes+audioclient+coml2api+combaseapi+debug+devpkey+handleapi+ksmedia+mmdeviceapi+objbase+std+synchapi+winuser
  • lazy_static 1.0