# 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(¶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. ```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+).