Hi Mark, what do you think about this MatrixBus Implementation? I don't know if I got all the locks right...
public class BusMatrixWaveProvider : IWaveProvider
{
private readonly IList<IWaveProvider> inputs;
private readonly WaveFormat waveFormat;
private readonly int outputChannelCount;
private int inputChannelCount;
private readonly int bytesPerSample;
private float[] volumes;
public float GetVolume(int inputIndex, int outputIndex)
{
if (inputIndex < 0 || inputIndex >= InputChannelCount)
{
throw new ArgumentException("Invalid input channel");
}
if (outputIndex < 0 || outputIndex >= OutputChannelCount)
{
throw new ArgumentException("Invalid output channel");
}
lock (volumes)
{
int index = inputIndex + (outputIndex * inputChannelCount);
return volumes[index];
}
}
public void SetVolume(int inputIndex, int outputIndex, float value)
{
if (inputIndex < 0 || inputIndex >= InputChannelCount)
{
throw new ArgumentException("Invalid input channel");
}
if (outputIndex < 0 || outputIndex >= OutputChannelCount)
{
throw new ArgumentException("Invalid output channel");
}
lock (volumes)
{
int index = inputIndex + (outputIndex * inputChannelCount);
volumes[index] = value;
}
}
public void AddInput(IWaveProvider waveProvider)
{
lock (volumes)
{
//inputChannelCount += 1;
inputChannelCount += waveProvider.WaveFormat.Channels;
lock (inputs)
{
this.inputs.Add(waveProvider);
Array.Resize<float>(ref volumes, outputChannelCount * inputChannelCount);
}
}
}
public void RemoveInput(IWaveProvider waveProvider)
{
int index = inputs.IndexOf(waveProvider);
if (index == -1)
throw new ArgumentException("input was not found in this mixer");
lock (volumes)
{
//inputChannelCount -= 1;
int inputChannels = waveProvider.WaveFormat.Channels;
inputChannelCount -= inputChannels;
lock (inputs)
{
inputs.Remove(waveProvider);
int offset = 0;
for (int i = index; i < volumes.Length; i += (inputChannelCount + inputChannels))
{
Array.Copy(volumes, i + inputChannels, volumes, i - offset, inputChannelCount);
offset += inputChannels;
}
Array.Resize<float>(ref volumes, outputChannelCount * inputChannelCount);
}
}
}
public void RemoveInput(int index)
{
if (index > inputs.Count-1 || index < 0)
throw new ArgumentOutOfRangeException("index was not found in this bus");
RemoveInput(inputs[index]);
}
/// <summary>
/// Creates a mixing wave provider, allowing mixing of input channels to different
/// output channels
/// </summary>
/// <param name="numberOfOutputChannels">Desired number of output channels.</param>
public BusMatrixWaveProvider(int numberOfOutputChannels)
{
this.outputChannelCount = numberOfOutputChannels;
if (numberOfOutputChannels < 1)
{
throw new ArgumentException("You must provide at least one output");
}
}
/// <summary>
/// Creates a mixing wave provider, allowing mixing of input channels to different
/// output channels
/// </summary>
/// <param name="inputs">Input wave providers. Must all be of the same format, but can have any number of channels</param>
/// <param name="numberOfOutputChannels">Desired number of output channels.</param>
public BusMatrixWaveProvider(IEnumerable<IWaveProvider> inputs, int numberOfOutputChannels)
{
this.inputs = new List<IWaveProvider>(inputs);
this.outputChannelCount = numberOfOutputChannels;
if (this.inputs.Count == 0)
{
throw new ArgumentException("You must provide at least one input");
}
if (numberOfOutputChannels < 1)
{
throw new ArgumentException("You must provide at least one output");
}
foreach (var input in this.inputs)
{
if (this.waveFormat == null)
{
if (input.WaveFormat.Encoding == WaveFormatEncoding.Pcm)
{
this.waveFormat = new WaveFormat(input.WaveFormat.SampleRate, input.WaveFormat.BitsPerSample, numberOfOutputChannels);
}
else if (input.WaveFormat.Encoding == WaveFormatEncoding.IeeeFloat)
{
this.waveFormat = WaveFormat.CreateIeeeFloatWaveFormat(input.WaveFormat.SampleRate, numberOfOutputChannels);
}
else
{
throw new ArgumentException("Only PCM and 32 bit float are supported");
}
}
else
{
if (input.WaveFormat.BitsPerSample != this.waveFormat.BitsPerSample)
{
throw new ArgumentException("All inputs must have the same bit depth");
}
if (input.WaveFormat.SampleRate != this.waveFormat.SampleRate)
{
throw new ArgumentException("All inputs must have the same sample rate");
}
}
inputChannelCount += input.WaveFormat.Channels;
}
this.bytesPerSample = this.waveFormat.BitsPerSample / 8;
this.volumes = new float[this.inputChannelCount * this.outputChannelCount];
}
private byte[] inputBuffer;
public int Read(byte[] buffer, int offset, int count)
{
//byte[] tempbuffer = new byte[count];
Array.Clear(buffer, offset, count);
int outputBytesPerFrame = bytesPerSample * outputChannelCount;
int sampleFramesRequested = count / outputBytesPerFrame;
int sampleFramesRead = 0;
lock (volumes)
{
// now we must read from all inputs, even if we don't need their data, so they stay in sync
for (int inputIndex = 0; inputIndex < inputChannelCount; inputIndex++)
{
int inputBytesPerFrame = bytesPerSample;
int bytesRequired = sampleFramesRequested * inputBytesPerFrame;
this.inputBuffer = BufferHelpers.Ensure(this.inputBuffer, bytesRequired);
lock (inputs)
{
int bytesRead = inputs[inputIndex].Read(inputBuffer, 0, bytesRequired);
sampleFramesRead = Math.Max(sampleFramesRead, bytesRead / inputBytesPerFrame);
Sum32BitAudio(buffer, inputBuffer, bytesRead, inputBytesPerFrame, inputIndex, outputChannelCount, volumes);
}
}
}
return sampleFramesRead * outputBytesPerFrame;
}
/// <summary>
/// Actually performs the mixing
/// </summary>
private static unsafe void Sum32BitAudio(
byte[] destBuffer,
byte[] sourceBuffer,
int bytesRead,
int inputBytesPerFrame,
int inputIndex,
int outputChannelCount,
float[] volumes)
{
fixed (byte* pDestBuffer = &destBuffer[0], pSourceBuffer = &sourceBuffer[0])
{
fixed (float* volume = &volumes[inputIndex])
{
float* pfDestBuffer = (float*)pDestBuffer;
float* pfReadBuffer = (float*)pSourceBuffer;
int samplesRead = bytesRead / inputBytesPerFrame;
for (int n = 0; n < samplesRead; n++)
{
for (int nOutput = 0; nOutput < outputChannelCount; nOutput++)
{
//pfDestBuffer[(n * outputChannelCount) + nOutput] += (pfReadBuffer[n]);
pfDestBuffer[n + nOutput] += (pfReadBuffer[n] * volume[outputChannelCount * nOutput]);
}
}
}
}
}
public WaveFormat WaveFormat
{
get { return waveFormat; }
}
public int InputChannelCount
{
get { return inputChannelCount; }
}
public int OutputChannelCount
{
get { return outputChannelCount; }
}
}