sysaudio: improve backend selection; remove i8 sample support (#767)

* sysaudio: fix compilation errors
* re-order backend selection
* remove i8 samples support from backends and disable signedToSigned convertion for now
* update sine-wave example
This commit is contained in:
Ali Chraghi 2023-04-30 16:36:39 -07:00 committed by GitHub
parent 8fbc36999f
commit ed05166348
Failed to generate hash of commit
8 changed files with 64 additions and 89 deletions

View file

@ -44,14 +44,13 @@ pub fn main() !void {
const pitch = 440.0;
const radians_per_second = pitch * 2.0 * std.math.pi;
var seconds_offset: f32 = 0.0;
fn writeCallback(_: ?*anyopaque, n_frame: usize) void {
fn writeCallback(_: ?*anyopaque, frames: usize) void {
const seconds_per_frame = 1.0 / @intToFloat(f32, player.sampleRate());
var frame: usize = 0;
while (frame < n_frame) : (frame += 1) {
const sample = std.math.sin((seconds_offset + @intToFloat(f32, frame) * seconds_per_frame) * radians_per_second);
player.writeAll(frame, sample);
for (0..frames) |fi| {
const sample = std.math.sin((seconds_offset + @intToFloat(f32, fi) * seconds_per_frame) * radians_per_second);
player.writeAll(fi, sample);
}
seconds_offset = @mod(seconds_offset + seconds_per_frame * @intToFloat(f32, n_frame), 1.0);
seconds_offset = @mod(seconds_offset + seconds_per_frame * @intToFloat(f32, frames), 1.0);
}
fn deviceChange(_: ?*anyopaque) void {

View file

@ -16,7 +16,9 @@ pub fn Sdk(comptime deps: anytype) type {
_module = b.createModule(.{
.source_file = .{ .path = sdkPath("/src/main.zig") },
.dependencies = &.{
.{ .name = "sysjs", .module = deps.sysjs.module(b) },
.{ .name = "sysjs", .module = b.createModule(.{
.source_file = .{ .path = "libs/mach-sysjs/src/main.zig" },
}) },
},
});
return _module.?;

View file

@ -17,7 +17,7 @@ const lib = struct {
var snd_pcm_state: *const fn (?*c.snd_pcm_t) callconv(.C) c.snd_pcm_state_t = undefined;
var snd_pcm_pause: *const fn (?*c.snd_pcm_t, c_int) callconv(.C) c_int = undefined;
var snd_pcm_writei: *const fn (?*c.snd_pcm_t, ?*const anyopaque, c.snd_pcm_uframes_t) callconv(.C) c.snd_pcm_sframes_t = undefined;
var snd_pcm_recover: *const fn (?*c.snd_pcm_t, c_int, c_int) callconv(.C) c_int = undefined;
var snd_pcm_prepare: *const fn (?*c.snd_pcm_t) callconv(.C) c_int = undefined;
var snd_pcm_info_set_device: *const fn (?*c.snd_pcm_info_t, c_uint) callconv(.C) void = undefined;
var snd_pcm_info_set_subdevice: *const fn (?*c.snd_pcm_info_t, c_uint) callconv(.C) void = undefined;
var snd_pcm_info_get_name: *const fn (?*const c.snd_pcm_info_t) callconv(.C) [*c]const u8 = undefined;
@ -71,7 +71,7 @@ const lib = struct {
snd_pcm_state = handle.lookup(@TypeOf(snd_pcm_state), "snd_pcm_state") orelse return error.SymbolLookup;
snd_pcm_pause = handle.lookup(@TypeOf(snd_pcm_pause), "snd_pcm_pause") orelse return error.SymbolLookup;
snd_pcm_writei = handle.lookup(@TypeOf(snd_pcm_writei), "snd_pcm_writei") orelse return error.SymbolLookup;
snd_pcm_recover = handle.lookup(@TypeOf(snd_pcm_recover), "snd_pcm_recover") orelse return error.SymbolLookup;
snd_pcm_prepare = handle.lookup(@TypeOf(snd_pcm_prepare), "snd_pcm_prepare") orelse return error.SymbolLookup;
snd_pcm_info_set_device = handle.lookup(@TypeOf(snd_pcm_info_set_device), "snd_pcm_info_set_device") orelse return error.SymbolLookup;
snd_pcm_info_set_subdevice = handle.lookup(@TypeOf(snd_pcm_info_set_subdevice), "snd_pcm_info_set_subdevice") orelse return error.SymbolLookup;
snd_pcm_info_get_name = handle.lookup(@TypeOf(snd_pcm_info_get_name), "snd_pcm_info_get_name") orelse return error.SymbolLookup;
@ -402,10 +402,7 @@ pub const Context = struct {
var fmt_arr = std.ArrayList(main.Format).init(self.allocator);
inline for (std.meta.tags(main.Format)) |format| {
if (lib.snd_pcm_format_mask_test(
fmt_mask,
toAlsaFormat(format) catch unreachable,
) != 0) {
if (lib.snd_pcm_format_mask_test(fmt_mask, toAlsaFormat(format)) != 0) {
try fmt_arr.append(format);
}
}
@ -465,7 +462,7 @@ pub const Context = struct {
if ((lib.snd_pcm_set_params(
pcm,
toAlsaFormat(format) catch unreachable,
toAlsaFormat(format),
c.SND_PCM_ACCESS_RW_INTERLEAVED,
@intCast(c_uint, device.channels.len),
sample_rate,
@ -584,19 +581,16 @@ pub const Player = struct {
ch.*.ptr = self.sample_buffer.ptr + self.format.frameSize(i);
}
var underrun = false;
while (!self.aborted.load(.Unordered)) {
var frames_left = self.period_size;
while (frames_left > 0) {
self.writeFn(self.user_data, frames_left);
const n = lib.snd_pcm_writei(self.pcm, self.sample_buffer.ptr, frames_left);
if (n < 0) {
if (lib.snd_pcm_recover(self.pcm, @intCast(c_int, n), 1) < 0) {
if (std.debug.runtime_safety) unreachable;
return;
}
return;
}
frames_left -= @intCast(c_uint, n);
if (!underrun) {
self.writeFn(self.user_data, self.period_size);
}
underrun = false;
const n = lib.snd_pcm_writei(self.pcm, self.sample_buffer.ptr, self.period_size);
if (n < 0) {
_ = lib.snd_pcm_prepare(self.pcm);
underrun = true;
}
}
}
@ -676,10 +670,9 @@ pub fn modeToStream(mode: main.Device.Mode) c_uint {
};
}
pub fn toAlsaFormat(format: main.Format) !c.snd_pcm_format_t {
pub fn toAlsaFormat(format: main.Format) c.snd_pcm_format_t {
return switch (format) {
.u8 => c.SND_PCM_FORMAT_U8,
.i8 => c.SND_PCM_FORMAT_S8,
.i16 => if (is_little) c.SND_PCM_FORMAT_S16_LE else c.SND_PCM_FORMAT_S16_BE,
.i24 => if (is_little) c.SND_PCM_FORMAT_S24_3LE else c.SND_PCM_FORMAT_S24_3BE,
.i24_4b => if (is_little) c.SND_PCM_FORMAT_S24_LE else c.SND_PCM_FORMAT_S24_BE,

View file

@ -4,16 +4,16 @@ const std = @import("std");
pub const Backend = std.meta.Tag(BackendContext);
pub const BackendContext = switch (builtin.os.tag) {
.linux => union(enum) {
pulseaudio: *@import("pulseaudio.zig").Context,
jack: *@import("jack.zig").Context,
pipewire: *@import("pipewire.zig").Context,
alsa: *@import("alsa.zig").Context,
pipewire: *@import("pipewire.zig").Context,
jack: *@import("jack.zig").Context,
pulseaudio: *@import("pulseaudio.zig").Context,
dummy: *@import("dummy.zig").Context,
},
.freebsd, .netbsd, .openbsd, .solaris => union(enum) {
pulseaudio: *@import("pulseaudio.zig").Context,
jack: *@import("jack.zig").Context,
pipewire: *@import("pipewire.zig").Context,
jack: *@import("jack.zig").Context,
pulseaudio: *@import("pulseaudio.zig").Context,
dummy: *@import("dummy.zig").Context,
},
.macos, .ios, .watchos, .tvos => union(enum) {
@ -39,16 +39,16 @@ pub const BackendContext = switch (builtin.os.tag) {
};
pub const BackendPlayer = switch (builtin.os.tag) {
.linux => union(enum) {
pulseaudio: *@import("pulseaudio.zig").Player,
jack: *@import("jack.zig").Player,
pipewire: *@import("pipewire.zig").Player,
alsa: *@import("alsa.zig").Player,
pipewire: *@import("pipewire.zig").Player,
jack: *@import("jack.zig").Player,
pulseaudio: *@import("pulseaudio.zig").Player,
dummy: *@import("dummy.zig").Player,
},
.freebsd, .netbsd, .openbsd, .solaris => union(enum) {
pulseaudio: *@import("pulseaudio.zig").Player,
jack: *@import("jack.zig").Player,
pipewire: *@import("pipewire.zig").Player,
jack: *@import("jack.zig").Player,
pulseaudio: *@import("pulseaudio.zig").Player,
dummy: *@import("dummy.zig").Player,
},
.macos, .ios, .watchos, .tvos => union(enum) {

View file

@ -337,7 +337,7 @@ pub const Context = struct {
return error.OpeningDevice;
}
const stream_desc = createStreamDesc(options.format, options.sample_rate, device.channels.len);
const stream_desc = try createStreamDesc(options.format, options.sample_rate, device.channels.len);
if (c.AudioUnitSetProperty(
audio_unit,
c.kAudioUnitProperty_StreamFormat,
@ -487,7 +487,6 @@ fn createStreamDesc(format: main.Format, sample_rate: u24, ch_count: usize) !c.A
.mSampleRate = @intToFloat(f64, sample_rate),
.mFormatID = c.kAudioFormatLinearPCM,
.mFormatFlags = switch (format) {
.i8 => c.kAudioFormatFlagIsSignedInteger,
.i16 => c.kAudioFormatFlagIsSignedInteger,
.i24 => c.kAudioFormatFlagIsSignedInteger,
.i32 => c.kAudioFormatFlagIsSignedInteger,
@ -500,7 +499,6 @@ fn createStreamDesc(format: main.Format, sample_rate: u24, ch_count: usize) !c.A
.mBytesPerFrame = format.frameSize(ch_count),
.mChannelsPerFrame = @intCast(c_uint, ch_count),
.mBitsPerChannel = switch (format) {
.i8 => 8,
.i16 => 16,
.i24 => 24,
.i32 => 32,

View file

@ -213,24 +213,17 @@ pub const Player = struct {
f32 => floatToUnsigned(u8, sample),
else => unreachable,
},
.i8 => std.mem.bytesAsValue(i8, ptr[0..@sizeOf(i8)]).* = switch (@TypeOf(sample)) {
i8 => sample,
u8 => unsignedToSigned(i8, sample),
i16, i24, i32 => signedToSigned(i8, sample),
f32 => floatToSigned(i8, sample),
else => unreachable,
},
.i16 => std.mem.bytesAsValue(i16, ptr[0..@sizeOf(i16)]).* = switch (@TypeOf(sample)) {
i16 => sample,
u8 => unsignedToSigned(i16, sample),
i8, i24, i32 => signedToSigned(i16, sample),
// i8, i24, i32 => signedToSigned(i16, sample),
f32 => floatToSigned(i16, sample),
else => unreachable,
},
.i24 => std.mem.bytesAsValue(i24, ptr[0..@sizeOf(i24)]).* = switch (@TypeOf(sample)) {
i24 => sample,
u8 => unsignedToSigned(i24, sample),
i8, i16, i32 => signedToSigned(i24, sample),
// i8, i16, i32 => signedToSigned(i24, sample),
f32 => floatToSigned(i24, sample),
else => unreachable,
},
@ -238,7 +231,7 @@ pub const Player = struct {
.i32 => std.mem.bytesAsValue(i32, ptr[0..@sizeOf(i32)]).* = switch (@TypeOf(sample)) {
i32 => sample,
u8 => unsignedToSigned(i32, sample),
i8, i16, i24 => signedToSigned(i32, sample),
// i8, i16, i24 => signedToSigned(i32, sample),
f32 => floatToSigned(i32, sample),
else => unreachable,
},
@ -377,7 +370,6 @@ pub const Channel = struct {
pub const Format = enum {
u8,
i8,
i16,
i24,
i24_4b,
@ -386,7 +378,7 @@ pub const Format = enum {
pub fn size(self: Format) u8 {
return switch (self) {
.u8, .i8 => 1,
.u8 => 1,
.i16 => 2,
.i24 => 3,
.i24_4b, .i32, .f32 => 4,
@ -395,7 +387,7 @@ pub const Format = enum {
pub fn validSize(self: Format) u8 {
return switch (self) {
.u8, .i8 => 1,
.u8 => 1,
.i16 => 2,
.i24, .i24_4b => 3,
.i32, .f32 => 4,

View file

@ -327,7 +327,7 @@ pub const Context = struct {
const sample_rate = device.sample_rate.clamp(options.sample_rate);
const sample_spec = c.pa_sample_spec{
.format = toPAFormat(format) catch unreachable,
.format = toPAFormat(format),
.rate = sample_rate,
.channels = @intCast(u5, device.channels.len),
};
@ -459,33 +459,28 @@ pub const Player = struct {
var self = @ptrCast(*Player, @alignCast(@alignOf(*Player), user_data.?));
var frames_left = nbytes;
while (frames_left > 0) {
var chunk_size = frames_left;
if (lib.pa_stream_begin_write(
self.stream,
@ptrCast(
[*c]?*anyopaque,
@alignCast(@alignOf([*c]?*anyopaque), &self.write_ptr),
),
&chunk_size,
) != 0) {
if (std.debug.runtime_safety) unreachable;
return;
}
if (lib.pa_stream_begin_write(
self.stream,
@ptrCast(
[*c]?*anyopaque,
@alignCast(@alignOf([*c]?*anyopaque), &self.write_ptr),
),
&frames_left,
) != 0) {
if (std.debug.runtime_safety) unreachable;
return;
}
for (self.channels, 0..) |*ch, i| {
ch.*.ptr = self.write_ptr + self.format.frameSize(i);
}
for (self.channels, 0..) |*ch, i| {
ch.*.ptr = self.write_ptr + self.format.frameSize(i);
}
const frames = chunk_size / self.format.frameSize(self.channels.len);
self.writeFn(self.user_data, frames);
const frames = frames_left / self.format.frameSize(self.channels.len);
self.writeFn(self.user_data, frames);
if (lib.pa_stream_write(self.stream, self.write_ptr, chunk_size, null, 0, c.PA_SEEK_RELATIVE) != 0) {
if (std.debug.runtime_safety) unreachable;
return;
}
frames_left -= chunk_size;
if (lib.pa_stream_write(self.stream, self.write_ptr, frames_left, null, 0, c.PA_SEEK_RELATIVE) != 0) {
if (std.debug.runtime_safety) unreachable;
return;
}
}
@ -616,7 +611,7 @@ pub fn fromPAChannelPos(pos: c.pa_channel_position_t) !main.Channel.Id {
c.PA_CHANNEL_POSITION_SIDE_RIGHT => .side_right,
// TODO: .front_center?
c.PA_CHANNEL_POSITION_AUX0...c.PA_CHANNEL_POSITION_AUX31 => error.Invalid,
c.PA_CHANNEL_POSITION_AUX0...c.PA_CHANNEL_POSITION_AUX31 => .front_center,
c.PA_CHANNEL_POSITION_TOP_CENTER => .top_center,
c.PA_CHANNEL_POSITION_TOP_FRONT_LEFT => .top_front_left,
@ -630,7 +625,7 @@ pub fn fromPAChannelPos(pos: c.pa_channel_position_t) !main.Channel.Id {
};
}
pub fn toPAFormat(format: main.Format) !c.pa_sample_format_t {
pub fn toPAFormat(format: main.Format) c.pa_sample_format_t {
return switch (format) {
.u8 => c.PA_SAMPLE_U8,
.i16 => if (is_little) c.PA_SAMPLE_S16LE else c.PA_SAMPLE_S16BE,
@ -638,8 +633,6 @@ pub fn toPAFormat(format: main.Format) !c.pa_sample_format_t {
.i24_4b => if (is_little) c.PA_SAMPLE_S24_32LE else c.PA_SAMPLE_S24_32BE,
.i32 => if (is_little) c.PA_SAMPLE_S32LE else c.PA_SAMPLE_S32BE,
.f32 => if (is_little) c.PA_SAMPLE_FLOAT32LE else c.PA_SAMPLE_FLOAT32BE,
.i8 => error.Invalid,
};
}

View file

@ -327,7 +327,7 @@ pub const Context = struct {
var fmt_arr = std.ArrayList(main.Format).init(self.allocator);
var closest_match: ?*win32.WAVEFORMATEX = null;
for (std.meta.tags(main.Format)) |format| {
setWaveFormatFormat(wf, format) catch continue;
setWaveFormatFormat(wf, format);
if (audio_client.?.IsFormatSupported(
.SHARED,
@ptrCast(?*const win32.WAVEFORMATEX, @alignCast(@alignOf(*win32.WAVEFORMATEX), wf)),
@ -417,7 +417,7 @@ pub const Context = struct {
};
}
fn setWaveFormatFormat(wf: *win32.WAVEFORMATEXTENSIBLE, format: main.Format) !void {
fn setWaveFormatFormat(wf: *win32.WAVEFORMATEXTENSIBLE, format: main.Format) void {
switch (format) {
.u8, .i16, .i24, .i24_4b, .i32 => {
wf.SubFormat = win32.CLSID_KSDATAFORMAT_SUBTYPE_PCM.*;
@ -425,7 +425,6 @@ pub const Context = struct {
.f32 => {
wf.SubFormat = win32.CLSID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT.*;
},
.i8 => return error.Invalid,
}
wf.Format.wBitsPerSample = format.sizeBits();
wf.Samples.wValidBitsPerSample = format.validSizeBits();
@ -488,7 +487,7 @@ pub const Context = struct {
.wValidBitsPerSample = format.validSizeBits(),
},
.dwChannelMask = toChannelMask(device.channels),
.SubFormat = toSubFormat(format) catch return error.OpeningDevice,
.SubFormat = toSubFormat(format),
};
if (!self.is_wine and audio_client3 != null) {
@ -608,7 +607,7 @@ pub const Context = struct {
return .{ .wasapi = player };
}
fn toSubFormat(format: main.Format) !win32.Guid {
fn toSubFormat(format: main.Format) win32.Guid {
return switch (format) {
.u8,
.i16,
@ -617,7 +616,6 @@ pub const Context = struct {
.i32,
=> win32.CLSID_KSDATAFORMAT_SUBTYPE_PCM.*,
.f32 => win32.CLSID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT.*,
else => error.Invalid,
};
}