While testing one of my apps I realized that long bits of speech were interfering with other sound effects in the app. Since they (unfortunately) can’t play simultaneously, I needed a way to cancel the speech sound if it exceeded three seconds. And, yes, this has a negative side-effect of abruptly cutting off mid-sentence. However, that’s mitigated by the same text appearing on screen.
I also need the Task to be cancelled externally if, for example, the user switches to another page or resets the app to the beginning. The following code is able to do both these things:
public async Task TextToSpeech(string text, CancellationToken externalCancellationToken) { try { var internalCancellationTokenSource = new CancellationTokenSource(); var internalCancellationToken = internalCancellationTokenSource.Token; using ( var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(internalCancellationToken, externalCancellationToken)) { linkedCts.CancelAfter(3000); var alreadyPlayed = false; while (!linkedCts.IsCancellationRequested) { if (!alreadyPlayed) { _speechSynthesizer.SpeakTextAsync(text); alreadyPlayed = true; } if (linkedCts.Token.IsCancellationRequested) { _speechSynthesizer.CancelAll(); } } } } catch (Exception err) { Debug.WriteLine(err.Message); } }
There are a couple funky things to note: You’ll get a warning about the async modifier on the Task. Remove it and the app won’t compile because it will expect a return type. Why not use a void method? It wouldn’t be cancellable. The other is the use of the SpeechSynthesizer.CancelAll() method. You can set SpeechSynthesizer.SpeakTextAsync() to an IAsyncAction, e.g. “var task = _speechSynthesizer.SpeakTextAsync(text);” and do task.Cancel(). But this didn’t seem to have any effect.
So use the CancelAll method and be sure to wrap it in a try-catch block to head off the error it will throw.