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));
        }
Esempio n. 5
0
        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;
        }
Esempio n. 7
0
 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));
        }