Decode ogg files as shorts instead of floats (#4715)

This commit is contained in:
Pieter-Jan Briers
2023-12-15 20:48:07 +01:00
committed by GitHub
parent 438fed2f0e
commit 3f83733a03
3 changed files with 67 additions and 16 deletions

View File

@@ -97,11 +97,11 @@ internal partial class AudioManager
// check the git history, I originally used libvorbisfile which worked and loaded 16 bit LPCM.
if (vorbis.Channels == 1)
{
format = ALFormat.MonoFloat32Ext;
format = ALFormat.Mono16;
}
else if (vorbis.Channels == 2)
{
format = ALFormat.StereoFloat32Ext;
format = ALFormat.Stereo16;
}
else
{
@@ -110,9 +110,9 @@ internal partial class AudioManager
unsafe
{
fixed (float* ptr = vorbis.Data.Span)
fixed (short* ptr = vorbis.Data.Span)
{
AL.BufferData(buffer, format, (IntPtr) ptr, vorbis.Data.Length * sizeof(float),
AL.BufferData(buffer, format, (IntPtr) ptr, vorbis.Data.Length * sizeof(short),
(int) vorbis.SampleRate);
}
}

View File

@@ -1,5 +1,6 @@
using System;
using System.IO;
using System.Numerics;
using NVorbis;
namespace Robust.Shared.Audio.AudioLoading;
@@ -16,7 +17,8 @@ internal static class AudioLoaderOgg
/// <param name="stream">Audio file stream to load.</param>
public static AudioMetadata LoadAudioMetadata(Stream stream)
{
using var reader = new VorbisReader(stream);
using var reader = new VorbisReader(stream, false);
reader.Initialize();
return new AudioMetadata(reader.TotalTime, reader.Channels, reader.Tags.Title, reader.Tags.Artist);
}
@@ -26,39 +28,88 @@ internal static class AudioLoaderOgg
/// <param name="stream">Audio file stream to load.</param>
public static OggVorbisData LoadAudioData(Stream stream)
{
using var vorbis = new NVorbis.VorbisReader(stream, false);
using var vorbis = new VorbisReader(stream, false);
vorbis.Initialize();
var sampleRate = vorbis.SampleRate;
var channels = vorbis.Channels;
var totalSamples = vorbis.TotalSamples;
var readSamples = 0;
var buffer = new float[totalSamples * channels];
var totalValues = totalSamples * channels;
var readValues = 0;
var buffer = new short[totalSamples * channels];
Span<float> readBuffer = stackalloc float[32768];
while (readSamples < totalSamples)
while (readValues < totalValues)
{
var read = vorbis.ReadSamples(buffer, readSamples * channels, buffer.Length - readSamples);
var read = ReadSamples(buffer.AsSpan(readValues), readBuffer, channels, vorbis);
if (read == 0)
{
break;
}
readSamples += read;
readValues += read;
}
return new OggVorbisData(totalSamples, sampleRate, channels, buffer, vorbis.Tags.Title, vorbis.Tags.Artist);
}
private static int ReadSamples(Span<short> dest, Span<float> readBuffer, int channels, VorbisReader reader)
{
var read = reader.ReadSamples(readBuffer);
read *= channels;
ConvertToShort(readBuffer[..read], dest[..read]);
return read;
}
private static void ConvertToShort(ReadOnlySpan<float> src, Span<short> dst)
{
if (src.Length != dst.Length)
throw new InvalidOperationException("Invalid lengths!");
var simdSamples = (src.Length / Vector<short>.Count) * Vector<short>.Count;
// Note: I think according to spec we'd actually need to multiply negative values with 2^15 instead of 2^15-1.
// because it's -32768 -> 32767
// Can't be arsed though
var factor = new Vector<float>(short.MaxValue);
ref readonly var srcBase = ref src[0];
ref var dstBase = ref dst[0];
for (var i = 0; i < simdSamples; i += Vector<short>.Count)
{
var lower = Vector.LoadUnsafe(in srcBase, (nuint) i);
var upper = Vector.LoadUnsafe(in srcBase, (nuint) (i + Vector<float>.Count));
lower *= factor;
upper *= factor;
var lowerInt = Vector.ConvertToInt32(lower);
var upperInt = Vector.ConvertToInt32(upper);
var merged = Vector.Narrow(lowerInt, upperInt);
merged.StoreUnsafe(ref dstBase, (nuint) i);
}
for (var i = simdSamples; i < src.Length; i++)
{
dst[i] = (short)(src[i] * short.MaxValue);
}
}
internal readonly struct OggVorbisData
{
public readonly long TotalSamples;
public readonly long SampleRate;
public readonly long Channels;
public readonly ReadOnlyMemory<float> Data;
public readonly ReadOnlyMemory<short> Data;
public readonly string Title;
public readonly string Artist;
public OggVorbisData(long totalSamples, long sampleRate, long channels, ReadOnlyMemory<float> data, string title, string artist)
public OggVorbisData(long totalSamples, long sampleRate, long channels, ReadOnlyMemory<short> data,
string title, string artist)
{
TotalSamples = totalSamples;
SampleRate = sampleRate;

View File

@@ -11,7 +11,7 @@
<PackageReference Include="Microsoft.ILVerification" Version="6.0.0" PrivateAssets="compile" />
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.2" />
<PackageReference Include="Nett" Version="0.15.0" PrivateAssets="compile" />
<PackageReference Include="NVorbis" Version="0.10.1" PrivateAssets="compile" />
<PackageReference Include="VorbisPizza" Version="1.3.0" PrivateAssets="compile" />
<PackageReference Include="Pidgin" Version="2.5.0" />
<PackageReference Include="prometheus-net" Version="4.1.1" />
<PackageReference Include="Robust.Shared.AuthLib" Version="0.1.2" />