NAudio icon indicating copy to clipboard operation
NAudio copied to clipboard

Incorrect AudioFileReader.TotalTime

Open o-alexandre-felipe opened this issue 3 years ago • 1 comments
trafficstars

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

o-alexandre-felipe avatar Oct 27 '22 12:10 o-alexandre-felipe

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.

markheath avatar Oct 27 '22 15:10 markheath