Implementing new features in the latest Concentus library - span-in, span-out Opus decoding (so we don't have to make temporary buffer copies), returning a more precise error code from the decoder, and automatically linking the native opus library with P/invoke if supported on the current system
This commit is contained in:
parent
65c035cdf8
commit
9d808e4cb4
1 changed files with 44 additions and 24 deletions
|
@ -30,41 +30,47 @@ namespace Ryujinx.Horizon.Sdk.Codec.Detail
|
|||
}
|
||||
}
|
||||
|
||||
private interface IDecoder
|
||||
private interface IDecoder : IDisposable
|
||||
{
|
||||
int SampleRate { get; }
|
||||
int ChannelsCount { get; }
|
||||
|
||||
int Decode(byte[] inData, int inDataOffset, int len, short[] outPcm, int outPcmOffset, int frameSize);
|
||||
int Decode(ReadOnlySpan<byte> inData, Span<short> outPcm, int frameSize);
|
||||
void ResetState();
|
||||
}
|
||||
|
||||
private class Decoder : IDecoder
|
||||
{
|
||||
private readonly OpusDecoder _decoder;
|
||||
private readonly IOpusDecoder _decoder;
|
||||
|
||||
public int SampleRate => _decoder.SampleRate;
|
||||
public int ChannelsCount => _decoder.NumChannels;
|
||||
|
||||
public Decoder(int sampleRate, int channelsCount)
|
||||
{
|
||||
_decoder = new OpusDecoder(sampleRate, channelsCount);
|
||||
Logger.Warning?.Print(LogClass.Audio, "Creating an Opus decoder!");
|
||||
_decoder = OpusCodecFactory.CreateDecoder(sampleRate, channelsCount, Console.Out);
|
||||
}
|
||||
|
||||
public int Decode(byte[] inData, int inDataOffset, int len, short[] outPcm, int outPcmOffset, int frameSize)
|
||||
public int Decode(ReadOnlySpan<byte> inData, Span<short> outPcm, int frameSize)
|
||||
{
|
||||
return _decoder.Decode(inData, inDataOffset, len, outPcm, outPcmOffset, frameSize);
|
||||
return _decoder.Decode(inData, outPcm, frameSize);
|
||||
}
|
||||
|
||||
public void ResetState()
|
||||
{
|
||||
_decoder.ResetState();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_decoder?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private class MultiSampleDecoder : IDecoder
|
||||
{
|
||||
private readonly OpusMSDecoder _decoder;
|
||||
private readonly IOpusMultiStreamDecoder _decoder;
|
||||
|
||||
public int SampleRate => _decoder.SampleRate;
|
||||
public int ChannelsCount { get; }
|
||||
|
@ -72,18 +78,24 @@ namespace Ryujinx.Horizon.Sdk.Codec.Detail
|
|||
public MultiSampleDecoder(int sampleRate, int channelsCount, int streams, int coupledStreams, byte[] mapping)
|
||||
{
|
||||
ChannelsCount = channelsCount;
|
||||
_decoder = new OpusMSDecoder(sampleRate, channelsCount, streams, coupledStreams, mapping);
|
||||
Logger.Warning?.Print(LogClass.Audio, "Creating an Opus multistream decoder!");
|
||||
_decoder = OpusCodecFactory.CreateMultiStreamDecoder(sampleRate, channelsCount, streams, coupledStreams, mapping, Console.Out);
|
||||
}
|
||||
|
||||
public int Decode(byte[] inData, int inDataOffset, int len, short[] outPcm, int outPcmOffset, int frameSize)
|
||||
public int Decode(ReadOnlySpan<byte> inData, Span<short> outPcm, int frameSize)
|
||||
{
|
||||
return _decoder.DecodeMultistream(inData, inDataOffset, len, outPcm, outPcmOffset, frameSize, 0);
|
||||
return _decoder.DecodeMultistream(inData, outPcm, frameSize, false);
|
||||
}
|
||||
|
||||
public void ResetState()
|
||||
{
|
||||
_decoder.ResetState();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_decoder?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private readonly IDecoder _decoder;
|
||||
|
@ -221,7 +233,8 @@ namespace Ryujinx.Horizon.Sdk.Codec.Detail
|
|||
{
|
||||
timeTaken = 0;
|
||||
|
||||
Result result = DecodeInterleaved(_decoder, reset, input, out short[] outPcmData, output.Length, out outConsumed, out outSamples);
|
||||
Span<short> outPcmSpace = MemoryMarshal.Cast<byte, short>(output);
|
||||
Result result = DecodeInterleaved(_decoder, reset, input, outPcmSpace, output.Length, out outConsumed, out outSamples);
|
||||
|
||||
if (withPerf)
|
||||
{
|
||||
|
@ -229,14 +242,12 @@ namespace Ryujinx.Horizon.Sdk.Codec.Detail
|
|||
timeTaken = 0;
|
||||
}
|
||||
|
||||
MemoryMarshal.Cast<short, byte>(outPcmData).CopyTo(output[..(outPcmData.Length * sizeof(short))]);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Result GetPacketNumSamples(IDecoder decoder, out int numSamples, byte[] packet)
|
||||
private static Result GetPacketNumSamples(IDecoder decoder, out int numSamples, ReadOnlySpan<byte> packet)
|
||||
{
|
||||
int result = OpusPacketInfo.GetNumSamples(packet, 0, packet.Length, decoder.SampleRate);
|
||||
int result = OpusPacketInfo.GetNumSamples(packet, decoder.SampleRate);
|
||||
|
||||
numSamples = result;
|
||||
|
||||
|
@ -256,12 +267,11 @@ namespace Ryujinx.Horizon.Sdk.Codec.Detail
|
|||
IDecoder decoder,
|
||||
bool reset,
|
||||
ReadOnlySpan<byte> input,
|
||||
out short[] outPcmData,
|
||||
Span<short> outPcmData,
|
||||
int outputSize,
|
||||
out int outConsumed,
|
||||
out int outSamples)
|
||||
{
|
||||
outPcmData = null;
|
||||
outConsumed = 0;
|
||||
outSamples = 0;
|
||||
|
||||
|
@ -281,7 +291,7 @@ namespace Ryujinx.Horizon.Sdk.Codec.Detail
|
|||
return CodecResult.InvalidLength;
|
||||
}
|
||||
|
||||
byte[] opusData = input.Slice(headerSize, (int)header.Length).ToArray();
|
||||
ReadOnlySpan<byte> opusData = input.Slice(headerSize, (int)header.Length);
|
||||
|
||||
Result result = GetPacketNumSamples(decoder, out int numSamples, opusData);
|
||||
|
||||
|
@ -292,8 +302,6 @@ namespace Ryujinx.Horizon.Sdk.Codec.Detail
|
|||
return CodecResult.InvalidLength;
|
||||
}
|
||||
|
||||
outPcmData = new short[numSamples * decoder.ChannelsCount];
|
||||
|
||||
if (reset)
|
||||
{
|
||||
decoder.ResetState();
|
||||
|
@ -301,13 +309,23 @@ namespace Ryujinx.Horizon.Sdk.Codec.Detail
|
|||
|
||||
try
|
||||
{
|
||||
outSamples = decoder.Decode(opusData, 0, opusData.Length, outPcmData, 0, outPcmData.Length / decoder.ChannelsCount);
|
||||
Logger.Debug?.Print(LogClass.Audio, "Decoding Opus audio!");
|
||||
outSamples = decoder.Decode(opusData, outPcmData, numSamples);
|
||||
outConsumed = (int)totalSize;
|
||||
}
|
||||
catch (OpusException)
|
||||
catch (OpusException e)
|
||||
{
|
||||
// TODO: As OpusException doesn't return the exact error code, this is inaccurate in some cases...
|
||||
return CodecResult.InvalidLength;
|
||||
switch (e.OpusErrorCode)
|
||||
{
|
||||
case OpusError.OPUS_BUFFER_TOO_SMALL:
|
||||
return CodecResult.InvalidLength;
|
||||
case OpusError.OPUS_BAD_ARG:
|
||||
return CodecResult.OpusBadArg;
|
||||
case OpusError.OPUS_INVALID_PACKET:
|
||||
return CodecResult.OpusInvalidPacket;
|
||||
default:
|
||||
return CodecResult.InvalidLength;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -324,6 +342,8 @@ namespace Ryujinx.Horizon.Sdk.Codec.Detail
|
|||
|
||||
_workBufferHandle = 0;
|
||||
}
|
||||
|
||||
_decoder?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue