AdpcmHelper.cs 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. //
  2. // Copyright (c) 2019-2020 Ryujinx
  3. //
  4. // This program is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU Lesser General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // This program is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU Lesser General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU Lesser General Public License
  15. // along with this program. If not, see <https://www.gnu.org/licenses/>.
  16. //
  17. using Ryujinx.Audio.Renderer.Dsp.State;
  18. using System;
  19. using System.Diagnostics;
  20. using System.Runtime.CompilerServices;
  21. namespace Ryujinx.Audio.Renderer.Dsp
  22. {
  23. public static class AdpcmHelper
  24. {
  25. private const int FixedPointPrecision = 11;
  26. private const int SamplesPerFrame = 14;
  27. private const int NibblesPerFrame = SamplesPerFrame + 2;
  28. private const int BytesPerFrame = 8;
  29. private const int BitsPerFrame = BytesPerFrame * 8;
  30. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  31. public static uint GetAdpcmDataSize(int sampleCount)
  32. {
  33. Debug.Assert(sampleCount >= 0);
  34. int frames = sampleCount / SamplesPerFrame;
  35. int extraSize = 0;
  36. if ((sampleCount % SamplesPerFrame) != 0)
  37. {
  38. extraSize = (sampleCount % SamplesPerFrame) / 2 + 1 + (sampleCount % 2);
  39. }
  40. return (uint)(BytesPerFrame * frames + extraSize);
  41. }
  42. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  43. public static int GetAdpcmOffsetFromSampleOffset(int sampleOffset)
  44. {
  45. Debug.Assert(sampleOffset >= 0);
  46. return GetNibblesFromSampleCount(sampleOffset) / 2;
  47. }
  48. public static int NibbleToSample(int nibble)
  49. {
  50. int frames = nibble / NibblesPerFrame;
  51. int extraNibbles = nibble % NibblesPerFrame;
  52. int samples = SamplesPerFrame * frames;
  53. return samples + extraNibbles - 2;
  54. }
  55. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  56. public static int GetNibblesFromSampleCount(int sampleCount)
  57. {
  58. byte headerSize = 0;
  59. if ((sampleCount % SamplesPerFrame) != 0)
  60. {
  61. headerSize = 2;
  62. }
  63. return sampleCount % SamplesPerFrame + NibblesPerFrame * (sampleCount / SamplesPerFrame) + headerSize;
  64. }
  65. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  66. private static short Saturate(int value)
  67. {
  68. if (value > short.MaxValue)
  69. value = short.MaxValue;
  70. if (value < short.MinValue)
  71. value = short.MinValue;
  72. return (short)value;
  73. }
  74. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  75. public static int Decode(Span<short> output, ReadOnlySpan<byte> input, int startSampleOffset, int endSampleOffset, int offset, int count, ReadOnlySpan<short> coefficients, ref AdpcmLoopContext loopContext)
  76. {
  77. if (input.IsEmpty || endSampleOffset < startSampleOffset)
  78. {
  79. return 0;
  80. }
  81. byte predScale = (byte)loopContext.PredScale;
  82. byte scale = (byte)(predScale & 0xF);
  83. byte coefficientIndex = (byte)((predScale >> 4) & 0xF);
  84. short history0 = loopContext.History0;
  85. short history1 = loopContext.History1;
  86. short coefficient0 = coefficients[coefficientIndex * 2 + 0];
  87. short coefficient1 = coefficients[coefficientIndex * 2 + 1];
  88. int decodedCount = Math.Min(count, endSampleOffset - startSampleOffset - offset);
  89. int nibbles = GetNibblesFromSampleCount(offset + startSampleOffset);
  90. int remaining = decodedCount;
  91. int outputBufferIndex = 0;
  92. int inputIndex = 0;
  93. ReadOnlySpan<byte> targetInput;
  94. targetInput = input.Slice(nibbles / 2);
  95. while (remaining > 0)
  96. {
  97. int samplesCount;
  98. if (((uint)nibbles % NibblesPerFrame) == 0)
  99. {
  100. predScale = targetInput[inputIndex++];
  101. scale = (byte)(predScale & 0xF);
  102. coefficientIndex = (byte)((predScale >> 4) & 0xF);
  103. coefficient0 = coefficients[coefficientIndex * 2 + 0];
  104. coefficient1 = coefficients[coefficientIndex * 2 + 1];
  105. nibbles += 2;
  106. samplesCount = Math.Min(remaining, SamplesPerFrame);
  107. }
  108. else
  109. {
  110. samplesCount = 1;
  111. }
  112. int scaleFixedPoint = FixedPointHelper.ToFixed(1.0f, FixedPointPrecision) << scale;
  113. if (samplesCount < SamplesPerFrame)
  114. {
  115. for (int i = 0; i < samplesCount; i++)
  116. {
  117. int value = targetInput[inputIndex];
  118. int sample;
  119. if ((nibbles & 1) != 0)
  120. {
  121. sample = (value << 28) >> 28;
  122. inputIndex++;
  123. }
  124. else
  125. {
  126. sample = (value << 24) >> 28;
  127. }
  128. nibbles++;
  129. int prediction = coefficient0 * history0 + coefficient1 * history1;
  130. sample = FixedPointHelper.RoundUpAndToInt(sample * scaleFixedPoint + prediction, FixedPointPrecision);
  131. short saturatedSample = Saturate(sample);
  132. history1 = history0;
  133. history0 = saturatedSample;
  134. output[outputBufferIndex++] = saturatedSample;
  135. remaining--;
  136. }
  137. }
  138. else
  139. {
  140. for (int i = 0; i < SamplesPerFrame / 2; i++)
  141. {
  142. int value = targetInput[inputIndex];
  143. int sample0;
  144. int sample1;
  145. sample0 = (value << 24) >> 28;
  146. sample1 = (value << 28) >> 28;
  147. inputIndex++;
  148. int prediction0 = coefficient0 * history0 + coefficient1 * history1;
  149. sample0 = FixedPointHelper.RoundUpAndToInt(sample0 * scaleFixedPoint + prediction0, FixedPointPrecision);
  150. short saturatedSample0 = Saturate(sample0);
  151. int prediction1 = coefficient0 * saturatedSample0 + coefficient1 * history0;
  152. sample1 = FixedPointHelper.RoundUpAndToInt(sample1 * scaleFixedPoint + prediction1, FixedPointPrecision);
  153. short saturatedSample1 = Saturate(sample1);
  154. history1 = saturatedSample0;
  155. history0 = saturatedSample1;
  156. output[outputBufferIndex++] = saturatedSample0;
  157. output[outputBufferIndex++] = saturatedSample1;
  158. }
  159. nibbles += SamplesPerFrame;
  160. remaining -= SamplesPerFrame;
  161. }
  162. }
  163. loopContext.PredScale = predScale;
  164. loopContext.History0 = history0;
  165. loopContext.History1 = history1;
  166. return decodedCount;
  167. }
  168. }
  169. }