Reputation: 829
Im creating an windows phone 8.1 app. When app is started, app prompts user to call certain telephone number. It does this with voice. After instructions are told by app, phone call dialog is showed. This is the code:
public MainPage()
{
this.InitializeComponent();
this.NavigationCacheMode = NavigationCacheMode.Required;
StartSpeaking("Please call number !");
CallDialog();
}
private async void StartSpeaking(string text)
{
MediaElement mediaElement = this.media;
// The object for controlling the speech synthesis engine (voice).
var synth = new Windows.Media.SpeechSynthesis.SpeechSynthesizer();
// Generate the audio stream from plain text.
SpeechSynthesisStream stream = await synth.SynthesizeTextToStreamAsync(text);
// Send the stream to the media object.
mediaElement.SetSource(stream, stream.ContentType);
mediaElement.Play();
}
private async void CallDialog()
{
Windows.ApplicationModel.Calls.PhoneCallManager.ShowPhoneCallUI("123", "123");
var messageDialog = new Windows.UI.Popups.MessageDialog("call ended", "Text spoken");
await messageDialog.ShowAsync();
}
The problem is that I must use synth.SynthesizeTextToStreamAsync method which is async method so call dialog shows up before text is said. How can I avoid that?
Upvotes: 1
Views: 2138
Reputation: 456887
async Task
methods should be embraced; it is only async void
methods that should be avoided (they should only be used as event handlers). I have an MSDN article that describes a few reasons to avoid async void
.
In your case, you can use an async void
event handler (e.g., for the Loaded
event), and make your methods async Task
instead of async void
and await
them:
async void MainPage_Loaded(..)
{
await StartSpeakingAsync("Please call number !");
await CallDialogAsync();
}
private async Task StartSpeakingAsync(string text);
private async Task CallDialogAsync();
Update
To (asynchronously) wait for the media to play, you need to hook into an event that notifies you it's complete. MediaEnded
looks like a good choice. Something like this should work:
public static Task PlayToEndAsync(this MediaElement @this)
{
var tcs = new TaskCompletionSource<object>();
RoutedEventHandler subscription = null;
subscription = (_, __) =>
{
@this.MediaEnded -= subscription;
tcs.TrySetResult(null);
};
@this.MediaEnded += subscription;
@this.Play();
return tcs.Task;
}
That method extends the MediaElement
with an async
-ready PlayToEndAsync
method, which you can use like this:
private async Task SpeakAsync(string text)
{
MediaElement mediaElement = this.media;
var synth = new Windows.Media.SpeechSynthesis.SpeechSynthesizer();
SpeechSynthesisStream stream = await synth.SynthesizeTextToStreamAsync(text);
mediaElement.SetSource(stream, stream.ContentType);
await mediaElement.PlayToEndAsync();
}
Upvotes: 2