From cadaa3e85bddabf9f6c4f5ebf87274dccb597e74 Mon Sep 17 00:00:00 2001 From: Ali Chraghi Date: Sat, 11 May 2024 21:25:13 +0330 Subject: [PATCH] sysaudio: revive i24 samples --- src/sysaudio/alsa.zig | 3 +- src/sysaudio/conv.zig | 409 +++++++++++++++++------------------- src/sysaudio/coreaudio.zig | 6 +- src/sysaudio/main.zig | 122 +++++------ src/sysaudio/pulseaudio.zig | 6 +- src/sysaudio/wasapi.zig | 7 +- 6 files changed, 262 insertions(+), 291 deletions(-) diff --git a/src/sysaudio/alsa.zig b/src/sysaudio/alsa.zig index 1367f984..e067753c 100644 --- a/src/sysaudio/alsa.zig +++ b/src/sysaudio/alsa.zig @@ -779,8 +779,7 @@ pub fn toAlsaFormat(format: main.Format) c.snd_pcm_format_t { return switch (format) { .u8 => c.SND_PCM_FORMAT_U8, .i16 => if (is_little) c.SND_PCM_FORMAT_S16_LE else c.SND_PCM_FORMAT_S16_BE, - // TODO(i24) - // .i24 => if (is_little) c.SND_PCM_FORMAT_S24_3LE else c.SND_PCM_FORMAT_S24_3BE, + .i24 => if (is_little) c.SND_PCM_FORMAT_S24_3LE else c.SND_PCM_FORMAT_S24_3BE, .i32 => if (is_little) c.SND_PCM_FORMAT_S32_LE else c.SND_PCM_FORMAT_S32_BE, .f32 => if (is_little) c.SND_PCM_FORMAT_FLOAT_LE else c.SND_PCM_FORMAT_FLOAT_BE, }; diff --git a/src/sysaudio/conv.zig b/src/sysaudio/conv.zig index 7a21759d..49aa6c54 100644 --- a/src/sysaudio/conv.zig +++ b/src/sysaudio/conv.zig @@ -1,19 +1,27 @@ const std = @import("std"); -const expectEqual = std.testing.expectEqual; +const asBytes = std.mem.asBytes; +const bytesAsValue = std.mem.bytesAsValue; const shl = std.math.shl; const shr = std.math.shr; const maxInt = std.math.maxInt; +const expectEqual = std.testing.expectEqual; +// TODO: SIMD pub fn unsignedToSigned( comptime SrcType: type, - src: []const SrcType, - comptime DestType: type, - dst: []DestType, + src_stride: u8, + src: []const u8, + comptime DstType: type, + dst_stride: u8, + dst: []u8, + len: usize, ) void { - for (src, dst) |*src_sample, *dst_sample| { - const half = (maxInt(SrcType) + 1) / 2; - const trunc = @bitSizeOf(DestType) - @bitSizeOf(SrcType); - dst_sample.* = shl(DestType, @intCast(src_sample.* -% half), trunc); + const half = (maxInt(SrcType) + 1) / 2; + const trunc = @bitSizeOf(DstType) - @bitSizeOf(SrcType); + for (0..len) |i| { + const src_sample: *const SrcType = @ptrCast(@alignCast(src[i * src_stride ..][0..src_stride])); + const dst_sample: DstType = shl(DstType, @intCast(src_sample.* -% half), trunc); + @memcpy(dst[i * dst_stride ..][0..dst_stride], asBytes(&dst_sample)[0..dst_stride]); } } @@ -22,43 +30,55 @@ test unsignedToSigned { var u8_to_i24: [1]i24 = undefined; var u8_to_i32: [1]i32 = undefined; - unsignedToSigned(u8, &.{5}, i16, &u8_to_i16); - unsignedToSigned(u8, &.{5}, i24, &u8_to_i24); - unsignedToSigned(u8, &.{5}, i32, &u8_to_i32); + unsignedToSigned(u8, 1, &[_]u8{5}, i16, 2, asBytes(&u8_to_i16), 1); + unsignedToSigned(u8, 1, &[_]u8{5}, i24, 3, asBytes(&u8_to_i24), 1); + unsignedToSigned(u8, 1, &[_]u8{5}, i32, 4, asBytes(&u8_to_i32), 1); try expectEqual(@as(i16, -31488), u8_to_i16[0]); try expectEqual(@as(i24, -8060928), u8_to_i24[0]); try expectEqual(@as(i32, -2063597568), u8_to_i32[0]); } +// TODO: SIMD pub fn unsignedToFloat( comptime SrcType: type, - src: []const SrcType, - comptime DestType: type, - dst: []DestType, + src_stride: u8, + src: []const u8, + comptime DstType: type, + dst_stride: u8, + dst: []u8, + len: usize, ) void { - for (src, dst) |*src_sample, *dst_sample| { - const half = (maxInt(SrcType) + 1) / 2; - dst_sample.* = (@as(DestType, @floatFromInt(src_sample.*)) - half) * 1.0 / half; + const half = (maxInt(SrcType) + 1) / 2; + for (0..len) |i| { + const src_sample: *const SrcType = @ptrCast(@alignCast(src[i * src_stride ..][0..src_stride])); + const dst_sample: DstType = (@as(DstType, @floatFromInt(src_sample.*)) - half) * 1.0 / half; + @memcpy(dst[i * dst_stride ..][0..dst_stride], asBytes(&dst_sample)[0..dst_stride]); } } test unsignedToFloat { var u8_to_f32: [1]f32 = undefined; - unsignedToFloat(u8, &.{5}, f32, &u8_to_f32); + unsignedToFloat(u8, 1, &[_]u8{5}, f32, 4, asBytes(&u8_to_f32), 1); try expectEqual(@as(f32, -0.9609375), u8_to_f32[0]); } +// TODO: SIMD pub fn signedToUnsigned( comptime SrcType: type, - src: []const SrcType, - comptime DestType: type, - dst: []DestType, + src_stride: u8, + src: []const u8, + comptime DstType: type, + dst_stride: u8, + dst: []u8, + len: usize, ) void { - for (src, dst) |*src_sample, *dst_sample| { - const half = (maxInt(DestType) + 1) / 2; - const trunc = @bitSizeOf(SrcType) - @bitSizeOf(DestType); - dst_sample.* = shr(DestType, @intCast(src_sample.*), trunc) + half; + const half = (maxInt(DstType) + 1) / 2; + const trunc = @bitSizeOf(SrcType) - @bitSizeOf(DstType); + for (0..len) |i| { + const src_sample: *const SrcType = @ptrCast(@alignCast(src[i * src_stride ..][0..src_stride])); + const dst_sample: DstType = shr(DstType, @intCast(src_sample.*), trunc) + half; + @memcpy(dst[i * dst_stride ..][0..dst_stride], asBytes(&dst_sample)[0..dst_stride]); } } @@ -67,9 +87,9 @@ test signedToUnsigned { var i24_to_u8: [1]u8 = undefined; var i32_to_u8: [1]u8 = undefined; - signedToUnsigned(i16, &.{5}, u8, &i16_to_u8); - signedToUnsigned(i24, &.{5}, u8, &i24_to_u8); - signedToUnsigned(i32, &.{5}, u8, &i32_to_u8); + signedToUnsigned(i16, 2, asBytes(&[_]i16{5}), u8, 1, asBytes(&i16_to_u8), 1); + signedToUnsigned(i24, 3, asBytes(&[_]i24{5}), u8, 1, asBytes(&i24_to_u8), 1); + signedToUnsigned(i32, 4, asBytes(&[_]i32{5}), u8, 1, asBytes(&i32_to_u8), 1); try expectEqual(@as(u8, 128), i16_to_u8[0]); try expectEqual(@as(u8, 128), i24_to_u8[0]); @@ -78,31 +98,38 @@ test signedToUnsigned { pub fn signedToSigned( comptime SrcType: type, - src: []const SrcType, - comptime DestType: type, - dst: []DestType, + src_stride: u8, + src: []const u8, + comptime DstType: type, + dst_stride: u8, + dst: []u8, + len: usize, ) void { - // TODO: Uncomment this (zig crashes) - // if (std.simd.suggestVectorLength(SrcType)) |_| { - // signedToSignedSIMD(SrcType, src, DestType, dst); - // } else { - signedToSignedScalar(SrcType, src, DestType, dst); - // } -} + const trunc = @bitSizeOf(SrcType) - @bitSizeOf(DstType); + var i: usize = 0; -pub fn signedToSignedScalar( - comptime SrcType: type, - src: []const SrcType, - comptime DestType: type, - dst: []DestType, -) void { - for (src, dst) |*src_sample, *dst_sample| { - const trunc = @bitSizeOf(SrcType) - @bitSizeOf(DestType); - dst_sample.* = shr(DestType, @intCast(src_sample.*), trunc); + // Use SIMD when available + if (std.simd.suggestVectorLength(SrcType)) |vec_size| { + const VecSrc = @Vector(vec_size, SrcType); + const VecDst = @Vector(vec_size, DstType); + const vec_blocks_len = len - (len % vec_size); + while (i < vec_blocks_len) : (i += vec_size) { + const src_vec = bytesAsValue(VecSrc, src[i * src_stride ..][0 .. vec_size * src_stride]).*; + const dst_sample: VecDst = shr(VecDst, @intCast(src_vec), trunc); + @memcpy(dst[i * dst_stride ..][0 .. vec_size * dst_stride], asBytes(&dst_sample)[0 .. vec_size * dst_stride]); + } + } + + // Convert the remaining samples + + while (i < len) : (i += 1) { + const src_sample: *const SrcType = @ptrCast(@alignCast(src[i * src_stride ..][0..src_stride])); + const dst_sample: DstType = shr(DstType, @intCast(src_sample.*), trunc); + @memcpy(dst[i * dst_stride ..][0..dst_stride], asBytes(&dst_sample)[0..dst_stride]); } } -test signedToSignedScalar { +test "signedToSigned single" { var i16_to_i24: [1]i24 = undefined; var i16_to_i32: [1]i32 = undefined; var i24_to_i16: [1]i16 = undefined; @@ -110,14 +137,14 @@ test signedToSignedScalar { var i32_to_i16: [1]i16 = undefined; var i32_to_i24: [1]i24 = undefined; - signedToSignedScalar(i24, &.{5}, i16, &i24_to_i16); - signedToSignedScalar(i32, &.{5}, i16, &i32_to_i16); + signedToSigned(i24, 3, asBytes(&[_]i24{5}), i16, 2, asBytes(&i24_to_i16), 1); + signedToSigned(i32, 4, asBytes(&[_]i32{5}), i16, 2, asBytes(&i32_to_i16), 1); - signedToSignedScalar(i16, &.{5}, i24, &i16_to_i24); - signedToSignedScalar(i32, &.{5}, i24, &i32_to_i24); + signedToSigned(i16, 2, asBytes(&[_]i16{5}), i24, 3, asBytes(&i16_to_i24), 1); + signedToSigned(i32, 4, asBytes(&[_]i32{5}), i24, 3, asBytes(&i32_to_i24), 1); - signedToSignedScalar(i16, &.{5}, i32, &i16_to_i32); - signedToSignedScalar(i24, &.{5}, i32, &i24_to_i32); + signedToSigned(i16, 2, asBytes(&[_]i16{5}), i32, 4, asBytes(&i16_to_i32), 1); + signedToSigned(i24, 3, asBytes(&[_]i24{5}), i32, 4, asBytes(&i24_to_i32), 1); try expectEqual(@as(i24, 1280), i16_to_i24[0]); try expectEqual(@as(i32, 327680), i16_to_i32[0]); @@ -129,221 +156,173 @@ test signedToSignedScalar { try expectEqual(@as(i24, 0), i32_to_i24[0]); } -pub fn signedToSignedSIMD( - comptime SrcType: type, - src: []const SrcType, - comptime DestType: type, - dst: []DestType, -) void { - const vec_size = std.simd.suggestVectorLength(SrcType).?; - const VecSrc = @Vector(vec_size, SrcType); - const VecDst = @Vector(vec_size, DestType); - const trunc = @bitSizeOf(SrcType) - @bitSizeOf(DestType); - const vec_blocks_len = src.len - (src.len % vec_size); - var i: usize = 0; - while (i < vec_blocks_len) : (i += vec_size) { - const src_vec: VecSrc = src[i..][0..vec_size].*; - dst[i..][0..vec_size].* = shr(VecDst, @intCast(src_vec), trunc); - } - if (i != src.len) signedToSignedScalar(SrcType, src[i..], DestType, dst[i..]); -} - -test signedToSignedSIMD { - var i16_to_i32: [32 + 7]i32 = undefined; - const items = [1]i16{5} ** (32 + 7); - signedToSignedSIMD(i16, &items, i32, &i16_to_i32); +test "signedToSigned multi" { + const len = 32 + 7; + var i16_to_i32: [len]i32 = undefined; + const items = [1]i16{5} ** (len); + signedToSigned(i16, 2, asBytes(&items), i32, 4, asBytes(&i16_to_i32), len); try expectEqual(@as(i32, 327680), i16_to_i32[0]); try expectEqual(i16_to_i32[0], i16_to_i32[i16_to_i32.len - 1]); } pub fn signedToFloat( comptime SrcType: type, - src: []const SrcType, - comptime DestType: type, - dst: []DestType, + src_stride: u8, + src: []const u8, + comptime DstType: type, + dst_stride: u8, + dst: []u8, + len: usize, ) void { - if (std.simd.suggestVectorLength(SrcType)) |_| { - signedToFloatSIMD(SrcType, src, DestType, dst); - } else { - signedToFloatScalar(SrcType, src, DestType, dst); + const div_by_max = 1.0 / @as(comptime_float, maxInt(SrcType) + 1); + var i: usize = 0; + + // Use SIMD when available + if (std.simd.suggestVectorLength(SrcType)) |vec_size| { + const VecSrc = @Vector(vec_size, SrcType); + const VecDst = @Vector(vec_size, DstType); + const vec_blocks_len = len - (len % vec_size); + const div_by_max_vec: VecDst = @splat(div_by_max); + while (i < vec_blocks_len) : (i += vec_size) { + const src_vec = bytesAsValue(VecSrc, src[i * src_stride ..][0 .. vec_size * src_stride]).*; + const dst_sample: VecDst = @as(VecDst, @floatFromInt(src_vec)) * div_by_max_vec; + @memcpy(dst[i * dst_stride ..][0 .. vec_size * dst_stride], asBytes(&dst_sample)[0 .. vec_size * dst_stride]); + } + } + + // Convert the remaining samples + while (i < len) : (i += 1) { + const src_sample: *const SrcType = @ptrCast(@alignCast(src[i * src_stride ..][0..src_stride])); + const dst_sample: DstType = @as(DstType, @floatFromInt(src_sample.*)) * div_by_max; + @memcpy(dst[i * dst_stride ..][0..dst_stride], asBytes(&dst_sample)[0..dst_stride]); } } -pub fn signedToFloatScalar( - comptime SrcType: type, - src: []const SrcType, - comptime DestType: type, - dst: []DestType, -) void { - const max: comptime_float = maxInt(SrcType) + 1; - const div_by_max = 1.0 / max; - for (src, dst) |*src_sample, *dst_sample| { - dst_sample.* = @as(DestType, @floatFromInt(src_sample.*)) * div_by_max; - } -} - -test signedToFloatScalar { +test "signedToFloat single" { var i16_to_f32: [1]f32 = undefined; var i24_to_f32: [1]f32 = undefined; var i32_to_f32: [1]f32 = undefined; - signedToFloatScalar(i16, &.{5}, f32, &i16_to_f32); - signedToFloatScalar(i24, &.{5}, f32, &i24_to_f32); - signedToFloatScalar(i32, &.{5}, f32, &i32_to_f32); + signedToFloat(i16, 2, asBytes(&[_]i16{5}), f32, 4, asBytes(&i16_to_f32), 1); + signedToFloat(i24, 3, asBytes(&[_]i24{5}), f32, 4, asBytes(&i24_to_f32), 1); + signedToFloat(i32, 4, asBytes(&[_]i32{5}), f32, 4, asBytes(&i32_to_f32), 1); try expectEqual(@as(f32, 1.52587890625e-4), i16_to_f32[0]); try expectEqual(@as(f32, 5.9604644775391e-7), i24_to_f32[0]); try expectEqual(@as(f32, 2.32830643e-09), i32_to_f32[0]); } -pub fn signedToFloatSIMD( - comptime SrcType: type, - src: []const SrcType, - comptime DestType: type, - dst: []DestType, -) void { - const vec_size = std.simd.suggestVectorLength(SrcType).?; - const VecSrc = @Vector(vec_size, SrcType); - const VecDst = @Vector(vec_size, DestType); - const div_by_max: VecDst = @splat(1.0 / @as(comptime_float, maxInt(SrcType) + 1)); - const vec_blocks_len = src.len - (src.len % vec_size); - var i: usize = 0; - while (i < vec_blocks_len) : (i += vec_size) { - const src_vec: VecSrc = src[i..][0..vec_size].*; - dst[i..][0..vec_size].* = @as(VecDst, @floatFromInt(src_vec)) * div_by_max; - } - if (i != src.len) signedToFloatScalar(SrcType, src[i..], DestType, dst[i..]); -} - -test signedToFloatSIMD { - var i32_to_f32: [32 + 7]f32 = undefined; - const items = [1]i32{5} ** (32 + 7); - signedToFloatSIMD(i32, &items, f32, &i32_to_f32); +test "signedToFloat multi" { + const len = 32 + 7; + var i32_to_f32: [len]f32 = undefined; + const items = [1]i32{5} ** (len); + signedToFloat(i32, 4, asBytes(&items), f32, 4, asBytes(&i32_to_f32), len); try expectEqual(@as(f32, 2.32830643e-09), i32_to_f32[0]); try expectEqual(i32_to_f32[0], i32_to_f32[i32_to_f32.len - 1]); } pub fn floatToUnsigned( comptime SrcType: type, - src: []const SrcType, - comptime DestType: type, - dst: []DestType, + src_stride: u8, + src: []const u8, + comptime DstType: type, + dst_stride: u8, + dst: []u8, + len: usize, ) void { - if (std.simd.suggestVectorLength(SrcType)) |_| { - floatToUnsignedSIMD(SrcType, src, DestType, dst); - } else { - floatToUnsignedScalar(SrcType, src, DestType, dst); + const half = maxInt(DstType) / 2; + const half_plus_one = half + 1; + var i: usize = 0; + + // Use SIMD when available + if (std.simd.suggestVectorLength(SrcType)) |vec_size| { + const VecSrc = @Vector(vec_size, SrcType); + const VecDst = @Vector(vec_size, DstType); + const half_vec: VecSrc = @splat(half); + const half_plus_one_vec: VecSrc = @splat(half_plus_one); + const vec_blocks_len = len - (len % vec_size); + while (i < vec_blocks_len) : (i += vec_size) { + const src_vec = bytesAsValue(VecSrc, src[i * src_stride ..][0 .. vec_size * src_stride]).*; + const dst_sample: VecDst = @intFromFloat(src_vec * half_vec + half_plus_one_vec); + @memcpy(dst[i * dst_stride ..][0 .. vec_size * dst_stride], asBytes(&dst_sample)[0 .. vec_size * dst_stride]); + } + } + + // Convert the remaining samples + while (i < len) : (i += 1) { + const src_sample: *const SrcType = @ptrCast(@alignCast(src[i * src_stride ..][0..src_stride])); + const dst_sample: DstType = @intFromFloat(src_sample.* * half + half_plus_one); + @memcpy(dst[i * dst_stride ..][0..dst_stride], asBytes(&dst_sample)[0..dst_stride]); } } -pub fn floatToUnsignedScalar( - comptime SrcType: type, - src: []const SrcType, - comptime DestType: type, - dst: []DestType, -) void { - for (src, dst) |*src_sample, *dst_sample| { - const half = maxInt(DestType) / 2; - dst_sample.* = @intFromFloat(src_sample.* * half + (half + 1)); - } -} - -test floatToUnsignedScalar { +test "floatToUnsigned single" { var f32_to_u8: [1]u8 = undefined; - floatToUnsignedScalar(f32, &.{0.5}, u8, &f32_to_u8); + floatToUnsigned(f32, 4, asBytes(&[_]f32{0.5}), u8, 1, asBytes(&f32_to_u8), 1); try expectEqual(@as(u8, 191), f32_to_u8[0]); } -pub fn floatToUnsignedSIMD( - comptime SrcType: type, - src: []const SrcType, - comptime DestType: type, - dst: []DestType, -) void { - const vec_size = std.simd.suggestVectorLength(SrcType).?; - const VecSrc = @Vector(vec_size, SrcType); - const VecDst = @Vector(vec_size, DestType); - const half: VecSrc = @splat(maxInt(DestType) / 2); - const half_plus_one: VecSrc = @splat(maxInt(DestType) / 2 + 1); - const vec_blocks_len = src.len - (src.len % vec_size); - var i: usize = 0; - while (i < vec_blocks_len) : (i += vec_size) { - const src_vec: VecSrc = src[i..][0..vec_size].*; - dst[i..][0..vec_size].* = @as(VecDst, @intFromFloat(src_vec * half + half_plus_one)); - } - if (i != src.len) floatToUnsignedScalar(SrcType, src[i..], DestType, dst[i..]); -} - -test floatToUnsignedSIMD { - var f32_to_u8: [32 + 7]u8 = undefined; - const items = [1]f32{0.5} ** (32 + 7); - floatToUnsignedSIMD(f32, &items, u8, &f32_to_u8); +test "floatToUnsigned multi" { + const len = 32 + 7; + var f32_to_u8: [len]u8 = undefined; + const items = [1]f32{0.5} ** (len); + floatToUnsigned(f32, 4, asBytes(&items), u8, 1, asBytes(&f32_to_u8), len); try expectEqual(@as(u8, 191), f32_to_u8[0]); try expectEqual(f32_to_u8[0], f32_to_u8[f32_to_u8.len - 1]); } pub fn floatToSigned( comptime SrcType: type, - src: []const SrcType, - comptime DestType: type, - dst: []DestType, + src_stride: u8, + src: []const u8, + comptime DstType: type, + dst_stride: u8, + dst: []u8, + len: usize, ) void { - if (std.simd.suggestVectorLength(SrcType)) |_| { - floatToSignedSIMD(SrcType, src, DestType, dst); - } else { - floatToSignedScalar(SrcType, src, DestType, dst); + const max = maxInt(DstType) + 1; + var i: usize = 0; + + // Use SIMD when available + if (std.simd.suggestVectorLength(SrcType)) |vec_size| { + const VecSrc = @Vector(vec_size, SrcType); + const VecDst = @Vector(vec_size, DstType); + const max_vec: VecSrc = @splat(max); + const vec_blocks_len = len - (len % vec_size); + while (i < vec_blocks_len) : (i += vec_size) { + const src_vec = bytesAsValue(VecSrc, src[i * src_stride ..][0 .. vec_size * src_stride]).*; + const dst_sample: VecDst = @intFromFloat(src_vec * max_vec); + @memcpy(dst[i * dst_stride ..][0 .. vec_size * dst_stride], asBytes(&dst_sample)[0 .. vec_size * dst_stride]); + } + } + + // Convert the remaining samples + while (i < len) : (i += 1) { + const src_sample: *const SrcType = @ptrCast(@alignCast(src[i * src_stride ..][0..src_stride])); + const dst_sample: DstType = @truncate(@as(i32, @intFromFloat(src_sample.* * max))); + @memcpy(dst[i * dst_stride ..][0..dst_stride], asBytes(&dst_sample)[0..dst_stride]); } } -pub fn floatToSignedScalar( - comptime SrcType: type, - src: []const SrcType, - comptime DestType: type, - dst: []DestType, -) void { - for (src, dst) |*src_sample, *dst_sample| { - const max = maxInt(DestType) + 1; - dst_sample.* = @truncate(@as(i32, @intFromFloat(src_sample.* * max))); - } -} - -test floatToSignedScalar { +test "floatToSigned single" { var f32_to_i16: [1]i16 = undefined; var f32_to_i24: [1]i24 = undefined; var f32_to_i32: [1]i32 = undefined; - floatToSignedScalar(f32, &.{0.5}, i16, &f32_to_i16); - floatToSignedScalar(f32, &.{0.5}, i24, &f32_to_i24); - floatToSignedScalar(f32, &.{0.5}, i32, &f32_to_i32); + floatToSigned(f32, 4, asBytes(&[_]f32{0.5}), i16, 2, asBytes(&f32_to_i16), 1); + floatToSigned(f32, 4, asBytes(&[_]f32{0.5}), i24, 3, asBytes(&f32_to_i24), 1); + floatToSigned(f32, 4, asBytes(&[_]f32{0.5}), i32, 4, asBytes(&f32_to_i32), 1); try expectEqual(@as(i16, 16384), f32_to_i16[0]); try expectEqual(@as(i24, 4194304), f32_to_i24[0]); try expectEqual(@as(i32, 1073741824), f32_to_i32[0]); } -pub fn floatToSignedSIMD( - comptime SrcType: type, - src: []const SrcType, - comptime DestType: type, - dst: []DestType, -) void { - const vec_size = std.simd.suggestVectorLength(SrcType).?; - const VecSrc = @Vector(vec_size, SrcType); - const VecDst = @Vector(vec_size, DestType); - const max: VecSrc = @splat(maxInt(DestType) + 1); - const vec_blocks_len = src.len - (src.len % vec_size); - var i: usize = 0; - while (i < vec_blocks_len) : (i += vec_size) { - const src_vec: VecSrc = src[i..][0..vec_size].*; - dst[i..][0..vec_size].* = @as(VecDst, @intFromFloat(src_vec * max)); - } - if (i != src.len) floatToSignedScalar(SrcType, src[i..], DestType, dst[i..]); -} - -test floatToSignedSIMD { - var f32_to_i16: [32 + 7]i16 = undefined; - const items = [1]f32{0.5} ** (32 + 7); - floatToSignedSIMD(f32, &items, i16, &f32_to_i16); +test "floatToSigned multi" { + const len = 32 + 7; + var f32_to_i16: [len]i16 = undefined; + const items = [1]f32{0.5} ** (len); + floatToSigned(f32, 4, asBytes(&items), i16, 2, asBytes(&f32_to_i16), len); try expectEqual(@as(i16, 16384), f32_to_i16[0]); try expectEqual(f32_to_i16[0], f32_to_i16[f32_to_i16.len - 1]); } diff --git a/src/sysaudio/coreaudio.zig b/src/sysaudio/coreaudio.zig index 1aaa4c41..5fa0fb2f 100644 --- a/src/sysaudio/coreaudio.zig +++ b/src/sysaudio/coreaudio.zig @@ -742,8 +742,7 @@ fn createStreamDesc(format: main.Format, sample_rate: u24, ch_count: usize) !c.A .mFormatID = c.kAudioFormatLinearPCM, .mFormatFlags = switch (format) { .i16 => c.kAudioFormatFlagIsSignedInteger, - // TODO(i24) - // .i24 => c.kAudioFormatFlagIsSignedInteger, + .i24 => c.kAudioFormatFlagIsSignedInteger, .i32 => c.kAudioFormatFlagIsSignedInteger, .f32 => c.kAudioFormatFlagIsFloat, .u8 => return error.IncompatibleDevice, @@ -754,8 +753,7 @@ fn createStreamDesc(format: main.Format, sample_rate: u24, ch_count: usize) !c.A .mChannelsPerFrame = @intCast(ch_count), .mBitsPerChannel = switch (format) { .i16 => 16, - // TODO(i24) - // .i24 => 24, + .i24 => 24, .i32 => 32, .f32 => 32, .u8 => return error.IncompatibleDevice, diff --git a/src/sysaudio/main.zig b/src/sysaudio/main.zig index 6bdd1f47..dc5c4298 100644 --- a/src/sysaudio/main.zig +++ b/src/sysaudio/main.zig @@ -321,85 +321,89 @@ pub const Recorder = struct { } }; -pub fn convertTo(comptime SrcType: type, src: []const SrcType, dst_format: Format, dst: []u8) void { - const dst_len = dst.len / dst_format.size(); - std.debug.assert(dst_len == src.len); +pub fn convertTo(comptime SrcType: type, src: []const SrcType, dst_format: Format, dst_bytes: []u8) void { + const src_stride = @bitSizeOf(SrcType) / 8; + const dst_stride = dst_format.size(); + const src_bytes = @as([*]const u8, @ptrCast(@alignCast(src)))[0 .. src.len * src_stride]; + std.debug.assert(@bitSizeOf(SrcType) % 8 == 0); + std.debug.assert(dst_bytes.len / dst_stride == src.len); return switch (dst_format) { .u8 => switch (SrcType) { - u8 => @memcpy(@as([*]u8, @ptrCast(@alignCast(dst)))[0..dst_len], src), - i8, i16, i24, i32 => conv.signedToUnsigned(SrcType, src, u8, @as([*]u8, @ptrCast(@alignCast(dst)))[0..dst_len]), - f32 => conv.floatToUnsigned(SrcType, src, u8, @as([*]u8, @ptrCast(@alignCast(dst)))[0..dst_len]), + u8 => @memcpy(dst_bytes, src_bytes), + i8, i16, i24, i32 => conv.signedToUnsigned(SrcType, src_stride, src_bytes, u8, dst_stride, dst_bytes, src.len), + f32 => conv.floatToUnsigned(SrcType, src_stride, src_bytes, u8, dst_stride, dst_bytes, src.len), else => unreachable, }, .i16 => switch (SrcType) { - i16 => @memcpy(@as([*]i16, @ptrCast(@alignCast(dst)))[0..dst_len], src), - u8 => conv.unsignedToSigned(SrcType, src, i16, @as([*]i16, @ptrCast(@alignCast(dst)))[0..dst_len]), - i8, i24, i32 => conv.signedToSigned(SrcType, src, i16, @as([*]i16, @ptrCast(@alignCast(dst)))[0..dst_len]), - f32 => conv.floatToSigned(SrcType, src, i16, @as([*]i16, @ptrCast(@alignCast(dst)))[0..dst_len]), + i16 => @memcpy(dst_bytes, src_bytes), + u8 => conv.unsignedToSigned(SrcType, src_stride, src_bytes, i16, dst_stride, dst_bytes, src.len), + i8, i24, i32 => conv.signedToSigned(SrcType, src_stride, src_bytes, i16, dst_stride, dst_bytes, src.len), + f32 => conv.floatToSigned(SrcType, src_stride, src_bytes, i16, dst_stride, dst_bytes, src.len), + else => unreachable, + }, + .i24 => switch (SrcType) { + i24 => @memcpy(dst_bytes, src_bytes), + u8 => conv.unsignedToSigned(SrcType, src_stride, src_bytes, i24, dst_stride, dst_bytes, src.len), + i8, i16, i32 => conv.signedToSigned(SrcType, src_stride, src_bytes, i24, dst_stride, dst_bytes, src.len), + f32 => conv.floatToSigned(SrcType, src_stride, src_bytes, i24, dst_stride, dst_bytes, src.len), else => unreachable, }, - // TODO(i24) - // .i24 => switch (SrcType) { - // i24 => @memcpy(@as([*]i24, @ptrCast(@alignCast(dst)))[0..dst_len], src), - // u8 => conv.unsignedToSigned(SrcType, src, i24, @as([*]i24, @ptrCast(@alignCast(dst)))[0..dst_len]), - // i8, i16, i32 => conv.signedToSigned(SrcType, src, i24, @as([*]i24, @ptrCast(@alignCast(dst)))[0..dst_len]), - // f32 => conv.floatToSigned(SrcType, src, i24, @as([*]i24, @ptrCast(@alignCast(dst)))[0..dst_len]), - // else => unreachable, - // }, .i32 => switch (SrcType) { - i32 => @memcpy(@as([*]i32, @ptrCast(@alignCast(dst)))[0..dst_len], src), - u8 => conv.unsignedToSigned(SrcType, src, i32, @as([*]i32, @ptrCast(@alignCast(dst)))[0..dst_len]), - i8, i16, i24 => conv.signedToSigned(SrcType, src, i32, @as([*]i32, @ptrCast(@alignCast(dst)))[0..dst_len]), - f32 => conv.floatToSigned(SrcType, src, i32, @as([*]i32, @ptrCast(@alignCast(dst)))[0..dst_len]), + i32 => @memcpy(dst_bytes, src_bytes), + u8 => conv.unsignedToSigned(SrcType, src_stride, src_bytes, i32, dst_stride, dst_bytes, src.len), + i8, i16, i24 => conv.signedToSigned(SrcType, src_stride, src_bytes, i32, dst_stride, dst_bytes, src.len), + f32 => conv.floatToSigned(SrcType, src_stride, src_bytes, i32, dst_stride, dst_bytes, src.len), else => unreachable, }, .f32 => switch (SrcType) { - f32 => @memcpy(@as([*]f32, @ptrCast(@alignCast(dst)))[0..dst_len], src), - u8 => conv.unsignedToFloat(SrcType, src, f32, @as([*]f32, @ptrCast(@alignCast(dst)))[0..dst_len]), - i8, i16, i24, i32 => conv.signedToFloat(SrcType, src, f32, @as([*]f32, @ptrCast(@alignCast(dst)))[0..dst_len]), + f32 => @memcpy(dst_bytes, src_bytes), + u8 => conv.unsignedToFloat(SrcType, src_stride, src_bytes, f32, dst_stride, dst_bytes, src.len), + i8, i16, i24, i32 => conv.signedToFloat(SrcType, src_stride, src_bytes, f32, dst_stride, dst_bytes, src.len), else => unreachable, }, }; } -pub fn convertFrom(comptime DestType: type, dst: []DestType, src_format: Format, src: []const u8) void { - const src_len = src.len / src_format.size(); - std.debug.assert(src_len == dst.len); +pub fn convertFrom(comptime DstType: type, dst: []DstType, src_format: Format, src_bytes: []const u8) void { + const src_stride = src_format.size(); + const dst_stride = @bitSizeOf(DstType) / 8; + const dst_bytes = @as([*]u8, @ptrCast(@alignCast(dst)))[0 .. dst.len * dst_stride]; + std.debug.assert(@bitSizeOf(DstType) % 8 == 0); + std.debug.assert(src_bytes.len / src_stride == dst.len); return switch (src_format) { - .u8 => switch (DestType) { - u8 => @memcpy(dst, @as([*]const u8, @ptrCast(@alignCast(src)))[0..src_len]), - i8, i16, i24, i32 => conv.unsignedToSigned(u8, @as([*]const u8, @ptrCast(@alignCast(src)))[0..src_len], DestType, dst), - f32 => conv.unsignedToFloat(u8, @as([*]const u8, @ptrCast(@alignCast(src)))[0..src_len], DestType, dst), + .u8 => switch (DstType) { + u8 => @memcpy(dst_bytes, src_bytes), + i8, i16, i24, i32 => conv.unsignedToSigned(u8, src_stride, src_bytes, DstType, dst_stride, dst_bytes, dst.len), + f32 => conv.unsignedToFloat(u8, src_stride, src_bytes, DstType, dst_stride, dst_bytes, dst.len), else => unreachable, }, - .i16 => switch (DestType) { - i16 => @memcpy(dst, @as([*]const i16, @ptrCast(@alignCast(src)))[0..src_len]), - u8 => conv.signedToUnsigned(i16, @as([*]const i16, @ptrCast(@alignCast(src)))[0..src_len], DestType, dst), - i8, i24, i32 => conv.signedToSigned(i16, @as([*]const i16, @ptrCast(@alignCast(src)))[0..src_len], DestType, dst), - f32 => conv.signedToFloat(i16, @as([*]const i16, @ptrCast(@alignCast(src)))[0..src_len], DestType, dst), + .i16 => switch (DstType) { + i16 => @memcpy(dst_bytes, src_bytes), + u8 => conv.signedToUnsigned(i16, src_stride, src_bytes, DstType, dst_stride, dst_bytes, dst.len), + i8, i24, i32 => conv.signedToSigned(i16, src_stride, src_bytes, DstType, dst_stride, dst_bytes, dst.len), + f32 => conv.signedToFloat(i16, src_stride, src_bytes, DstType, dst_stride, dst_bytes, dst.len), else => unreachable, }, - // TODO(i24) - // .i24 => switch (DestType) { - // i24 => @memcpy(dst, @as([*]const i24, @ptrCast(@alignCast(src)))[0..src_len]), - // u8 => conv.signedToUnsigned(i24, @as([*]const i24, @ptrCast(@alignCast(src)))[0..src_len], DestType, dst), - // i8, i16, i32 => conv.signedToSigned(i24, @as([*]const i24, @ptrCast(@alignCast(src)))[0..src_len], DestType, dst), - // f32 => conv.signedToFloat(i24, @as([*]const i24, @ptrCast(@alignCast(src)))[0..src_len], DestType, dst), - // else => unreachable, - // }, - .i32 => switch (DestType) { - i32 => @memcpy(dst, @as([*]const i32, @ptrCast(@alignCast(src)))[0..src_len]), - u8 => conv.signedToUnsigned(i32, @as([*]const i32, @ptrCast(@alignCast(src)))[0..src_len], DestType, dst), - i8, i16, i24 => conv.signedToSigned(i32, @as([*]const i32, @ptrCast(@alignCast(src)))[0..src_len], DestType, dst), - f32 => conv.signedToFloat(i32, @as([*]const i32, @ptrCast(@alignCast(src)))[0..src_len], DestType, dst), + .i24 => switch (DstType) { + i24 => @memcpy(dst_bytes, src_bytes), + u8 => conv.signedToUnsigned(i24, src_stride, src_bytes, DstType, dst_stride, dst_bytes, dst.len), + i8, i16, i32 => conv.signedToSigned(i24, src_stride, src_bytes, DstType, dst_stride, dst_bytes, dst.len), + f32 => conv.signedToFloat(i24, src_stride, src_bytes, DstType, dst_stride, dst_bytes, dst.len), else => unreachable, }, - .f32 => switch (DestType) { - f32 => @memcpy(dst, @as([*]const f32, @ptrCast(@alignCast(src)))[0..src_len]), - u8 => conv.floatToUnsigned(f32, @as([*]const f32, @ptrCast(@alignCast(src)))[0..src_len], DestType, dst), - i8, i16, i24, i32 => conv.floatToSigned(f32, @as([*]const f32, @ptrCast(@alignCast(src)))[0..src_len], DestType, dst), + .i32 => switch (DstType) { + i32 => @memcpy(dst_bytes, src_bytes), + u8 => conv.signedToUnsigned(i32, src_stride, src_bytes, DstType, dst_stride, dst_bytes, dst.len), + i8, i16, i24 => conv.signedToSigned(i32, src_stride, src_bytes, DstType, dst_stride, dst_bytes, dst.len), + f32 => conv.signedToFloat(i32, src_stride, src_bytes, DstType, dst_stride, dst_bytes, dst.len), + else => unreachable, + }, + .f32 => switch (DstType) { + f32 => @memcpy(dst_bytes, src_bytes), + u8 => conv.floatToUnsigned(f32, src_stride, src_bytes, DstType, dst_stride, dst_bytes, dst.len), + i8, i16, i24, i32 => conv.floatToSigned(f32, src_stride, src_bytes, DstType, dst_stride, dst_bytes, dst.len), else => unreachable, }, }; @@ -418,6 +422,7 @@ pub const Device = struct { capture, }; + // TODO: don't call this in backends. let the user use it pub fn preferredFormat(device: Device, format: ?Format) Format { if (format) |f| { for (device.formats) |fmt| if (f == fmt) return fmt; @@ -455,8 +460,7 @@ pub const ChannelPosition = enum { pub const Format = enum { u8, i16, - // TODO(i24): Uncomment when https://github.com/hexops/mach/issues/1152 is fixed - // i24 = 2, + i24, i32, f32, @@ -464,8 +468,7 @@ pub const Format = enum { return switch (format) { .u8 => 1, .i16 => 2, - // TODO(i24) - // .i24 => 3, + .i24 => 3, .i32, .f32 => 4, }; } @@ -474,8 +477,7 @@ pub const Format = enum { return switch (format) { .u8 => 1, .i16 => 2, - // TODO(i24) - // .i24 => 3, + .i24 => 3, .i32, .f32 => 4, }; } diff --git a/src/sysaudio/pulseaudio.zig b/src/sysaudio/pulseaudio.zig index 2713198d..a2b58c73 100644 --- a/src/sysaudio/pulseaudio.zig +++ b/src/sysaudio/pulseaudio.zig @@ -746,8 +746,7 @@ fn performOperation(main_loop: *c.pa_threaded_mainloop, op: ?*c.pa_operation) vo pub const available_formats = &[_]main.Format{ .u8, .i16, - // TODO(i24) - // .i24, + .i24, .i32, .f32, }; @@ -783,8 +782,7 @@ 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, - // TODO(i24) - // .i24 => if (is_little) c.PA_SAMPLE_S24LE else c.PA_SAMPLE_S24LE, + .i24 => if (is_little) c.PA_SAMPLE_S24LE else c.PA_SAMPLE_S24LE, .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, }; diff --git a/src/sysaudio/wasapi.zig b/src/sysaudio/wasapi.zig index 44899238..8438075e 100644 --- a/src/sysaudio/wasapi.zig +++ b/src/sysaudio/wasapi.zig @@ -677,12 +677,7 @@ pub const Context = struct { fn toSubFormat(format: main.Format) win32.Guid { return switch (format) { - .u8, - .i16, - // TODO(i24) - // .i24, - .i32, - => win32.CLSID_KSDATAFORMAT_SUBTYPE_PCM.*, + .u8, .i16, .i24, .i32 => win32.CLSID_KSDATAFORMAT_SUBTYPE_PCM.*, .f32 => win32.CLSID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT.*, }; }