[WIP] ADPCM decoder

This commit is contained in:
Starlet 2018-06-13 17:02:14 -04:00
parent e581abb2f5
commit 4cf6524a87
3 changed files with 102 additions and 0 deletions

View file

@ -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;
}
}
}

View file

@ -0,0 +1,9 @@
namespace Ryujinx.Audio.ADPCM
{
struct Info
{
public short History1;
public short History2;
public short[] Coefficients;
}
}

View file

@ -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);
}
}
}