Minimize lag when using System.Speech.Synthesis - c#

I am playing with using TTS built into .NET 4 and want the speech to happen immediately, but am instead encountering a lag between when I call Speak and when I get the audio.
I am developing a simple count-down timer that calls off the last five seconds and completion (5... 4... 3... 2... 1... Done), but when the screen updates with the new time, the TTS lags behind, getting worse for every invocation. I tried using SpeakAsync, but this only made it worse. Currently, Speak is being called outside the UI thread (in the Timer Tick event handler).
Is there a way to minimize this lag, such as pre-computing the speech and caching it or creating some kind of special TTS thread?

I somehow read past the API call I needed at least a hundred times. I was looking for SpeechSynthesizer.SetOutputToWaveStream.
MemoryStream stream = new MemoryStream();
SpeechSynthesizer synth = new SpeechSynthesizer();
synth.SetOutputToWaveStream(stream);
synth.Play(text);
stream.Position = 0;
SoundPlayer player = new SoundPlayer(stream);
player.Play();
This code will use TTS to turn text into a WAV file that is streamed into stream. You need to reset the position of the MemoryStream so that when you create a SoundPlayer from it, it starts reading the stream from the beginning instead of the end. Once you have the SoundPlayer initialized, you can save it somewhere so you can play it later instantly instead of having to wait for the TTS to initialize and play the sound.

Related

Need for thread.sleep() when playing a sound array using DirectSoundOut

dso = new DirectSoundOut(Guid.Parse(AudioOutDevice));
var ms = new MemoryStream(soundArray.ToArray()))
{
IWaveProvider provider = new RawSourceWaveStream(ms, new WaveFormat());
dso.Init(provider);
dso.Play();
Thread.Sleep(3000);
}
I am able to play sound array through desired output device using the above code, and i am unable to hear the sound if there is thread.sleep. But I am unable to understand the reason for using thread.sleep. Can any one let me know the reason for thread.sleep()
The call to Play is not blocking. It simply starts playback. So you must keep dso alive until playback ends or you have stopped it manually.
You can use code like this if you want to block yourself (obviously only use this if your audio isn't infinitely long)
dso.Play();
while (dso.PlaybackState == PlaybackState.Playing)
{
Thread.Sleep(500);
}
dso.Dispose();

Microphone stops providing data with NAudio

I'm using WaveInEvent of NAudio to record microphone data. It works fine for a while, but after a few times, it stops providing input data- the DataAvailable callback is never called with new data.
I have tried creating a new WaveInEvent each time, but this did not resolve the problem. I've also tried using the WASAPI input, which always called DataAvailable - with zero bytes of data.
How can I record audio from the microphone reliably with NAudio?
Currently, my code looks like this:
StartRecording() {
microphone = new WaveInEvent();
microphone.DeviceNumber = 0;
microphone.WaveFormat = outformat;
microphone.BufferMilliseconds = 50;
microphone.DataAvailable += (_, recArgs) =>
{
session.OnAudioData(recArgs.Buffer, recArgs.BytesRecorded);
};
microphone.StartRecording();
}
StopRecording() {
if (microphone != null)
{
microphone.StopRecording();
microphone.Dispose();
microphone = null;
}
}
There's no other naudio code in the project except using WaveFormat to describe wave formats.
NAudio throws an access violation exception trying to call WaveInBuffer.Reuse() from a threadpool worker. I'm not sure why this doesn't do something more serious than just drop audio data.
For the condition where I did not recreate the WaveInEvent, I get an MmException instead- invalid handle calling waveInPrepareHeader, in the same place.
Frankly, the fact that I get different results heavily implies that NAudio is doing some funky shit it shouldn't to share state between instances, and looking at the source on Codeplex, I'm not really sure WTF is going on.
It seems that the drivers for the USB microphone do not behave correctly. When the buffer is sent to the user through the WIM_DATA message, it is full. However when waveInUnprepareHeader is called, it's still in the queue, even though it was literally just passed as full. So I think that the drivers for the microphone are ultimately to blame.
I've been looking more closely at the microphone and it seems that this particular unit is actually known to have been damaged.

Send mixed sound from WCF using callbacks

I would like transfer mixed sound from WCF server to all connected clients. Using WCF service callbacks for this. Sound is mixed using naudio library.
Here is little example of server-side (WCF method):
MixingSampleProvider _mixer = new MixingSampleProvider(sound32.WaveFormat);
SampleToWaveProvider _sampleToWave = new SampleToWaveProvider(_mixer);
// service method
byte[] buffer = new byte[1000];
do{
_sampleToWave.Read(buffer, 0, 1000);
client.Callback.SendBuffer(buffer);
} while (_isPlaying)
and client-side:
BufferedWaveProvider _bufferedWave = new BufferedWaveProvider(sound32.WaveFormat);
// DirectSoundOut _output = new DirectSoundOut();
WaveOut _output = new WaveOut();
_output.Init(_bufferedWave);
// callback event method
if (_output.PlaybackState != PlaybackState.Playing)
_bufferedWave.AddSamples(buffer, 0, 1000);
// now in timer_tick event method
// if(_bufferedWave.BufferedDuration.TotalSeconds > 0.5)
// _output.Play();
// else
// _output.Pause();
I'm new in this, so I have a few questions.
Is this idea a good one? Is there simpler option to handle this?
[EDIT_1] I created test app with local two methods, which should simulate this and I found, that _bufferedWave.BufferedBytes are not clearing when is buffered sound playing (and it will overflow immediately). Can somebody tell me, why?
[EDIT_1] Changed type of _output field from DirectSoundOut to WaveOut and it's helpful.
Second change I did was, that I added DispatcherTimer to handle when is buffered duration greater than 0.5 (according naudio MP3Streaming example).
Now, I'm fighting with buffer time. I can hear sound only for time in _timer_Tick event method:
_bufferedWave.BufferedDuration.TotalSeconds > XX // this XX is time I can hear sound
Any ideas or opinions?
I'm not sure that this will work the way you hope. WCF is TCP based and TCP is not designed for broadcasting audio video or anything that requires speed (like games) due to it's constant checking of packets.
I have previously used naudio to transmit sound over a network to a listener client but for it to work you will need to use UDP.
If you want this to work over the internet then you will need to look into UDP hole-punching.
Also before you transmit your audio you should compress it from 16bit to 8bit and then back to 16bit upon receiving it using something like an ALaw or MuLaw Decoder/Encoder.

How do I read a file in parts with NAudio?

I am fairly new to C# and Mark's library NAudio. So I've tried learning by myself and I've come up with an basic audio player. But I have a problem.
When trying to load big files in the player the app freezes for 2-10 seconds while loading the entire file (I suppose). This is my code for reading the file:
if (target.EndsWith("mp3") || target.EndsWith("Mp3") || target.EndsWith("MP3"))
{
NAudio.Wave.WaveStream pcm = NAudio.Wave.WaveFormatConversionStream.CreatePcmStream(new NAudio.Wave.Mp3FileReader(target));
stream = new NAudio.Wave.BlockAlignReductionStream(pcm);
}
All I really want is to read the file in parts. Like a buffer. Read 10 seconds from the HDD to RAM memory, then after those 10 seconds run out, read the next 10 seconds, and so on. I think this should resolve the freeze issue I have with large files.
The cause of the delay is that Mp3FileReader creates a table of contents to allow it to determine the file length and to enable quicker repositioning. You could try using MediaFoundationReader instead which would be quicker, but won't work on Windows XP.
all programs have delay to load big files. this is depended to client computer speed.
but you can use backgroundWorker in your program and show a loading animation on your application Form during the file loading.
add backgroundWorker tool on your form
use this code on open button click:
backgroundWorker_name.RunWorkerAsync();
and put your code to the DoWork event
private void backgroundWorker_name_DoWork(object sender, DoWorkEventArgs e)
{
}

Windows Phone 7 Multiple Sounds Lag

I'm making a drum machine. From what I've read, it looks like the XNA SoundEffect class runs based on a timer, which causes a noticeable lag, and stops the rhythm being smooth.
I tried to use MediaElement, 'til I found out you cannot play multiple sounds at the same time.
Are there any workarounds for this? The sounds are handled by a timer, and need to play instantly.
I've done some in-game use of the XNA SoundEffect class and not seen any lag when responding to user events - e.g. button presses - especially when the sound effect is pre-loaded from resources.
The XNA class is designed to be used for sound effects - so it should be ideal for a single drum machine hit.
If you then see problems with timing on IsLooping, then I guess you'll have to implement your own timer to trigger new instances - but my advice would be to try it first.
Hope that helps
I've been using some sound in my app and I used the code from the example given on the msdn code samples website: http://msdn.microsoft.com/en-us/library/ff431744(v=vs.92).aspx
It looks like they are updating the timer every 50ms. Also note that the SoundEffect variables (coyoteSound and birdSound) are private data members where they only load once. The event handler on the button clicks simply call SoundEffect.play().
public MainPage()
{
InitializeComponent();
LoadSound("Resources/NightAmbientCreatureOneShot_01.wav", out coyoteSound);
LoadSound("Resources/AfternoonAmbientBirdOneShot_09.wav", out birdSound);
LoadSoundInstance("Resources/NightAmbienceSimple_01.wav", out ambienceSound, out ambienceInstance);
// Set the volume a little lower than full so it becomes the background.
ambienceInstance.Volume = 0.8f;
// Turn on looping so it runs continually in the background.
ambienceInstance.IsLooped = true;
// Timer to simulate the XNA game loop (SoundEffect classes are from the XNA Framework)
DispatcherTimer XnaDispatchTimer = new DispatcherTimer();
XnaDispatchTimer.Interval = TimeSpan.FromMilliseconds(50);
// Call FrameworkDispatcher.Update to update the XNA Framework internals.
XnaDispatchTimer.Tick += delegate { try { FrameworkDispatcher.Update(); } catch { } };
// Start the DispatchTimer running.
XnaDispatchTimer.Start();
}

Categories