Downmixing.cs 5.4 KB

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