Skip to content

Commit a438997

Browse files
Speech Recognition Windows Ensure unsubscribe from events on Stop Recording (#2705)
1 parent 39afb8b commit a438997

File tree

4 files changed

+44
-24
lines changed

4 files changed

+44
-24
lines changed

samples/CommunityToolkit.Maui.Sample/Platforms/Windows/Package.appxmanifest

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757

5858
<Capabilities>
5959
<rescap:Capability Name="runFullTrust" />
60+
<DeviceCapability Name="microphone"/>
6061
</Capabilities>
6162

6263
</Package>

samples/CommunityToolkit.Maui.Sample/ViewModels/Essentials/SpeechToTextViewModel.cs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,9 @@
88

99
namespace CommunityToolkit.Maui.Sample.ViewModels.Essentials;
1010

11-
public partial class SpeechToTextViewModel : BaseViewModel
11+
public partial class SpeechToTextViewModel : BaseViewModel, IAsyncDisposable
1212
{
1313
const string defaultLanguage = "en-US";
14-
const string defaultLanguage_android = "en";
15-
const string defaultLanguage_tizen = "en_US";
1614

1715
readonly ITextToSpeech textToSpeech;
1816
readonly ISpeechToText speechToText;
@@ -55,7 +53,7 @@ async Task SetLocales(CancellationToken token)
5553
Locales.Add(locale);
5654
}
5755

58-
CurrentLocale = Locales.FirstOrDefault(x => x.Language is defaultLanguage or defaultLanguage_android or defaultLanguage_tizen) ?? Locales.FirstOrDefault();
56+
CurrentLocale = Locales.FirstOrDefault();
5957
}
6058

6159
[RelayCommand]
@@ -148,4 +146,9 @@ void HandleLocalesCollectionChanged(object? sender, NotifyCollectionChangedEvent
148146
{
149147
OnPropertyChanged(nameof(CurrentLocale));
150148
}
149+
150+
public async ValueTask DisposeAsync()
151+
{
152+
await speechToText.DisposeAsync();
153+
}
151154
}

src/CommunityToolkit.Maui.Core/Essentials/SpeechToText/OfflineSpeechToTextImplementation.windows.cs

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,6 @@ public SpeechToTextState CurrentState
2828
public ValueTask DisposeAsync()
2929
{
3030
InternalStopListening();
31-
32-
offlineSpeechRecognizer?.Dispose();
33-
offlineSpeechRecognizer = null;
3431
return ValueTask.CompletedTask;
3532
}
3633

@@ -39,7 +36,6 @@ Task InternalStartListening(SpeechToTextOptions options, CancellationToken token
3936
Initialize(options);
4037

4138
offlineSpeechRecognizer.AudioStateChanged += OfflineSpeechRecognizer_StateChanged;
42-
offlineSpeechRecognizer.LoadGrammar(new DictationGrammar());
4339

4440
offlineSpeechRecognizer.InitialSilenceTimeout = TimeSpan.MaxValue;
4541
offlineSpeechRecognizer.BabbleTimeout = TimeSpan.MaxValue;
@@ -48,7 +44,12 @@ Task InternalStartListening(SpeechToTextOptions options, CancellationToken token
4844

4945
offlineSpeechRecognizer.RecognizeCompleted += OnRecognizeCompleted;
5046
offlineSpeechRecognizer.SpeechRecognized += OnSpeechRecognized;
51-
offlineSpeechRecognizer.RecognizeAsync(RecognizeMode.Multiple);
47+
48+
if (offlineSpeechRecognizer.AudioState == AudioState.Stopped)
49+
{
50+
offlineSpeechRecognizer.RecognizeAsync(RecognizeMode.Multiple);
51+
}
52+
5253
return Task.CompletedTask;
5354
}
5455

@@ -81,19 +82,26 @@ void InternalStopListening()
8182
{
8283
try
8384
{
84-
if (offlineSpeechRecognizer is not null)
85+
if (offlineSpeechRecognizer is not null && offlineSpeechRecognizer.AudioState != AudioState.Stopped)
8586
{
8687
offlineSpeechRecognizer.RecognizeAsyncStop();
87-
88-
offlineSpeechRecognizer.AudioStateChanged -= OfflineSpeechRecognizer_StateChanged;
89-
offlineSpeechRecognizer.RecognizeCompleted -= OnRecognizeCompleted;
90-
offlineSpeechRecognizer.SpeechRecognized -= OnSpeechRecognized;
9188
}
9289
}
9390
catch
9491
{
9592
// ignored. Recording may be already stopped
9693
}
94+
finally
95+
{
96+
if (offlineSpeechRecognizer is not null)
97+
{
98+
offlineSpeechRecognizer.AudioStateChanged -= OfflineSpeechRecognizer_StateChanged;
99+
offlineSpeechRecognizer.RecognizeCompleted -= OnRecognizeCompleted;
100+
offlineSpeechRecognizer.SpeechRecognized -= OnSpeechRecognized;
101+
offlineSpeechRecognizer?.Dispose();
102+
offlineSpeechRecognizer = null;
103+
}
104+
}
97105
}
98106

99107
[MemberNotNull(nameof(recognitionText), nameof(offlineSpeechRecognizer), nameof(speechToTextOptions))]
@@ -102,6 +110,7 @@ void Initialize(SpeechToTextOptions options)
102110
speechToTextOptions = options;
103111
recognitionText = string.Empty;
104112
offlineSpeechRecognizer = new SpeechRecognitionEngine(options.Culture);
113+
offlineSpeechRecognizer.LoadGrammarAsync(new DictationGrammar());
105114
}
106115

107116
void OfflineSpeechRecognizer_StateChanged(object? sender, AudioStateChangedEventArgs e)

src/CommunityToolkit.Maui.Core/Essentials/SpeechToText/SpeechToTextImplementation.windows.cs

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1+
using System.Diagnostics;
12
using System.Diagnostics.CodeAnalysis;
2-
using System.Globalization;
3-
using System.Speech.Recognition;
43
using Microsoft.Maui.ApplicationModel;
54
using Windows.Globalization;
65
using Windows.Media.SpeechRecognition;
@@ -35,8 +34,6 @@ public SpeechToTextState CurrentState
3534
public async ValueTask DisposeAsync()
3635
{
3736
await StopRecording(CancellationToken.None);
38-
speechRecognizer?.Dispose();
39-
speechRecognizer = null;
4037
}
4138

4239
async Task InternalStartListeningAsync(SpeechToTextOptions options, CancellationToken cancellationToken)
@@ -48,7 +45,10 @@ async Task InternalStartListeningAsync(SpeechToTextOptions options, Cancellation
4845
speechRecognizer.ContinuousRecognitionSession.Completed += OnCompleted;
4946
try
5047
{
51-
await speechRecognizer.ContinuousRecognitionSession.StartAsync().AsTask(cancellationToken);
48+
if (speechRecognizer.State == SpeechRecognizerState.Idle)
49+
{
50+
await speechRecognizer.ContinuousRecognitionSession.StartAsync().AsTask(cancellationToken);
51+
}
5252
}
5353
catch (Exception ex) when ((uint)ex.HResult is privacyStatementDeclinedCode)
5454
{
@@ -88,12 +88,8 @@ async Task StopRecording(CancellationToken cancellationToken)
8888
{
8989
try
9090
{
91-
if (speechRecognizer is not null)
91+
if (speechRecognizer is not null && speechRecognizer.State != SpeechRecognizerState.Idle)
9292
{
93-
speechRecognizer.StateChanged -= SpeechRecognizer_StateChanged;
94-
speechRecognizer.ContinuousRecognitionSession.ResultGenerated -= ResultGenerated;
95-
speechRecognizer.ContinuousRecognitionSession.Completed -= OnCompleted;
96-
9793
cancellationToken.ThrowIfCancellationRequested();
9894
await speechRecognizer.ContinuousRecognitionSession.StopAsync().AsTask(cancellationToken);
9995
}
@@ -102,6 +98,17 @@ async Task StopRecording(CancellationToken cancellationToken)
10298
{
10399
// ignored. Recording may be already stopped
104100
}
101+
finally
102+
{
103+
if (speechRecognizer is not null)
104+
{
105+
speechRecognizer.StateChanged -= SpeechRecognizer_StateChanged;
106+
speechRecognizer.ContinuousRecognitionSession.ResultGenerated -= ResultGenerated;
107+
speechRecognizer.ContinuousRecognitionSession.Completed -= OnCompleted;
108+
speechRecognizer?.Dispose();
109+
speechRecognizer = null;
110+
}
111+
}
105112
}
106113

107114
[MemberNotNull(nameof(recognitionText), nameof(speechRecognizer), nameof(speechToTextOptions))]

0 commit comments

Comments
 (0)