mirror of
https://git.naxdy.org/Mirror/Ryujinx.git
synced 2024-12-30 12:33:04 +00:00
[WIP] ADPCM decoder
This commit is contained in:
parent
e581abb2f5
commit
4cf6524a87
3 changed files with 102 additions and 0 deletions
62
Ryujinx.Audio/ADPCM/Decoder.cs
Normal file
62
Ryujinx.Audio/ADPCM/Decoder.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
9
Ryujinx.Audio/ADPCM/Info.cs
Normal file
9
Ryujinx.Audio/ADPCM/Info.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
namespace Ryujinx.Audio.ADPCM
|
||||
{
|
||||
struct Info
|
||||
{
|
||||
public short History1;
|
||||
public short History2;
|
||||
public short[] Coefficients;
|
||||
}
|
||||
}
|
31
Ryujinx.Audio/AudioHelper.cs
Normal file
31
Ryujinx.Audio/AudioHelper.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue