sysaudio: revive i24 samples

This commit is contained in:
Ali Chraghi 2024-05-11 21:25:13 +03:30 committed by Stephen Gutekanst
parent 73a1ea4650
commit cadaa3e85b
6 changed files with 262 additions and 291 deletions

View file

@ -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,
};

View file

@ -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]);
}

View file

@ -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,

View file

@ -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,
};
}

View file

@ -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,
};

View file

@ -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.*,
};
}