aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Ryujinx.Audio/Adpcm/AdpcmDecoder.cs91
-rw-r--r--Ryujinx.Audio/Adpcm/AdpcmDecoderContext.cs10
-rw-r--r--Ryujinx.Audio/DspUtils.cs16
-rw-r--r--Ryujinx.Audio/IAalOutput.cs8
-rw-r--r--Ryujinx.Audio/OpenAL/OpenALAudioOut.cs53
-rw-r--r--Ryujinx.HLE/OsHle/Services/Aud/AudErr.cs1
-rw-r--r--Ryujinx.HLE/OsHle/Services/Aud/AudioOut/AudioOutData.cs (renamed from Ryujinx.HLE/OsHle/Services/Aud/AudioOutData.cs)2
-rw-r--r--Ryujinx.HLE/OsHle/Services/Aud/AudioOut/IAudioOut.cs (renamed from Ryujinx.HLE/OsHle/Services/Aud/IAudioOut.cs)3
-rw-r--r--Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/AudioConsts.cs8
-rw-r--r--Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/BehaviorIn.cs11
-rw-r--r--Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/BiquadFilter.cs16
-rw-r--r--Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/IAudioRenderer.cs316
-rw-r--r--Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/MemoryPoolContext.cs12
-rw-r--r--Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/MemoryPoolIn.cs14
-rw-r--r--Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/MemoryPoolOut.cs12
-rw-r--r--Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/MemoryPoolState.cs (renamed from Ryujinx.HLE/OsHle/Services/Aud/MemoryPoolState.cs)2
-rw-r--r--Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/PlayState.cs9
-rw-r--r--Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/Resampler.cs191
-rw-r--r--Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/UpdateDataHeader.cs (renamed from Ryujinx.HLE/OsHle/Services/Aud/UpdateDataHeader.cs)12
-rw-r--r--Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/VoiceChannelResourceIn.cs10
-rw-r--r--Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/VoiceContext.cs188
-rw-r--r--Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/VoiceIn.cs49
-rw-r--r--Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/VoiceOut.cs12
-rw-r--r--Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/WaveBuffer.cs20
-rw-r--r--Ryujinx.HLE/OsHle/Services/Aud/AudioRendererParameter.cs8
-rw-r--r--Ryujinx.HLE/OsHle/Services/Aud/IAudioOutManager.cs5
-rw-r--r--Ryujinx.HLE/OsHle/Services/Aud/IAudioRenderer.cs136
-rw-r--r--Ryujinx.HLE/OsHle/Services/Aud/IAudioRendererManager.cs135
-rw-r--r--Ryujinx.HLE/OsHle/Services/Aud/SampleFormat.cs (renamed from Ryujinx.Audio/AudioFormat.cs)8
-rw-r--r--Ryujinx.HLE/OsHle/Services/Aud/VoiceState.cs9
-rw-r--r--Ryujinx.HLE/OsHle/Services/Nv/NvMap/NvMapIoctl.cs4
-rw-r--r--Ryujinx.HLE/OsHle/Utilities/IntUtils.cs4
-rw-r--r--Ryujinx.HLE/OsHle/Utilities/StructReader.cs45
-rw-r--r--Ryujinx.HLE/OsHle/Utilities/StructWriter.cs25
34 files changed, 1171 insertions, 274 deletions
diff --git a/Ryujinx.Audio/Adpcm/AdpcmDecoder.cs b/Ryujinx.Audio/Adpcm/AdpcmDecoder.cs
new file mode 100644
index 00000000..24455b41
--- /dev/null
+++ b/Ryujinx.Audio/Adpcm/AdpcmDecoder.cs
@@ -0,0 +1,91 @@
+namespace Ryujinx.Audio.Adpcm
+{
+ public static class AdpcmDecoder
+ {
+ private const int SamplesPerFrame = 14;
+ private const int BytesPerFrame = 8;
+
+ public static int[] Decode(byte[] Buffer, AdpcmDecoderContext Context)
+ {
+ int Samples = GetSamplesCountFromSize(Buffer.Length);
+
+ int[] Pcm = new int[Samples * 2];
+
+ short History0 = Context.History0;
+ short History1 = Context.History1;
+
+ int InputOffset = 0;
+ int OutputOffset = 0;
+
+ while (InputOffset < Buffer.Length)
+ {
+ byte Header = Buffer[InputOffset++];
+
+ int Scale = 0x800 << (Header & 0xf);
+
+ int CoeffIndex = (Header >> 4) & 7;
+
+ short Coeff0 = Context.Coefficients[CoeffIndex * 2 + 0];
+ short Coeff1 = Context.Coefficients[CoeffIndex * 2 + 1];
+
+ int FrameSamples = SamplesPerFrame;
+
+ if (FrameSamples > Samples)
+ {
+ FrameSamples = Samples;
+ }
+
+ int Value = 0;
+
+ for (int SampleIndex = 0; SampleIndex < FrameSamples; SampleIndex++)
+ {
+ int Sample;
+
+ if ((SampleIndex & 1) == 0)
+ {
+ Value = Buffer[InputOffset++];
+
+ Sample = (Value << 24) >> 28;
+ }
+ else
+ {
+ Sample = (Value << 28) >> 28;
+ }
+
+ int Prediction = Coeff0 * History0 + Coeff1 * History1;
+
+ Sample = (Sample * Scale + Prediction + 0x400) >> 11;
+
+ short SaturatedSample = DspUtils.Saturate(Sample);
+
+ History1 = History0;
+ History0 = SaturatedSample;
+
+ Pcm[OutputOffset++] = SaturatedSample;
+ Pcm[OutputOffset++] = SaturatedSample;
+ }
+
+ Samples -= FrameSamples;
+ }
+
+ Context.History0 = History0;
+ Context.History1 = History1;
+
+ return Pcm;
+ }
+
+ public static long GetSizeFromSamplesCount(int SamplesCount)
+ {
+ int Frames = SamplesCount / SamplesPerFrame;
+
+ return Frames * BytesPerFrame;
+ }
+
+ public static int GetSamplesCountFromSize(long Size)
+ {
+ int Frames = (int)(Size / BytesPerFrame);
+
+ return Frames * SamplesPerFrame;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Audio/Adpcm/AdpcmDecoderContext.cs b/Ryujinx.Audio/Adpcm/AdpcmDecoderContext.cs
new file mode 100644
index 00000000..91730333
--- /dev/null
+++ b/Ryujinx.Audio/Adpcm/AdpcmDecoderContext.cs
@@ -0,0 +1,10 @@
+namespace Ryujinx.Audio.Adpcm
+{
+ public class AdpcmDecoderContext
+ {
+ public short[] Coefficients;
+
+ public short History0;
+ public short History1;
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Audio/DspUtils.cs b/Ryujinx.Audio/DspUtils.cs
new file mode 100644
index 00000000..c048161d
--- /dev/null
+++ b/Ryujinx.Audio/DspUtils.cs
@@ -0,0 +1,16 @@
+namespace Ryujinx.Audio.Adpcm
+{
+ public static class DspUtils
+ {
+ public static short Saturate(int Value)
+ {
+ if (Value > short.MaxValue)
+ Value = short.MaxValue;
+
+ if (Value < short.MinValue)
+ Value = short.MinValue;
+
+ return (short)Value;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Audio/IAalOutput.cs b/Ryujinx.Audio/IAalOutput.cs
index f9978ee4..e903c5c5 100644
--- a/Ryujinx.Audio/IAalOutput.cs
+++ b/Ryujinx.Audio/IAalOutput.cs
@@ -2,11 +2,7 @@ namespace Ryujinx.Audio
{
public interface IAalOutput
{
- int OpenTrack(
- int SampleRate,
- int Channels,
- ReleaseCallback Callback,
- out AudioFormat Format);
+ int OpenTrack(int SampleRate, int Channels, ReleaseCallback Callback);
void CloseTrack(int Track);
@@ -14,7 +10,7 @@ namespace Ryujinx.Audio
long[] GetReleasedBuffers(int Track, int MaxCount);
- void AppendBuffer(int Track, long Tag, byte[] Buffer);
+ void AppendBuffer<T>(int Track, long Tag, T[] Buffer) where T : struct;
void Start(int Track);
void Stop(int Track);
diff --git a/Ryujinx.Audio/OpenAL/OpenALAudioOut.cs b/Ryujinx.Audio/OpenAL/OpenALAudioOut.cs
index 2860dc2e..1dd63202 100644
--- a/Ryujinx.Audio/OpenAL/OpenALAudioOut.cs
+++ b/Ryujinx.Audio/OpenAL/OpenALAudioOut.cs
@@ -3,6 +3,7 @@ using OpenTK.Audio.OpenAL;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
+using System.Runtime.InteropServices;
using System.Threading;
namespace Ryujinx.Audio.OpenAL
@@ -226,15 +227,9 @@ namespace Ryujinx.Audio.OpenAL
while (KeepPolling);
}
- public int OpenTrack(
- int SampleRate,
- int Channels,
- ReleaseCallback Callback,
- out AudioFormat Format)
+ public int OpenTrack(int SampleRate, int Channels, ReleaseCallback Callback)
{
- Format = AudioFormat.PcmInt16;
-
- Track Td = new Track(SampleRate, GetALFormat(Channels, Format), Callback);
+ Track Td = new Track(SampleRate, GetALFormat(Channels), Callback);
for (int Id = 0; Id < MaxTracks; Id++)
{
@@ -247,38 +242,16 @@ namespace Ryujinx.Audio.OpenAL
return -1;
}
- private ALFormat GetALFormat(int Channels, AudioFormat Format)
+ private ALFormat GetALFormat(int Channels)
{
- if (Channels == 1)
- {
- switch (Format)
- {
- case AudioFormat.PcmInt8: return ALFormat.Mono8;
- case AudioFormat.PcmInt16: return ALFormat.Mono16;
- }
- }
- else if (Channels == 2)
- {
- switch (Format)
- {
- case AudioFormat.PcmInt8: return ALFormat.Stereo8;
- case AudioFormat.PcmInt16: return ALFormat.Stereo16;
- }
- }
- else if (Channels == 6)
- {
- switch (Format)
- {
- case AudioFormat.PcmInt8: return ALFormat.Multi51Chn8Ext;
- case AudioFormat.PcmInt16: return ALFormat.Multi51Chn16Ext;
- }
- }
- else
+ switch (Channels)
{
- throw new ArgumentOutOfRangeException(nameof(Channels));
+ case 1: return ALFormat.Mono16;
+ case 2: return ALFormat.Stereo16;
+ case 6: return ALFormat.Multi51Chn16Ext;
}
- throw new ArgumentException(nameof(Format));
+ throw new ArgumentOutOfRangeException(nameof(Channels));
}
public void CloseTrack(int Track)
@@ -309,13 +282,15 @@ namespace Ryujinx.Audio.OpenAL
return null;
}
- public void AppendBuffer(int Track, long Tag, byte[] Buffer)
+ public void AppendBuffer<T>(int Track, long Tag, T[] Buffer) where T : struct
{
if (Tracks.TryGetValue(Track, out Track Td))
{
int BufferId = Td.AppendBuffer(Tag);
- AL.BufferData(BufferId, Td.Format, Buffer, Buffer.Length, Td.SampleRate);
+ int Size = Buffer.Length * Marshal.SizeOf<T>();
+
+ AL.BufferData<T>(BufferId, Td.Format, Buffer, Size, Td.SampleRate);
AL.SourceQueueBuffer(Td.SourceId, BufferId);
@@ -366,7 +341,5 @@ namespace Ryujinx.Audio.OpenAL
return PlaybackState.Stopped;
}
-
-
}
} \ No newline at end of file
diff --git a/Ryujinx.HLE/OsHle/Services/Aud/AudErr.cs b/Ryujinx.HLE/OsHle/Services/Aud/AudErr.cs
index fa201d8c..72c3e65e 100644
--- a/Ryujinx.HLE/OsHle/Services/Aud/AudErr.cs
+++ b/Ryujinx.HLE/OsHle/Services/Aud/AudErr.cs
@@ -3,6 +3,7 @@ namespace Ryujinx.HLE.OsHle.Services.Aud
static class AudErr
{
public const int DeviceNotFound = 1;
+ public const int UnsupportedRevision = 2;
public const int UnsupportedSampleRate = 3;
}
} \ No newline at end of file
diff --git a/Ryujinx.HLE/OsHle/Services/Aud/AudioOutData.cs b/Ryujinx.HLE/OsHle/Services/Aud/AudioOut/AudioOutData.cs
index 9d68c24a..6887a38b 100644
--- a/Ryujinx.HLE/OsHle/Services/Aud/AudioOutData.cs
+++ b/Ryujinx.HLE/OsHle/Services/Aud/AudioOut/AudioOutData.cs
@@ -1,6 +1,6 @@
using System.Runtime.InteropServices;
-namespace Ryujinx.HLE.OsHle.Services.Aud
+namespace Ryujinx.HLE.OsHle.Services.Aud.AudioOut
{
[StructLayout(LayoutKind.Sequential)]
struct AudioOutData
diff --git a/Ryujinx.HLE/OsHle/Services/Aud/IAudioOut.cs b/Ryujinx.HLE/OsHle/Services/Aud/AudioOut/IAudioOut.cs
index 49c87a56..d89fc293 100644
--- a/Ryujinx.HLE/OsHle/Services/Aud/IAudioOut.cs
+++ b/Ryujinx.HLE/OsHle/Services/Aud/AudioOut/IAudioOut.cs
@@ -1,12 +1,11 @@
using ChocolArm64.Memory;
using Ryujinx.Audio;
-using Ryujinx.HLE.Logging;
using Ryujinx.HLE.OsHle.Handles;
using Ryujinx.HLE.OsHle.Ipc;
using System;
using System.Collections.Generic;
-namespace Ryujinx.HLE.OsHle.Services.Aud
+namespace Ryujinx.HLE.OsHle.Services.Aud.AudioOut
{
class IAudioOut : IpcService, IDisposable
{
diff --git a/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/AudioConsts.cs b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/AudioConsts.cs
new file mode 100644
index 00000000..fed41959
--- /dev/null
+++ b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/AudioConsts.cs
@@ -0,0 +1,8 @@
+namespace Ryujinx.HLE.OsHle.Services.Aud.AudioRenderer
+{
+ static class AudioConsts
+ {
+ public const int HostSampleRate = 48000;
+ public const int HostChannelsCount = 2;
+ }
+}
diff --git a/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/BehaviorIn.cs b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/BehaviorIn.cs
new file mode 100644
index 00000000..4e33de62
--- /dev/null
+++ b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/BehaviorIn.cs
@@ -0,0 +1,11 @@
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.OsHle.Services.Aud.AudioRenderer
+{
+ [StructLayout(LayoutKind.Sequential, Size = 0x10, Pack = 4)]
+ struct BehaviorIn
+ {
+ public long Unknown0;
+ public long Unknown8;
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/BiquadFilter.cs b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/BiquadFilter.cs
new file mode 100644
index 00000000..9fa4cd33
--- /dev/null
+++ b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/BiquadFilter.cs
@@ -0,0 +1,16 @@
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.OsHle.Services.Aud.AudioRenderer
+{
+ [StructLayout(LayoutKind.Sequential, Size = 0xc, Pack = 1)]
+ struct BiquadFilter
+ {
+ public byte Enable;
+ public byte Padding;
+ public short B0;
+ public short B1;
+ public short B2;
+ public short A1;
+ public short A2;
+ }
+}
diff --git a/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/IAudioRenderer.cs b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/IAudioRenderer.cs
new file mode 100644
index 00000000..f91a8da3
--- /dev/null
+++ b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/IAudioRenderer.cs
@@ -0,0 +1,316 @@
+using ChocolArm64.Memory;
+using Ryujinx.Audio;
+using Ryujinx.Audio.Adpcm;
+using Ryujinx.HLE.Logging;
+using Ryujinx.HLE.OsHle.Handles;
+using Ryujinx.HLE.OsHle.Ipc;
+using Ryujinx.HLE.OsHle.Utilities;
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.OsHle.Services.Aud.AudioRenderer
+{
+ class IAudioRenderer : IpcService, IDisposable
+ {
+ //This is the amount of samples that are going to be appended
+ //each time that RequestUpdateAudioRenderer is called. Ideally,
+ //this value shouldn't be neither too small (to avoid the player
+ //starving due to running out of samples) or too large (to avoid
+ //high latency).
+ private const int MixBufferSamplesCount = 960;
+
+ private Dictionary<int, ServiceProcessRequest> m_Commands;
+
+ public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+
+ private KEvent UpdateEvent;
+
+ private AMemory Memory;
+
+ private IAalOutput AudioOut;
+
+ private AudioRendererParameter Params;
+
+ private MemoryPoolContext[] MemoryPools;
+
+ private VoiceContext[] Voices;
+
+ private int Track;
+
+ public IAudioRenderer(AMemory Memory, IAalOutput AudioOut, AudioRendererParameter Params)
+ {
+ m_Commands = new Dictionary<int, ServiceProcessRequest>()
+ {
+ { 4, RequestUpdateAudioRenderer },
+ { 5, StartAudioRenderer },
+ { 6, StopAudioRenderer },
+ { 7, QuerySystemEvent }
+ };
+
+ UpdateEvent = new KEvent();
+
+ this.Memory = Memory;
+ this.AudioOut = AudioOut;
+ this.Params = Params;
+
+ Track = AudioOut.OpenTrack(
+ AudioConsts.HostSampleRate,
+ AudioConsts.HostChannelsCount,
+ AudioCallback);
+
+ MemoryPools = CreateArray<MemoryPoolContext>(Params.EffectCount + Params.VoiceCount * 4);
+
+ Voices = CreateArray<VoiceContext>(Params.VoiceCount);
+
+ InitializeAudioOut();
+ }
+
+ private void AudioCallback()
+ {
+ UpdateEvent.WaitEvent.Set();
+ }
+
+ private static T[] CreateArray<T>(int Size) where T : new()
+ {
+ T[] Output = new T[Size];
+
+ for (int Index = 0; Index < Size; Index++)
+ {
+ Output[Index] = new T();
+ }
+
+ return Output;
+ }
+
+ private void InitializeAudioOut()
+ {
+ AppendMixedBuffer(0);
+ AppendMixedBuffer(1);
+ AppendMixedBuffer(2);
+
+ AudioOut.Start(Track);
+ }
+
+ public long RequestUpdateAudioRenderer(ServiceCtx Context)
+ {
+ long OutputPosition = Context.Request.ReceiveBuff[0].Position;
+ long OutputSize = Context.Request.ReceiveBuff[0].Size;
+
+ AMemoryHelper.FillWithZeros(Context.Memory, OutputPosition, (int)OutputSize);
+
+ long InputPosition = Context.Request.SendBuff[0].Position;
+
+ StructReader Reader = new StructReader(Context.Memory, InputPosition);
+ StructWriter Writer = new StructWriter(Context.Memory, OutputPosition);
+
+ UpdateDataHeader InputHeader = Reader.Read<UpdateDataHeader>();
+
+ Reader.Read<BehaviorIn>(InputHeader.BehaviorSize);
+
+ MemoryPoolIn[] MemoryPoolsIn = Reader.Read<MemoryPoolIn>(InputHeader.MemoryPoolSize);
+
+ for (int Index = 0; Index < MemoryPoolsIn.Length; Index++)
+ {
+ MemoryPoolIn MemoryPool = MemoryPoolsIn[Index];
+
+ if (MemoryPool.State == MemoryPoolState.RequestAttach)
+ {
+ MemoryPools[Index].OutStatus.State = MemoryPoolState.Attached;
+ }
+ else if (MemoryPool.State == MemoryPoolState.RequestDetach)
+ {
+ MemoryPools[Index].OutStatus.State = MemoryPoolState.Detached;
+ }
+ }
+
+ Reader.Read<VoiceChannelResourceIn>(InputHeader.VoiceResourceSize);
+
+ VoiceIn[] VoicesIn = Reader.Read<VoiceIn>(InputHeader.VoiceSize);
+
+ for (int Index = 0; Index < VoicesIn.Length; Index++)
+ {
+ VoiceIn Voice = VoicesIn[Index];
+
+ VoiceContext VoiceCtx = Voices[Index];
+
+ VoiceCtx.SetAcquireState(Voice.Acquired != 0);
+
+ if (Voice.Acquired == 0)
+ {
+ continue;
+ }
+
+ if (Voice.FirstUpdate != 0)
+ {
+ VoiceCtx.AdpcmCtx = GetAdpcmDecoderContext(
+ Voice.AdpcmCoeffsPosition,
+ Voice.AdpcmCoeffsSize);
+
+ VoiceCtx.SampleFormat = Voice.SampleFormat;
+ VoiceCtx.SampleRate = Voice.SampleRate;
+ VoiceCtx.ChannelsCount = Voice.ChannelsCount;
+
+ VoiceCtx.SetBufferIndex(Voice.BaseWaveBufferIndex);
+ }
+
+ VoiceCtx.WaveBuffers[0] = Voice.WaveBuffer0;
+ VoiceCtx.WaveBuffers[1] = Voice.WaveBuffer1;
+ VoiceCtx.WaveBuffers[2] = Voice.WaveBuffer2;
+ VoiceCtx.WaveBuffers[3] = Voice.WaveBuffer3;
+ VoiceCtx.Volume = Voice.Volume;
+ VoiceCtx.PlayState = Voice.PlayState;
+ }
+
+ UpdateAudio();
+
+ UpdateDataHeader OutputHeader = new UpdateDataHeader();
+
+ int UpdateHeaderSize = Marshal.SizeOf<UpdateDataHeader>();
+
+ OutputHeader.Revision = IAudioRendererManager.RevMagic;
+ OutputHeader.BehaviorSize = 0xb0;
+ OutputHeader.MemoryPoolSize = (Params.EffectCount + Params.VoiceCount * 4) * 0x10;
+ OutputHeader.VoiceSize = Params.VoiceCount * 0x10;
+ OutputHeader.EffectSize = Params.EffectCount * 0x10;
+ OutputHeader.SinkSize = Params.SinkCount * 0x20;
+ OutputHeader.PerformanceManagerSize = 0x10;
+ OutputHeader.TotalSize = UpdateHeaderSize +
+ OutputHeader.BehaviorSize +
+ OutputHeader.MemoryPoolSize +
+ OutputHeader.VoiceSize +
+ OutputHeader.EffectSize +
+ OutputHeader.SinkSize +
+ OutputHeader.PerformanceManagerSize;
+
+ Writer.Write(OutputHeader);
+
+ foreach (MemoryPoolContext MemoryPool in MemoryPools)
+ {
+ Writer.Write(MemoryPool.OutStatus);
+ }
+
+ foreach (VoiceContext Voice in Voices)
+ {
+ Writer.Write(Voice.OutStatus);
+ }
+
+ return 0;
+ }
+
+ public long StartAudioRenderer(ServiceCtx Context)
+ {
+ Context.Ns.Log.PrintStub(LogClass.ServiceAudio, "Stubbed.");
+
+ return 0;
+ }
+
+ public long StopAudioRenderer(ServiceCtx Context)
+ {
+ Context.Ns.Log.PrintStub(LogClass.ServiceAudio, "Stubbed.");
+
+ return 0;
+ }
+
+ public long QuerySystemEvent(ServiceCtx Context)
+ {
+ int Handle = Context.Process.HandleTable.OpenHandle(UpdateEvent);
+
+ Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle);
+
+ return 0;
+ }
+
+ private AdpcmDecoderContext GetAdpcmDecoderContext(long Position, long Size)
+ {
+ if (Size == 0)
+ {
+ return null;
+ }
+
+ AdpcmDecoderContext Context = new AdpcmDecoderContext();
+
+ Context.Coefficients = new short[Size >> 1];
+
+ for (int Offset = 0; Offset < Size; Offset += 2)
+ {
+ Context.Coefficients[Offset >> 1] = Memory.ReadInt16(Position + Offset);
+ }
+
+ return Context;
+ }
+
+ private void UpdateAudio()
+ {
+ long[] Released = AudioOut.GetReleasedBuffers(Track, 2);
+
+ for (int Index = 0; Index < Released.Length; Index++)
+ {
+ AppendMixedBuffer(Released[Index]);
+ }
+ }
+
+ private void AppendMixedBuffer(long Tag)
+ {
+ int[] MixBuffer = new int[MixBufferSamplesCount * AudioConsts.HostChannelsCount];
+
+ foreach (VoiceContext Voice in Voices)
+ {
+ if (!Voice.Playing)
+ {
+ continue;
+ }
+
+ int OutOffset = 0;
+
+ int PendingSamples = MixBufferSamplesCount;
+
+ while (PendingSamples > 0)
+ {
+ int[] Samples = Voice.GetBufferData(Memory, PendingSamples, out int ReturnedSamples);
+
+ if (ReturnedSamples == 0)
+ {
+ break;
+ }
+
+ PendingSamples -= ReturnedSamples;
+
+ for (int Offset = 0; Offset < Samples.Length; Offset++)
+ {
+ int Sample = (int)(Samples[Offset] * Voice.Volume);
+
+ MixBuffer[OutOffset++] += Sample;
+ }
+ }
+ }
+
+ AudioOut.AppendBuffer(Track, Tag, GetFinalBuffer(MixBuffer));
+ }
+
+ private static short[] GetFinalBuffer(int[] Buffer)
+ {
+ short[] Output = new short[Buffer.Length];
+
+ for (int Offset = 0; Offset < Buffer.Length; Offset++)
+ {
+ Output[Offset] = DspUtils.Saturate(Buffer[Offset]);
+ }
+
+ return Output;
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ }
+
+ protected virtual void Dispose(bool Disposing)
+ {
+ if (Disposing)
+ {
+ UpdateEvent.Dispose();
+ }
+ }
+ }
+}
diff --git a/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/MemoryPoolContext.cs b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/MemoryPoolContext.cs
new file mode 100644
index 00000000..b7af1d3f
--- /dev/null
+++ b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/MemoryPoolContext.cs
@@ -0,0 +1,12 @@
+namespace Ryujinx.HLE.OsHle.Services.Aud.AudioRenderer
+{
+ class MemoryPoolContext
+ {
+ public MemoryPoolOut OutStatus;
+
+ public MemoryPoolContext()
+ {
+ OutStatus.State = MemoryPoolState.Detached;
+ }
+ }
+}
diff --git a/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/MemoryPoolIn.cs b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/MemoryPoolIn.cs
new file mode 100644
index 00000000..c852b519
--- /dev/null
+++ b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/MemoryPoolIn.cs
@@ -0,0 +1,14 @@
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.OsHle.Services.Aud.AudioRenderer
+{
+ [StructLayout(LayoutKind.Sequential, Size = 0x20, Pack = 4)]
+ struct MemoryPoolIn
+ {
+ public long Address;
+ public long Size;
+ public MemoryPoolState State;
+ public int Unknown14;
+ public long Unknown18;
+ }
+}
diff --git a/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/MemoryPoolOut.cs b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/MemoryPoolOut.cs
new file mode 100644
index 00000000..dd65df86
--- /dev/null
+++ b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/MemoryPoolOut.cs
@@ -0,0 +1,12 @@
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.OsHle.Services.Aud.AudioRenderer
+{
+ [StructLayout(LayoutKind.Sequential, Size = 0x10, Pack = 4)]
+ struct MemoryPoolOut
+ {
+ public MemoryPoolState State;
+ public int Unknown14;
+ public long Unknown18;
+ }
+}
diff --git a/Ryujinx.HLE/OsHle/Services/Aud/MemoryPoolState.cs b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/MemoryPoolState.cs
index 892cde49..f96a0c09 100644
--- a/Ryujinx.HLE/OsHle/Services/Aud/MemoryPoolState.cs
+++ b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/MemoryPoolState.cs
@@ -1,4 +1,4 @@
-namespace Ryujinx.HLE.OsHle.Services.Aud
+namespace Ryujinx.HLE.OsHle.Services.Aud.AudioRenderer
{
enum MemoryPoolState : int
{
diff --git a/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/PlayState.cs b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/PlayState.cs
new file mode 100644
index 00000000..e8bcf64f
--- /dev/null
+++ b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/PlayState.cs
@@ -0,0 +1,9 @@
+namespace Ryujinx.HLE.OsHle.Services.Aud.AudioRenderer
+{
+ enum PlayState : byte
+ {
+ Playing = 0,
+ Stopped = 1,
+ Paused = 2
+ }
+}
diff --git a/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/Resampler.cs b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/Resampler.cs
new file mode 100644
index 00000000..31e0ebec
--- /dev/null
+++ b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/Resampler.cs
@@ -0,0 +1,191 @@
+using System;
+
+namespace Ryujinx.HLE.OsHle.Services.Aud.AudioRenderer
+{
+ static class Resampler
+ {
+#region "LookUp Tables"
+ private static short[] CurveLut0 = new short[]
+ {
+ 6600, 19426, 6722, 3, 6479, 19424, 6845, 9, 6359, 19419, 6968, 15, 6239, 19412, 7093, 22,
+ 6121, 19403, 7219, 28, 6004, 19391, 7345, 34, 5888, 19377, 7472, 41, 5773, 19361, 7600, 48,
+ 5659, 19342, 7728, 55, 5546, 19321, 7857, 62, 5434, 19298, 7987, 69, 5323, 19273, 8118, 77,
+ 5213, 19245, 8249, 84, 5104, 19215, 8381, 92, 4997, 19183, 8513, 101, 4890, 19148, 8646, 109,
+ 4785, 19112, 8780, 118, 4681, 19073, 8914, 127, 4579, 19031, 9048, 137, 4477, 18988, 9183, 147,
+ 4377, 18942, 9318, 157, 4277, 18895, 9454, 168, 4179, 18845, 9590, 179, 4083, 18793, 9726, 190,
+ 3987, 18738, 9863, 202, 3893, 18682, 10000, 215, 3800, 18624, 10137, 228, 3709, 18563, 10274, 241,
+ 3618, 18500, 10411, 255, 3529, 18436, 10549, 270, 3441, 18369, 10687, 285, 3355, 18300, 10824, 300,
+ 3269, 18230, 10962, 317, 3186, 18157, 11100, 334, 3103, 18082, 11238, 351, 3022, 18006, 11375, 369,
+ 2942, 17927, 11513, 388, 2863, 17847, 11650, 408, 2785, 17765, 11788, 428, 2709, 17681, 11925, 449,
+ 2635, 17595, 12062, 471, 2561, 17507, 12198, 494, 2489, 17418, 12334, 517, 2418, 17327, 12470, 541,
+ 2348, 17234, 12606, 566, 2280, 17140, 12741, 592, 2213, 17044, 12876, 619, 2147, 16946, 13010, 647,
+ 2083, 16846, 13144, 675, 2020, 16745, 13277, 704, 1958, 16643, 13409, 735, 1897, 16539, 13541, 766,
+ 1838, 16434, 13673, 798, 1780, 16327, 13803, 832, 1723, 16218, 13933, 866, 1667, 16109, 14062, 901,
+ 1613, 15998, 14191, 937, 1560, 15885, 14318, 975, 1508, 15772, 14445, 1013, 1457, 15657, 14571, 1052,
+ 1407, 15540, 14695, 1093, 1359, 15423, 14819, 1134, 1312, 15304, 14942, 1177, 1266, 15185, 15064, 1221,
+ 1221, 15064, 15185, 1266, 1177, 14942, 15304, 1312, 1134, 14819, 15423, 1359, 1093, 14695, 15540, 1407,
+ 1052, 14571, 15657, 1457, 1013, 14445, 15772, 1508, 975, 14318, 15885, 1560, 937, 14191, 15998, 1613,
+ 901, 14062, 16109, 1667, 866, 13933, 16218, 1723, 832, 13803, 16327, 1780, 798, 13673, 16434, 1838,
+ 766, 13541, 16539, 1897, 735, 13409, 16643, 1958, 704, 13277, 16745, 2020, 675, 13144, 16846, 2083,
+ 647, 13010, 16946, 2147, 619, 12876, 17044, 2213, 592, 12741, 17140, 2280, 566, 12606, 17234, 2348,
+ 541, 12470, 17327, 2418, 517, 12334, 17418, 2489, 494, 12198, 17507, 2561, 471, 12062, 17595, 2635,
+ 449, 11925, 17681, 2709, 428, 11788, 17765, 2785, 408, 11650, 17847, 2863, 388, 11513, 17927, 2942,
+ 369, 11375, 18006, 3022, 351, 11238, 18082, 3103, 334, 11100, 18157, 3186, 317, 10962, 18230, 3269,
+ 300, 10824, 18300, 3355, 285, 10687, 18369, 3441, 270, 10549, 18436, 3529, 255, 10411, 18500, 3618,
+ 241, 10274, 18563, 3709, 228, 10137, 18624, 3800, 215, 10000, 18682, 3893, 202, 9863, 18738, 3987,
+ 190, 9726, 18793, 4083, 179, 9590, 18845, 4179, 168, 9454, 18895, 4277, 157, 9318, 18942, 4377,
+ 147, 9183, 18988, 4477, 137, 9048, 19031, 4579, 127, 8914, 19073, 4681, 118, 8780, 19112, 4785,
+ 109, 8646, 19148, 4890, 101, 8513, 19183, 4997, 92, 8381, 19215, 5104, 84, 8249, 19245, 5213,
+ 77, 8118, 19273, 5323, 69, 7987, 19298, 5434, 62, 7857, 19321, 5546, 55, 7728, 19342, 5659,
+ 48, 7600, 19361, 5773, 41, 7472, 19377, 5888, 34, 7345, 19391, 6004, 28, 7219, 19403, 6121,
+ 22, 7093, 19412, 6239, 15, 6968, 19419, 6359, 9, 6845, 19424, 6479, 3, 6722, 19426, 6600
+ };
+
+ private static short[] CurveLut1 = new short[]
+ {
+ -68, 32639, 69, -5, -200, 32630, 212, -15, -328, 32613, 359, -26, -450, 32586, 512, -36,
+ -568, 32551, 669, -47, -680, 32507, 832, -58, -788, 32454, 1000, -69, -891, 32393, 1174, -80,
+ -990, 32323, 1352, -92, -1084, 32244, 1536, -103, -1173, 32157, 1724, -115, -1258, 32061, 1919, -128,
+ -1338, 31956, 2118, -140, -1414, 31844, 2322, -153, -1486, 31723, 2532, -167, -1554, 31593, 2747, -180,
+ -1617, 31456, 2967, -194, -1676, 31310, 3192, -209, -1732, 31157, 3422, -224, -1783, 30995, 3657, -240,
+ -1830, 30826, 3897, -256, -1874, 30649, 4143, -272, -1914, 30464, 4393, -289, -1951, 30272, 4648, -307,
+ -1984, 30072, 4908, -325, -2014, 29866, 5172, -343, -2040, 29652, 5442, -362, -2063, 29431, 5716, -382,
+ -2083, 29203, 5994, -403, -2100, 28968, 6277, -424, -2114, 28727, 6565, -445, -2125, 28480, 6857, -468,
+ -2133, 28226, 7153, -490, -2139, 27966, 7453, -514, -2142, 27700, 7758, -538, -2142, 27428, 8066, -563,
+ -2141, 27151, 8378, -588, -2136, 26867, 8694, -614, -2130, 26579, 9013, -641, -2121, 26285, 9336, -668,
+ -2111, 25987, 9663, -696, -2098, 25683, 9993, -724, -2084, 25375, 10326, -753, -2067, 25063, 10662, -783,
+ -2049, 24746, 11000, -813, -2030, 24425, 11342, -844, -2009, 24100, 11686, -875, -1986, 23771, 12033, -907,
+ -1962, 23438, 12382, -939, -1937, 23103, 12733, -972, -1911, 22764, 13086, -1005, -1883, 22422, 13441, -1039,
+ -1855, 22077, 13798, -1072, -1825, 21729, 14156, -1107, -1795, 21380, 14516, -1141, -1764, 21027, 14877, -1176,
+ -1732, 20673, 15239, -1211, -1700, 20317, 15602, -1246, -1667, 19959, 15965, -1282, -1633, 19600, 16329, -1317,
+ -1599, 19239, 16694, -1353, -1564, 18878, 17058, -1388, -1530, 18515, 17423, -1424, -1495, 18151, 17787, -1459,
+ -1459, 17787, 18151, -1495, -1424, 17423, 18515, -1530, -1388, 17058, 18878, -1564, -1353, 16694, 19239, -1599,
+ -1317, 16329, 19600, -1633, -1282, 15965, 19959, -1667, -1246, 15602, 20317, -1700, -1211, 15239, 20673, -1732,
+ -1176, 14877, 21027, -1764, -1141, 14516, 21380, -1795, -1107, 14156, 21729, -1825, -1072, 13798, 22077, -1855,
+ -1039, 13441, 22422, -1883, -1005, 13086, 22764, -1911, -972, 12733, 23103, -1937, -939, 12382, 23438, -1962,
+ -907, 12033, 23771, -1986, -875, 11686, 24100, -2009, -844, 11342, 24425, -2030, -813, 11000, 24746, -2049,
+ -783, 10662, 25063, -2067, -753, 10326, 25375, -2084, -724, 9993, 25683, -2098, -696, 9663, 25987, -2111,
+ -668, 9336, 26285, -2121, -641, 9013, 26579, -2130, -614, 8694, 26867, -2136, -588, 8378, 27151, -2141,
+ -563, 8066, 27428, -2142, -538, 7758, 27700, -2142, -514, 7453, 27966, -2139, -490, 7153, 28226, -2133,
+ -468, 6857, 28480, -2125, -445, 6565, 28727, -2114, -424, 6277, 28968, -2100, -403, 5994, 29203, -2083,
+ -382, 5716, 29431, -2063, -362, 5442, 29652, -2040, -343, 5172, 29866, -2014, -325, 4908, 30072, -1984,
+ -307, 4648, 30272, -1951, -289, 4393, 30464, -1914, -272, 4143, 30649, -1874, -256, 3897, 30826, -1830,
+ -240, 3657, 30995, -1783, -224, 3422, 31157, -1732, -209, 3192, 31310, -1676, -194, 2967, 31456, -1617,
+ -180, 2747, 31593, -1554, -167, 2532, 31723, -1486, -153, 2322, 31844, -1414, -140, 2118, 31956, -1338,
+ -128, 1919, 32061, -1258, -115, 1724, 32157, -1173, -103, 1536, 32244, -1084, -92, 1352, 32323, -990,
+ -80, 1174, 32393, -891, -69, 1000, 32454, -788, -58, 832, 32507, -680, -47, 669, 32551, -568,
+ -36, 512, 32586, -450, -26, 359, 32613, -328, -15, 212, 32630, -200, -5, 69, 32639, -68
+ };
+
+ private static short[] CurveLut2 = new short[]
+ {
+ 3195, 26287, 3329, -32, 3064, 26281, 3467, -34, 2936, 26270, 3608, -38, 2811, 26253, 3751, -42,
+ 2688, 26230, 3897, -46, 2568, 26202, 4046, -50, 2451, 26169, 4199, -54, 2338, 26130, 4354, -58,
+ 2227, 26085, 4512, -63, 2120, 26035, 4673, -67, 2015, 25980, 4837, -72, 1912, 25919, 5004, -76,
+ 1813, 25852, 5174, -81, 1716, 25780, 5347, -87, 1622, 25704, 5522, -92, 1531, 25621, 5701, -98,
+ 1442, 25533, 5882, -103, 1357, 25440, 6066, -109, 1274, 25342, 6253, -115, 1193, 25239, 6442, -121,
+ 1115, 25131, 6635, -127, 1040, 25018, 6830, -133, 967, 24899, 7027, -140, 897, 24776, 7227, -146,
+ 829, 24648, 7430, -153, 764, 24516, 7635, -159, 701, 24379, 7842, -166, 641, 24237, 8052, -174,
+ 583, 24091, 8264, -181, 526, 23940, 8478, -187, 472, 23785, 8695, -194, 420, 23626, 8914, -202,
+ 371, 23462, 9135, -209, 324, 23295, 9358, -215, 279, 23123, 9583, -222, 236, 22948, 9809, -230,
+ 194, 22769, 10038, -237, 154, 22586, 10269, -243, 117, 22399, 10501, -250, 81, 22208, 10735, -258,
+ 47, 22015, 10970, -265, 15, 21818, 11206, -271, -16, 21618, 11444, -277, -44, 21415, 11684, -283,
+ -71, 21208, 11924, -290, -97, 20999, 12166, -296, -121, 20786, 12409, -302, -143, 20571, 12653, -306,
+ -163, 20354, 12898, -311, -183, 20134, 13143, -316, -201, 19911, 13389, -321, -218, 19686, 13635, -325,
+ -234, 19459, 13882, -328, -248, 19230, 14130, -332, -261, 18998, 14377, -335, -273, 18765, 14625, -337,
+ -284, 18531, 14873, -339, -294, 18295, 15121, -341, -302, 18057, 15369, -341, -310, 17817, 15617, -341,
+ -317, 17577, 15864, -340, -323, 17335, 16111, -340, -328, 17092, 16357, -338, -332, 16848, 16603, -336,
+ -336, 16603, 16848, -332, -338, 16357, 17092, -328, -340, 16111, 17335, -323, -340, 15864, 17577, -317,
+ -341, 15617, 17817, -310, -341, 15369, 18057, -302, -341, 15121, 18295, -294, -339, 14873, 18531, -284,
+ -337, 14625, 18765, -273, -335, 14377, 18998, -261, -332, 14130, 19230, -248, -328, 13882, 19459, -234,
+ -325, 13635, 19686, -218, -321, 13389, 19911, -201, -316, 13143, 20134, -183, -311, 12898, 20354, -163,
+ -306, 12653, 20571, -143, -302, 12409, 20786, -121, -296, 12166, 20999, -97, -290, 11924, 21208, -71,
+ -283, 11684, 21415, -44, -277, 11444, 21618, -16, -271, 11206, 21818, 15, -265, 10970, 22015, 47,
+ -258, 10735, 22208, 81, -250, 10501, 22399, 117, -243, 10269, 22586, 154, -237, 10038, 22769, 194,
+ -230, 9809, 22948, 236, -222, 9583, 23123, 279, -215, 9358, 23295, 324, -209, 9135, 23462, 371,
+ -202, 8914, 23626, 420, -194, 8695, 23785, 472, -187, 8478, 23940, 526, -181, 8264, 24091, 583,
+ -174, 8052, 24237, 641, -166, 7842, 24379, 701, -159, 7635, 24516, 764, -153, 7430, 24648, 829,
+ -146, 7227, 24776, 897, -140, 7027, 24899, 967, -133, 6830, 25018, 1040, -127, 6635, 25131, 1115,
+ -121, 6442, 25239, 1193, -115, 6253, 25342, 1274, -109, 6066, 25440, 1357, -103, 5882, 25533, 1442,
+ -98, 5701, 25621, 1531, -92, 5522, 25704, 1622, -87, 5347, 25780, 1716, -81, 5174, 25852, 1813,
+ -76, 5004, 25919, 1912, -72, 4837, 25980, 2015, -67, 4673, 26035, 2120, -63, 4512, 26085, 2227,
+ -58, 4354, 26130, 2338, -54, 4199, 26169, 2451, -50, 4046, 26202, 2568, -46, 3897, 26230, 2688,
+ -42, 3751, 26253, 2811, -38, 3608, 26270, 2936, -34, 3467, 26281, 3064, -32, 3329, 26287, 3195
+ };
+#endregion
+
+ public static int[] Resample2Ch(
+ int[] Buffer,
+ int SrcSampleRate,
+ int DstSampleRate,
+ int SamplesCount,
+ ref int FracPart)
+ {
+ if (Buffer == null)
+ {
+ throw new ArgumentNullException(nameof(Buffer));
+ }
+
+ if (SrcSampleRate <= 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(SrcSampleRate));
+ }
+
+ if (DstSampleRate <= 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(DstSampleRate));
+ }
+
+ double Ratio = (double)SrcSampleRate / DstSampleRate;
+
+ int NewSamplesCount = (int)(SamplesCount / Ratio);
+
+ int Step = (int)(Ratio * 0x8000);
+
+ int[] Output = new int[NewSamplesCount * 2];
+
+ short[] Lut;
+
+ if (Step > 0xaaaa)
+ {
+ Lut = CurveLut0;
+ }
+ else if (Step <= 0x8000)
+ {
+ Lut = CurveLut1;
+ }
+ else
+ {
+ Lut = CurveLut2;
+ }
+
+ int InOffs = 0;
+
+ for (int OutOffs = 0; OutOffs < Output.Length; OutOffs += 2)
+ {
+ int LutIndex = (FracPart >> 8) * 4;
+
+ int Sample0 = Buffer[(InOffs + 0) * 2 + 0] * Lut[LutIndex + 0] +
+ Buffer[(InOffs + 1) * 2 + 0] * Lut[LutIndex + 1] +
+ Buffer[(InOffs + 2) * 2 + 0] * Lut[LutIndex + 2] +
+ Buffer[(InOffs + 3) * 2 + 0] * Lut[LutIndex + 3];
+
+ int Sample1 = Buffer[(InOffs + 0) * 2 + 1] * Lut[LutIndex + 0] +
+ Buffer[(InOffs + 1) * 2 + 1] * Lut[LutIndex + 1] +
+ Buffer[(InOffs + 2) * 2 + 1] * Lut[LutIndex + 2] +
+ Buffer[(InOffs + 3) * 2 + 1] * Lut[LutIndex + 3];
+
+ int NewOffset = FracPart + Step;
+
+ InOffs += NewOffset >> 15;
+
+ FracPart = NewOffset & 0x7fff;
+
+ Output[OutOffs + 0] = Sample0 >> 15;
+ Output[OutOffs + 1] = Sample1 >> 15;
+ }
+
+ return Output;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/OsHle/Services/Aud/UpdateDataHeader.cs b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/UpdateDataHeader.cs
index f944b302..a6dfbc0b 100644
--- a/Ryujinx.HLE/OsHle/Services/Aud/UpdateDataHeader.cs
+++ b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/UpdateDataHeader.cs
@@ -1,15 +1,15 @@
-namespace Ryujinx.HLE.OsHle.Services.Aud
+namespace Ryujinx.HLE.OsHle.Services.Aud.AudioRenderer
{
struct UpdateDataHeader
{
public int Revision;
public int BehaviorSize;
- public int MemoryPoolsSize;
- public int VoicesSize;
+ public int MemoryPoolSize;
+ public int VoiceSize;
public int VoiceResourceSize;
- public int EffectsSize;
- public int MixesSize;
- public int SinksSize;
+ public int EffectSize;
+ public int MixeSize;
+ public int SinkSize;
public int PerformanceManagerSize;
public int Unknown24;
public int Unknown28;
diff --git a/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/VoiceChannelResourceIn.cs b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/VoiceChannelResourceIn.cs
new file mode 100644
index 00000000..0916b03e
--- /dev/null
+++ b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/VoiceChannelResourceIn.cs
@@ -0,0 +1,10 @@
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.OsHle.Services.Aud.AudioRenderer
+{
+ [StructLayout(LayoutKind.Sequential, Size = 0x70, Pack = 1)]
+ struct VoiceChannelResourceIn
+ {
+ //???
+ }
+}
diff --git a/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/VoiceContext.cs b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/VoiceContext.cs
new file mode 100644
index 00000000..1bf9ed73
--- /dev/null
+++ b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/VoiceContext.cs
@@ -0,0 +1,188 @@
+using ChocolArm64.Memory;
+using Ryujinx.Audio.Adpcm;
+using System;
+
+namespace Ryujinx.HLE.OsHle.Services.Aud.AudioRenderer
+{
+ class VoiceContext
+ {
+ private bool Acquired;
+ private bool BufferReload;
+
+ private int ResamplerFracPart;
+
+ private int BufferIndex;
+ private int Offset;
+
+ public int SampleRate;
+ public int ChannelsCount;
+
+ public float Volume;
+
+ public PlayState PlayState;
+
+ public SampleFormat SampleFormat;
+
+ public AdpcmDecoderContext AdpcmCtx;
+
+ public WaveBuffer[] WaveBuffers;
+
+ public VoiceOut OutStatus;
+
+ private int[] Samples;
+
+ public bool Playing => Acquired && PlayState == PlayState.Playing;
+
+ public VoiceContext()
+ {
+ WaveBuffers = new WaveBuffer[4];
+ }
+
+ public void SetAcquireState(bool NewState)
+ {
+ if (Acquired && !NewState)
+ {
+ //Release.
+ Reset();
+ }
+
+ Acquired = NewState;
+ }
+
+ private void Reset()
+ {
+ BufferReload = true;
+
+ BufferIndex = 0;
+ Offset = 0;
+
+ OutStatus.PlayedSamplesCount = 0;
+ OutStatus.PlayedWaveBuffersCount = 0;
+ OutStatus.VoiceDropsCount = 0;
+ }
+
+ public int[] GetBufferData(AMemory Memory, int MaxSamples, out int SamplesCount)
+ {
+ if (!Playing)
+ {
+ SamplesCount = 0;
+
+ return null;
+ }
+
+ if (BufferReload)
+ {
+ BufferReload = false;
+
+ UpdateBuffer(Memory);
+ }
+
+ WaveBuffer Wb = WaveBuffers[BufferIndex];
+
+ int MaxSize = Samples.Length - Offset;
+
+ int Size = MaxSamples * AudioConsts.HostChannelsCount;
+
+ if (Size > MaxSize)
+ {
+ Size = MaxSize;
+ }
+
+ int[] Output = new int[Size];
+
+ Array.Copy(Samples, Offset, Output, 0, Size);
+
+ SamplesCount = Size / AudioConsts.HostChannelsCount;
+
+ OutStatus.PlayedSamplesCount += SamplesCount;
+
+ Offset += Size;
+
+ if (Offset == Samples.Length)
+ {
+ Offset = 0;
+
+ if (Wb.Looping == 0)
+ {
+ SetBufferIndex((BufferIndex + 1) & 3);
+ }
+
+ OutStatus.PlayedWaveBuffersCount++;
+
+ if (Wb.LastBuffer != 0)
+ {
+ PlayState = PlayState.Paused;
+ }
+ }
+
+ return Output;
+ }
+
+ private void UpdateBuffer(AMemory Memory)
+ {
+ //TODO: Implement conversion for formats other
+ //than interleaved stereo (2 channels).
+ //As of now, it assumes that HostChannelsCount == 2.
+ WaveBuffer Wb = WaveBuffers[BufferIndex];
+
+ if (SampleFormat == SampleFormat.PcmInt16)
+ {
+ int SamplesCount = (int)(Wb.Size / (sizeof(short) * ChannelsCount));
+
+ Samples = new int[SamplesCount * AudioConsts.HostChannelsCount];
+
+ if (ChannelsCount == 1)
+ {
+ for (int Index = 0; Index < SamplesCount; Index++)
+ {
+ short Sample = Memory.ReadInt16(Wb.Position + Index * 2);
+
+ Samples[Index * 2 + 0] = Sample;
+ Samples[Index * 2 + 1] = Sample;
+ }
+ }
+ else
+ {
+ for (int Index = 0; Index < SamplesCount * 2; Index++)
+ {
+ Samples[Index] = Memory.ReadInt16(Wb.Position + Index * 2);
+ }
+ }
+ }
+ else if (SampleFormat == SampleFormat.Adpcm)
+ {
+ byte[] Buffer = Memory.ReadBytes(Wb.Position, Wb.Size);
+
+ Samples = AdpcmDecoder.Decode(Buffer, AdpcmCtx);
+ }
+ else
+ {
+ throw new InvalidOperationException();
+ }
+
+ if (SampleRate != AudioConsts.HostSampleRate)
+ {
+ //TODO: We should keep the frames being discarded (see the 4 below)
+ //on a buffer and include it on the next samples buffer, to allow
+ //the resampler to do seamless interpolation between wave buffers.
+ int SamplesCount = Samples.Length / AudioConsts.HostChannelsCount;
+
+ SamplesCount = Math.Max(SamplesCount - 4, 0);
+
+ Samples = Resampler.Resample2Ch(
+ Samples,
+ SampleRate,
+ AudioConsts.HostSampleRate,
+ SamplesCount,
+ ref ResamplerFracPart);
+ }
+ }
+
+ public void SetBufferIndex(int Index)
+ {
+ BufferIndex = Index & 3;
+
+ BufferReload = true;
+ }
+ }
+}
diff --git a/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/VoiceIn.cs b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/VoiceIn.cs
new file mode 100644
index 00000000..790affb2
--- /dev/null
+++ b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/VoiceIn.cs
@@ -0,0 +1,49 @@
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.OsHle.Services.Aud.AudioRenderer
+{
+ [StructLayout(LayoutKind.Sequential, Size = 0x170, Pack = 1)]
+ struct VoiceIn
+ {
+ public int VoiceSlot;
+ public int NodeId;
+
+ public byte FirstUpdate;
+ public byte Acquired;
+
+ public PlayState PlayState;
+
+ public SampleFormat SampleFormat;
+
+ public int SampleRate;
+
+ public int Priority;
+
+ public int Unknown14;
+
+ public int ChannelsCount;
+
+ public float Pitch;
+ public float Volume;
+
+ public BiquadFilter BiquadFilter0;
+ public BiquadFilter BiquadFilter1;
+
+ public int AppendedWaveBuffersCount;
+
+ public int BaseWaveBufferIndex;
+
+ public int Unknown44;
+
+ public long AdpcmCoeffsPosition;
+ public long AdpcmCoeffsSize;
+
+ public int VoiceDestination;
+ public int Padding;
+
+ public WaveBuffer WaveBuffer0;
+ public WaveBuffer WaveBuffer1;
+ public WaveBuffer WaveBuffer2;
+ public WaveBuffer WaveBuffer3;
+ }
+}
diff --git a/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/VoiceOut.cs b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/VoiceOut.cs
new file mode 100644
index 00000000..1fcf929f
--- /dev/null
+++ b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/VoiceOut.cs
@@ -0,0 +1,12 @@
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.OsHle.Services.Aud.AudioRenderer
+{
+ [StructLayout(LayoutKind.Sequential, Size = 0x10, Pack = 4)]
+ struct VoiceOut
+ {
+ public long PlayedSamplesCount;
+ public int PlayedWaveBuffersCount;
+ public int VoiceDropsCount; //?
+ }
+}
diff --git a/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/WaveBuffer.cs b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/WaveBuffer.cs
new file mode 100644
index 00000000..6b56b908
--- /dev/null
+++ b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/WaveBuffer.cs
@@ -0,0 +1,20 @@
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.OsHle.Services.Aud.AudioRenderer
+{
+ [StructLayout(LayoutKind.Sequential, Size = 0x38, Pack = 1)]
+ struct WaveBuffer
+ {
+ public long Position;
+ public long Size;
+ public int FirstSampleOffset;
+ public int LastSampleOffset;
+ public byte Looping;
+ public byte LastBuffer;
+ public short Unknown1A;
+ public int Unknown1C;
+ public long AdpcmLoopContextPosition;
+ public long AdpcmLoopContextSize;
+ public long Unknown30;
+ }
+}
diff --git a/Ryujinx.HLE/OsHle/Services/Aud/AudioRendererParameter.cs b/Ryujinx.HLE/OsHle/Services/Aud/AudioRendererParameter.cs
index 0a0792ec..d7e1df01 100644
--- a/Ryujinx.HLE/OsHle/Services/Aud/AudioRendererParameter.cs
+++ b/Ryujinx.HLE/OsHle/Services/Aud/AudioRendererParameter.cs
@@ -8,14 +8,14 @@ namespace Ryujinx.HLE.OsHle.Services.Aud
public int SampleRate;
public int SampleCount;
public int Unknown8;
- public int UnknownC;
+ public int MixCount;
public int VoiceCount;
public int SinkCount;
public int EffectCount;
- public int Unknown1C;
- public int Unknown20;
+ public int PerformanceManagerCount;
+ public int VoiceDropEnable;
public int SplitterCount;
- public int Unknown28;
+ public int SplitterDestinationDataCount;
public int Unknown2C;
public int Revision;
}
diff --git a/Ryujinx.HLE/OsHle/Services/Aud/IAudioOutManager.cs b/Ryujinx.HLE/OsHle/Services/Aud/IAudioOutManager.cs
index 8c78d1d4..7a3bc4d4 100644
--- a/Ryujinx.HLE/OsHle/Services/Aud/IAudioOutManager.cs
+++ b/Ryujinx.HLE/OsHle/Services/Aud/IAudioOutManager.cs
@@ -3,6 +3,7 @@ using Ryujinx.Audio;
using Ryujinx.HLE.Logging;
using Ryujinx.HLE.OsHle.Handles;
using Ryujinx.HLE.OsHle.Ipc;
+using Ryujinx.HLE.OsHle.Services.Aud.AudioOut;
using System.Collections.Generic;
using System.Text;
@@ -154,13 +155,13 @@ namespace Ryujinx.HLE.OsHle.Services.Aud
IAalOutput AudioOut = Context.Ns.AudioOut;
- int Track = AudioOut.OpenTrack(SampleRate, Channels, Callback, out AudioFormat Format);
+ int Track = AudioOut.OpenTrack(SampleRate, Channels, Callback);
MakeObject(Context, new IAudioOut(AudioOut, ReleaseEvent, Track));
Context.ResponseData.Write(SampleRate);
Context.ResponseData.Write(Channels);
- Context.ResponseData.Write((int)Format);
+ Context.ResponseData.Write((int)SampleFormat.PcmInt16);
Context.ResponseData.Write((int)PlaybackState.Stopped);
return 0;
diff --git a/Ryujinx.HLE/OsHle/Services/Aud/IAudioRenderer.cs b/Ryujinx.HLE/OsHle/Services/Aud/IAudioRenderer.cs
deleted file mode 100644
index bd9188c3..00000000
--- a/Ryujinx.HLE/OsHle/Services/Aud/IAudioRenderer.cs
+++ /dev/null
@@ -1,136 +0,0 @@
-using ChocolArm64.Memory;
-using Ryujinx.HLE.Logging;
-using Ryujinx.HLE.OsHle.Handles;
-using Ryujinx.HLE.OsHle.Ipc;
-using System;
-using System.Collections.Generic;
-using System.Runtime.InteropServices;
-
-namespace Ryujinx.HLE.OsHle.Services.Aud
-{
- class IAudioRenderer : IpcService, IDisposable
- {
- private Dictionary<int, ServiceProcessRequest> m_Commands;
-
- public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
-
- private KEvent UpdateEvent;
-
- private AudioRendererParameter Params;
-
- public IAudioRenderer(AudioRendererParameter Params)
- {
- m_Commands = new Dictionary<int, ServiceProcessRequest>()
- {
- { 4, RequestUpdateAudioRenderer },
- { 5, StartAudioRenderer },
- { 6, StopAudioRenderer },
- { 7, QuerySystemEvent }
- };
-
- UpdateEvent = new KEvent();
-
- this.Params = Params;
- }
-
- public long RequestUpdateAudioRenderer(ServiceCtx Context)
- {
- long OutputPosition = Context.Request.ReceiveBuff[0].Position;
- long OutputSize = Context.Request.ReceiveBuff[0].Size;
-
- AMemoryHelper.FillWithZeros(Context.Memory, OutputPosition, (int)OutputSize);
-
- long InputPosition = Context.Request.SendBuff[0].Position;
-
- UpdateDataHeader InputDataHeader = AMemoryHelper.Read<UpdateDataHeader>(Context.Memory, InputPosition);
-
- UpdateDataHeader OutputDataHeader = new UpdateDataHeader();
-
- int UpdateHeaderSize = Marshal.SizeOf<UpdateDataHeader>();
-
- OutputDataHeader.Revision = Params.Revision;
- OutputDataHeader.BehaviorSize = 0xb0;
- OutputDataHeader.MemoryPoolsSize = (Params.EffectCount + Params.VoiceCount * 4) * 0x10;
- OutputDataHeader.VoicesSize = Params.VoiceCount * 0x10;
- OutputDataHeader.EffectsSize = Params.EffectCount * 0x10;
- OutputDataHeader.SinksSize = Params.SinkCount * 0x20;
- OutputDataHeader.PerformanceManagerSize = 0x10;
- OutputDataHeader.TotalSize = UpdateHeaderSize +
- OutputDataHeader.BehaviorSize +
- OutputDataHeader.MemoryPoolsSize +
- OutputDataHeader.VoicesSize +
- OutputDataHeader.EffectsSize +
- OutputDataHeader.SinksSize +
- OutputDataHeader.PerformanceManagerSize;
-
- AMemoryHelper.Write(Context.Memory, OutputPosition, OutputDataHeader);
-
- int InMemoryPoolOffset = UpdateHeaderSize + InputDataHeader.BehaviorSize;
-
- int OutMemoryPoolOffset = UpdateHeaderSize;
-
- for (int Offset = 0; Offset < OutputDataHeader.MemoryPoolsSize; Offset += 0x10, InMemoryPoolOffset += 0x20)
- {
- MemoryPoolState PoolState = (MemoryPoolState)Context.Memory.ReadInt32(InputPosition + InMemoryPoolOffset + 0x10);
-
- //TODO: Figure out what the other values does.
- if (PoolState == MemoryPoolState.RequestAttach)
- {
- Context.Memory.WriteInt32(OutputPosition + OutMemoryPoolOffset + Offset, (int)MemoryPoolState.Attached);
- }
- else if (PoolState == MemoryPoolState.RequestDetach)
- {
- Context.Memory.WriteInt32(OutputPosition + OutMemoryPoolOffset + Offset, (int)MemoryPoolState.Detached);
- }
- }
-
- int OutVoicesOffset = OutMemoryPoolOffset + OutputDataHeader.MemoryPoolsSize;
-
- for (int Offset = 0; Offset < OutputDataHeader.VoicesSize; Offset += 0x10)
- {
- Context.Memory.WriteInt32(OutputPosition + OutVoicesOffset + Offset + 8, (int)VoicePlaybackState.Finished);
- }
-
- //TODO: We shouldn't be signaling this here.
- UpdateEvent.WaitEvent.Set();
-
- return 0;
- }
-
- public long StartAudioRenderer(ServiceCtx Context)
- {
- Context.Ns.Log.PrintStub(LogClass.ServiceAudio, "Stubbed.");
-
- return 0;
- }
-
- public long StopAudioRenderer(ServiceCtx Context)
- {
- Context.Ns.Log.PrintStub(LogClass.ServiceAudio, "Stubbed.");
-
- return 0;
- }
-
- public long QuerySystemEvent(ServiceCtx Context)
- {
- int Handle = Context.Process.HandleTable.OpenHandle(UpdateEvent);
-
- Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle);
-
- return 0;
- }
-
- public void Dispose()
- {
- Dispose(true);
- }
-
- protected virtual void Dispose(bool Disposing)
- {
- if (Disposing)
- {
- UpdateEvent.Dispose();
- }
- }
- }
-}
diff --git a/Ryujinx.HLE/OsHle/Services/Aud/IAudioRendererManager.cs b/Ryujinx.HLE/OsHle/Services/Aud/IAudioRendererManager.cs
index a7daeedd..021c3635 100644
--- a/Ryujinx.HLE/OsHle/Services/Aud/IAudioRendererManager.cs
+++ b/Ryujinx.HLE/OsHle/Services/Aud/IAudioRendererManager.cs
@@ -1,7 +1,11 @@
+using Ryujinx.Audio;
using Ryujinx.HLE.Logging;
using Ryujinx.HLE.OsHle.Ipc;
+using Ryujinx.HLE.OsHle.Services.Aud.AudioRenderer;
+using Ryujinx.HLE.OsHle.Utilities;
using System.Collections.Generic;
-using System.Runtime.InteropServices;
+
+using static Ryujinx.HLE.OsHle.ErrorCode;
namespace Ryujinx.HLE.OsHle.Services.Aud
{
@@ -12,6 +16,10 @@ namespace Ryujinx.HLE.OsHle.Services.Aud
('V' << 16) |
('0' << 24);
+ private const int Rev = 4;
+
+ public const int RevMagic = Rev0Magic + Rev;
+
private Dictionary<int, ServiceProcessRequest> m_Commands;
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
@@ -28,76 +36,69 @@ namespace Ryujinx.HLE.OsHle.Services.Aud
public long OpenAudioRenderer(ServiceCtx Context)
{
- //Same buffer as GetAudioRendererWorkBufferSize is receive here.
+ IAalOutput AudioOut = Context.Ns.AudioOut;
- AudioRendererParameter Params = new AudioRendererParameter();
+ AudioRendererParameter Params = GetAudioRendererParameter(Context);
- Params.SampleRate = Context.RequestData.ReadInt32();
- Params.SampleCount = Context.RequestData.ReadInt32();
- Params.Unknown8 = Context.RequestData.ReadInt32();
- Params.UnknownC = Context.RequestData.ReadInt32();
- Params.VoiceCount = Context.RequestData.ReadInt32();
- Params.SinkCount = Context.RequestData.ReadInt32();
- Params.EffectCount = Context.RequestData.ReadInt32();
- Params.Unknown1C = Context.RequestData.ReadInt32();
- Params.Unknown20 = Context.RequestData.ReadInt32();
- Params.SplitterCount = Context.RequestData.ReadInt32();
- Params.Unknown28 = Context.RequestData.ReadInt32();
- Params.Unknown2C = Context.RequestData.ReadInt32();
- Params.Revision = Context.RequestData.ReadInt32();
-
- MakeObject(Context, new IAudioRenderer(Params));
+ MakeObject(Context, new IAudioRenderer(Context.Memory, AudioOut, Params));
return 0;
}
public long GetAudioRendererWorkBufferSize(ServiceCtx Context)
{
- long SampleRate = Context.RequestData.ReadUInt32();
- long Unknown4 = Context.RequestData.ReadUInt32();
- long Unknown8 = Context.RequestData.ReadUInt32();
- long UnknownC = Context.RequestData.ReadUInt32();
- long Unknown10 = Context.RequestData.ReadUInt32(); //VoiceCount
- long Unknown14 = Context.RequestData.ReadUInt32(); //SinkCount
- long Unknown18 = Context.RequestData.ReadUInt32(); //EffectCount
- long Unknown1c = Context.RequestData.ReadUInt32(); //Boolean
- long Unknown20 = Context.RequestData.ReadUInt32(); //Not used here in FW3.0.1 - Boolean
- long Unknown24 = Context.RequestData.ReadUInt32();
- long Unknown28 = Context.RequestData.ReadUInt32(); //SplitterCount
- long Unknown2c = Context.RequestData.ReadUInt32(); //Not used here in FW3.0.1
- int RevMagic = Context.RequestData.ReadInt32();
-
- int Version = (RevMagic - Rev0Magic) >> 24;
-
- if (Version <= 3) //REV3 Max is supported
+ AudioRendererParameter Params = GetAudioRendererParameter(Context);
+
+ int Revision = (Params.Revision - Rev0Magic) >> 24;
+
+ if (Revision <= Rev) //REV3 Max is supported
{
- long Size = RoundUp(Unknown8 * 4, 64);
- Size += (UnknownC << 10);
- Size += (UnknownC + 1) * 0x940;
- Size += Unknown10 * 0x3F0;
- Size += RoundUp((UnknownC + 1) * 8, 16);
- Size += RoundUp(Unknown10 * 8, 16);
- Size += RoundUp((0x3C0 * (Unknown14 + UnknownC) + 4 * Unknown4) * (Unknown8 + 6), 64);
- Size += 0x2C0 * (Unknown14 + UnknownC) + 0x30 * (Unknown18 + (4 * Unknown10)) + 0x50;
-
- if (Version >= 3) //IsSplitterSupported
+ bool IsSplitterSupported = Revision >= 3;
+
+ long Size;
+
+ Size = IntUtils.AlignUp(Params.Unknown8 * 4, 64);
+ Size += Params.MixCount * 0x400;
+ Size += (Params.MixCount + 1) * 0x940;
+ Size += Params.VoiceCount * 0x3F0;
+ Size += IntUtils.AlignUp((Params.MixCount + 1) * 8, 16);
+ Size += IntUtils.AlignUp(Params.VoiceCount * 8, 16);
+ Size += IntUtils.AlignUp(
+ ((Params.SinkCount + Params.MixCount) * 0x3C0 + Params.SampleCount * 4) *
+ (Params.Unknown8 + 6), 64);
+ Size += (Params.SinkCount + Params.MixCount) * 0x2C0;
+ Size += (Params.EffectCount + 4 * Params.VoiceCount) * 0x30 + 0x50;
+
+ if (IsSplitterSupported)
{
- Size += RoundUp((NodeStatesGetWorkBufferSize((int)UnknownC + 1) + EdgeMatrixGetWorkBufferSize((int)UnknownC + 1)), 16);
- Size += 0xE0 * Unknown28 + 0x20 * Unknown24 + RoundUp(Unknown28 * 4, 16);
+ Size += IntUtils.AlignUp((
+ NodeStatesGetWorkBufferSize(Params.MixCount + 1) +
+ EdgeMatrixGetWorkBufferSize(Params.MixCount + 1)), 16);
+
+ Size += Params.SplitterDestinationDataCount * 0xE0;
+ Size += Params.SplitterCount * 0x20;
+ Size += IntUtils.AlignUp(Params.SplitterDestinationDataCount * 4, 16);
}
- Size = 0x4C0 * Unknown18 + RoundUp(Size, 64) + 0x170 * Unknown14 + ((Unknown10 << 8) | 0x40);
+ Size = Params.EffectCount * 0x4C0 +
+ Params.SinkCount * 0x170 +
+ Params.VoiceCount * 0x100 +
+ IntUtils.AlignUp(Size, 64) + 0x40;
- if (Unknown1c >= 1)
+ if (Params.PerformanceManagerCount >= 1)
{
- Size += ((((Unknown18 + Unknown14 + Unknown10 + UnknownC + 1) * 16) + 0x658) * (Unknown1c + 1) + 0x13F) & ~0x3FL;
+ Size += (((Params.EffectCount +
+ Params.SinkCount +
+ Params.VoiceCount +
+ Params.MixCount + 1) * 16 + 0x658) *
+ (Params.PerformanceManagerCount + 1) + 0x13F) & ~0x3FL;
}
- long WorkBufferSize = (Size + 0x1907D) & ~0xFFFL;
+ Size = (Size + 0x1907D) & ~0xFFFL;
- Context.ResponseData.Write(WorkBufferSize);
+ Context.ResponseData.Write(Size);
- Context.Ns.Log.PrintDebug(LogClass.ServiceAudio, $"WorkBufferSize is 0x{WorkBufferSize:x16}.");
+ Context.Ns.Log.PrintDebug(LogClass.ServiceAudio, $"WorkBufferSize is 0x{Size:x16}.");
return 0;
}
@@ -105,20 +106,36 @@ namespace Ryujinx.HLE.OsHle.Services.Aud
{
Context.ResponseData.Write(0L);
- Context.Ns.Log.PrintError(LogClass.ServiceAudio, $"Library Revision 0x{RevMagic:x8} is not supported!");
+ Context.Ns.Log.PrintWarning(LogClass.ServiceAudio, $"Library Revision 0x{Params.Revision:x8} is not supported!");
- return 0x499;
+ return MakeError(ErrorModule.Audio, AudErr.UnsupportedRevision);
}
}
- private static long RoundUp(long Value, int Size)
+ private AudioRendererParameter GetAudioRendererParameter(ServiceCtx Context)
{
- return (Value + (Size - 1)) & ~((long)Size - 1);
+ AudioRendererParameter Params = new AudioRendererParameter();
+
+ Params.SampleRate = Context.RequestData.ReadInt32();
+ Params.SampleCount = Context.RequestData.ReadInt32();
+ Params.Unknown8 = Context.RequestData.ReadInt32();
+ Params.MixCount = Context.RequestData.ReadInt32();
+ Params.VoiceCount = Context.RequestData.ReadInt32();
+ Params.SinkCount = Context.RequestData.ReadInt32();
+ Params.EffectCount = Context.RequestData.ReadInt32();
+ Params.PerformanceManagerCount = Context.RequestData.ReadInt32();
+ Params.VoiceDropEnable = Context.RequestData.ReadInt32();
+ Params.SplitterCount = Context.RequestData.ReadInt32();
+ Params.SplitterDestinationDataCount = Context.RequestData.ReadInt32();
+ Params.Unknown2C = Context.RequestData.ReadInt32();
+ Params.Revision = Context.RequestData.ReadInt32();
+
+ return Params;
}
private static int NodeStatesGetWorkBufferSize(int Value)
{
- int Result = (int)RoundUp(Value, 64);
+ int Result = IntUtils.AlignUp(Value, 64);
if (Result < 0)
{
@@ -130,7 +147,7 @@ namespace Ryujinx.HLE.OsHle.Services.Aud
private static int EdgeMatrixGetWorkBufferSize(int Value)
{
- int Result = (int)RoundUp(Value * Value, 64);
+ int Result = IntUtils.AlignUp(Value * Value, 64);
if (Result < 0)
{
diff --git a/Ryujinx.Audio/AudioFormat.cs b/Ryujinx.HLE/OsHle/Services/Aud/SampleFormat.cs
index 8250d136..06ab4929 100644
--- a/Ryujinx.Audio/AudioFormat.cs
+++ b/Ryujinx.HLE/OsHle/Services/Aud/SampleFormat.cs
@@ -1,12 +1,12 @@
-namespace Ryujinx.Audio
+namespace Ryujinx.HLE.OsHle.Services.Aud
{
- public enum AudioFormat
+ enum SampleFormat : byte
{
Invalid = 0,
PcmInt8 = 1,
PcmInt16 = 2,
- PcmImt24 = 3,
- PcmImt32 = 4,
+ PcmInt24 = 3,
+ PcmInt32 = 4,
PcmFloat = 5,
Adpcm = 6
}
diff --git a/Ryujinx.HLE/OsHle/Services/Aud/VoiceState.cs b/Ryujinx.HLE/OsHle/Services/Aud/VoiceState.cs
deleted file mode 100644
index 8b343323..00000000
--- a/Ryujinx.HLE/OsHle/Services/Aud/VoiceState.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace Ryujinx.HLE.OsHle.Services.Aud
-{
- enum VoicePlaybackState : int
- {
- Playing = 0,
- Finished = 1,
- Paused = 2
- }
-}
diff --git a/Ryujinx.HLE/OsHle/Services/Nv/NvMap/NvMapIoctl.cs b/Ryujinx.HLE/OsHle/Services/Nv/NvMap/NvMapIoctl.cs
index ec10a375..4c6107f8 100644
--- a/Ryujinx.HLE/OsHle/Services/Nv/NvMap/NvMapIoctl.cs
+++ b/Ryujinx.HLE/OsHle/Services/Nv/NvMap/NvMapIoctl.cs
@@ -48,7 +48,7 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvMap
return NvResult.InvalidInput;
}
- int Size = IntUtils.RoundUp(Args.Size, NvGpuVmm.PageSize);
+ int Size = IntUtils.AlignUp(Args.Size, NvGpuVmm.PageSize);
Args.Handle = AddNvMap(Context, new NvMapHandle(Size));
@@ -121,7 +121,7 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvMap
Map.Align = Args.Align;
Map.Kind = (byte)Args.Kind;
- int Size = IntUtils.RoundUp(Map.Size, NvGpuVmm.PageSize);
+ int Size = IntUtils.AlignUp(Map.Size, NvGpuVmm.PageSize);
long Address = Args.Address;
diff --git a/Ryujinx.HLE/OsHle/Utilities/IntUtils.cs b/Ryujinx.HLE/OsHle/Utilities/IntUtils.cs
index ba0726c3..39cced28 100644
--- a/Ryujinx.HLE/OsHle/Utilities/IntUtils.cs
+++ b/Ryujinx.HLE/OsHle/Utilities/IntUtils.cs
@@ -2,12 +2,12 @@ namespace Ryujinx.HLE.OsHle.Utilities
{
static class IntUtils
{
- public static int RoundUp(int Value, int Size)
+ public static int AlignUp(int Value, int Size)
{
return (Value + (Size - 1)) & ~(Size - 1);
}
- public static long RoundUp(long Value, int Size)
+ public static long AlignUp(long Value, int Size)
{
return (Value + (Size - 1)) & ~((long)Size - 1);
}
diff --git a/Ryujinx.HLE/OsHle/Utilities/StructReader.cs b/Ryujinx.HLE/OsHle/Utilities/StructReader.cs
new file mode 100644
index 00000000..e218288b
--- /dev/null
+++ b/Ryujinx.HLE/OsHle/Utilities/StructReader.cs
@@ -0,0 +1,45 @@
+using ChocolArm64.Memory;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.OsHle.Utilities
+{
+ class StructReader
+ {
+ private AMemory Memory;
+
+ public long Position { get; private set; }
+
+ public StructReader(AMemory Memory, long Position)
+ {
+ this.Memory = Memory;
+ this.Position = Position;
+ }
+
+ public T Read<T>() where T : struct
+ {
+ T Value = AMemoryHelper.Read<T>(Memory, Position);
+
+ Position += Marshal.SizeOf<T>();
+
+ return Value;
+ }
+
+ public T[] Read<T>(int Size) where T : struct
+ {
+ int StructSize = Marshal.SizeOf<T>();
+
+ int Count = Size / StructSize;
+
+ T[] Output = new T[Count];
+
+ for (int Index = 0; Index < Count; Index++)
+ {
+ Output[Index] = AMemoryHelper.Read<T>(Memory, Position);
+
+ Position += StructSize;
+ }
+
+ return Output;
+ }
+ }
+}
diff --git a/Ryujinx.HLE/OsHle/Utilities/StructWriter.cs b/Ryujinx.HLE/OsHle/Utilities/StructWriter.cs
new file mode 100644
index 00000000..7daa95fb
--- /dev/null
+++ b/Ryujinx.HLE/OsHle/Utilities/StructWriter.cs
@@ -0,0 +1,25 @@
+using ChocolArm64.Memory;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.OsHle.Utilities
+{
+ class StructWriter
+ {
+ private AMemory Memory;
+
+ public long Position { get; private set; }
+
+ public StructWriter(AMemory Memory, long Position)
+ {
+ this.Memory = Memory;
+ this.Position = Position;
+ }
+
+ public void Write<T>(T Value) where T : struct
+ {
+ AMemoryHelper.Write(Memory, Position, Value);
+
+ Position += Marshal.SizeOf<T>();
+ }
+ }
+}