| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216 |
- using Ryujinx.Audio.Renderer.Dsp.State;
- using Ryujinx.Common.Logging;
- using System;
- using System.Diagnostics;
- using System.Runtime.CompilerServices;
- namespace Ryujinx.Audio.Renderer.Dsp
- {
- public static class AdpcmHelper
- {
- private const int FixedPointPrecision = 11;
- private const int SamplesPerFrame = 14;
- private const int NibblesPerFrame = SamplesPerFrame + 2;
- private const int BytesPerFrame = 8;
- private const int BitsPerFrame = BytesPerFrame * 8;
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static uint GetAdpcmDataSize(int sampleCount)
- {
- Debug.Assert(sampleCount >= 0);
- int frames = sampleCount / SamplesPerFrame;
- int extraSize = 0;
- if ((sampleCount % SamplesPerFrame) != 0)
- {
- extraSize = (sampleCount % SamplesPerFrame) / 2 + 1 + (sampleCount % 2);
- }
- return (uint)(BytesPerFrame * frames + extraSize);
- }
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static int GetAdpcmOffsetFromSampleOffset(int sampleOffset)
- {
- Debug.Assert(sampleOffset >= 0);
- return GetNibblesFromSampleCount(sampleOffset) / 2;
- }
- public static int NibbleToSample(int nibble)
- {
- int frames = nibble / NibblesPerFrame;
- int extraNibbles = nibble % NibblesPerFrame;
- int samples = SamplesPerFrame * frames;
- return samples + extraNibbles - 2;
- }
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static int GetNibblesFromSampleCount(int sampleCount)
- {
- byte headerSize = 0;
- if ((sampleCount % SamplesPerFrame) != 0)
- {
- headerSize = 2;
- }
- return sampleCount % SamplesPerFrame + NibblesPerFrame * (sampleCount / SamplesPerFrame) + headerSize;
- }
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static short Saturate(int value)
- {
- if (value > short.MaxValue)
- value = short.MaxValue;
- if (value < short.MinValue)
- value = short.MinValue;
- return (short)value;
- }
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static short GetCoefficientAtIndex(ReadOnlySpan<short> coefficients, int index)
- {
- if ((uint)index > (uint)coefficients.Length)
- {
- Logger.Error?.Print(LogClass.AudioRenderer, $"Out of bound read for coefficient at index {index}");
- return 0;
- }
- return coefficients[index];
- }
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static int Decode(Span<short> output, ReadOnlySpan<byte> input, int startSampleOffset, int endSampleOffset, int offset, int count, ReadOnlySpan<short> coefficients, ref AdpcmLoopContext loopContext)
- {
- if (input.IsEmpty || endSampleOffset < startSampleOffset)
- {
- return 0;
- }
- byte predScale = (byte)loopContext.PredScale;
- byte scale = (byte)(predScale & 0xF);
- byte coefficientIndex = (byte)((predScale >> 4) & 0xF);
- short history0 = loopContext.History0;
- short history1 = loopContext.History1;
- short coefficient0 = GetCoefficientAtIndex(coefficients, coefficientIndex * 2 + 0);
- short coefficient1 = GetCoefficientAtIndex(coefficients, coefficientIndex * 2 + 1);
- int decodedCount = Math.Min(count, endSampleOffset - startSampleOffset - offset);
- int nibbles = GetNibblesFromSampleCount(offset + startSampleOffset);
- int remaining = decodedCount;
- int outputBufferIndex = 0;
- int inputIndex = 0;
- ReadOnlySpan<byte> targetInput;
- targetInput = input.Slice(nibbles / 2);
- while (remaining > 0)
- {
- int samplesCount;
- if (((uint)nibbles % NibblesPerFrame) == 0)
- {
- predScale = targetInput[inputIndex++];
- scale = (byte)(predScale & 0xF);
- coefficientIndex = (byte)((predScale >> 4) & 0xF);
- coefficient0 = GetCoefficientAtIndex(coefficients, coefficientIndex * 2);
- coefficient1 = GetCoefficientAtIndex(coefficients, coefficientIndex * 2 + 1);
- nibbles += 2;
- samplesCount = Math.Min(remaining, SamplesPerFrame);
- }
- else
- {
- samplesCount = 1;
- }
- int scaleFixedPoint = FixedPointHelper.ToFixed(1.0f, FixedPointPrecision) << scale;
- if (samplesCount < SamplesPerFrame)
- {
- for (int i = 0; i < samplesCount; i++)
- {
- int value = targetInput[inputIndex];
- int sample;
- if ((nibbles & 1) != 0)
- {
- sample = (value << 28) >> 28;
- inputIndex++;
- }
- else
- {
- sample = (value << 24) >> 28;
- }
- nibbles++;
- int prediction = coefficient0 * history0 + coefficient1 * history1;
- sample = FixedPointHelper.RoundUpAndToInt(sample * scaleFixedPoint + prediction, FixedPointPrecision);
- short saturatedSample = Saturate(sample);
- history1 = history0;
- history0 = saturatedSample;
- output[outputBufferIndex++] = saturatedSample;
- remaining--;
- }
- }
- else
- {
- for (int i = 0; i < SamplesPerFrame / 2; i++)
- {
- int value = targetInput[inputIndex];
- int sample0;
- int sample1;
- sample0 = (value << 24) >> 28;
- sample1 = (value << 28) >> 28;
- inputIndex++;
- int prediction0 = coefficient0 * history0 + coefficient1 * history1;
- sample0 = FixedPointHelper.RoundUpAndToInt(sample0 * scaleFixedPoint + prediction0, FixedPointPrecision);
- short saturatedSample0 = Saturate(sample0);
- int prediction1 = coefficient0 * saturatedSample0 + coefficient1 * history0;
- sample1 = FixedPointHelper.RoundUpAndToInt(sample1 * scaleFixedPoint + prediction1, FixedPointPrecision);
- short saturatedSample1 = Saturate(sample1);
- history1 = saturatedSample0;
- history0 = saturatedSample1;
- output[outputBufferIndex++] = saturatedSample0;
- output[outputBufferIndex++] = saturatedSample1;
- }
- nibbles += SamplesPerFrame;
- remaining -= SamplesPerFrame;
- }
- }
- loopContext.PredScale = predScale;
- loopContext.History0 = history0;
- loopContext.History1 = history1;
- return decodedCount;
- }
- }
- }
|