aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Audio/Decoders/Adpcm/AdpcmDecoder.cs
blob: 24455b4187f84eba766c6a31c3e1aef8ce1aca3b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
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;
        }
    }
}