Migrate Audio service to new IPC (#6285)

* Migrate audren to new IPC

* Migrate audout

* Migrate audin

* Migrate hwopus

* Bye bye old audio service

* Switch volume control to IHardwareDeviceDriver

* Somewhat unrelated changes

* Remove Concentus reference from HLE

* Implement OpenAudioRendererForManualExecution

* Remove SetVolume/GetVolume methods that are not necessary

* Remove SetVolume/GetVolume methods that are not necessary (2)

* Fix incorrect volume update

* PR feedback

* PR feedback

* Stub audrec

* Init outParameter

* Make FinalOutputRecorderParameter/Internal readonly

* Make FinalOutputRecorder IDisposable

* Fix HardwareOpusDecoderManager parameter buffers

* Opus work buffer size and error handling improvements

* Add AudioInProtocolName enum

* Fix potential divisions by zero
This commit is contained in:
gdkchan
2024-02-22 16:58:33 -03:00
committed by GitHub
parent 57d8afd0c9
commit d4d0a48bfe
130 changed files with 3096 additions and 3174 deletions

View File

@@ -0,0 +1,16 @@
using Ryujinx.Horizon.Common;
namespace Ryujinx.Horizon.Sdk.Codec
{
static class CodecResult
{
private const int ModuleId = 111;
public static Result InvalidLength => new(ModuleId, 3);
public static Result OpusBadArg => new(ModuleId, 130);
public static Result OpusInvalidPacket => new(ModuleId, 133);
public static Result InvalidNumberOfStreams => new(ModuleId, 1000);
public static Result InvalidSampleRate => new(ModuleId, 1001);
public static Result InvalidChannelCount => new(ModuleId, 1002);
}
}

View File

@@ -0,0 +1,336 @@
using Concentus;
using Concentus.Enums;
using Concentus.Structs;
using Ryujinx.Common.Logging;
using Ryujinx.Horizon.Common;
using Ryujinx.Horizon.Sdk.Sf;
using Ryujinx.Horizon.Sdk.Sf.Hipc;
using System;
using System.Buffers.Binary;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Ryujinx.Horizon.Sdk.Codec.Detail
{
partial class HardwareOpusDecoder : IHardwareOpusDecoder, IDisposable
{
[StructLayout(LayoutKind.Sequential)]
private struct OpusPacketHeader
{
public uint Length;
public uint FinalRange;
public static OpusPacketHeader FromSpan(ReadOnlySpan<byte> data)
{
return new()
{
Length = BinaryPrimitives.ReadUInt32BigEndian(data),
FinalRange = BinaryPrimitives.ReadUInt32BigEndian(data[sizeof(uint)..]),
};
}
}
private interface IDecoder
{
int SampleRate { get; }
int ChannelsCount { get; }
int Decode(byte[] inData, int inDataOffset, int len, short[] outPcm, int outPcmOffset, int frameSize);
void ResetState();
}
private class Decoder : IDecoder
{
private readonly OpusDecoder _decoder;
public int SampleRate => _decoder.SampleRate;
public int ChannelsCount => _decoder.NumChannels;
public Decoder(int sampleRate, int channelsCount)
{
_decoder = new OpusDecoder(sampleRate, channelsCount);
}
public int Decode(byte[] inData, int inDataOffset, int len, short[] outPcm, int outPcmOffset, int frameSize)
{
return _decoder.Decode(inData, inDataOffset, len, outPcm, outPcmOffset, frameSize);
}
public void ResetState()
{
_decoder.ResetState();
}
}
private class MultiSampleDecoder : IDecoder
{
private readonly OpusMSDecoder _decoder;
public int SampleRate => _decoder.SampleRate;
public int ChannelsCount { get; }
public MultiSampleDecoder(int sampleRate, int channelsCount, int streams, int coupledStreams, byte[] mapping)
{
ChannelsCount = channelsCount;
_decoder = new OpusMSDecoder(sampleRate, channelsCount, streams, coupledStreams, mapping);
}
public int Decode(byte[] inData, int inDataOffset, int len, short[] outPcm, int outPcmOffset, int frameSize)
{
return _decoder.DecodeMultistream(inData, inDataOffset, len, outPcm, outPcmOffset, frameSize, 0);
}
public void ResetState()
{
_decoder.ResetState();
}
}
private readonly IDecoder _decoder;
private int _workBufferHandle;
private HardwareOpusDecoder(int workBufferHandle)
{
_workBufferHandle = workBufferHandle;
}
public HardwareOpusDecoder(int sampleRate, int channelsCount, int workBufferHandle) : this(workBufferHandle)
{
_decoder = new Decoder(sampleRate, channelsCount);
}
public HardwareOpusDecoder(int sampleRate, int channelsCount, int streams, int coupledStreams, byte[] mapping, int workBufferHandle) : this(workBufferHandle)
{
_decoder = new MultiSampleDecoder(sampleRate, channelsCount, streams, coupledStreams, mapping);
}
[CmifCommand(0)]
public Result DecodeInterleavedOld(
out int outConsumed,
out int outSamples,
[Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias)] Span<byte> output,
[Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan<byte> input)
{
return DecodeInterleavedInternal(out outConsumed, out outSamples, out _, output, input, reset: false, withPerf: false);
}
[CmifCommand(1)]
public Result SetContext(ReadOnlySpan<byte> context)
{
Logger.Stub?.PrintStub(LogClass.ServiceAudio);
return Result.Success;
}
[CmifCommand(2)] // 3.0.0+
public Result DecodeInterleavedForMultiStreamOld(
out int outConsumed,
out int outSamples,
[Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias)] Span<byte> output,
[Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan<byte> input)
{
return DecodeInterleavedInternal(out outConsumed, out outSamples, out _, output, input, reset: false, withPerf: false);
}
[CmifCommand(3)] // 3.0.0+
public Result SetContextForMultiStream(ReadOnlySpan<byte> arg0)
{
Logger.Stub?.PrintStub(LogClass.ServiceAudio);
return Result.Success;
}
[CmifCommand(4)] // 4.0.0+
public Result DecodeInterleavedWithPerfOld(
out int outConsumed,
out long timeTaken,
out int outSamples,
[Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias | HipcBufferFlags.MapTransferAllowsNonSecure)] Span<byte> output,
[Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan<byte> input)
{
return DecodeInterleavedInternal(out outConsumed, out outSamples, out timeTaken, output, input, reset: false, withPerf: true);
}
[CmifCommand(5)] // 4.0.0+
public Result DecodeInterleavedForMultiStreamWithPerfOld(
out int outConsumed,
out long timeTaken,
out int outSamples,
[Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias | HipcBufferFlags.MapTransferAllowsNonSecure)] Span<byte> output,
[Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan<byte> input)
{
return DecodeInterleavedInternal(out outConsumed, out outSamples, out timeTaken, output, input, reset: false, withPerf: true);
}
[CmifCommand(6)] // 6.0.0+
public Result DecodeInterleavedWithPerfAndResetOld(
out int outConsumed,
out long timeTaken,
out int outSamples,
[Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias | HipcBufferFlags.MapTransferAllowsNonSecure)] Span<byte> output,
[Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan<byte> input,
bool reset)
{
return DecodeInterleavedInternal(out outConsumed, out outSamples, out timeTaken, output, input, reset, withPerf: true);
}
[CmifCommand(7)] // 6.0.0+
public Result DecodeInterleavedForMultiStreamWithPerfAndResetOld(
out int outConsumed,
out long timeTaken,
out int outSamples,
[Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias | HipcBufferFlags.MapTransferAllowsNonSecure)] Span<byte> output,
[Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan<byte> input,
bool reset)
{
return DecodeInterleavedInternal(out outConsumed, out outSamples, out timeTaken, output, input, reset, withPerf: true);
}
[CmifCommand(8)] // 7.0.0+
public Result DecodeInterleaved(
out int outConsumed,
out long timeTaken,
out int outSamples,
[Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias | HipcBufferFlags.MapTransferAllowsNonSecure)] Span<byte> output,
[Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias | HipcBufferFlags.MapTransferAllowsNonSecure)] ReadOnlySpan<byte> input,
bool reset)
{
return DecodeInterleavedInternal(out outConsumed, out outSamples, out timeTaken, output, input, reset, withPerf: true);
}
[CmifCommand(9)] // 7.0.0+
public Result DecodeInterleavedForMultiStream(
out int outConsumed,
out long timeTaken,
out int outSamples,
[Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias | HipcBufferFlags.MapTransferAllowsNonSecure)] Span<byte> output,
[Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias | HipcBufferFlags.MapTransferAllowsNonSecure)] ReadOnlySpan<byte> input,
bool reset)
{
return DecodeInterleavedInternal(out outConsumed, out outSamples, out timeTaken, output, input, reset, withPerf: true);
}
private Result DecodeInterleavedInternal(
out int outConsumed,
out int outSamples,
out long timeTaken,
Span<byte> output,
ReadOnlySpan<byte> input,
bool reset,
bool withPerf)
{
timeTaken = 0;
Result result = DecodeInterleaved(_decoder, reset, input, out short[] outPcmData, output.Length, out outConsumed, out outSamples);
if (withPerf)
{
// This is the time the DSP took to process the request, TODO: fill this.
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)
{
int result = OpusPacketInfo.GetNumSamples(packet, 0, packet.Length, decoder.SampleRate);
numSamples = result;
if (result == OpusError.OPUS_INVALID_PACKET)
{
return CodecResult.OpusInvalidPacket;
}
else if (result == OpusError.OPUS_BAD_ARG)
{
return CodecResult.OpusBadArg;
}
return Result.Success;
}
private static Result DecodeInterleaved(
IDecoder decoder,
bool reset,
ReadOnlySpan<byte> input,
out short[] outPcmData,
int outputSize,
out int outConsumed,
out int outSamples)
{
outPcmData = null;
outConsumed = 0;
outSamples = 0;
int streamSize = input.Length;
if (streamSize < Unsafe.SizeOf<OpusPacketHeader>())
{
return CodecResult.InvalidLength;
}
OpusPacketHeader header = OpusPacketHeader.FromSpan(input);
int headerSize = Unsafe.SizeOf<OpusPacketHeader>();
uint totalSize = header.Length + (uint)headerSize;
if (totalSize > streamSize)
{
return CodecResult.InvalidLength;
}
byte[] opusData = input.Slice(headerSize, (int)header.Length).ToArray();
Result result = GetPacketNumSamples(decoder, out int numSamples, opusData);
if (result.IsSuccess)
{
if ((uint)numSamples * (uint)decoder.ChannelsCount * sizeof(short) > outputSize)
{
return CodecResult.InvalidLength;
}
outPcmData = new short[numSamples * decoder.ChannelsCount];
if (reset)
{
decoder.ResetState();
}
try
{
outSamples = decoder.Decode(opusData, 0, opusData.Length, outPcmData, 0, outPcmData.Length / decoder.ChannelsCount);
outConsumed = (int)totalSize;
}
catch (OpusException)
{
// TODO: As OpusException doesn't return the exact error code, this is inaccurate in some cases...
return CodecResult.InvalidLength;
}
}
return Result.Success;
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (_workBufferHandle != 0)
{
HorizonStatic.Syscall.CloseHandle(_workBufferHandle);
_workBufferHandle = 0;
}
}
}
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}
}

View File

@@ -0,0 +1,386 @@
using Ryujinx.Common;
using Ryujinx.Horizon.Common;
using Ryujinx.Horizon.Sdk.Sf;
using Ryujinx.Horizon.Sdk.Sf.Hipc;
using System;
namespace Ryujinx.Horizon.Sdk.Codec.Detail
{
partial class HardwareOpusDecoderManager : IHardwareOpusDecoderManager
{
[CmifCommand(0)]
public Result OpenHardwareOpusDecoder(
out IHardwareOpusDecoder decoder,
HardwareOpusDecoderParameterInternal parameter,
[CopyHandle] int workBufferHandle,
int workBufferSize)
{
decoder = null;
if (!IsValidSampleRate(parameter.SampleRate))
{
HorizonStatic.Syscall.CloseHandle(workBufferHandle);
return CodecResult.InvalidSampleRate;
}
if (!IsValidChannelCount(parameter.ChannelsCount))
{
HorizonStatic.Syscall.CloseHandle(workBufferHandle);
return CodecResult.InvalidChannelCount;
}
decoder = new HardwareOpusDecoder(parameter.SampleRate, parameter.ChannelsCount, workBufferHandle);
return Result.Success;
}
[CmifCommand(1)]
public Result GetWorkBufferSize(out int size, HardwareOpusDecoderParameterInternal parameter)
{
size = 0;
if (!IsValidChannelCount(parameter.ChannelsCount))
{
return CodecResult.InvalidChannelCount;
}
if (!IsValidSampleRate(parameter.SampleRate))
{
return CodecResult.InvalidSampleRate;
}
int opusDecoderSize = GetOpusDecoderSize(parameter.ChannelsCount);
int sampleRateRatio = parameter.SampleRate != 0 ? 48000 / parameter.SampleRate : 0;
int frameSize = BitUtils.AlignUp(sampleRateRatio != 0 ? parameter.ChannelsCount * 1920 / sampleRateRatio : 0, 64);
size = opusDecoderSize + 1536 + frameSize;
return Result.Success;
}
[CmifCommand(2)] // 3.0.0+
public Result OpenHardwareOpusDecoderForMultiStream(
out IHardwareOpusDecoder decoder,
[Buffer(HipcBufferFlags.In | HipcBufferFlags.Pointer, 0x110)] in HardwareOpusMultiStreamDecoderParameterInternal parameter,
[CopyHandle] int workBufferHandle,
int workBufferSize)
{
decoder = null;
if (!IsValidSampleRate(parameter.SampleRate))
{
HorizonStatic.Syscall.CloseHandle(workBufferHandle);
return CodecResult.InvalidSampleRate;
}
if (!IsValidMultiChannelCount(parameter.ChannelsCount))
{
HorizonStatic.Syscall.CloseHandle(workBufferHandle);
return CodecResult.InvalidChannelCount;
}
if (!IsValidNumberOfStreams(parameter.NumberOfStreams, parameter.NumberOfStereoStreams, parameter.ChannelsCount))
{
HorizonStatic.Syscall.CloseHandle(workBufferHandle);
return CodecResult.InvalidNumberOfStreams;
}
decoder = new HardwareOpusDecoder(
parameter.SampleRate,
parameter.ChannelsCount,
parameter.NumberOfStreams,
parameter.NumberOfStereoStreams,
parameter.ChannelMappings.AsSpan().ToArray(),
workBufferHandle);
return Result.Success;
}
[CmifCommand(3)] // 3.0.0+
public Result GetWorkBufferSizeForMultiStream(
out int size,
[Buffer(HipcBufferFlags.In | HipcBufferFlags.Pointer, 0x110)] in HardwareOpusMultiStreamDecoderParameterInternal parameter)
{
size = 0;
if (!IsValidMultiChannelCount(parameter.ChannelsCount))
{
return CodecResult.InvalidChannelCount;
}
if (!IsValidSampleRate(parameter.SampleRate))
{
return CodecResult.InvalidSampleRate;
}
if (!IsValidNumberOfStreams(parameter.NumberOfStreams, parameter.NumberOfStereoStreams, parameter.ChannelsCount))
{
return CodecResult.InvalidSampleRate;
}
int opusDecoderSize = GetOpusMultistreamDecoderSize(parameter.NumberOfStreams, parameter.NumberOfStereoStreams);
int streamSize = BitUtils.AlignUp(parameter.NumberOfStreams * 1500, 64);
int sampleRateRatio = parameter.SampleRate != 0 ? 48000 / parameter.SampleRate : 0;
int frameSize = BitUtils.AlignUp(sampleRateRatio != 0 ? parameter.ChannelsCount * 1920 / sampleRateRatio : 0, 64);
size = opusDecoderSize + streamSize + frameSize;
return Result.Success;
}
[CmifCommand(4)] // 12.0.0+
public Result OpenHardwareOpusDecoderEx(
out IHardwareOpusDecoder decoder,
HardwareOpusDecoderParameterInternalEx parameter,
[CopyHandle] int workBufferHandle,
int workBufferSize)
{
decoder = null;
if (!IsValidChannelCount(parameter.ChannelsCount))
{
HorizonStatic.Syscall.CloseHandle(workBufferHandle);
return CodecResult.InvalidChannelCount;
}
if (!IsValidSampleRate(parameter.SampleRate))
{
HorizonStatic.Syscall.CloseHandle(workBufferHandle);
return CodecResult.InvalidSampleRate;
}
decoder = new HardwareOpusDecoder(parameter.SampleRate, parameter.ChannelsCount, workBufferHandle);
return Result.Success;
}
[CmifCommand(5)] // 12.0.0+
public Result GetWorkBufferSizeEx(out int size, HardwareOpusDecoderParameterInternalEx parameter)
{
return GetWorkBufferSizeExImpl(out size, in parameter, fromDsp: false);
}
[CmifCommand(6)] // 12.0.0+
public Result OpenHardwareOpusDecoderForMultiStreamEx(
out IHardwareOpusDecoder decoder,
[Buffer(HipcBufferFlags.In | HipcBufferFlags.Pointer, 0x118)] in HardwareOpusMultiStreamDecoderParameterInternalEx parameter,
[CopyHandle] int workBufferHandle,
int workBufferSize)
{
decoder = null;
if (!IsValidSampleRate(parameter.SampleRate))
{
HorizonStatic.Syscall.CloseHandle(workBufferHandle);
return CodecResult.InvalidSampleRate;
}
if (!IsValidMultiChannelCount(parameter.ChannelsCount))
{
HorizonStatic.Syscall.CloseHandle(workBufferHandle);
return CodecResult.InvalidChannelCount;
}
if (!IsValidNumberOfStreams(parameter.NumberOfStreams, parameter.NumberOfStereoStreams, parameter.ChannelsCount))
{
HorizonStatic.Syscall.CloseHandle(workBufferHandle);
return CodecResult.InvalidNumberOfStreams;
}
decoder = new HardwareOpusDecoder(
parameter.SampleRate,
parameter.ChannelsCount,
parameter.NumberOfStreams,
parameter.NumberOfStereoStreams,
parameter.ChannelMappings.AsSpan().ToArray(),
workBufferHandle);
return Result.Success;
}
[CmifCommand(7)] // 12.0.0+
public Result GetWorkBufferSizeForMultiStreamEx(
out int size,
[Buffer(HipcBufferFlags.In | HipcBufferFlags.Pointer, 0x118)] in HardwareOpusMultiStreamDecoderParameterInternalEx parameter)
{
return GetWorkBufferSizeForMultiStreamExImpl(out size, in parameter, fromDsp: false);
}
[CmifCommand(8)] // 16.0.0+
public Result GetWorkBufferSizeExEx(out int size, HardwareOpusDecoderParameterInternalEx parameter)
{
return GetWorkBufferSizeExImpl(out size, in parameter, fromDsp: true);
}
[CmifCommand(9)] // 16.0.0+
public Result GetWorkBufferSizeForMultiStreamExEx(
out int size,
[Buffer(HipcBufferFlags.In | HipcBufferFlags.Pointer, 0x118)] in HardwareOpusMultiStreamDecoderParameterInternalEx parameter)
{
return GetWorkBufferSizeForMultiStreamExImpl(out size, in parameter, fromDsp: true);
}
private Result GetWorkBufferSizeExImpl(out int size, in HardwareOpusDecoderParameterInternalEx parameter, bool fromDsp)
{
size = 0;
if (!IsValidChannelCount(parameter.ChannelsCount))
{
return CodecResult.InvalidChannelCount;
}
if (!IsValidSampleRate(parameter.SampleRate))
{
return CodecResult.InvalidSampleRate;
}
int opusDecoderSize = fromDsp ? GetDspOpusDecoderSize(parameter.ChannelsCount) : GetOpusDecoderSize(parameter.ChannelsCount);
int frameSizeMono48KHz = parameter.Flags.HasFlag(OpusDecoderFlags.LargeFrameSize) ? 5760 : 1920;
int sampleRateRatio = parameter.SampleRate != 0 ? 48000 / parameter.SampleRate : 0;
int frameSize = BitUtils.AlignUp(sampleRateRatio != 0 ? parameter.ChannelsCount * frameSizeMono48KHz / sampleRateRatio : 0, 64);
size = opusDecoderSize + 1536 + frameSize;
return Result.Success;
}
private Result GetWorkBufferSizeForMultiStreamExImpl(out int size, in HardwareOpusMultiStreamDecoderParameterInternalEx parameter, bool fromDsp)
{
size = 0;
if (!IsValidMultiChannelCount(parameter.ChannelsCount))
{
return CodecResult.InvalidChannelCount;
}
if (!IsValidSampleRate(parameter.SampleRate))
{
return CodecResult.InvalidSampleRate;
}
if (!IsValidNumberOfStreams(parameter.NumberOfStreams, parameter.NumberOfStereoStreams, parameter.ChannelsCount))
{
return CodecResult.InvalidSampleRate;
}
int opusDecoderSize = fromDsp
? GetDspOpusMultistreamDecoderSize(parameter.NumberOfStreams, parameter.NumberOfStereoStreams)
: GetOpusMultistreamDecoderSize(parameter.NumberOfStreams, parameter.NumberOfStereoStreams);
int frameSizeMono48KHz = parameter.Flags.HasFlag(OpusDecoderFlags.LargeFrameSize) ? 5760 : 1920;
int streamSize = BitUtils.AlignUp(parameter.NumberOfStreams * 1500, 64);
int sampleRateRatio = parameter.SampleRate != 0 ? 48000 / parameter.SampleRate : 0;
int frameSize = BitUtils.AlignUp(sampleRateRatio != 0 ? parameter.ChannelsCount * frameSizeMono48KHz / sampleRateRatio : 0, 64);
size = opusDecoderSize + streamSize + frameSize;
return Result.Success;
}
private static int GetDspOpusDecoderSize(int channelsCount)
{
// TODO: Figure out the size returned here.
// Not really important because we don't use the work buffer, and the size being lower is fine.
return 0;
}
private static int GetDspOpusMultistreamDecoderSize(int streams, int coupledStreams)
{
// TODO: Figure out the size returned here.
// Not really important because we don't use the work buffer, and the size being lower is fine.
return 0;
}
private static int GetOpusDecoderSize(int channelsCount)
{
const int SilkDecoderSize = 0x2160;
if (channelsCount < 1 || channelsCount > 2)
{
return 0;
}
int celtDecoderSize = GetCeltDecoderSize(channelsCount);
int opusDecoderSize = GetOpusDecoderAllocSize(channelsCount) | 0x50;
return opusDecoderSize + SilkDecoderSize + celtDecoderSize;
}
private static int GetOpusMultistreamDecoderSize(int streams, int coupledStreams)
{
if (streams < 1 || coupledStreams > streams || coupledStreams < 0)
{
return 0;
}
int coupledSize = GetOpusDecoderSize(2);
int monoSize = GetOpusDecoderSize(1);
return Align4(monoSize - GetOpusDecoderAllocSize(1)) * (streams - coupledStreams) +
Align4(coupledSize - GetOpusDecoderAllocSize(2)) * coupledStreams + 0xb920;
}
private static int Align4(int value)
{
return BitUtils.AlignUp(value, 4);
}
private static int GetOpusDecoderAllocSize(int channelsCount)
{
return channelsCount * 0x800 + 0x4800;
}
private static int GetCeltDecoderSize(int channelsCount)
{
const int DecodeBufferSize = 0x2030;
const int Overlap = 120;
const int EBandsCount = 21;
return (DecodeBufferSize + Overlap * 4) * channelsCount + EBandsCount * 16 + 0x54;
}
private static bool IsValidChannelCount(int channelsCount)
{
return channelsCount > 0 && channelsCount <= 2;
}
private static bool IsValidMultiChannelCount(int channelsCount)
{
return channelsCount > 0 && channelsCount <= 255;
}
private static bool IsValidSampleRate(int sampleRate)
{
switch (sampleRate)
{
case 8000:
case 12000:
case 16000:
case 24000:
case 48000:
return true;
}
return false;
}
private static bool IsValidNumberOfStreams(int numberOfStreams, int numberOfStereoStreams, int channelsCount)
{
return numberOfStreams > 0 &&
numberOfStreams + numberOfStereoStreams <= channelsCount &&
numberOfStereoStreams >= 0 &&
numberOfStereoStreams <= numberOfStreams;
}
}
}

View File

@@ -0,0 +1,11 @@
using System.Runtime.InteropServices;
namespace Ryujinx.Horizon.Sdk.Codec.Detail
{
[StructLayout(LayoutKind.Sequential, Size = 0x8, Pack = 0x4)]
struct HardwareOpusDecoderParameterInternal
{
public int SampleRate;
public int ChannelsCount;
}
}

View File

@@ -0,0 +1,13 @@
using System.Runtime.InteropServices;
namespace Ryujinx.Horizon.Sdk.Codec.Detail
{
[StructLayout(LayoutKind.Sequential, Size = 0x10, Pack = 0x4)]
struct HardwareOpusDecoderParameterInternalEx
{
public int SampleRate;
public int ChannelsCount;
public OpusDecoderFlags Flags;
public uint Reserved;
}
}

View File

@@ -0,0 +1,15 @@
using Ryujinx.Common.Memory;
using System.Runtime.InteropServices;
namespace Ryujinx.Horizon.Sdk.Codec.Detail
{
[StructLayout(LayoutKind.Sequential, Size = 0x110)]
struct HardwareOpusMultiStreamDecoderParameterInternal
{
public int SampleRate;
public int ChannelsCount;
public int NumberOfStreams;
public int NumberOfStereoStreams;
public Array256<byte> ChannelMappings;
}
}

View File

@@ -0,0 +1,17 @@
using Ryujinx.Common.Memory;
using System.Runtime.InteropServices;
namespace Ryujinx.Horizon.Sdk.Codec.Detail
{
[StructLayout(LayoutKind.Sequential, Size = 0x118)]
struct HardwareOpusMultiStreamDecoderParameterInternalEx
{
public int SampleRate;
public int ChannelsCount;
public int NumberOfStreams;
public int NumberOfStereoStreams;
public OpusDecoderFlags Flags;
public uint Reserved;
public Array256<byte> ChannelMappings;
}
}

View File

@@ -0,0 +1,20 @@
using Ryujinx.Horizon.Common;
using Ryujinx.Horizon.Sdk.Sf;
using System;
namespace Ryujinx.Horizon.Sdk.Codec.Detail
{
interface IHardwareOpusDecoder : IServiceObject
{
Result DecodeInterleavedOld(out int outConsumed, out int outSamples, Span<byte> output, ReadOnlySpan<byte> input);
Result SetContext(ReadOnlySpan<byte> context);
Result DecodeInterleavedForMultiStreamOld(out int outConsumed, out int outSamples, Span<byte> output, ReadOnlySpan<byte> input);
Result SetContextForMultiStream(ReadOnlySpan<byte> context);
Result DecodeInterleavedWithPerfOld(out int outConsumed, out long timeTaken, out int outSamples, Span<byte> output, ReadOnlySpan<byte> input);
Result DecodeInterleavedForMultiStreamWithPerfOld(out int outConsumed, out long timeTaken, out int outSamples, Span<byte> output, ReadOnlySpan<byte> input);
Result DecodeInterleavedWithPerfAndResetOld(out int outConsumed, out long timeTaken, out int outSamples, Span<byte> output, ReadOnlySpan<byte> input, bool reset);
Result DecodeInterleavedForMultiStreamWithPerfAndResetOld(out int outConsumed, out long timeTaken, out int outSamples, Span<byte> output, ReadOnlySpan<byte> input, bool reset);
Result DecodeInterleaved(out int outConsumed, out long timeTaken, out int outSamples, Span<byte> output, ReadOnlySpan<byte> input, bool reset);
Result DecodeInterleavedForMultiStream(out int outConsumed, out long timeTaken, out int outSamples, Span<byte> output, ReadOnlySpan<byte> input, bool reset);
}
}

View File

@@ -0,0 +1,19 @@
using Ryujinx.Horizon.Common;
using Ryujinx.Horizon.Sdk.Sf;
namespace Ryujinx.Horizon.Sdk.Codec.Detail
{
interface IHardwareOpusDecoderManager : IServiceObject
{
Result OpenHardwareOpusDecoder(out IHardwareOpusDecoder decoder, HardwareOpusDecoderParameterInternal parameter, int workBufferHandle, int workBufferSize);
Result GetWorkBufferSize(out int size, HardwareOpusDecoderParameterInternal parameter);
Result OpenHardwareOpusDecoderForMultiStream(out IHardwareOpusDecoder decoder, in HardwareOpusMultiStreamDecoderParameterInternal parameter, int workBufferHandle, int workBufferSize);
Result GetWorkBufferSizeForMultiStream(out int size, in HardwareOpusMultiStreamDecoderParameterInternal parameter);
Result OpenHardwareOpusDecoderEx(out IHardwareOpusDecoder decoder, HardwareOpusDecoderParameterInternalEx parameter, int workBufferHandle, int workBufferSize);
Result GetWorkBufferSizeEx(out int size, HardwareOpusDecoderParameterInternalEx parameter);
Result OpenHardwareOpusDecoderForMultiStreamEx(out IHardwareOpusDecoder decoder, in HardwareOpusMultiStreamDecoderParameterInternalEx parameter, int workBufferHandle, int workBufferSize);
Result GetWorkBufferSizeForMultiStreamEx(out int size, in HardwareOpusMultiStreamDecoderParameterInternalEx parameter);
Result GetWorkBufferSizeExEx(out int size, HardwareOpusDecoderParameterInternalEx parameter);
Result GetWorkBufferSizeForMultiStreamExEx(out int size, in HardwareOpusMultiStreamDecoderParameterInternalEx parameter);
}
}

View File

@@ -0,0 +1,11 @@
using System;
namespace Ryujinx.Horizon.Sdk.Codec.Detail
{
[Flags]
enum OpusDecoderFlags : uint
{
None,
LargeFrameSize = 1 << 0,
}
}