Allyourcodebase style zig build for libalsa. Static build, your mileage may vary
Find a file
2025-09-09 20:11:59 -07:00
.gitignore alsa-lib ported to zig-build 2025-09-09 17:01:24 -07:00
.mise.toml make this work for x86_64, aarch64, and riscv64 2025-09-09 20:11:59 -07:00
build.zig make this work for x86_64, aarch64, and riscv64 2025-09-09 20:11:59 -07:00
build.zig.zon add c files to build.zig.zon 2025-09-09 18:43:10 -07:00
ctl_symbols_list.c add README and update license to match upstream 2025-09-09 18:36:44 -07:00
LICENSE add README and update license to match upstream 2025-09-09 18:36:44 -07:00
pcm_symbols_list.c add README and update license to match upstream 2025-09-09 18:36:44 -07:00
README.md add README and update license to match upstream 2025-09-09 18:36:44 -07:00
timer_symbols_list.c add README and update license to match upstream 2025-09-09 18:36:44 -07:00

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:

.dependencies = .{
    .alsa = .{
        .path = "../path/to/0-alsa-lib",
    },
},
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(&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.

# 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

  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

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