diff --git a/src/Essentials/samples/Samples/View/TextToSpeechPage.xaml b/src/Essentials/samples/Samples/View/TextToSpeechPage.xaml
index 774c0a1e53f2..9d054ae2b5e6 100644
--- a/src/Essentials/samples/Samples/View/TextToSpeechPage.xaml
+++ b/src/Essentials/samples/Samples/View/TextToSpeechPage.xaml
@@ -33,6 +33,10 @@
+
+
+
+
diff --git a/src/Essentials/samples/Samples/ViewModel/TextToSpeechViewModel.cs b/src/Essentials/samples/Samples/ViewModel/TextToSpeechViewModel.cs
index f84cd8fac83c..828a2a7692ea 100644
--- a/src/Essentials/samples/Samples/ViewModel/TextToSpeechViewModel.cs
+++ b/src/Essentials/samples/Samples/ViewModel/TextToSpeechViewModel.cs
@@ -16,6 +16,7 @@ public class TextToSpeechViewModel : BaseViewModel
bool advancedOptions;
float volume;
float pitch;
+ float rate;
string locale = "Default";
Locale selectedLocale;
@@ -30,6 +31,7 @@ public TextToSpeechViewModel()
AdvancedOptions = false;
Volume = 1.0f;
Pitch = 1.0f;
+ Rate = 1.0f;
}
public override void OnDisappearing()
@@ -55,7 +57,8 @@ void OnSpeak(bool multiple)
{
Volume = Volume,
Pitch = Pitch,
- Locale = selectedLocale
+ Locale = selectedLocale,
+ Rate=Rate
};
}
@@ -141,6 +144,12 @@ public float Pitch
set => SetProperty(ref pitch, value);
}
+ public float Rate
+ {
+ get => rate;
+ set => SetProperty(ref rate, value);
+ }
+
public string Locale
{
get => locale;
diff --git a/src/Essentials/src/PublicAPI/net-android/PublicAPI.Shipped.txt b/src/Essentials/src/PublicAPI/net-android/PublicAPI.Shipped.txt
index 7fa8c9d36d91..73154ff3c3af 100644
--- a/src/Essentials/src/PublicAPI/net-android/PublicAPI.Shipped.txt
+++ b/src/Essentials/src/PublicAPI/net-android/PublicAPI.Shipped.txt
@@ -845,6 +845,8 @@ Microsoft.Maui.Media.SpeechOptions.Locale.get -> Microsoft.Maui.Media.Locale?
Microsoft.Maui.Media.SpeechOptions.Locale.set -> void
Microsoft.Maui.Media.SpeechOptions.Pitch.get -> float?
Microsoft.Maui.Media.SpeechOptions.Pitch.set -> void
+Microsoft.Maui.Media.SpeechOptions.Rate.get -> float?
+Microsoft.Maui.Media.SpeechOptions.Rate.set -> void
Microsoft.Maui.Media.SpeechOptions.SpeechOptions() -> void
Microsoft.Maui.Media.SpeechOptions.Volume.get -> float?
Microsoft.Maui.Media.SpeechOptions.Volume.set -> void
diff --git a/src/Essentials/src/PublicAPI/net-ios/PublicAPI.Shipped.txt b/src/Essentials/src/PublicAPI/net-ios/PublicAPI.Shipped.txt
index 00d9cf0c126d..d1e37c1f6d79 100644
--- a/src/Essentials/src/PublicAPI/net-ios/PublicAPI.Shipped.txt
+++ b/src/Essentials/src/PublicAPI/net-ios/PublicAPI.Shipped.txt
@@ -839,6 +839,8 @@ Microsoft.Maui.Media.SpeechOptions.Locale.get -> Microsoft.Maui.Media.Locale?
Microsoft.Maui.Media.SpeechOptions.Locale.set -> void
Microsoft.Maui.Media.SpeechOptions.Pitch.get -> float?
Microsoft.Maui.Media.SpeechOptions.Pitch.set -> void
+Microsoft.Maui.Media.SpeechOptions.Rate.get -> float?
+Microsoft.Maui.Media.SpeechOptions.Rate.set -> void
Microsoft.Maui.Media.SpeechOptions.SpeechOptions() -> void
Microsoft.Maui.Media.SpeechOptions.Volume.get -> float?
Microsoft.Maui.Media.SpeechOptions.Volume.set -> void
diff --git a/src/Essentials/src/PublicAPI/net-maccatalyst/PublicAPI.Shipped.txt b/src/Essentials/src/PublicAPI/net-maccatalyst/PublicAPI.Shipped.txt
index 00d9cf0c126d..d1e37c1f6d79 100644
--- a/src/Essentials/src/PublicAPI/net-maccatalyst/PublicAPI.Shipped.txt
+++ b/src/Essentials/src/PublicAPI/net-maccatalyst/PublicAPI.Shipped.txt
@@ -839,6 +839,8 @@ Microsoft.Maui.Media.SpeechOptions.Locale.get -> Microsoft.Maui.Media.Locale?
Microsoft.Maui.Media.SpeechOptions.Locale.set -> void
Microsoft.Maui.Media.SpeechOptions.Pitch.get -> float?
Microsoft.Maui.Media.SpeechOptions.Pitch.set -> void
+Microsoft.Maui.Media.SpeechOptions.Rate.get -> float?
+Microsoft.Maui.Media.SpeechOptions.Rate.set -> void
Microsoft.Maui.Media.SpeechOptions.SpeechOptions() -> void
Microsoft.Maui.Media.SpeechOptions.Volume.get -> float?
Microsoft.Maui.Media.SpeechOptions.Volume.set -> void
diff --git a/src/Essentials/src/PublicAPI/net-tizen/PublicAPI.Shipped.txt b/src/Essentials/src/PublicAPI/net-tizen/PublicAPI.Shipped.txt
index 9e3706cddfe9..e33a88e269c1 100644
--- a/src/Essentials/src/PublicAPI/net-tizen/PublicAPI.Shipped.txt
+++ b/src/Essentials/src/PublicAPI/net-tizen/PublicAPI.Shipped.txt
@@ -800,6 +800,8 @@ Microsoft.Maui.Media.SpeechOptions.Locale.get -> Microsoft.Maui.Media.Locale?
Microsoft.Maui.Media.SpeechOptions.Locale.set -> void
Microsoft.Maui.Media.SpeechOptions.Pitch.get -> float?
Microsoft.Maui.Media.SpeechOptions.Pitch.set -> void
+Microsoft.Maui.Media.SpeechOptions.Rate.get -> float?
+Microsoft.Maui.Media.SpeechOptions.Rate.set -> void
Microsoft.Maui.Media.SpeechOptions.SpeechOptions() -> void
Microsoft.Maui.Media.SpeechOptions.Volume.get -> float?
Microsoft.Maui.Media.SpeechOptions.Volume.set -> void
diff --git a/src/Essentials/src/PublicAPI/net-windows/PublicAPI.Shipped.txt b/src/Essentials/src/PublicAPI/net-windows/PublicAPI.Shipped.txt
index 0e21f858945f..283f84efcb39 100644
--- a/src/Essentials/src/PublicAPI/net-windows/PublicAPI.Shipped.txt
+++ b/src/Essentials/src/PublicAPI/net-windows/PublicAPI.Shipped.txt
@@ -799,6 +799,8 @@ Microsoft.Maui.Media.SpeechOptions.Locale.get -> Microsoft.Maui.Media.Locale?
Microsoft.Maui.Media.SpeechOptions.Locale.set -> void
Microsoft.Maui.Media.SpeechOptions.Pitch.get -> float?
Microsoft.Maui.Media.SpeechOptions.Pitch.set -> void
+Microsoft.Maui.Media.SpeechOptions.Rate.get -> float?
+Microsoft.Maui.Media.SpeechOptions.Rate.set -> void
Microsoft.Maui.Media.SpeechOptions.SpeechOptions() -> void
Microsoft.Maui.Media.SpeechOptions.Volume.get -> float?
Microsoft.Maui.Media.SpeechOptions.Volume.set -> void
diff --git a/src/Essentials/src/PublicAPI/net/PublicAPI.Shipped.txt b/src/Essentials/src/PublicAPI/net/PublicAPI.Shipped.txt
index 53ca425e09e2..49dcd4165c6b 100644
--- a/src/Essentials/src/PublicAPI/net/PublicAPI.Shipped.txt
+++ b/src/Essentials/src/PublicAPI/net/PublicAPI.Shipped.txt
@@ -781,6 +781,8 @@ Microsoft.Maui.Media.SpeechOptions.Locale.get -> Microsoft.Maui.Media.Locale?
Microsoft.Maui.Media.SpeechOptions.Locale.set -> void
Microsoft.Maui.Media.SpeechOptions.Pitch.get -> float?
Microsoft.Maui.Media.SpeechOptions.Pitch.set -> void
+Microsoft.Maui.Media.SpeechOptions.Rate.get -> float?
+Microsoft.Maui.Media.SpeechOptions.Rate.set -> void
Microsoft.Maui.Media.SpeechOptions.SpeechOptions() -> void
Microsoft.Maui.Media.SpeechOptions.Volume.get -> float?
Microsoft.Maui.Media.SpeechOptions.Volume.set -> void
diff --git a/src/Essentials/src/PublicAPI/netstandard/PublicAPI.Shipped.txt b/src/Essentials/src/PublicAPI/netstandard/PublicAPI.Shipped.txt
index 53ca425e09e2..49dcd4165c6b 100644
--- a/src/Essentials/src/PublicAPI/netstandard/PublicAPI.Shipped.txt
+++ b/src/Essentials/src/PublicAPI/netstandard/PublicAPI.Shipped.txt
@@ -781,6 +781,8 @@ Microsoft.Maui.Media.SpeechOptions.Locale.get -> Microsoft.Maui.Media.Locale?
Microsoft.Maui.Media.SpeechOptions.Locale.set -> void
Microsoft.Maui.Media.SpeechOptions.Pitch.get -> float?
Microsoft.Maui.Media.SpeechOptions.Pitch.set -> void
+Microsoft.Maui.Media.SpeechOptions.Rate.get -> float?
+Microsoft.Maui.Media.SpeechOptions.Rate.set -> void
Microsoft.Maui.Media.SpeechOptions.SpeechOptions() -> void
Microsoft.Maui.Media.SpeechOptions.Volume.get -> float?
Microsoft.Maui.Media.SpeechOptions.Volume.set -> void
diff --git a/src/Essentials/src/TextToSpeech/TextToSpeech.android.cs b/src/Essentials/src/TextToSpeech/TextToSpeech.android.cs
index 8f2ce2d6eeb1..8b80f08cd53d 100644
--- a/src/Essentials/src/TextToSpeech/TextToSpeech.android.cs
+++ b/src/Essentials/src/TextToSpeech/TextToSpeech.android.cs
@@ -146,7 +146,10 @@ public async Task SpeakAsync(string text, int max, SpeechOptions options, Cancel
else
tts.SetPitch(TextToSpeechImplementation.PitchDefault);
- tts.SetSpeechRate(1.0f);
+ if (options?.Rate.HasValue ?? false)
+ tts.SetSpeechRate((float)options.Rate);
+ else
+ tts.SetSpeechRate(1.0f);
var parts = TextToSpeech.SplitSpeak(text, max);
diff --git a/src/Essentials/src/TextToSpeech/TextToSpeech.ios.tvos.watchos.cs b/src/Essentials/src/TextToSpeech/TextToSpeech.ios.tvos.watchos.cs
index 5764ec44faf1..42b4eae6a4a2 100644
--- a/src/Essentials/src/TextToSpeech/TextToSpeech.ios.tvos.watchos.cs
+++ b/src/Essentials/src/TextToSpeech/TextToSpeech.ios.tvos.watchos.cs
@@ -40,6 +40,9 @@ static AVSpeechUtterance GetSpeechUtterance(string text, SpeechOptions options)
if (options.Volume.HasValue)
speechUtterance.Volume = options.Volume.Value;
+
+ if (options.Rate.HasValue)
+ speechUtterance.Rate = options.Rate.Value;
}
return speechUtterance;
diff --git a/src/Essentials/src/TextToSpeech/TextToSpeech.macos.cs b/src/Essentials/src/TextToSpeech/TextToSpeech.macos.cs
index 2aabf215ab0f..573fde6efe7b 100644
--- a/src/Essentials/src/TextToSpeech/TextToSpeech.macos.cs
+++ b/src/Essentials/src/TextToSpeech/TextToSpeech.macos.cs
@@ -32,6 +32,9 @@ async Task PlatformSpeakAsync(string text, SpeechOptions options, CancellationTo
if (options.Locale != null)
ss.Voice = options.Locale.Id;
+
+ if (options.Rate.HasValue)
+ ss.Rate = options.Rate.Value;
}
ssd.FinishedSpeaking += OnFinishedSpeaking;
diff --git a/src/Essentials/src/TextToSpeech/TextToSpeech.shared.cs b/src/Essentials/src/TextToSpeech/TextToSpeech.shared.cs
index 3eddfb59d58f..7fc2a8bf7409 100644
--- a/src/Essentials/src/TextToSpeech/TextToSpeech.shared.cs
+++ b/src/Essentials/src/TextToSpeech/TextToSpeech.shared.cs
@@ -148,6 +148,10 @@ partial class TextToSpeechImplementation : ITextToSpeech
internal const float VolumeDefault = 0.5f;
internal const float VolumeMin = 0.0f;
+ internal const float RateMax = 2.0f;
+ internal const float RateDefault = 1.0f;
+ internal const float RateMin = 0.1f;
+
SemaphoreSlim? semaphore;
public Task> GetLocalesAsync() =>
@@ -170,6 +174,12 @@ public async Task SpeakAsync(string text, SpeechOptions? options = default, Canc
throw new ArgumentOutOfRangeException($"Pitch must be >= {PitchMin} and <= {PitchMin}");
}
+ if (options?.Rate.HasValue ?? false)
+ {
+ if (options.Rate.Value < RateMin || options.Rate.Value > RateMax)
+ throw new ArgumentOutOfRangeException($"Rate must be >= {RateMin} and <= {RateMin}");
+ }
+
if (semaphore == null)
semaphore = new SemaphoreSlim(1, 1);
@@ -245,5 +255,11 @@ public class SpeechOptions
///
/// This value should be between 0f and 1.0f.
public float? Volume { get; set; }
+
+ ///
+ /// The speech rate to use when speaking.
+ ///
+ /// This value should be between 0.1f and 2.0f.
+ public float? Rate { get; set; }
}
}
diff --git a/src/Essentials/src/TextToSpeech/TextToSpeech.tizen.cs b/src/Essentials/src/TextToSpeech/TextToSpeech.tizen.cs
index f90bd7bc649f..686f87a9c421 100644
--- a/src/Essentials/src/TextToSpeech/TextToSpeech.tizen.cs
+++ b/src/Essentials/src/TextToSpeech/TextToSpeech.tizen.cs
@@ -41,11 +41,11 @@ async Task PlatformSpeakAsync(string text, SpeechOptions options, CancellationTo
}
}
- var pitch = 0;
- if (options?.Pitch.HasValue ?? false)
- pitch = (int)Math.Round(options.Pitch.Value / PitchMax * tts.GetSpeedRange().Max, MidpointRounding.AwayFromZero);
+ var rate = 0;
+ if (options?.Rate.HasValue ?? false)
+ rate = (int)Math.Round(options.Rate.Value / RateMax * tts.GetSpeedRange().Max, MidpointRounding.AwayFromZero);
- tts.AddText(text, language, (int)voiceType, pitch);
+ tts.AddText(text, language, (int)voiceType, rate);
tts.Play();
await tcsUtterances.Task;
diff --git a/src/Essentials/src/TextToSpeech/TextToSpeech.uwp.cs b/src/Essentials/src/TextToSpeech/TextToSpeech.uwp.cs
index 794b9b18dd62..ec9c9a9d0929 100644
--- a/src/Essentials/src/TextToSpeech/TextToSpeech.uwp.cs
+++ b/src/Essentials/src/TextToSpeech/TextToSpeech.uwp.cs
@@ -71,7 +71,7 @@ static string GetSpeakParametersSSMLProsody(string text, SpeechOptions options)
{
var volume = "default";
var pitch = "default";
- var rate = "default";
+ var rate = "medium";
// Look for the specified language, otherwise the default voice
var locale = options?.Locale?.Language ?? SpeechSynthesizer.DefaultVoice.Language;
@@ -82,6 +82,9 @@ static string GetSpeakParametersSSMLProsody(string text, SpeechOptions options)
if (options?.Pitch.HasValue ?? false)
pitch = ProsodyPitch(options.Pitch);
+ if (options?.Rate.HasValue ?? false)
+ rate = (options.Rate.Value * 100f).ToString(CultureInfo.InvariantCulture)+"%";
+
// SSML generation
var ssml = new StringBuilder();
ssml.AppendLine($"");