NAudio
NAudio copied to clipboard
Incorrect AudioFileReader.TotalTime
I was writing unit tests where I play the audio and wait it to finish with Thread.Sleep(audio.TotalTime) and I noticed it was not continuing after the audio finished.
Using NAudio version 2.1.0, from NuGet. OS Microsoft Windows NT 10.0.22000.0.
string msg = "";
foreach (WaveStream w in new WaveStream[] {
new MediaFoundationReader(path),
new Mp3FileReader(path),
new AudioFileReader(path)
}){
msg += string.Format("class {0}", w.GetType());
msg += string.Format(
"\n{0} bytes" +
"\n average {1} bytes per second, " +
"\n {2} bits per sample, " +
"\n {3} channels," +
"\n {4} samples per second" +
"\n TotalTime {5}",
w.Length,
w.WaveFormat.AverageBytesPerSecond,
w.WaveFormat.BitsPerSample,
w.WaveFormat.Channels,
w.WaveFormat.SampleRate,
w.TotalTime
);
}
That results in
class NAudio.Wave.MediaFoundationReader
86109836 bytes
average 176400 bytes per second,
16 bits per sample,
2 channels,
44100 samples per second
TotalTime 00:08:08.1510000
class NAudio.Wave.Mp3FileReader
21141504 bytes
average 176400 bytes per second,
16 bits per sample,
2 channels,
44100 samples per second
TotalTime 00:01:59.8500000
class NAudio.Wave.AudioFileReader
172219672 bytes
average 352800 bytes per second,
32 bits per sample,
2 channels,
44100 samples per second
TotalTime 00:08:08.1510000'
Average bytes per second, bits per sample, number of channels, and sample rate is consistent in all classes.
The duration computed by AudioFileReader is the same as the one computed by MediaFoundationReader.
The source code of MediaFoundationReader has a note about (in)accuracy.
https://github.com/naudio/NAudio/blob/d66e6821020f35429f1c26f75303290fc8aa3375/NAudio.Wasapi/MediaFoundationReader.cs#L307-L315
Here is how it is computed https://github.com/naudio/NAudio/blob/d66e6821020f35429f1c26f75303290fc8aa3375/NAudio.Wasapi/MediaFoundationReader.cs#L187-L202 BTW: Update link for the documentation is this. I didn't check what goes inside NAudio.MediaFoundation.
Wondering how this happened, I found that for newer OS it will use MediaFoundationReader instead of Mp3Reader https://github.com/naudio/NAudio/blob/d66e6821020f35429f1c26f75303290fc8aa3375/NAudio/AudioFileReader.cs#L58-L61
AudioFileReader is just a convenience top-level wrapper around some of the lower-level readers including MediaFoundationReader, WaveFileReader etc. Mp3FileReader does try to calculate total time, but does so by reading the entire file to produce a table of contents, which can be slow (and even then isn't guaranteed to be accurate). MediaFoundationReader uses whatever Media Foundation does under the hood, which I suspect is a form of guesswork, but much faster. If you prefer the calculation from Mp3FileReader, just use that class directly.
If you want to wait for playback to stop, a better approach would be to use the PlaybackStopped event from the output device.