Skip to content

Commit

Permalink
ConcatenatingSampleProvider and FollowedBy extension method
Browse files Browse the repository at this point in the history
  • Loading branch information
markheath committed Oct 1, 2015
1 parent 3325411 commit e2f9a2f
Show file tree
Hide file tree
Showing 6 changed files with 129 additions and 1 deletion.
1 change: 1 addition & 0 deletions NAudio/NAudio.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@
<Compile Include="Wave\Asio\AsioSampleType.cs" />
<Compile Include="Wave\Compression\AcmDriverAddFlags.cs" />
<Compile Include="Wave\SampleProviders\AdsrSampleProvider.cs" />
<Compile Include="Wave\SampleProviders\ConcatenatingSampleProvider.cs" />
<Compile Include="Wave\SampleProviders\FadeInOutSampleProvider.cs" />
<Compile Include="Wave\SampleProviders\MultiplexingSampleProvider.cs" />
<Compile Include="Wave\SampleProviders\OffsetSampleProvider.cs" />
Expand Down
49 changes: 49 additions & 0 deletions NAudio/Wave/SampleProviders/ConcatenatingSampleProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using System.Linq;

namespace NAudio.Wave.SampleProviders
{
/// <summary>
/// Sample Provider to concatenate multiple sample providers together
/// </summary>
public class ConcatenatingSampleProvider : ISampleProvider
{
private readonly ISampleProvider[] providers;
private int currentProviderIndex;

/// <summary>
/// Creates a new ConcatenatingSampleProvider
/// </summary>
/// <param name="providers">The source providers to play one after the other. Must all share the same sample rate and channel count</param>
public ConcatenatingSampleProvider(IEnumerable<ISampleProvider> providers)
{
if (providers == null) throw new ArgumentNullException("providers");
this.providers = providers.ToArray();
if (this.providers.Length == 0) throw new ArgumentException("Must provide at least one input", "providers");
if (this.providers.Any(p => p.WaveFormat.Channels != WaveFormat.Channels)) throw new ArgumentException("All inputs must have the same channel count", "providers");
if (this.providers.Any(p => p.WaveFormat.SampleRate != WaveFormat.SampleRate)) throw new ArgumentException("All inputs must have the same sample rate", "providers");
}

/// <summary>
/// The WaveFormat of this Sample Provider
/// </summary>
public WaveFormat WaveFormat { get { return providers[0].WaveFormat; } }

/// <summary>
/// Read Samples from this sample provider
/// </summary>
public int Read(float[] buffer, int offset, int count)
{
var read = 0;
while (read < count && currentProviderIndex < providers.Length)
{
var needed = count - read;
var readThisTime = providers[currentProviderIndex].Read(buffer, read, needed);
read += readThisTime;
if (readThisTime == 0) currentProviderIndex++;
}
return read;
}
}
}
29 changes: 28 additions & 1 deletion NAudio/Wave/WaveExtensionMethods.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using NAudio.Wave.SampleProviders;
using System;
using NAudio.Wave.SampleProviders;

namespace NAudio.Wave
{
Expand Down Expand Up @@ -60,5 +61,31 @@ public static IWaveProvider ToWaveProvider16(this ISampleProvider sampleProvider
{
return new SampleToWaveProvider16(sampleProvider);
}

/// <summary>
/// Concatenates one Sample Provider on the end of another
/// </summary>
/// <param name="sampleProvider">The sample provider to play first</param>
/// <param name="next">The sample provider to play next</param>
/// <returns>A single sampleprovider to play one after the other</returns>
public static ISampleProvider FollowedBy(this ISampleProvider sampleProvider, ISampleProvider next)
{
return new ConcatenatingSampleProvider(new[] { sampleProvider, next});
}

/// <summary>
/// Concatenates one Sample Provider on the end of another with silence inserted
/// </summary>
/// <param name="sampleProvider">The sample provider to play first</param>
/// <param name="silenceDuration">Silence duration to insert between the two</param>
/// <param name="next">The sample provider to play next</param>
/// <returns>A single sample provider</returns>
public static ISampleProvider FollowedBy(this ISampleProvider sampleProvider, TimeSpan silenceDuration, ISampleProvider next)
{
var silenceAppended = new OffsetSampleProvider(sampleProvider) {LeadOut = silenceDuration};
return new ConcatenatingSampleProvider(new[] { silenceAppended, next });
}


}
}
1 change: 1 addition & 0 deletions NAudioTests/NAudioTests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@
<Compile Include="WaveStreams\BlockAlignmentReductionStreamTests.cs" />
<Compile Include="WaveStreams\BufferedWaveProviderTests.cs" />
<Compile Include="WaveStreams\CircularBufferTests.cs" />
<Compile Include="WaveStreams\ConcatenatingSampleProviderTests.cs" />
<Compile Include="WaveStreams\FadeInOutSampleProviderTests.cs" />
<Compile Include="WaveStreams\ChunkIdentifierTests.cs" />
<Compile Include="WaveStreams\MixingSampleProviderTests.cs" />
Expand Down
49 changes: 49 additions & 0 deletions NAudioTests/WaveStreams/ConcatenatingSampleProviderTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using System;
using System.Linq;
using NAudio.Wave.SampleProviders;
using NUnit.Framework;

namespace NAudioTests.WaveStreams
{
[TestFixture]
public class ConcatenatingSampleProviderTests
{
[Test]
public void CanPassASingleProvider()
{
// arrange
const int expectedLength = 5000;
var input = new TestSampleProvider(44100, 2, expectedLength);
var concatenator = new ConcatenatingSampleProvider(new[] {input});
var buffer = new float[2000];
var totalRead = 0;

// act
while (true)
{
var read = concatenator.Read(buffer, 0, buffer.Length);
if (read == 0) break;
totalRead += read;
Assert.That(totalRead <= expectedLength);
}
Assert.That(totalRead == expectedLength);
}

[Test]
public void CanPassTwoProviders()
{
// arrange
var expectedLength = 100;
var input1 = new TestSampleProvider(44100, 2, 50);
var input2 = new TestSampleProvider(44100, 2, 50);
var concatenator = new ConcatenatingSampleProvider(new[] { input1, input2 });
var buffer = new float[2000];

var read = concatenator.Read(buffer, 0, buffer.Length);
Assert.AreEqual(expectedLength, read, "read == expectedLength");
Assert.AreEqual(49, buffer[49]);
Assert.AreEqual(0, buffer[50]);
Assert.AreEqual(49, buffer[99]);
}
}
}
1 change: 1 addition & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
* Improvements to Mp3FileReader seeking and position reporting (thanks to protyposis)
* updated NAudio build process to use FAKE, retiring the old MSBuild and IronPython scripts
* NAudio.Wma project is moved out into its own [GitHub repository](https://github.com/naudio/NAudio.Wma)
* ConcatenatingSampleProvider and FollowedBy extension method making it easy to concatenate Sample Providers
* Various bugfixes and enhancements. See commit log for full details
* WasapiCapture buffer sizes can be specified
* MMDeviceEnumerator is disposable
Expand Down

0 comments on commit e2f9a2f

Please sign in to comment.