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