Playing audio files with libao
Categories:
Playing Audio Files with libao: A C Developer's Guide

Learn how to integrate libao into your C applications to play various audio formats like WAV and AIFF on Linux systems, covering initialization, device selection, and audio output.
Playing audio files programmatically is a common requirement for many applications, from games to media players. On Linux, libao
provides a simple, cross-platform audio output library that abstracts away the complexities of different audio drivers and APIs (like ALSA, PulseAudio, OSS). This article will guide you through the process of using libao
in a C program to play audio files, focusing on common formats like WAV and AIFF.
Understanding libao Basics
libao
is designed to be a straightforward audio output library. It handles the low-level details of sending audio data to your sound card. Before you can play any audio, you need to initialize the library, select an output device, and open it with the correct audio format parameters (sample rate, number of channels, bits per sample). Once the device is open, you can write raw audio data to it.
flowchart TD A[Start Application] --> B{Initialize libao}; B --> C{Select Default Driver}; C --> D{Define Audio Format (Rate, Channels, Bits)}; D --> E{Open Audio Device}; E --> F{Read Audio Data from File}; F --> G{Write Data to Audio Device}; G -- Loop until EOF --> F; G --> H{Close Audio Device}; H --> I{Shutdown libao}; I --> J[End Application];
Basic libao Audio Playback Workflow
Setting Up Your Development Environment
Before you can compile and run libao
examples, you need to install the library and its development headers. On most Debian-based systems (like Ubuntu), you can do this using apt
.
sudo apt update
sudo apt install libao-dev
Installing libao development files on Debian/Ubuntu
dnf install libao-devel
for Fedora, pacman -S libao
for Arch Linux). Ensure you install the development package, which typically ends with -dev
or -devel
.Playing a Raw PCM Stream
The simplest way to use libao
is to feed it raw Pulse Code Modulation (PCM) data. This requires you to know the audio file's format (sample rate, channels, bit depth) beforehand. For demonstration, let's assume we have a WAV file and we've already parsed its header to get these details. The following C code snippet illustrates the core libao
functions for opening a device and writing audio data.
#include <ao/ao.h>
#include <stdio.h>
#include <stdlib.h>
#define BITS 16
int main(int argc, char *argv[]) {
ao_device *device;
ao_sample_format format;
int default_driver;
char *buffer;
int buf_size;
FILE *infile;
int num_bytes_read;
if (argc < 2) {
fprintf(stderr, "Usage: %s <audio_file.raw>\n", argv[0]);
return 1;
}
ao_initialize();
default_driver = ao_default_driver_id();
format.bits = BITS;
format.channels = 2; /* Stereo */
format.rate = 44100; /* 44.1 kHz */
format.byte_format = AO_FMT_LITTLE;
format.matrix = 0;
device = ao_open_live(default_driver, &format, NULL);
if (device == NULL) {
fprintf(stderr, "Error opening device.\n");
return 1;
}
infile = fopen(argv[1], "rb");
if (infile == NULL) {
fprintf(stderr, "Error opening input file %s.\n", argv[1]);
ao_close(device);
ao_shutdown();
return 1;
}
/* Buffer for 4KB of audio data */
buf_size = 4096;
buffer = (char *)calloc(buf_size, sizeof(char));
while ((num_bytes_read = fread(buffer, 1, buf_size, infile)) > 0) {
ao_play(device, buffer, num_bytes_read);
}
free(buffer);
fclose(infile);
ao_close(device);
ao_shutdown();
return 0;
}
Basic C program to play a raw PCM audio stream using libao
ao_sample_format
.Compiling and Running the Example
To compile the C code, you need to link against the libao
library. Use pkg-config
to get the correct compiler flags.
gcc -o play_raw play_raw.c $(pkg-config --cflags --libs libao)
./play_raw your_audio_file.raw
Compiling and running the libao example
If you don't have a raw PCM file, you can convert a WAV file to raw PCM using sox
or ffmpeg
for testing purposes. For example, to convert a WAV to 16-bit, stereo, 44.1kHz little-endian raw PCM:
ffmpeg -i input.wav -f s16le -acodec pcm_s16le -ar 44100 -ac 2 output.raw
Converting a WAV file to raw PCM using ffmpeg
Integrating with WAV/AIFF Parsers
For a robust audio player, you would typically use a library to parse WAV or AIFF headers to extract the audio format information. Libraries like libsndfile
are excellent for this purpose, as they handle various audio file formats and provide a unified API to read audio data. Once you've read the header and determined the sample rate, channels, and bit depth, you can pass this information to ao_open_live
.
sequenceDiagram participant App as Application participant File as Audio File (WAV/AIFF) participant Libao as libao Library participant SoundCard as Sound Card App->>File: Open file App->>File: Read header (get format) App->>Libao: ao_initialize() App->>Libao: ao_default_driver_id() App->>Libao: ao_open_live(driver, format, NULL) Libao->>SoundCard: Initialize audio device loop while data available App->>File: Read audio data block App->>Libao: ao_play(device, buffer, size) Libao->>SoundCard: Write audio data end App->>Libao: ao_close(device) Libao->>SoundCard: Release audio device App->>Libao: ao_shutdown() App->>File: Close file
Sequence Diagram for Playing WAV/AIFF with libao and a Parser