UpsamplerHelper.cs 6.8 KB

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