HymnalProjector/Projector/FourChannelToStereoSampleProvider.cs
2025-07-09 21:35:58 -07:00

85 lines
2.5 KiB
C#

using NAudio.Wave;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Projector
{
// Custom ISampleProvider to mix 4 channels to stereo
public class FourChannelToStereoSampleProvider : ISampleProvider
{
private readonly ISampleProvider _source;
private float offTransition = 1f;
private float _voiceVolume = 0f;
private bool goingOff = false;
public WaveFormat WaveFormat { get; }
public void SetOff()
{
goingOff = true;
}
public bool IsOff => goingOff && offTransition == 0f;
public float VoiceVolume
{
get
{
return _voiceVolume;
}
set
{
_voiceVolume = Math.Clamp(value, 0f, 1f);
}
}
public FourChannelToStereoSampleProvider(ISampleProvider source)
{
if (source.WaveFormat.Channels != 4)
throw new ArgumentException("Source must have 4 channels");
if (source.WaveFormat.Encoding != WaveFormatEncoding.IeeeFloat)
throw new ArgumentException("Source must be IEEE float format");
_source = source;
WaveFormat = WaveFormat.CreateIeeeFloatWaveFormat(source.WaveFormat.SampleRate, 2);
}
public int Read(float[] buffer, int offset, int count)
{
int samplesNeeded = (count / 2) * 4;
float[] sourceBuffer = new float[samplesNeeded];
int samplesRead = _source.Read(sourceBuffer, 0, samplesNeeded);
int stereoSamples = samplesRead / 4 * 2;
int outIndex = offset;
if (goingOff && offTransition > 0f)
{
offTransition -= 0.2f;
if (offTransition <= 0f)
{
offTransition = 0f;
}
}
float musicVolume = 1f - VoiceVolume;
float voiceVolume = VoiceVolume;
for (int i = 0; i < samplesRead; i += 4)
{
// Mix channel 0 and 2 to left, 1 and 3 to right
float left = (sourceBuffer[i] * musicVolume) + (sourceBuffer[i + 2] * voiceVolume);
float right = (sourceBuffer[i + 1] * musicVolume) + (sourceBuffer[i + 3] * voiceVolume);
buffer[outIndex++] = left * offTransition;
buffer[outIndex++] = right * offTransition;
}
return stereoSamples;
}
}
}