Skip to content

Commit

Permalink
documentation explaining the differences between WaveStream, IWavePro…
Browse files Browse the repository at this point in the history
…vider and ISampleProvider
  • Loading branch information
markheath committed Nov 21, 2017
1 parent af5c437 commit d90bdec
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 1 deletion.
80 changes: 80 additions & 0 deletions Docs/WaveProviders.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# WaveStream, IWaveProvider and ISampleProvider

When you play audio with NAudio or construct a playback graph, you are typically working with either `IWaveProvider` or `ISampleProvider` interface implementations. This article explains the three main base interfaces and classes you will encounter in NAudio and when you might use them.

## WaveStream

`WaveStream` was the first base class in NAudio, and inherits from `System.IO.Stream`. It represents a stream of audio data, and its format can be determined by looking at the `WaveFormat` property.

It supports reporting `Length` and `Position` and these are both measured in terms of bytes, not samples. `WaveStreams` can be repositioned (assuming the underlying implementation supports that), although care must often be taken to reposition to a multiple of the `BlockAlign` of the `WaveFormat`. For example if the wave stream produces 16 bit samples, you should always reposition to an even numbered byte position.

Audio data is from a stream using the `Read` method which has the signature:

```c#
int Read(byte[] destBuffer, int offset, int numBytes)
```

This method is inherited from `System.IO.Stream`, and works in the standard way. The `destBuffer` is the buffer into which audio should be written. The `offset` parameter specifies where in the buffer to write audio to (this parameter is almost always 0), and the `numBytes` parameter is how many bytes of audio should be read.

The `Read` method returns the number for bytes that were read. This should never be more than `numBytes` and can only be less if the end of the audio stream is reached. NAudio playback devices will stop playing when `Read` returns 0.

`WaveStream` is the base class for NAudio file reader classes such as `WaveFileReader`, `Mp3FileReader`, `AiffFileReader` and `MediaFoundationReader`. It is a good choice of base class because these inherently support repositioning. `RawSourceWaveStream` is also a `WaveStream`, and delegates repositioning requests down to its source stream.

For a more detailed look at all the methods on `WaveStream`, see [this article](http://markheath.net/post/naudio-wavestream-in-depth)

## IWaveProvider

Implementing `WaveStream` can be quite a lot of work, and for non-repositionable streams can seem like overkill. Also, streams that simply read from a source and modify or analyse audio as it passes through don't really benefit from inheriting from `WaveStream`.

So the `IWaveProvider` interface provides a much simpler, minimal interface that simply has the `Read` method, and a `WaveFormat` property.

```c#
public interface IWaveProvider
{
WaveFormat WaveFormat { get; }
int Read(byte[] buffer, int offset, int count);
}
```

The `IWavePlayer` interface only needs an `IWaveProvider` passed to its `Init` method in order to be able to play audio. `WaveFileWriter.CreateWaveFile` and `MediaFoundationEncoder.EncodeToMp3` also only needs an `IWaveProvider` to dump the audio out to a WAV file. So in many cases you won't need to create a `WaveStream` implementation, just implement `IWavePlayer` and you've got an audio source that can be played or rendered to a file.

`BufferedWaveProvider` is a good example of a `IWaveProvider` as it has no ability to reposition - it simply returns any buffered audio from its `Read` method.

## ISampleProvider

The strength of `IWaveProvider` is that it can be used to represent audio in any format. It can be used for 16,24 or 32 bit PCM audio, and even for compressed audio (MP3, G.711 etc). But if you are performing any kind of signal processing or analysis on the audio, it is very likely that you want the audio to be in 32 bit IEEE floating point format. And it can be a pain to try to read floating point values out of a `byte[]` in C#.

So `ISampleProvider` defines an interface where the samples are all 32 bit floating point:

```c#
public interface ISampleProvider
{
WaveFormat WaveFormat { get; }
int Read(float[] buffer, int offset, int count);
}
```

The `WaveFormat` will always be 32 bit floating point, but the number of channels or sample rate may of course vary.

The `Read` method's `count` parameter specifies the number of samples to be read, and the method returns the number of samples written into `buffer`.

`ISampleProvider` is a great base interface to inherit from if you are implementing any kind of audio effects. In the `Read` method you typically read from your source `ISampleProvider`, then modify the floating point samples, before returning them. Here's the implementation of the `Read` method in `VolumeSampleProvider` showing how simple this can be:

```c#
public int Read(float[] buffer, int offset, int sampleCount)
{
int samplesRead = source.Read(buffer, offset, sampleCount);
if (volume != 1f)
{
for (int n = 0; n < sampleCount; n++)
{
buffer[offset + n] *= volume;
}
}
return samplesRead;
}
```

NAudio makes it easy to go from an `IWaveProvider` to an `ISampleProvider` with the `ToSampleProvider` extension method. You can also use `AudioFileReader` which reads a wide variety of file types and implements `ISampleProvider`.

You can get back to an `IWaveProvider` with the `ToWaveProvider` extension method. Or there's the `ToWaveProvider16` extension method if you want to go back to 16 bit integer samples.
1 change: 1 addition & 0 deletions NAudio.sln
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Docs", "Docs", "{BA7F6DBB-9
Docs\RecordWavFileWinFormsWaveIn.md = Docs\RecordWavFileWinFormsWaveIn.md
Docs\WasapiLoopbackCapture.md = Docs\WasapiLoopbackCapture.md
Docs\WasapiOut.md = Docs\WasapiOut.md
Docs\WaveProviders.md = Docs\WaveProviders.md
EndProjectSection
EndProject
Global
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ NAudio comes with several demo applications which are the quickest way to see ho
- [Implement "Fire and Forget" Playback (e.g. game sound effects)](http://markheath.net/post/fire-and-forget-audio-playback-with)
- [Play streaming MP3](http://markheath.net/post/how-to-play-back-streaming-mp3-using)
- [Handling playback stopped](Docs/PlaybackStopped.md)
- [Understanding WaveStream, IWavePlayer and ISampleProvider](Docs/WaveProviders.md)

### Working with Codecs

Expand All @@ -80,7 +81,6 @@ NAudio comes with several demo applications which are the quickest way to see ho
- [Enumerate ACM Codecs](Docs/EnumerateAcmDrivers.md)
- [Fix the NoDriver calling acmFormatSuggest issue](http://markheath.net/post/nodriver-calling-acmformatsuggest)


### Working with audio files

- [Mix Two Audio Files to WAV](Docs/MixTwoAudioFilesToWav.md)
Expand Down

0 comments on commit d90bdec

Please sign in to comment.