From 4cf6524a87b0bed81ef91b2e18b823bba204861a Mon Sep 17 00:00:00 2001 From: Starlet Date: Wed, 13 Jun 2018 17:02:14 -0400 Subject: [PATCH 1/3] [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 0000000000..f2553397f6 --- /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 0000000000..bfd19d21b4 --- /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 0000000000..c995b66b13 --- /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); + } + } +} From 199ceeb01a33be8fea93d80a8c38bce653c68fcb Mon Sep 17 00:00:00 2001 From: Starlet Date: Fri, 15 Jun 2018 08:43:23 -0400 Subject: [PATCH 2/3] Compliant with review. --- Ryujinx.Audio/ADPCM/Decoder.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Ryujinx.Audio/ADPCM/Decoder.cs b/Ryujinx.Audio/ADPCM/Decoder.cs index f2553397f6..d8a7a38b63 100644 --- a/Ryujinx.Audio/ADPCM/Decoder.cs +++ b/Ryujinx.Audio/ADPCM/Decoder.cs @@ -40,7 +40,10 @@ namespace Ryujinx.Audio.ADPCM for (int SampleIndex = 0; SampleIndex < SamplesToRead; SampleIndex++) { - int ADPCMSample = SampleIndex % 2 == 0 ? Helper.GetHighNibble(ADPCM[InIndex]) : Helper.GetLowNibble(ADPCM[InIndex++]); + int ADPCMSample = SampleIndex % 2 == 0 ? Helper.GetHighNibble(ADPCM[InIndex]) : Helper.GetLowNibble(ADPCM[InIndex++]); + ADPCMSample <<= 28; + ADPCMSample >>= 28; + int Distance = Scale * ADPCMSample; int PredictedSample = Coef1 * Hist1 + Coef2 * Hist2; int CorrectedSample = PredictedSample + Distance; From 4cc4e4cce6e91699f3977d87fa7e240e8289d0d5 Mon Sep 17 00:00:00 2001 From: Starlet Date: Fri, 15 Jun 2018 09:19:29 -0400 Subject: [PATCH 3/3] Compliant with review. (2) --- Ryujinx.Audio/ADPCM/{Info.cs => ADPCMInfo.cs} | 2 +- Ryujinx.Audio/ADPCM/Decoder.cs | 12 ++++++------ Ryujinx.Audio/AudioHelper.cs | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) rename Ryujinx.Audio/ADPCM/{Info.cs => ADPCMInfo.cs} (87%) diff --git a/Ryujinx.Audio/ADPCM/Info.cs b/Ryujinx.Audio/ADPCM/ADPCMInfo.cs similarity index 87% rename from Ryujinx.Audio/ADPCM/Info.cs rename to Ryujinx.Audio/ADPCM/ADPCMInfo.cs index bfd19d21b4..63ef3cfa1c 100644 --- a/Ryujinx.Audio/ADPCM/Info.cs +++ b/Ryujinx.Audio/ADPCM/ADPCMInfo.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Audio.ADPCM { - struct Info + struct ADPCMInfo { public short History1; public short History2; diff --git a/Ryujinx.Audio/ADPCM/Decoder.cs b/Ryujinx.Audio/ADPCM/Decoder.cs index d8a7a38b63..cd6f4b5def 100644 --- a/Ryujinx.Audio/ADPCM/Decoder.cs +++ b/Ryujinx.Audio/ADPCM/Decoder.cs @@ -13,13 +13,13 @@ namespace Ryujinx.Audio.ADPCM Helper = new AudioHelper(); } - public short[] Decode(byte[] ADPCM, Info ADPCMInfo, int Samples) + public short[] Decode(byte[] ADPCM, ADPCMInfo Info, int Samples) { short[] PCM = new short[Samples]; - short Hist1 = ADPCMInfo.History1; - short Hist2 = ADPCMInfo.History2; - short[] Coefficients = ADPCMInfo.Coefficients; + short Hist1 = Info.History1; + short Hist2 = Info.History2; + short[] Coefficients = Info.Coefficients; int FrameCount = Helper.DivideByRoundUp(Samples, SamplesPerFrame); int SamplesRemaining = Samples; @@ -33,8 +33,8 @@ namespace Ryujinx.Audio.ADPCM 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]; + short Coef1 = Info.Coefficients[Predictor * 2]; + short Coef2 = Info.Coefficients[Predictor * 2 + 1]; int SamplesToRead = Math.Min(SamplesPerFrame, SamplesRemaining); diff --git a/Ryujinx.Audio/AudioHelper.cs b/Ryujinx.Audio/AudioHelper.cs index c995b66b13..ffca0838c1 100644 --- a/Ryujinx.Audio/AudioHelper.cs +++ b/Ryujinx.Audio/AudioHelper.cs @@ -25,7 +25,7 @@ namespace Ryujinx.Audio public int DivideByRoundUp(int Value, int Divisor) { - return (int)Math.Ceiling((double)Value / Divisor); + return (Value + (Divisor - 1)) / Divisor; } } }