6.9 KiB
6.9 KiB
Static ALSA Library for Zig
A minimal, self-contained static build of ALSA (Advanced Linux Sound Architecture) designed for embedding in Zig applications.
What This Library Provides
Included Components
- Core ALSA: Configuration, error handling, async operations
- PCM (Audio Playback/Recording): Hardware access, format conversion, routing
- Control: Hardware mixer controls, card enumeration
- Timer: Hardware timer access
- Built-in Plugins: Essential audio processing without dynamic loading
Built-in PCM Plugins
hw
- Direct hardware accesscopy
- Simple audio copyinglinear
- Linear format conversionnull
- Null audio sinkempty
- Empty audio sourceadpcm
- ADPCM compressionalaw
- A-law compressionmulaw
- μ-law compressioniec958
- S/PDIF digital audiolfloat
- Linear floating point conversionmmap_emul
- Memory mapping emulationroute
- Audio channel routing
Limitations
What's NOT Included
- Dynamic Plugin Loading: No
dlopen()
support - only built-in plugins work - Complex Plugins: No
dmix
,dsnoop
,pulse
,jack
plugins - Rate Conversion: No
rate
plugin (would need external rate converter) - Advanced Routing: No
multi
,asym
plugins - Software Volume: No
softvol
plugin - File I/O: No
file
plugin for audio recording to disk
Audio Limitations
- Only direct hardware access (
hw:X,Y
) and built-in conversions - No automatic sample rate conversion
- No software mixing of multiple streams
- No PulseAudio/JACK integration
Integration into Zig Projects
1. Add as Dependency
In your build.zig.zon
:
.dependencies = .{
.alsa = .{
.path = "../path/to/0-alsa-lib",
},
},
2. Link in build.zig
const std = @import("std");
pub fn build(b: *std.Build) void {
const exe = b.addExecutable(.{
.name = "my-audio-app",
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
});
// Add ALSA dependency
const alsa_dep = b.dependency("alsa", .{});
const alsa_lib = alsa_dep.artifact("asound");
exe.linkLibC();
exe.linkLibrary(alsa_lib);
exe.addIncludePath(alsa_dep.path("zig-out/include"));
b.installArtifact(exe);
}
3. Basic Usage Example
const std = @import("std");
const c = @cImport({
@cInclude("alsa/asoundlib.h");
});
pub fn main() !void {
var pcm: ?*c.snd_pcm_t = null;
// Open PCM device for recording
const result = c.snd_pcm_open(
&pcm,
"hw:0,0", // Card 0, Device 0
c.SND_PCM_STREAM_CAPTURE,
0
);
if (result < 0) {
std.log.err("Cannot open audio device: {s}", .{c.snd_strerror(result)});
return;
}
defer _ = c.snd_pcm_close(pcm);
// Configure PCM parameters
var params: ?*c.snd_pcm_hw_params_t = null;
_ = c.snd_pcm_hw_params_malloc(¶ms);
defer c.snd_pcm_hw_params_free(params);
_ = c.snd_pcm_hw_params_any(pcm, params);
_ = c.snd_pcm_hw_params_set_access(pcm, params, c.SND_PCM_ACCESS_RW_INTERLEAVED);
_ = c.snd_pcm_hw_params_set_format(pcm, params, c.SND_PCM_FORMAT_S16_LE);
_ = c.snd_pcm_hw_params_set_channels(pcm, params, 1);
var rate: c_uint = 16000;
_ = c.snd_pcm_hw_params_set_rate_near(pcm, params, &rate, null);
_ = c.snd_pcm_hw_params(pcm, params);
// Read audio data
var buffer: [1024]i16 = undefined;
const frames_read = c.snd_pcm_readi(pcm, &buffer, buffer.len / 2);
std.log.info("Read {} frames", .{frames_read});
}
Configuration
ALSA Configuration File
The library uses ALSA's configuration system. The configuration file defines how device names are resolved and what plugins are available.
Critical: Without proper configuration, even basic hw:X,Y
device access may fail.
# Set custom config path (recommended)
export ALSA_CONFIG_PATH=/path/to/your/alsa.conf
# Run your application
./my-audio-app
# Or disable config entirely (hardware-only, may not work)
export ALSA_CONFIG_PATH=/dev/null
Example alsa.conf (Minimal Working Configuration)
This is the minimal configuration needed for hw:X,Y
device access:
# Essential: Define the hw PCM type
pcm.hw {
@args [ CARD DEV SUBDEV ]
@args.CARD {
type string
default 0
}
@args.DEV {
type integer
default 0
}
@args.SUBDEV {
type integer
default -1
}
type hw
card $CARD
device $DEV
subdevice $SUBDEV
}
# Optional: Define default devices
pcm.!default {
type hw
card 0
device 0
}
ctl.!default {
type hw
card 0
}
# Optional: Define named devices
pcm.microphone {
type hw
card 3
device 0
}
Configuration Behavior
- With minimal alsa.conf:
hw:X,Y
device access works - Without alsa.conf (
ALSA_CONFIG_PATH=/dev/null
): May fail with "Unknown PCM" errors - With system config: May fail if it references plugins not built into this static library
Device Selection
Hardware Device Format
hw:CARD,DEVICE
Examples:
hw:0,0
- Card 0, Device 0 (usually built-in audio)hw:1,0
- Card 1, Device 0 (usually USB audio)hw:3,0
- Card 3, Device 0 (specific USB device)
Finding Available Devices
# List audio cards
cat /proc/asound/cards
# List PCM devices
cat /proc/asound/pcm
# Test device access
aplay -l # playback devices
arecord -l # capture devices
Build Configuration
The library is configured for static linking with these key settings:
// In build.zig - these are already set
.HAVE_LIBDL = 1, // Keep dlmisc.c but use static symbols
.BUILD_PCM_PLUGIN_* = "1", // Enable built-in plugins
// PIC undefined // Enable static plugin symbols
Compiler Flags
-DALSA_STATIC
- Static library build-DBUILD_STATIC_ONLY
- No shared library support-UPIC
- Undefine PIC to enable built-in plugin symbols
Troubleshooting
Common Issues
-
"Unknown PCM device"
- Device doesn't exist or wrong card/device number
- Check
/proc/asound/cards
and/proc/asound/pcm
-
"No such device or address"
- Device is busy or doesn't support the requested format
- Try different sample rate/format or check if device is in use
-
"Cannot open shared library [builtin]"
- PIC is defined - this shouldn't happen with this build
- Indicates dynamic loading attempt (build configuration error)
Debug Environment Variables
# Enable ALSA debug output
export ALSA_DEBUG=1
# Use minimal config
export ALSA_CONFIG_PATH=/dev/null
# Specify custom config
export ALSA_CONFIG_PATH=./my-alsa.conf
Performance Characteristics
- Memory: ~200KB static library
- Latency: Direct hardware access (no software mixing overhead)
- CPU: Minimal - only format conversion overhead for built-in plugins
- Compatibility: Works with any ALSA-compatible hardware
License
This static build follows the original ALSA library licensing (LGPL 2.1+).