static string GetSpeakParametersSSMLProsody(string text, SpeakSettings settings) { var volume = "default"; var pitch = "default"; var rate = "default"; // Look for the specified language, otherwise the default voice var locale = settings?.Locale.Language ?? SpeechSynthesizer.DefaultVoice.Language; if (settings?.Volume.HasValue ?? false) { volume = (settings.Volume.Value * 100f).ToString(CultureInfo.InvariantCulture); } if (settings?.Pitch.HasValue ?? false) { pitch = ProsodyPitch(settings.Pitch); } // SSML generation var ssml = new StringBuilder(); ssml.AppendLine($"<speak version='1.0' xmlns='http://www.w3.org/2001/10/synthesis' xml:lang='{locale}'>"); ssml.AppendLine($"<prosody pitch='{pitch}' rate='{rate}' volume='{volume}'>{text}</prosody> "); ssml.AppendLine($"</speak>"); return(ssml.ToString()); }
private static AVSpeechUtterance GetSpeechUtterance(string text, SpeakSettings settings) { var speechUtterance = new AVSpeechUtterance(text); if (settings != null) { // null voice if fine - it is the default speechUtterance.Voice = AVSpeechSynthesisVoice.FromLanguage(settings.Locale?.Language) ?? AVSpeechSynthesisVoice.FromLanguage(AVSpeechSynthesisVoice.CurrentLanguageCode); // the platform has a range of 0.5 - 2.0 // anything lower than 0.5 is set to 0.5 if (settings.Pitch.HasValue) { speechUtterance.PitchMultiplier = settings.Pitch.Value; } if (settings.Volume.HasValue) { speechUtterance.Volume = settings.Volume.Value; } } return(speechUtterance); }
internal static async Task PlatformSpeakAsync(string text, SpeakSettings settings, CancellationToken cancelToken = default) { var tcsUtterance = new TaskCompletionSource <bool>(); try { var player = new MediaPlayer(); var ssml = GetSpeakParametersSSMLProsody(text, settings); var speechSynthesizer = new SpeechSynthesizer(); if (!string.IsNullOrWhiteSpace(settings?.Locale.Id)) { var voiceInfo = SpeechSynthesizer.AllVoices.FirstOrDefault(v => v.Id == settings.Locale.Id) ?? SpeechSynthesizer.DefaultVoice; speechSynthesizer.Voice = voiceInfo; } var stream = await speechSynthesizer.SynthesizeSsmlToStreamAsync(ssml); player.MediaEnded += PlayerMediaEnded; player.Source = MediaSource.CreateFromStream(stream, stream.ContentType); player.Play(); void OnCancel() { player.PlaybackSession.PlaybackRate = 0; tcsUtterance.TrySetResult(true); } using (cancelToken.Register(OnCancel)) { await tcsUtterance.Task; } player.MediaEnded -= PlayerMediaEnded; player.Dispose(); void PlayerMediaEnded(MediaPlayer sender, object args) { tcsUtterance.TrySetResult(true); } } catch (Exception ex) { Debug.WriteLine("Unable to playback stream: " + ex); tcsUtterance.TrySetException(ex); } }
internal static Task PlatformSpeakAsync(string text, SpeakSettings settings, CancellationToken cancelToken = default) { var textToSpeech = GetTextToSpeech(); if (textToSpeech == null) { throw new PlatformNotSupportedException("Unable to start text-to-speech engine, not supported on device."); } var max = maxSpeechInputLengthDefault; if (Platform.HasApiLevel(BuildVersionCodes.JellyBeanMr2)) { max = AndroidTextToSpeech.MaxSpeechInputLength; } return(textToSpeech.SpeakAsync(text, max, settings, cancelToken)); }
public static async Task SpeakAsync(string text, SpeakSettings settings, CancellationToken cancelToken = default) { if (string.IsNullOrEmpty(text)) { throw new ArgumentNullException(nameof(text), "Text cannot be null or empty string"); } if (settings?.Volume.HasValue ?? false) { if (settings.Volume.Value < VolumeMin || settings.Volume.Value > VolumeMax) { throw new ArgumentOutOfRangeException($"Volume must be >= {VolumeMin} and <= {VolumeMax}"); } } if (settings?.Pitch.HasValue ?? false) { if (settings.Pitch.Value < PitchMin || settings.Pitch.Value > PitchMax) { throw new ArgumentOutOfRangeException($"Pitch must be >= {PitchMin} and <= {PitchMin}"); } } if (semaphore == null) { semaphore = new SemaphoreSlim(1, 1); } try { await semaphore.WaitAsync(cancelToken); await PlatformSpeakAsync(text, settings, cancelToken); } finally { if (semaphore.CurrentCount == 0) { semaphore.Release(); } } }
public async Task SpeakAsync(string text, int max, SpeakSettings settings, CancellationToken cancelToken) { await Initialize(); // Wait for any previous calls to finish up if (tcsUtterances?.Task != null) { await tcsUtterances.Task; } if (cancelToken != null) { cancelToken.Register(() => { try { tts?.Stop(); tcsUtterances?.TrySetResult(true); } catch { } }); } if (settings?.Locale.Language != null) { JavaLocale locale = null; if (!string.IsNullOrWhiteSpace(settings?.Locale.Country)) { locale = new JavaLocale(settings.Locale.Language, settings.Locale.Country); } else { locale = new JavaLocale(settings.Locale.Language); } tts.SetLanguage(locale); } else { SetDefaultLanguage(); } if (settings?.Pitch.HasValue ?? false) { tts.SetPitch(settings.Pitch.Value); } else { tts.SetPitch(TextToSpeech.PitchDefault); } var parts = text.SplitSpeak(max); numExpectedUtterances = parts.Count; tcsUtterances = new TaskCompletionSource <bool>(); var guid = Guid.NewGuid().ToString(); for (var i = 0; i < parts.Count && !cancelToken.IsCancellationRequested; i++) { // We require the utterance id to be set if we want the completed listener to fire var map = new Dictionary <string, string> { { AndroidTextToSpeech.Engine.KeyParamUtteranceId, $"{guid}.{i}" } }; if (settings != null && settings.Volume.HasValue) { map.Add(AndroidTextToSpeech.Engine.KeyParamVolume, settings.Volume.Value.ToString(CultureInfo.InvariantCulture)); } // We use an obsolete overload here so it works on older API levels at runtime // Flush on first entry and add (to not flush our own previous) subsequent entries #pragma warning disable CS0618 tts.Speak(parts[i], i == 0 ? QueueMode.Flush : QueueMode.Add, map); #pragma warning restore CS0618 } await tcsUtterances.Task; }
internal static Task PlatformSpeakAsync(string text, SpeakSettings settings, CancellationToken cancelToken = default) => throw new NotImplementedInReferenceAssemblyException();
internal static Task PlatformSpeakAsync(string text, SpeakSettings settings, CancellationToken cancelToken = default) { var speechUtterance = GetSpeechUtterance(text, settings); return(SpeakUtterance(speechUtterance, cancelToken)); }