[Audio] Correct float PCM endpoint conversion (#291)

This commit is contained in:
jimmyjumbo
2026-07-17 02:13:06 +02:00
committed by GitHub
parent 8ee0fe8e44
commit 3585519007
2 changed files with 70 additions and 2 deletions
+13 -2
View File
@@ -52,8 +52,19 @@ internal static class AudioPcmConversion
}
var bits = BinaryPrimitives.ReadInt32LittleEndian(sample);
var value = Math.Clamp(BitConverter.Int32BitsToSingle(bits), -1.0f, 1.0f);
return checked((short)MathF.Round(value * short.MaxValue));
return ConvertFloatSample(BitConverter.Int32BitsToSingle(bits));
}
private static short ConvertFloatSample(float value)
{
if (float.IsNaN(value))
{
return 0;
}
value = Math.Clamp(value, -1.0f, 1.0f);
var scale = value < 0.0f ? 32768.0f : short.MaxValue;
return checked((short)MathF.Round(value * scale));
}
private static short ApplyVolume(short sample, float volume)
@@ -0,0 +1,57 @@
// Copyright (C) 2026 SharpEmu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
using System.Buffers.Binary;
using SharpEmu.Libs.Audio;
using Xunit;
namespace SharpEmu.Libs.Tests.Audio;
public sealed class AudioPcmConversionTests
{
[Fact]
public void FloatFullScaleMapsToSignedPcmEndpoints()
{
Span<byte> source = stackalloc byte[sizeof(float) * 2];
WriteFloat(source, 0, -1.0f);
WriteFloat(source, 1, 1.0f);
Span<byte> destination = stackalloc byte[AudioPcmConversion.OutputFrameSize];
AudioPcmConversion.ConvertToStereoPcm16(
source,
destination,
frames: 1,
channels: 2,
bytesPerSample: sizeof(float),
isFloat: true,
volume: 1.0f);
Assert.Equal(short.MinValue, BinaryPrimitives.ReadInt16LittleEndian(destination));
Assert.Equal(short.MaxValue, BinaryPrimitives.ReadInt16LittleEndian(destination[2..]));
}
[Fact]
public void FloatNaNMapsToSilence()
{
Span<byte> source = stackalloc byte[sizeof(float)];
WriteFloat(source, 0, float.NaN);
Span<byte> destination = stackalloc byte[AudioPcmConversion.OutputFrameSize];
AudioPcmConversion.ConvertToStereoPcm16(
source,
destination,
frames: 1,
channels: 1,
bytesPerSample: sizeof(float),
isFloat: true,
volume: 1.0f);
Assert.Equal(0, BinaryPrimitives.ReadInt16LittleEndian(destination));
Assert.Equal(0, BinaryPrimitives.ReadInt16LittleEndian(destination[2..]));
}
private static void WriteFloat(Span<byte> destination, int sample, float value) =>
BinaryPrimitives.WriteInt32LittleEndian(
destination[(sample * sizeof(float))..],
BitConverter.SingleToInt32Bits(value));
}