|
1 | 1 | using System;
|
2 | 2 | using System.Collections.Concurrent;
|
| 3 | +using System.Diagnostics; |
3 | 4 | using System.IO;
|
4 | 5 | using System.Speech.Synthesis;
|
5 | 6 | using System.Threading;
|
@@ -230,71 +231,101 @@ private void PlaySoundsThread() {
|
230 | 231 | /// <param name="sender">The twitch chat login of the user that sent the message.</param>
|
231 | 232 | /// <param name="chatMessage">The chat message to convert to TTS.</param>
|
232 | 233 | private void InitializeAndPlayTts(string sender, string chatMessage) {
|
233 |
| - // Create a microsoft TTS object and a stream for outputting its audio file to. |
234 |
| - using var synth = new SpeechSynthesizer(); |
235 |
| - using var stream = new MemoryStream(); |
236 |
| - |
237 |
| - WaveFileReader reader; |
| 234 | + string filename = Path.GetTempFileName(); |
| 235 | + string filename2 = Path.GetTempFileName(); |
| 236 | + |
238 | 237 | try {
|
239 |
| - // Setup the microsoft TTS object according to the settings. |
240 |
| - synth.SetOutputToWaveStream(stream); |
241 |
| - synth.SelectVoice(ChatConfig?.TtsVoice); |
242 |
| - synth.Volume = (int)(ChatConfig?.TtsVolume ?? 50); |
243 |
| - synth.Speak(chatMessage); |
244 |
| - |
245 |
| - // Now that we filled the stream, seek to the beginning so we can play it. |
246 |
| - stream.Seek(0, SeekOrigin.Begin); |
247 |
| - reader = new WaveFileReader(stream); |
248 |
| - } |
249 |
| - catch (Exception ex) { |
250 |
| - Console.WriteLine($"Exception initializing a new microsoft speech object: {ex}"); |
251 |
| - return; |
252 |
| - } |
| 238 | + string fileToPlay = filename; |
253 | 239 |
|
254 |
| - // This is only meant to ensure we don't play TTS over a sound alert. It can still happen but this |
255 |
| - // fixes most of the issues with very little investment. |
256 |
| - while (GlobalSoundManager.Instance.CurrentlyPlayingSound) { |
257 |
| - Thread.Sleep(100); |
258 |
| - } |
| 240 | + WaveFileReader reader; |
| 241 | + try { |
| 242 | + // Create a microsoft TTS object and a stream for outputting its audio file to. |
| 243 | + using var synth = new SpeechSynthesizer(); |
| 244 | + |
| 245 | + // Setup the microsoft TTS object according to the settings. |
| 246 | + synth.SetOutputToWaveFile(filename); |
| 247 | + synth.SelectVoice(ChatConfig?.TtsVoice); |
| 248 | + synth.Volume = (int)(ChatConfig?.TtsVolume ?? 50); |
| 249 | + synth.Speak(chatMessage); |
| 250 | + } |
| 251 | + catch (Exception ex) { |
| 252 | + Console.WriteLine($"Exception initializing a new microsoft speech object: {ex}"); |
| 253 | + return; |
| 254 | + } |
259 | 255 |
|
260 |
| - try { |
261 |
| - // Make sure we lock the objects used on multiple threads and play the file. |
262 |
| - lock (_ttsSoundOutputLock) |
263 |
| - lock (_ttsSoundOutputSignalLock) { |
264 |
| - _ttsSoundOutput = new WaveOutEvent(); |
265 |
| - _ttsSoundOutputSignal = new ManualResetEvent(false); |
266 |
| - |
267 |
| - _ttsSoundOutput.DeviceNumber = NAudioUtilities.GetOutputDeviceIndex(ChatConfig?.OutputDevice); |
268 |
| - _ttsSoundOutput.Volume = (ChatConfig?.TtsVolume ?? 50.0f) / 100.0f; |
269 |
| - |
270 |
| - _ttsSoundOutput.Init(reader); |
271 |
| - |
272 |
| - // Play is async so we will make it synchronous here so we don't have to deal with |
273 |
| - // queueing. We can improve this to remove the hack in the future. |
274 |
| - _ttsSoundOutput.PlaybackStopped += delegate { |
275 |
| - lock (_ttsSoundOutputSignalLock) { |
276 |
| - _ttsSoundOutputSignal?.Set(); |
277 |
| - } |
278 |
| - }; |
| 256 | + var startInfo = new ProcessStartInfo { |
| 257 | + FileName = @"3rdParty\soundstretch.exe", |
| 258 | + Arguments = $"\"{filename}\" \"{filename2}\" -tempo=+100 -speech", |
| 259 | + UseShellExecute = false, |
| 260 | + CreateNoWindow = true |
| 261 | + }; |
| 262 | + |
| 263 | + using var process = new Process { StartInfo = startInfo }; |
| 264 | + process.Start(); |
| 265 | + process.WaitForExit(); |
| 266 | + if (0 == process.ExitCode) { |
| 267 | + fileToPlay = filename2; |
| 268 | + } |
279 | 269 |
|
280 |
| - // Play it. |
281 |
| - _ttsSoundOutput.Play(); |
| 270 | + // This is only meant to ensure we don't play TTS over a sound alert. It can still happen but this |
| 271 | + // fixes most of the issues with very little investment. |
| 272 | + while (GlobalSoundManager.Instance.CurrentlyPlayingSound) { |
| 273 | + Thread.Sleep(100); |
282 | 274 | }
|
283 | 275 |
|
284 |
| - // Wait for the play to finish, we will get signaled. |
285 |
| - CurrentUsername = sender; |
286 |
| - ManualResetEvent? signal = _ttsSoundOutputSignal; |
287 |
| - signal?.WaitOne(); |
288 |
| - CurrentUsername = null; |
| 276 | + try { |
| 277 | + // Make sure we lock the objects used on multiple threads and play the file. |
| 278 | + lock (_ttsSoundOutputLock) |
| 279 | + lock (_ttsSoundOutputSignalLock) { |
| 280 | + _ttsSoundOutput = new WaveOutEvent(); |
| 281 | + _ttsSoundOutputSignal = new ManualResetEvent(false); |
| 282 | + |
| 283 | + _ttsSoundOutput.DeviceNumber = NAudioUtilities.GetOutputDeviceIndex(ChatConfig?.OutputDevice); |
| 284 | + _ttsSoundOutput.Volume = (ChatConfig?.TtsVolume ?? 50.0f) / 100.0f; |
| 285 | + |
| 286 | + reader = new WaveFileReader(fileToPlay); |
| 287 | + _ttsSoundOutput.Init(reader); |
| 288 | + |
| 289 | + // Play is async so we will make it synchronous here so we don't have to deal with |
| 290 | + // queueing. We can improve this to remove the hack in the future. |
| 291 | + _ttsSoundOutput.PlaybackStopped += delegate { |
| 292 | + lock (_ttsSoundOutputSignalLock) { |
| 293 | + _ttsSoundOutputSignal?.Set(); |
| 294 | + } |
| 295 | + }; |
| 296 | + |
| 297 | + // Play it. |
| 298 | + _ttsSoundOutput.Play(); |
| 299 | + } |
| 300 | + |
| 301 | + // Wait for the play to finish, we will get signaled. |
| 302 | + CurrentUsername = sender; |
| 303 | + ManualResetEvent? signal = _ttsSoundOutputSignal; |
| 304 | + signal?.WaitOne(); |
| 305 | + CurrentUsername = null; |
| 306 | + } |
| 307 | + finally { |
| 308 | + // Finally dispose of everything safely in the lock. |
| 309 | + lock (_ttsSoundOutputLock) |
| 310 | + lock (_ttsSoundOutputSignalLock) { |
| 311 | + _ttsSoundOutput?.Dispose(); |
| 312 | + _ttsSoundOutput = null; |
| 313 | + _ttsSoundOutputSignal?.Dispose(); |
| 314 | + _ttsSoundOutputSignal = null; |
| 315 | + } |
| 316 | + } |
289 | 317 | }
|
290 | 318 | finally {
|
291 |
| - // Finally dispose of everything safely in the lock. |
292 |
| - lock (_ttsSoundOutputLock) |
293 |
| - lock (_ttsSoundOutputSignalLock) { |
294 |
| - _ttsSoundOutput?.Dispose(); |
295 |
| - _ttsSoundOutput = null; |
296 |
| - _ttsSoundOutputSignal?.Dispose(); |
297 |
| - _ttsSoundOutputSignal = null; |
| 319 | + string[] filesToDelete = { filename, filename2 }; |
| 320 | + foreach (string file in filesToDelete) { |
| 321 | + try { |
| 322 | + if (File.Exists(file)) { |
| 323 | + File.Delete(file); |
| 324 | + } |
| 325 | + } |
| 326 | + catch { |
| 327 | + // Do nothing, just try to clean up the best you can. |
| 328 | + } |
298 | 329 | }
|
299 | 330 | }
|
300 | 331 | }
|
|
0 commit comments