AdpcmHelper.cs 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. using Ryujinx.Audio.Renderer.Dsp.State;
  2. using System;
  3. using System.Diagnostics;
  4. using System.Runtime.CompilerServices;
  5. namespace Ryujinx.Audio.Renderer.Dsp
  6. {
  7. public static class AdpcmHelper
  8. {
  9. private const int FixedPointPrecision = 11;
  10. private const int SamplesPerFrame = 14;
  11. private const int NibblesPerFrame = SamplesPerFrame + 2;
  12. private const int BytesPerFrame = 8;
  13. private const int BitsPerFrame = BytesPerFrame * 8;
  14. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  15. public static uint GetAdpcmDataSize(int sampleCount)
  16. {
  17. Debug.Assert(sampleCount >= 0);
  18. int frames = sampleCount / SamplesPerFrame;
  19. int extraSize = 0;
  20. if ((sampleCount % SamplesPerFrame) != 0)
  21. {
  22. extraSize = (sampleCount % SamplesPerFrame) / 2 + 1 + (sampleCount % 2);
  23. }
  24. return (uint)(BytesPerFrame * frames + extraSize);
  25. }
  26. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  27. public static int GetAdpcmOffsetFromSampleOffset(int sampleOffset)
  28. {
  29. Debug.Assert(sampleOffset >= 0);
  30. return GetNibblesFromSampleCount(sampleOffset) / 2;
  31. }
  32. public static int NibbleToSample(int nibble)
  33. {
  34. int frames = nibble / NibblesPerFrame;
  35. int extraNibbles = nibble % NibblesPerFrame;
  36. int samples = SamplesPerFrame * frames;
  37. return samples + extraNibbles - 2;
  38. }
  39. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  40. public static int GetNibblesFromSampleCount(int sampleCount)
  41. {
  42. byte headerSize = 0;
  43. if ((sampleCount % SamplesPerFrame) != 0)
  44. {
  45. headerSize = 2;
  46. }
  47. return sampleCount % SamplesPerFrame + NibblesPerFrame * (sampleCount / SamplesPerFrame) + headerSize;
  48. }
  49. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  50. private static short Saturate(int value)
  51. {
  52. if (value > short.MaxValue)
  53. value = short.MaxValue;
  54. if (value < short.MinValue)
  55. value = short.MinValue;
  56. return (short)value;
  57. }
  58. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  59. public static int Decode(Span<short> output, ReadOnlySpan<byte> input, int startSampleOffset, int endSampleOffset, int offset, int count, ReadOnlySpan<short> coefficients, ref AdpcmLoopContext loopContext)
  60. {
  61. if (input.IsEmpty || endSampleOffset < startSampleOffset)
  62. {
  63. return 0;
  64. }
  65. byte predScale = (byte)loopContext.PredScale;
  66. byte scale = (byte)(predScale & 0xF);
  67. byte coefficientIndex = (byte)((predScale >> 4) & 0xF);
  68. short history0 = loopContext.History0;
  69. short history1 = loopContext.History1;
  70. short coefficient0 = coefficients[coefficientIndex * 2 + 0];
  71. short coefficient1 = coefficients[coefficientIndex * 2 + 1];
  72. int decodedCount = Math.Min(count, endSampleOffset - startSampleOffset - offset);
  73. int nibbles = GetNibblesFromSampleCount(offset + startSampleOffset);
  74. int remaining = decodedCount;
  75. int outputBufferIndex = 0;
  76. int inputIndex = 0;
  77. ReadOnlySpan<byte> targetInput;
  78. targetInput = input.Slice(nibbles / 2);
  79. while (remaining > 0)
  80. {
  81. int samplesCount;
  82. if (((uint)nibbles % NibblesPerFrame) == 0)
  83. {
  84. predScale = targetInput[inputIndex++];
  85. scale = (byte)(predScale & 0xF);
  86. coefficientIndex = (byte)((predScale >> 4) & 0xF);
  87. coefficient0 = coefficients[coefficientIndex * 2 + 0];
  88. coefficient1 = coefficients[coefficientIndex * 2 + 1];
  89. nibbles += 2;
  90. samplesCount = Math.Min(remaining, SamplesPerFrame);
  91. }
  92. else
  93. {
  94. samplesCount = 1;
  95. }
  96. int scaleFixedPoint = FixedPointHelper.ToFixed(1.0f, FixedPointPrecision) << scale;
  97. if (samplesCount < SamplesPerFrame)
  98. {
  99. for (int i = 0; i < samplesCount; i++)
  100. {
  101. int value = targetInput[inputIndex];
  102. int sample;
  103. if ((nibbles & 1) != 0)
  104. {
  105. sample = (value << 28) >> 28;
  106. inputIndex++;
  107. }
  108. else
  109. {
  110. sample = (value << 24) >> 28;
  111. }
  112. nibbles++;
  113. int prediction = coefficient0 * history0 + coefficient1 * history1;
  114. sample = FixedPointHelper.RoundUpAndToInt(sample * scaleFixedPoint + prediction, FixedPointPrecision);
  115. short saturatedSample = Saturate(sample);
  116. history1 = history0;
  117. history0 = saturatedSample;
  118. output[outputBufferIndex++] = saturatedSample;
  119. remaining--;
  120. }
  121. }
  122. else
  123. {
  124. for (int i = 0; i < SamplesPerFrame / 2; i++)
  125. {
  126. int value = targetInput[inputIndex];
  127. int sample0;
  128. int sample1;
  129. sample0 = (value << 24) >> 28;
  130. sample1 = (value << 28) >> 28;
  131. inputIndex++;
  132. int prediction0 = coefficient0 * history0 + coefficient1 * history1;
  133. sample0 = FixedPointHelper.RoundUpAndToInt(sample0 * scaleFixedPoint + prediction0, FixedPointPrecision);
  134. short saturatedSample0 = Saturate(sample0);
  135. int prediction1 = coefficient0 * saturatedSample0 + coefficient1 * history0;
  136. sample1 = FixedPointHelper.RoundUpAndToInt(sample1 * scaleFixedPoint + prediction1, FixedPointPrecision);
  137. short saturatedSample1 = Saturate(sample1);
  138. history1 = saturatedSample0;
  139. history0 = saturatedSample1;
  140. output[outputBufferIndex++] = saturatedSample0;
  141. output[outputBufferIndex++] = saturatedSample1;
  142. }
  143. nibbles += SamplesPerFrame;
  144. remaining -= SamplesPerFrame;
  145. }
  146. }
  147. loopContext.PredScale = predScale;
  148. loopContext.History0 = history0;
  149. loopContext.History1 = history1;
  150. return decodedCount;
  151. }
  152. }
  153. }