Downmixing.cs 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. using System;
  2. using System.Runtime.CompilerServices;
  3. using System.Runtime.InteropServices;
  4. namespace Ryujinx.Audio
  5. {
  6. public static class Downmixing
  7. {
  8. [StructLayout(LayoutKind.Sequential, Pack = 1)]
  9. private struct Channel51FormatPCM16
  10. {
  11. public short FrontLeft;
  12. public short FrontRight;
  13. public short FrontCenter;
  14. public short LowFrequency;
  15. public short BackLeft;
  16. public short BackRight;
  17. }
  18. [StructLayout(LayoutKind.Sequential, Pack = 1)]
  19. private struct ChannelStereoFormatPCM16
  20. {
  21. public short Left;
  22. public short Right;
  23. }
  24. private const int Q15Bits = 16;
  25. private const int RawQ15One = 1 << Q15Bits;
  26. private const int RawQ15HalfOne = (int)(0.5f * RawQ15One);
  27. private const int Minus3dBInQ15 = (int)(0.707f * RawQ15One);
  28. private const int Minus6dBInQ15 = (int)(0.501f * RawQ15One);
  29. private const int Minus12dBInQ15 = (int)(0.251f * RawQ15One);
  30. private static int[] DefaultSurroundToStereoCoefficients = new int[4]
  31. {
  32. RawQ15One,
  33. Minus3dBInQ15,
  34. Minus12dBInQ15,
  35. Minus3dBInQ15,
  36. };
  37. private static int[] DefaultStereoToMonoCoefficients = new int[2]
  38. {
  39. Minus6dBInQ15,
  40. Minus6dBInQ15,
  41. };
  42. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  43. private static ReadOnlySpan<Channel51FormatPCM16> GetSurroundBuffer(ReadOnlySpan<short> data)
  44. {
  45. return MemoryMarshal.Cast<short, Channel51FormatPCM16>(data);
  46. }
  47. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  48. private static ReadOnlySpan<ChannelStereoFormatPCM16> GetStereoBuffer(ReadOnlySpan<short> data)
  49. {
  50. return MemoryMarshal.Cast<short, ChannelStereoFormatPCM16>(data);
  51. }
  52. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  53. private static short DownMixStereoToMono(ReadOnlySpan<int> coefficients, short left, short right)
  54. {
  55. return (short)((left * coefficients[0] + right * coefficients[1]) >> Q15Bits);
  56. }
  57. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  58. private static short DownMixSurroundToStereo(ReadOnlySpan<int> coefficients, short back, short lfe, short center, short front)
  59. {
  60. return (short)((coefficients[3] * back + coefficients[2] * lfe + coefficients[1] * center + coefficients[0] * front + RawQ15HalfOne) >> Q15Bits);
  61. }
  62. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  63. private static short[] DownMixSurroundToStereo(ReadOnlySpan<int> coefficients, ReadOnlySpan<short> data)
  64. {
  65. const int SurroundChannelCount = 6;
  66. const int StereoChannelCount = 2;
  67. int samplePerChannelCount = data.Length / SurroundChannelCount;
  68. short[] downmixedBuffer = new short[samplePerChannelCount * StereoChannelCount];
  69. ReadOnlySpan<Channel51FormatPCM16> channels = GetSurroundBuffer(data);
  70. for (int i = 0; i < samplePerChannelCount; i++)
  71. {
  72. Channel51FormatPCM16 channel = channels[i];
  73. downmixedBuffer[i * 2] = DownMixSurroundToStereo(coefficients, channel.BackLeft, channel.LowFrequency, channel.FrontCenter, channel.FrontLeft);
  74. downmixedBuffer[i * 2 + 1] = DownMixSurroundToStereo(coefficients, channel.BackRight, channel.LowFrequency, channel.FrontCenter, channel.FrontRight);
  75. }
  76. return downmixedBuffer;
  77. }
  78. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  79. private static short[] DownMixStereoToMono(ReadOnlySpan<int> coefficients, ReadOnlySpan<short> data)
  80. {
  81. const int StereoChannelCount = 2;
  82. const int MonoChannelCount = 1;
  83. int samplePerChannelCount = data.Length / StereoChannelCount;
  84. short[] downmixedBuffer = new short[samplePerChannelCount * MonoChannelCount];
  85. ReadOnlySpan<ChannelStereoFormatPCM16> channels = GetStereoBuffer(data);
  86. for (int i = 0; i < samplePerChannelCount; i++)
  87. {
  88. ChannelStereoFormatPCM16 channel = channels[i];
  89. downmixedBuffer[i] = DownMixStereoToMono(coefficients, channel.Left, channel.Right);
  90. }
  91. return downmixedBuffer;
  92. }
  93. public static short[] DownMixStereoToMono(ReadOnlySpan<short> data)
  94. {
  95. return DownMixStereoToMono(DefaultStereoToMonoCoefficients, data);
  96. }
  97. public static short[] DownMixSurroundToStereo(ReadOnlySpan<short> data)
  98. {
  99. return DownMixSurroundToStereo(DefaultSurroundToStereoCoefficients, data);
  100. }
  101. }
  102. }