8

I have some simple code generating a wave file using TTS and then playing it:

public void TestSpeech()
{
    SpeechSynthesizer synth = new SpeechSynthesizer();
    using (MemoryStream stream = new MemoryStream())
    {
        synth.SetOutputToWaveStream(stream);
        synth.Speak("Hello world");
        stream.Seek(0, SeekOrigin.Begin);
        IWaveSource source = new WaveFileReader(stream);
        EventWaitHandle waitHandle = new EventWaitHandle(false, EventResetMode.AutoReset);
        var soundOut = new WasapiOut();
        soundOut.Initialize(source);
        soundOut.Stopped += (s, e) => waitHandle.Set();
        soundOut.Play();
        waitHandle.WaitOne();
        soundOut.Dispose();
        source.Dispose();
    }
}

Everything is working fine, but I want to know before I start to play the wave file how long it will go on for. Is there some way of calculating this, or is it available somewhere?

If it is available somewhere, how is it calculated? I assume that it's related to the amount of data in the stream, but how?

Jonas W
  • 3,200
  • 1
  • 31
  • 44
  • similar question was already answered here http://stackoverflow.com/questions/82319/how-can-i-determine-the-length-of-a-wav-file-in-c – Maxim Fleitling Feb 09 '16 at 19:34
  • There will be a way, nothing is impossible. This one has me hooked. I'll post what I come up with, if I come up with anything - on that event I will walk into the distance with my tail between my legs. – Gabe Feb 09 '16 at 19:36
  • As posted in the linked article there is no _reliable_ way of doing it prior, however Genti's solution is a good way of achieving your results. See Comments of Genti's description for length calculation details. – Gabe Feb 09 '16 at 20:29

3 Answers3

7

You can use CSCore or NAudio:

CSCore (extracted from this sample, current playback position and total duration are used here):

using System;
using CSCore;
using CSCore.Codecs.WAV;

IWaveSource wavSource = new WaveFileReader(stream);
TimeSpan totalTime = wavSource.GetLength();

NAudio:

using System;
using NAudio.Wave;

using (var wfr = new WaveFileReader(stream))
{
    TimeSpan totalTime = wfr.TotalTime;
}

Also see the MSDN docs for TimeSpan.

The duration is calculated from the total length of the WAVE data (which can be an estimate for compressed files) and the average bytes per second (as per the NAudio source in property TotalTime):

totalTimeInSeconds = LengthInBytes / AverageBytesPerSecond;
Florian
  • 5,918
  • 3
  • 47
  • 86
Genti Saliu
  • 2,643
  • 4
  • 23
  • 43
  • He has access to the `stream` though. The synthesizer writes to the `stream`. – Genti Saliu Feb 09 '16 at 20:13
  • 1
    I deleted my comment directly after posting it as I saw the error of my logic as I clicked "add". Sorry my friend. – Gabe Feb 09 '16 at 20:17
  • Thanks for this. "The duration is calculated from the total length of the WAVE data (which can be an estimate for compressed files) and the average bytes per seconds" Where do I find the average bytes per second? This is using CSCore. –  Feb 09 '16 at 20:42
  • It is in the WAV file header, but I'll take a look at how you can do that in CSCore. – Genti Saliu Feb 09 '16 at 20:43
  • `AverageBtyesPerSecond` for NAudio [HERE](https://daisy-trac.cvsdude.com/urakawa-sdk/browser/trunk/csharp/audio/NAudio/Wave/WaveFormats/WaveFormat.cs?rev=1346) This is what I've found so far, based off SampleRate and Length Just follow the source code back to where each of those are defined and you'll find your answer pretty quickly, could even peek at the definitions in VS, would be faster – Gabe Feb 09 '16 at 20:45
  • 1
    In CSCore you can obtain it with `wavSource.WaveFormat.BytesPerSecond`. – Genti Saliu Feb 09 '16 at 20:46
  • After running it with a few samples this gives a close enough answer to use. Thanks! –  Feb 09 '16 at 20:50
0
using CSCore;

IWaveSource waveSource = new WaveFileReader(stream);
TimeSpan totalTime = waveSource.GetLength( ); //get length returns a timespan
Florian
  • 5,918
  • 3
  • 47
  • 86
0

In case someone's looking for a workaround, I've handeled it like this: (pardon my first StackOverflow comment)

  1. I created bool mouseScrewsAround = false
  2. timer event, that changes trackBar position while playing, fires only if !mouseScrewsAround
  3. trackBar_MouseDown -> mouseScrewsAround = true
  4. trackBar_MouseUp -> change track position, then mouseScrewsAround = false