aycb-alsa-lib/README.md

279 lines
6.9 KiB
Markdown

# 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 access
- `copy` - Simple audio copying
- `linear` - Linear format conversion
- `null` - Null audio sink
- `empty` - Empty audio source
- `adpcm` - ADPCM compression
- `alaw` - A-law compression
- `mulaw` - μ-law compression
- `iec958` - S/PDIF digital audio
- `lfloat` - Linear floating point conversion
- `mmap_emul` - Memory mapping emulation
- `route` - 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`:
```zig
.dependencies = .{
.alsa = .{
.path = "../path/to/0-alsa-lib",
},
},
```
### 2. Link in build.zig
```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
```zig
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(&params);
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.
```bash
# 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
```bash
# 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:
```zig
// 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
1. **"Unknown PCM device"**
- Device doesn't exist or wrong card/device number
- Check `/proc/asound/cards` and `/proc/asound/pcm`
2. **"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
3. **"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
```bash
# 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+).