From 4cf6524a87b0bed81ef91b2e18b823bba204861a Mon Sep 17 00:00:00 2001 From: Starlet Date: Wed, 13 Jun 2018 17:02:14 -0400 Subject: [PATCH] [WIP] ADPCM decoder --- Ryujinx.Audio/ADPCM/Decoder.cs | 62 ++++++++++++++++++++++++++++++++++ Ryujinx.Audio/ADPCM/Info.cs | 9 +++++ Ryujinx.Audio/AudioHelper.cs | 31 +++++++++++++++++ 3 files changed, 102 insertions(+) create mode 100644 Ryujinx.Audio/ADPCM/Decoder.cs create mode 100644 Ryujinx.Audio/ADPCM/Info.cs create mode 100644 Ryujinx.Audio/AudioHelper.cs diff --git a/Ryujinx.Audio/ADPCM/Decoder.cs b/Ryujinx.Audio/ADPCM/Decoder.cs new file mode 100644 index 000000000..f2553397f --- /dev/null +++ b/Ryujinx.Audio/ADPCM/Decoder.cs @@ -0,0 +1,62 @@ +using System; + +namespace Ryujinx.Audio.ADPCM +{ + class Decoder + { + private const int SamplesPerFrame = 14; + + private AudioHelper Helper; + + public Decoder() + { + Helper = new AudioHelper(); + } + + public short[] Decode(byte[] ADPCM, Info ADPCMInfo, int Samples) + { + short[] PCM = new short[Samples]; + + short Hist1 = ADPCMInfo.History1; + short Hist2 = ADPCMInfo.History2; + short[] Coefficients = ADPCMInfo.Coefficients; + + int FrameCount = Helper.DivideByRoundUp(Samples, SamplesPerFrame); + int SamplesRemaining = Samples; + + int OutIndex = 0; + int InIndex = 0; + + for (int Index = 0; Index < FrameCount; Index++) + { + byte PredictorScale = ADPCM[InIndex++]; + int Scale = (1 << Helper.GetLowNibble(PredictorScale)) * 2048; + int Predictor = Helper.GetHighNibble(PredictorScale); + + short Coef1 = ADPCMInfo.Coefficients[Predictor * 2]; + short Coef2 = ADPCMInfo.Coefficients[Predictor * 2 + 1]; + + int SamplesToRead = Math.Min(SamplesPerFrame, SamplesRemaining); + + for (int SampleIndex = 0; SampleIndex < SamplesToRead; SampleIndex++) + { + int ADPCMSample = SampleIndex % 2 == 0 ? Helper.GetHighNibble(ADPCM[InIndex]) : Helper.GetLowNibble(ADPCM[InIndex++]); + int Distance = Scale * ADPCMSample; + int PredictedSample = Coef1 * Hist1 + Coef2 * Hist2; + int CorrectedSample = PredictedSample + Distance; + int ScaledSample = (CorrectedSample + 1024) >> 11; + short ClampedSample = Helper.Clamp16(ScaledSample); + + Hist2 = Hist1; + Hist1 = ClampedSample; + + PCM[OutIndex++] = ClampedSample; + } + + SamplesRemaining -= SamplesToRead; + } + + return PCM; + } + } +} diff --git a/Ryujinx.Audio/ADPCM/Info.cs b/Ryujinx.Audio/ADPCM/Info.cs new file mode 100644 index 000000000..bfd19d21b --- /dev/null +++ b/Ryujinx.Audio/ADPCM/Info.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.Audio.ADPCM +{ + struct Info + { + public short History1; + public short History2; + public short[] Coefficients; + } +} diff --git a/Ryujinx.Audio/AudioHelper.cs b/Ryujinx.Audio/AudioHelper.cs new file mode 100644 index 000000000..c995b66b1 --- /dev/null +++ b/Ryujinx.Audio/AudioHelper.cs @@ -0,0 +1,31 @@ +using System; + +namespace Ryujinx.Audio +{ + class AudioHelper + { + public byte GetHighNibble(byte Value) + { + return (byte)((Value >> 4) & 0xF); + } + + public byte GetLowNibble(byte Value) + { + return (byte)(Value & 0xF); + } + + public short Clamp16(int Value) + { + if (Value > short.MaxValue) + return short.MaxValue; + if (Value < short.MinValue) + return short.MinValue; + return (short)Value; + } + + public int DivideByRoundUp(int Value, int Divisor) + { + return (int)Math.Ceiling((double)Value / Divisor); + } + } +}