{sysaudio,examples}: get sysaudio running on linux, separate audio configuration and descriptor (#518)

* Get sysaudio working on linux
* Separate audio configuration and descriptor
* Config/Descriptor -> Options/Properties
- Rename sysaudio DeviceConfig and DeviceDescriptor to Device.Options and Device.Properties
- example: Convert buffer before passing to renderWithType
* make Device.start() idempotent
This commit is contained in:
Louis Pearson 2022-09-09 09:58:03 -06:00 committed by GitHub
parent f807c85232
commit 0e71daf504
Failed to generate hash of commit
4 changed files with 193 additions and 98 deletions

View file

@ -1,9 +1,12 @@
const std = @import("std");
const Mode = @import("main.zig").Mode;
const DeviceDescriptor = @import("main.zig").DeviceDescriptor;
const DeviceOptions = @import("main.zig").DeviceOptions;
const DeviceProperties = @import("main.zig").DeviceProperties;
const Format = @import("main.zig").Format;
const c = @import("soundio").c;
const Aim = @import("soundio").Aim;
const SoundIo = @import("soundio").SoundIo;
const SoundIoFormat = @import("soundio").Format;
const SoundIoDevice = @import("soundio").Device;
const SoundIoInStream = @import("soundio").InStream;
const SoundIoOutStream = @import("soundio").OutStream;
@ -21,13 +24,17 @@ else
*const fn (device: *Device, user_data: ?*anyopaque, buffer: []u8) void;
pub const Device = struct {
descriptor: DeviceDescriptor,
properties: Properties,
// Internal fields.
handle: SoundIoStream,
data_callback: ?DataCallback = null,
user_data: ?*anyopaque = null,
planar_buffer: [512000]u8 = undefined,
started: bool = false,
pub const Options = DeviceOptions;
pub const Properties = DeviceProperties;
pub fn deinit(self: *Device, allocator: std.mem.Allocator) void {
switch (self.handle) {
@ -111,6 +118,7 @@ pub const Device = struct {
}
pub fn pause(device: *Device) Error!void {
if (!device.started) return;
return (switch (device.handle) {
.input => |d| d.pause(true),
.output => |d| d.pause(true),
@ -124,15 +132,28 @@ pub const Device = struct {
pub fn start(device: *Device) Error!void {
// TODO(sysaudio): after pause, may need to call d.pause(false) instead of d.start()?
return (switch (device.handle) {
.input => |d| d.start(),
.output => |d| d.start(),
}) catch |err| {
return switch (err) {
error.OutOfMemory => error.OutOfMemory,
else => @panic(@errorName(err)),
if (!device.started) {
device.started = true;
return (switch (device.handle) {
.input => |d| d.start(),
.output => |d| d.start(),
}) catch |err| {
return switch (err) {
error.OutOfMemory => error.OutOfMemory,
else => @panic(@errorName(err)),
};
};
};
} else {
return (switch (device.handle) {
.input => |d| d.pause(false),
.output => |d| d.pause(false),
}) catch |err| {
return switch (err) {
error.OutOfMemory => error.OutOfMemory,
else => @panic(@errorName(err)),
};
};
}
}
};
@ -142,14 +163,14 @@ pub const DeviceIterator = struct {
device_len: u16,
index: u16,
pub fn next(self: *DeviceIterator) IteratorError!?DeviceDescriptor {
pub fn next(self: *DeviceIterator) IteratorError!?DeviceOptions {
if (self.index < self.device_len) {
const device_desc = switch (self.mode) {
.input => self.ctx.handle.getInputDevice(self.index) orelse return null,
.output => self.ctx.handle.getOutputDevice(self.index) orelse return null,
};
self.index += 1;
return DeviceDescriptor{
return DeviceOptions{
.mode = switch (@intToEnum(Aim, device_desc.handle.aim)) {
.input => .input,
.output => .output,
@ -217,18 +238,18 @@ pub fn waitEvents(self: Audio) void {
self.handle.waitEvents();
}
pub fn requestDevice(self: Audio, allocator: std.mem.Allocator, config: DeviceDescriptor) Error!*Device {
pub fn requestDevice(self: Audio, allocator: std.mem.Allocator, options: DeviceOptions) Error!*Device {
var sio_device: SoundIoDevice = undefined;
if (config.id) |id| {
if (config.mode == null or config.is_raw == null)
if (options.id) |id| {
if (options.is_raw == null)
return error.InvalidParameter;
sio_device = switch (config.mode.?) {
.input => self.handle.getInputDeviceFromID(id, config.is_raw.?),
.output => self.handle.getOutputDeviceFromID(id, config.is_raw.?),
sio_device = switch (options.mode) {
.input => self.handle.getInputDeviceFromID(id, options.is_raw.?),
.output => self.handle.getOutputDeviceFromID(id, options.is_raw.?),
} orelse {
return if (switch (config.mode.?) {
return if (switch (options.mode) {
.input => self.handle.inputDeviceCount().?,
.output => self.handle.outputDeviceCount().?,
} == 0)
@ -237,19 +258,17 @@ pub fn requestDevice(self: Audio, allocator: std.mem.Allocator, config: DeviceDe
error.DeviceUnavailable;
};
} else {
if (config.mode == null) return error.InvalidParameter;
const id = switch (config.mode.?) {
const id = switch (options.mode) {
.input => self.handle.defaultInputDeviceIndex(),
.output => self.handle.defaultOutputDeviceIndex(),
} orelse return error.NoDeviceFound;
sio_device = switch (config.mode.?) {
sio_device = switch (options.mode) {
.input => self.handle.getInputDevice(id),
.output => self.handle.getOutputDevice(id),
} orelse return error.DeviceUnavailable;
}
const handle = switch (config.mode.?) {
const handle = switch (options.mode) {
.input => SoundIoStream{ .input = try sio_device.createInStream() },
.output => SoundIoStream{ .output = try sio_device.createOutStream() },
};
@ -264,21 +283,52 @@ pub fn requestDevice(self: Audio, allocator: std.mem.Allocator, config: DeviceDe
.input => |d| d.handle.userdata = device,
.output => |d| d.handle.userdata = device,
}
var descriptor = config;
descriptor.mode = descriptor.mode orelse .output;
descriptor.channels = @intCast(u8, switch (handle) {
.input => |d| d.layout().channelCount(),
.output => |d| d.layout().channelCount(),
});
descriptor.sample_rate = @intCast(u32, switch (handle) {
.input => |d| d.sampleRate(),
.output => |d| d.sampleRate(),
});
std.log.info("channels {}", .{descriptor.channels.?});
std.log.info("sample_rate {}\n", .{descriptor.sample_rate.?});
// TODO(sysaudio): handle big endian architectures
const format: Format = switch (handle) {
.input => |d| switch (@intToEnum(SoundIoFormat, d.handle.format)) {
.U8 => .U8,
.S16LE => .S16,
.S24LE => .S24,
.S32LE => .S32,
.float32LE => .F32,
else => return error.InvalidParameter,
},
.output => |d| switch (@intToEnum(SoundIoFormat, d.handle.format)) {
.U8 => .U8,
.S16LE => .S16,
.S24LE => .S24,
.S32LE => .S32,
.float32LE => .F32,
else => return error.InvalidParameter,
},
};
// TODO(sysaudio): Get the device name. Calling span or sliceTo on the name is causing segfaults on NixOS
// const name_ptr = switch(handle) {
// .input => |d| d.handle.name,
// .output => |d| d.handle.name,
// };
// const name = std.mem.sliceTo(name_ptr, 0);
var properties = DeviceProperties{
.is_raw = options.is_raw orelse false,
.format = format,
.mode = options.mode,
.id = std.mem.span(sio_device.handle.id),
.name = "",
.channels = @intCast(u8, switch (handle) {
.input => |d| d.layout().channelCount(),
.output => |d| d.layout().channelCount(),
}),
.sample_rate = @intCast(u32, switch (handle) {
.input => |d| d.sampleRate(),
.output => |d| d.sampleRate(),
}),
};
device.* = .{
.descriptor = descriptor,
.properties = properties,
.handle = handle,
};
return device;