Skip to content

Commit bffca23

Browse files
committed
Added channel downmix logic
1 parent 32de576 commit bffca23

2 files changed

Lines changed: 108 additions & 2 deletions

File tree

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
namespace DS4AudioStreamer.Sound;
2+
3+
public static class Downmixer
4+
{
5+
public static void DownmixToStereo(float[] input, float[] output, int frames, int channels)
6+
{
7+
if (channels < 2)
8+
{
9+
throw new ArgumentException("Need at least 2 channels to downmix");
10+
}
11+
12+
int inIdx = 0;
13+
int outIdx = 0;
14+
15+
for (int i = 0; i < frames; i++)
16+
{
17+
float left = input[inIdx]; // Front Left
18+
float right = input[inIdx + 1]; // Front Right
19+
20+
if (channels > 2)
21+
{
22+
// Center
23+
if (channels > 2)
24+
{
25+
left += input[inIdx + 2] * 0.7f;
26+
right += input[inIdx + 2] * 0.7f;
27+
}
28+
29+
// LFE
30+
if (channels > 3)
31+
{
32+
left += input[inIdx + 3] * 0.5f;
33+
right += input[inIdx + 3] * 0.5f;
34+
}
35+
36+
// SL/SR
37+
if (channels > 4)
38+
{
39+
left += input[inIdx + 4] * 0.7f;
40+
}
41+
42+
if (channels > 5)
43+
{
44+
right += input[inIdx + 5] * 0.7f;
45+
}
46+
47+
// SBL/SBR (7.1)
48+
if (channels > 6)
49+
{
50+
left += input[inIdx + 6] * 0.7f;
51+
}
52+
53+
if (channels > 7)
54+
{
55+
right += input[inIdx + 7] * 0.7f;
56+
}
57+
}
58+
59+
// Prevent clipping by scaling down
60+
output[outIdx] = left * 0.5f;
61+
output[outIdx + 1] = right * 0.5f;
62+
63+
inIdx += channels;
64+
outIdx += 2;
65+
}
66+
}
67+
68+
public static unsafe void Downmix6To2(Span<float> input, Span<float> output, int frames)
69+
{
70+
int inIdx = 0;
71+
int outIdx = 0;
72+
73+
for (int i = 0; i < frames; i++)
74+
{
75+
// Load 6 floats (L, R, C, LFE, SL, SR)
76+
// We can’t load all 6 at once with Vector<float> (needs multiple of Vector<float>.Count),
77+
// so SIMD wins mostly for bulk copy/scale. For mixing, plain scalar math is fine.
78+
79+
float l = input[inIdx];
80+
float r = input[inIdx + 1];
81+
float c = input[inIdx + 2];
82+
float lfe = input[inIdx + 3];
83+
float sl = input[inIdx + 4];
84+
float sr = input[inIdx + 5];
85+
86+
float left = l + (c * 0.7f) + (lfe * 0.5f) + (sl * 0.7f);
87+
float right = r + (c * 0.7f) + (lfe * 0.5f) + (sr * 0.7f);
88+
89+
output[outIdx] = left * 0.5f;
90+
output[outIdx + 1] = right * 0.5f;
91+
92+
inIdx += 6;
93+
outIdx += 2;
94+
}
95+
}
96+
}

DS4AudioStreamer/Sound/SbcAudioStream.cs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System.Buffers;
12
using System.Diagnostics;
23
using System.Runtime.InteropServices;
34

@@ -59,7 +60,7 @@ public SbcAudioStream()
5960

6061
_captureDevice = new BufferedLoopbackCapture(device);
6162
_captureDevice.DataAvailable += OnAudioCaptured;
62-
63+
6364
Console.WriteLine($"Wave format: {_captureDevice.WaveFormat}");
6465

6566
// Resampler
@@ -155,7 +156,16 @@ private unsafe void OnAudioCaptured(object? sender, WaveInEventArgs e)
155156
short* finalShortFormat = stackalloc short[outSamples];
156157
int finalSampleCount = 0;
157158

158-
fixed (float* pcmBuffer = MemoryMarshal.Cast<byte, float>(e.Buffer))
159+
Span<float> pcmData = MemoryMarshal.Cast<byte, float>(e.Buffer);
160+
float[] downmixed = ArrayPool<float>.Shared.Rent(inSamples);
161+
162+
// TODO: experimental
163+
if (_captureDevice.WaveFormat.Channels == 6)
164+
{
165+
Downmixer.Downmix6To2(pcmData, downmixed, inFrameCount);
166+
}
167+
168+
fixed (float* pcmBuffer = _captureDevice.WaveFormat.Channels == 6 ? downmixed : pcmData)
159169
{
160170
if (SbcSampleRate != _captureDevice.WaveFormat.SampleRate)
161171
{

0 commit comments

Comments
 (0)