5

I would like to use NAudio together with SpeechSynthesizer, but I cannot make it work without writing a .wav file to disk first.

I cannot figure out why, I tried both RAW wave data and wave with headers, see the following examples...

Example 1 - this works, but saves a .wav file

  using (var synth = new SpeechSynthesizer())
  {
    synth.SetOutputToWaveFile(@".\Test.wav");
    synth.Speak("This is sample text-to-speech output.");
    synth.SetOutputToNull();

    var reader = new WaveFileReader(@".\Test.wav");
    var waveOut = new WaveOut();
    waveOut.Init(reader);
    waveOut.Play();
  }

Example 2 - this also works, but still uses a file

  using (var synth = new SpeechSynthesizer())
  using (var stream = new MemoryStream())
  {
    synth.SetOutputToWaveStream(stream);
    synth.Speak("This is sample text-to-speech output.");

    using (var fileStream = File.Create(@".\Test.wav"))
    {
      stream.Seek(0, SeekOrigin.Begin);
      stream.CopyTo(fileStream);
    }

    var reader = new WaveFileReader(@".\Test.wav");
    var waveOut = new WaveOut();
    waveOut.Init(reader);
    waveOut.Play();
  }

Example 3 - this don´t work, it just play a fraction of a second and stops

It´s on purpose that I use SetOutputToWaveStream to keep the RIFF header, to avoid setting wave format.

    using (var synth = new SpeechSynthesizer())
    using (var stream = new MemoryStream())
    {
      synth.SetOutputToWaveStream(stream);
      synth.Speak("This is sample text-to-speech output.");

      stream.Seek(0, SeekOrigin.Begin);
      var reader = new WaveFileReader(stream);
      var waveOut = new WaveOut();
      waveOut.Init(reader);
      waveOut.Play();
    }

Example 4 - same result as example 3

This uses raw data and is a bit bulky.

  using (var synth = new SpeechSynthesizer())
  using (var stream = new MemoryStream())
  {
    synth.SetOutputToAudioStream(stream, new SpeechAudioFormatInfo(44100, AudioBitsPerSample.Sixteen, AudioChannel.Mono));
    synth.Speak("This is sample text-to-speech output.");

    stream.Seek(0, SeekOrigin.Begin);
    IWaveProvider provider = new RawSourceWaveStream(stream, new WaveFormat(44100, 16, 1));
    var waveOut = new WaveOut();
    waveOut.Init(provider);
    waveOut.Play();
  }

I also tried using WasapiOut, but that was even worse.

E. Malmqvist
  • 310
  • 2
  • 11

2 Answers2

2

You are just about there with Example 3. Call Flush on your memory stream before repositioning to the start. Also, Play only starts playing, so you need to either wait for playback to finish or move your WaveOut instance to be outside the using block. Otherwise it's just going to get garbage collected before it can get very far into playback.

Mark Heath
  • 48,273
  • 29
  • 137
  • 194
  • Thank you, was starring myself blind on it, It was off cause because the garbage collection. I thought that Flush() on a memory stream was redundant since it´s already written directly to RAM ??. [link](https://msdn.microsoft.com/en-us/library/vstudio/system.io.memorystream.flush(v=vs.100).aspx) – E. Malmqvist Jul 02 '15 at 09:00
  • 1
    yeah probably the flush isn't necessary, it's just what I have in my own implementation so I thought I'd mention it anyway – Mark Heath Jul 02 '15 at 09:50
  • I also had to put the SpeechSynthesizer in its own thread, otherwise I would still have the problem only getting a fragment of the output. See also this [answer](http://stackoverflow.com/a/5599972/3131828) for more information. – MagicMau Oct 22 '15 at 09:57
  • no need for another thread, just wait until playback is finished. You're disposing the player too early. – Mark Heath Oct 22 '15 at 20:18
0

this code works for me, if can help (for solution 3).

        IWaveProvider provider = null;
        var stream = new MemoryStream();
        using (var synth = new SpeechSynthesizer())
        {

            synth.SetOutputToAudioStream(stream,
                new SpeechAudioFormatInfo(44100, AudioBitsPerSample.Sixteen, AudioChannel.Mono));
            synth.Speak("This is sample text-to-speech output.");

            stream.Seek(0, SeekOrigin.Begin);
            provider = new RawSourceWaveStream(stream, new WaveFormat(44100, 16, 1));

        }
        var waveOut = new WaveOut();
        waveOut.Init(provider);
        waveOut.Play();