Skip to content

Commit

Permalink
MixingSampleProvider raises events as inputs are removed, and allows …
Browse files Browse the repository at this point in the history
…access to list of inputs
  • Loading branch information
markheath committed Sep 9, 2016
1 parent 8f1b8eb commit c4ea062
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 27 deletions.
71 changes: 48 additions & 23 deletions NAudio/Wave/SampleProviders/MixingSampleProvider.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Text;
using NAudio.Utils;

namespace NAudio.Wave.SampleProviders
Expand All @@ -10,10 +9,9 @@ namespace NAudio.Wave.SampleProviders
/// </summary>
public class MixingSampleProvider : ISampleProvider
{
private List<ISampleProvider> sources;
private WaveFormat waveFormat;
private readonly List<ISampleProvider> sources;
private float[] sourceBuffer;
private const int maxInputs = 1024; // protect ourselves against doing something silly
private const int MaxInputs = 1024; // protect ourselves against doing something silly

/// <summary>
/// Creates a new MixingSampleProvider, with no inputs, but a specified WaveFormat
Expand All @@ -25,8 +23,8 @@ public MixingSampleProvider(WaveFormat waveFormat)
{
throw new ArgumentException("Mixer wave format must be IEEE float");
}
this.sources = new List<ISampleProvider>();
this.waveFormat = waveFormat;
sources = new List<ISampleProvider>();
WaveFormat = waveFormat;
}

/// <summary>
Expand All @@ -47,6 +45,11 @@ public MixingSampleProvider(IEnumerable<ISampleProvider> sources)
}
}

/// <summary>
/// Returns the mixer inputs (read-only - use AddMixerInput to add an input
/// </summary>
public IEnumerable<ISampleProvider> MixerInputs => sources;

/// <summary>
/// When set to true, the Read method always returns the number
/// of samples requested, even if there are no inputs, or if the
Expand Down Expand Up @@ -76,26 +79,31 @@ public void AddMixerInput(ISampleProvider mixerInput)
// the same time as a Read, rather than two AddMixerInput calls at the same time
lock (sources)
{
if (this.sources.Count >= maxInputs)
if (sources.Count >= MaxInputs)
{
throw new InvalidOperationException("Too many mixer inputs");
}
this.sources.Add(mixerInput);
sources.Add(mixerInput);
}
if (this.waveFormat == null)
if (WaveFormat == null)
{
this.waveFormat = mixerInput.WaveFormat;
WaveFormat = mixerInput.WaveFormat;
}
else
{
if (this.WaveFormat.SampleRate != mixerInput.WaveFormat.SampleRate ||
this.WaveFormat.Channels != mixerInput.WaveFormat.Channels)
if (WaveFormat.SampleRate != mixerInput.WaveFormat.SampleRate ||
WaveFormat.Channels != mixerInput.WaveFormat.Channels)
{
throw new ArgumentException("All mixer inputs must have the same WaveFormat");
}
}
}

/// <summary>
/// Raised when a mixer input has been removed because it has ended
/// </summary>
public event EventHandler<SampleProviderEventArgs> MixerInputEnded;

/// <summary>
/// Removes a mixer input
/// </summary>
Expand All @@ -104,7 +112,7 @@ public void RemoveMixerInput(ISampleProvider mixerInput)
{
lock (sources)
{
this.sources.Remove(mixerInput);
sources.Remove(mixerInput);
}
}

Expand All @@ -115,17 +123,14 @@ public void RemoveAllMixerInputs()
{
lock (sources)
{
this.sources.Clear();
sources.Clear();
}
}

/// <summary>
/// The output WaveFormat of this sample provider
/// </summary>
public WaveFormat WaveFormat
{
get { return this.waveFormat; }
}
public WaveFormat WaveFormat { get; private set; }

/// <summary>
/// Reads samples from this sample provider
Expand All @@ -137,29 +142,30 @@ public WaveFormat WaveFormat
public int Read(float[] buffer, int offset, int count)
{
int outputSamples = 0;
this.sourceBuffer = BufferHelpers.Ensure(this.sourceBuffer, count);
sourceBuffer = BufferHelpers.Ensure(sourceBuffer, count);
lock (sources)
{
int index = sources.Count - 1;
while (index >= 0)
{
var source = sources[index];
int samplesRead = source.Read(this.sourceBuffer, 0, count);
int samplesRead = source.Read(sourceBuffer, 0, count);
int outIndex = offset;
for (int n = 0; n < samplesRead; n++)
{
if (n >= outputSamples)
{
buffer[outIndex++] = this.sourceBuffer[n];
buffer[outIndex++] = sourceBuffer[n];
}
else
{
buffer[outIndex++] += this.sourceBuffer[n];
buffer[outIndex++] += sourceBuffer[n];
}
}
outputSamples = Math.Max(samplesRead, outputSamples);
if (samplesRead == 0)
if (samplesRead < count)
{
MixerInputEnded?.Invoke(this, new SampleProviderEventArgs(source));
sources.RemoveAt(index);
}
index--;
Expand All @@ -178,4 +184,23 @@ public int Read(float[] buffer, int offset, int count)
return outputSamples;
}
}

/// <summary>
/// SampleProvider event args
/// </summary>
public class SampleProviderEventArgs : EventArgs
{
/// <summary>
/// Constructs a new SampleProviderEventArgs
/// </summary>
public SampleProviderEventArgs(ISampleProvider sampleProvider)
{
SampleProvider = sampleProvider;
}

/// <summary>
/// The Sample Provider
/// </summary>
public ISampleProvider SampleProvider { get; private set; }
}
}
25 changes: 21 additions & 4 deletions NAudioTests/WaveStreams/MixingSampleProviderTests.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using NAudio.Wave;
using NAudio.Wave.SampleProviders;
using NUnit.Framework;
Expand Down Expand Up @@ -56,7 +53,7 @@ public void FullyReadCausesPartialBufferToBeZeroedOut()
var input1 = new TestSampleProvider(44100, 2, 800);
var msp = new MixingSampleProvider(new[] { input1 });
msp.ReadFully = true;
// of 1000 floats of value 9999
// buffer of 1000 floats of value 9999
var buffer = Enumerable.Range(1,1000).Select(n => 9999f).ToArray();

Assert.AreEqual(buffer.Length, msp.Read(buffer, 0, buffer.Length));
Expand All @@ -67,5 +64,25 @@ public void FullyReadCausesPartialBufferToBeZeroedOut()
Assert.AreEqual(0, buffer[999]);
}

[Test]
public void MixerInputEndedInvoked()
{
var input1 = new TestSampleProvider(44100, 2, 8000);
var input2 = new TestSampleProvider(44100, 2, 800);
var msp = new MixingSampleProvider(new[] { input1, input2 });
ISampleProvider endedInput = null;
msp.MixerInputEnded += (s, a) =>
{
Assert.IsNull(endedInput);
endedInput = a.SampleProvider;
};
// buffer of 1000 floats of value 9999
var buffer = Enumerable.Range(1, 1000).Select(n => 9999f).ToArray();

Assert.AreEqual(buffer.Length, msp.Read(buffer, 0, buffer.Length));
Assert.AreSame(input2, endedInput);
Assert.AreEqual(1,msp.MixerInputs.Count());
}

}
}

0 comments on commit c4ea062

Please sign in to comment.