Something went wrong. Try again.
A pure Zig PipeWire client library that speaks the native wire protocol to play audio and manage the graph, with no libpipewire and no libc.
Something went wrong. Try again.
2.0 kB · 52 lines
Zig
at main
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253// SPDX-FileCopyrightText: © 2026 Jeffrey C. Ollie <jeff@ocjtech.us>// SPDX-License-Identifier: MIT
//! Says how long after each cycle starts its audio is heard, as the graph//! reckons it for the sink the stream was linked to. It plays silence, so it//! can be run beside anything.//!//! zig build run-latency//!//! An ALSA sink says one quantum; a Bluetooth sink adds its codec's delay,//! which is usually the larger part. Move the stream to another sink with//! `pavucontrol` or `wpctl` while it runs and the figure follows.
const std = @import("std");const pw = @import("pipewire");
var stream_ptr: std.atomic.Value(?*pw.Stream) = .init(null);/// How far ahead of the clock each cycle's first frame is heard, as the/// callback saw it, in microseconds.var ahead_us: std.atomic.Value(i64) = .init(0);
fn process(_: *anyopaque, planes: []const []f32, frames: u32) void { for (planes) |plane| @memset(plane[0..frames], 0); const stream = stream_ptr.load(.acquire) orelse return; const t = stream.time(); ahead_us.store(@divTrunc(t.presentationNs() - @as(i64, @intCast(pw.nowNsec())), std.time.ns_per_us), .monotonic);}
pub fn main(init: std.process.Init) !void { var ctx: u8 = 0; const stream = try pw.Stream.open(init.gpa, .{ .name = "zig-pipewire latency", .channels = 2, .environ = init.minimal.environ, .process = .{ .ctx = &ctx, .func = process }, }); defer stream.close(); stream_ptr.store(stream, .release);
if (!stream.waitStreaming(5000)) return error.NotStreaming; for (0..5) |_| { pw.sleep(std.time.ns_per_s); const t = stream.time(); std.debug.print("{d} Hz, {d} frames a cycle: heard {d} frames ({d:.1} ms) after the cycle starts, {d} us from now\n", .{ t.rate, t.quantum, t.delay, @as(f64, @floatFromInt(t.delayNs())) / std.time.ns_per_ms, ahead_us.load(.monotonic), }); }}