UpsamplerHelper.cs 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. using Ryujinx.Audio.Renderer.Server.Upsampler;
  2. using Ryujinx.Common.Memory;
  3. using System;
  4. using System.Diagnostics;
  5. using System.Numerics;
  6. using System.Runtime.CompilerServices;
  7. namespace Ryujinx.Audio.Renderer.Dsp
  8. {
  9. public class UpsamplerHelper
  10. {
  11. private const int HistoryLength = UpsamplerBufferState.HistoryLength;
  12. private const int FilterBankLength = 20;
  13. // Bank0 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
  14. private const int Bank0CenterIndex = 9;
  15. private static readonly Array20<float> Bank1 = PrecomputeFilterBank(1.0f / 6.0f);
  16. private static readonly Array20<float> Bank2 = PrecomputeFilterBank(2.0f / 6.0f);
  17. private static readonly Array20<float> Bank3 = PrecomputeFilterBank(3.0f / 6.0f);
  18. private static readonly Array20<float> Bank4 = PrecomputeFilterBank(4.0f / 6.0f);
  19. private static readonly Array20<float> Bank5 = PrecomputeFilterBank(5.0f / 6.0f);
  20. private static Array20<float> PrecomputeFilterBank(float offset)
  21. {
  22. float Sinc(float x)
  23. {
  24. if (x == 0)
  25. {
  26. return 1.0f;
  27. }
  28. return (MathF.Sin(MathF.PI * x) / (MathF.PI * x));
  29. }
  30. float BlackmanWindow(float x)
  31. {
  32. const float a = 0.18f;
  33. const float a0 = 0.5f - 0.5f * a;
  34. const float a1 = -0.5f;
  35. const float a2 = 0.5f * a;
  36. return a0 + a1 * MathF.Cos(2 * MathF.PI * x) + a2 * MathF.Cos(4 * MathF.PI * x);
  37. }
  38. Array20<float> result = new Array20<float>();
  39. for (int i = 0; i < FilterBankLength; i++)
  40. {
  41. float x = (Bank0CenterIndex - i) + offset;
  42. result[i] = Sinc(x) * BlackmanWindow(x / FilterBankLength + 0.5f);
  43. }
  44. return result;
  45. }
  46. // Polyphase upsampling algorithm
  47. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  48. public static void Upsample(Span<float> outputBuffer, ReadOnlySpan<float> inputBuffer, int outputSampleCount, int inputSampleCount, ref UpsamplerBufferState state)
  49. {
  50. if (!state.Initialized)
  51. {
  52. state.Scale = inputSampleCount switch
  53. {
  54. 40 => 6.0f,
  55. 80 => 3.0f,
  56. 160 => 1.5f,
  57. _ => throw new ArgumentOutOfRangeException()
  58. };
  59. state.Initialized = true;
  60. }
  61. if (outputSampleCount == 0)
  62. {
  63. return;
  64. }
  65. float DoFilterBank(ref UpsamplerBufferState state, in Array20<float> bank)
  66. {
  67. float result = 0.0f;
  68. Debug.Assert(state.History.Length == HistoryLength);
  69. Debug.Assert(bank.Length == FilterBankLength);
  70. int curIdx = 0;
  71. if (Vector.IsHardwareAccelerated)
  72. {
  73. // Do SIMD-accelerated block operations where possible.
  74. // Only about a 2x speedup since filter bank length is short
  75. int stopIdx = FilterBankLength - (FilterBankLength % Vector<float>.Count);
  76. while (curIdx < stopIdx)
  77. {
  78. result += Vector.Dot(
  79. new Vector<float>(bank.AsSpan().Slice(curIdx, Vector<float>.Count)),
  80. new Vector<float>(state.History.AsSpan().Slice(curIdx, Vector<float>.Count)));
  81. curIdx += Vector<float>.Count;
  82. }
  83. }
  84. while (curIdx < FilterBankLength)
  85. {
  86. result += bank[curIdx] * state.History[curIdx];
  87. curIdx++;
  88. }
  89. return result;
  90. }
  91. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  92. void NextInput(ref UpsamplerBufferState state, float input)
  93. {
  94. state.History.AsSpan().Slice(1).CopyTo(state.History.AsSpan());
  95. state.History[HistoryLength - 1] = input;
  96. }
  97. int inputBufferIndex = 0;
  98. switch (state.Scale)
  99. {
  100. case 6.0f:
  101. for (int i = 0; i < outputSampleCount; i++)
  102. {
  103. switch (state.Phase)
  104. {
  105. case 0:
  106. NextInput(ref state, inputBuffer[inputBufferIndex++]);
  107. outputBuffer[i] = state.History[Bank0CenterIndex];
  108. break;
  109. case 1:
  110. outputBuffer[i] = DoFilterBank(ref state, Bank1);
  111. break;
  112. case 2:
  113. outputBuffer[i] = DoFilterBank(ref state, Bank2);
  114. break;
  115. case 3:
  116. outputBuffer[i] = DoFilterBank(ref state, Bank3);
  117. break;
  118. case 4:
  119. outputBuffer[i] = DoFilterBank(ref state, Bank4);
  120. break;
  121. case 5:
  122. outputBuffer[i] = DoFilterBank(ref state, Bank5);
  123. break;
  124. }
  125. state.Phase = (state.Phase + 1) % 6;
  126. }
  127. break;
  128. case 3.0f:
  129. for (int i = 0; i < outputSampleCount; i++)
  130. {
  131. switch (state.Phase)
  132. {
  133. case 0:
  134. NextInput(ref state, inputBuffer[inputBufferIndex++]);
  135. outputBuffer[i] = state.History[Bank0CenterIndex];
  136. break;
  137. case 1:
  138. outputBuffer[i] = DoFilterBank(ref state, Bank2);
  139. break;
  140. case 2:
  141. outputBuffer[i] = DoFilterBank(ref state, Bank4);
  142. break;
  143. }
  144. state.Phase = (state.Phase + 1) % 3;
  145. }
  146. break;
  147. case 1.5f:
  148. // Upsample by 3 then decimate by 2.
  149. for (int i = 0; i < outputSampleCount; i++)
  150. {
  151. switch (state.Phase)
  152. {
  153. case 0:
  154. NextInput(ref state, inputBuffer[inputBufferIndex++]);
  155. outputBuffer[i] = state.History[Bank0CenterIndex];
  156. break;
  157. case 1:
  158. outputBuffer[i] = DoFilterBank(ref state, Bank4);
  159. break;
  160. case 2:
  161. NextInput(ref state, inputBuffer[inputBufferIndex++]);
  162. outputBuffer[i] = DoFilterBank(ref state, Bank2);
  163. break;
  164. }
  165. state.Phase = (state.Phase + 1) % 3;
  166. }
  167. break;
  168. default:
  169. throw new ArgumentOutOfRangeException();
  170. }
  171. }
  172. }
  173. }