forked from Mirror/Ryujinx
c20f3fbebd
* haydn: Add support for PCMFloat, PCM32 and PCM8 conversions This adds support in the compatibility layer for other sample format than PCM16. This should help extends compatibility with soundio on devices that doesn't expose PCM16. I ommited PCM24 conversion for now as it's not simplest of all. * Address TSRBerry's comment * Address comments * Fix conversion issue and clean up saturation usage * Revert saturation changes * Address gdkchan's comment
117 lines
No EOL
4.2 KiB
C#
117 lines
No EOL
4.2 KiB
C#
using System;
|
|
using System.Numerics;
|
|
using System.Runtime.CompilerServices;
|
|
|
|
namespace Ryujinx.Audio.Renderer.Dsp
|
|
{
|
|
public static class PcmHelper
|
|
{
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static int GetCountToDecode(int startSampleOffset, int endSampleOffset, int offset, int count)
|
|
{
|
|
return Math.Min(count, endSampleOffset - startSampleOffset - offset);
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static ulong GetBufferOffset<T>(int startSampleOffset, int offset, int channelCount) where T : unmanaged
|
|
{
|
|
return (ulong)(Unsafe.SizeOf<T>() * channelCount * (startSampleOffset + offset));
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static int GetBufferSize<T>(int startSampleOffset, int endSampleOffset, int offset, int count) where T : unmanaged
|
|
{
|
|
return GetCountToDecode(startSampleOffset, endSampleOffset, offset, count) * Unsafe.SizeOf<T>();
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static float ConvertSampleToPcmFloat(short sample)
|
|
{
|
|
return (float)sample / short.MaxValue;
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static short ConvertSampleToPcmInt16(float sample)
|
|
{
|
|
return Saturate(sample * short.MaxValue);
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static TOutput ConvertSample<TInput, TOutput>(TInput value) where TInput: INumber<TInput>, IMinMaxValue<TInput> where TOutput : INumber<TOutput>, IMinMaxValue<TOutput>
|
|
{
|
|
TInput conversionRate = TInput.CreateSaturating(TOutput.MaxValue / TOutput.CreateSaturating(TInput.MaxValue));
|
|
|
|
return TOutput.CreateSaturating(value * conversionRate);
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static void Convert<TInput, TOutput>(Span<TOutput> output, ReadOnlySpan<TInput> input) where TInput : INumber<TInput>, IMinMaxValue<TInput> where TOutput : INumber<TOutput>, IMinMaxValue<TOutput>
|
|
{
|
|
for (int i = 0; i < input.Length; i++)
|
|
{
|
|
output[i] = ConvertSample<TInput, TOutput>(input[i]);
|
|
}
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static void ConvertSampleToPcmFloat(Span<float> output, ReadOnlySpan<short> input)
|
|
{
|
|
for (int i = 0; i < input.Length; i++)
|
|
{
|
|
output[i] = ConvertSampleToPcmFloat(input[i]);
|
|
}
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static int Decode(Span<short> output, ReadOnlySpan<short> input, int startSampleOffset, int endSampleOffset, int channelIndex, int channelCount)
|
|
{
|
|
if (input.IsEmpty || endSampleOffset < startSampleOffset)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int decodedCount = input.Length / channelCount;
|
|
|
|
for (int i = 0; i < decodedCount; i++)
|
|
{
|
|
output[i] = input[i * channelCount + channelIndex];
|
|
}
|
|
|
|
return decodedCount;
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static int Decode(Span<short> output, ReadOnlySpan<float> input, int startSampleOffset, int endSampleOffset, int channelIndex, int channelCount)
|
|
{
|
|
if (input.IsEmpty || endSampleOffset < startSampleOffset)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int decodedCount = input.Length / channelCount;
|
|
|
|
for (int i = 0; i < decodedCount; i++)
|
|
{
|
|
output[i] = ConvertSampleToPcmInt16(input[i * channelCount + channelIndex]);
|
|
}
|
|
|
|
return decodedCount;
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static short Saturate(float value)
|
|
{
|
|
if (value > short.MaxValue)
|
|
{
|
|
return short.MaxValue;
|
|
}
|
|
|
|
if (value < short.MinValue)
|
|
{
|
|
return short.MinValue;
|
|
}
|
|
|
|
return (short)value;
|
|
}
|
|
}
|
|
} |