-
Notifications
You must be signed in to change notification settings - Fork 493
Expand file tree
/
Copy pathOfflineSharedSpeechToTextImplementation.macios.cs
More file actions
148 lines (121 loc) · 4.22 KB
/
OfflineSharedSpeechToTextImplementation.macios.cs
File metadata and controls
148 lines (121 loc) · 4.22 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
using AVFoundation;
using CommunityToolkit.Maui.Core;
using Microsoft.Maui.ApplicationModel;
using Microsoft.Maui.Dispatching;
using Speech;
namespace CommunityToolkit.Maui.Media;
public sealed partial class OfflineSpeechToTextImplementation
{
const nuint audioEngineBusTap = 0;
readonly AVAudioEngine audioEngine = new();
IDispatcherTimer? silenceTimer;
SFSpeechRecognizer? speechRecognizer;
SFSpeechRecognitionTask? recognitionTask;
SFSpeechAudioBufferRecognitionRequest? liveSpeechRequest;
/// <inheritdoc/>
public SpeechToTextState CurrentState => recognitionTask?.State is SFSpeechRecognitionTaskState.Running
? SpeechToTextState.Listening
: SpeechToTextState.Stopped;
/// <inheritdoc />
public ValueTask DisposeAsync()
{
audioEngine.Dispose();
speechRecognizer?.Dispose();
liveSpeechRequest?.Dispose();
recognitionTask?.Dispose();
speechRecognizer = null;
liveSpeechRequest = null;
recognitionTask = null;
return ValueTask.CompletedTask;
}
/// <inheritdoc />
public Task<bool> RequestPermissions(CancellationToken cancellationToken = default)
{
var taskResult = new TaskCompletionSource<bool>();
SFSpeechRecognizer.RequestAuthorization(status => taskResult.SetResult(status is SFSpeechRecognizerAuthorizationStatus.Authorized));
return taskResult.Task.WaitAsync(cancellationToken);
}
static void InitializeAvAudioSession(out AVAudioSession sharedAvAudioSession)
{
sharedAvAudioSession = AVAudioSession.SharedInstance();
if (UIDevice.CurrentDevice.CheckSystemVersion(15, 0))
{
sharedAvAudioSession.SetSupportsMultichannelContent(true, out _);
}
sharedAvAudioSession.SetCategory(
AVAudioSessionCategory.PlayAndRecord,
AVAudioSessionCategoryOptions.DefaultToSpeaker | AVAudioSessionCategoryOptions.AllowBluetooth | AVAudioSessionCategoryOptions.AllowAirPlay | AVAudioSessionCategoryOptions.AllowBluetoothA2DP);
}
void InternalStopListening()
{
silenceTimer?.Tick -= OnSilenceTimerTick;
silenceTimer?.Stop();
liveSpeechRequest?.EndAudio();
recognitionTask?.Finish();
audioEngine.Stop();
audioEngine.InputNode.RemoveTapOnBus(audioEngineBusTap);
recognitionTask?.Dispose();
speechRecognizer?.Dispose();
liveSpeechRequest?.Dispose();
speechRecognizer = null;
liveSpeechRequest = null;
recognitionTask = null;
// Dispose all IDisposables before calling `OnSpeechToTextStateChanged` to ensure CurrentState == SpeechToTextState.Stopped
OnSpeechToTextStateChanged(CurrentState);
}
void OnSilenceTimerTick(object? sender, EventArgs e)
{
InternalStopListening();
}
SFSpeechRecognitionTask CreateSpeechRecognizerTask(SFSpeechRecognizer sfSpeechRecognizer, SFSpeechAudioBufferRecognitionRequest sfSpeechAudioBufferRecognitionRequest)
{
int currentIndex = 0;
return sfSpeechRecognizer.GetRecognitionTask(sfSpeechAudioBufferRecognitionRequest, (result, err) =>
{
if (err is not null)
{
currentIndex = 0;
InternalStopListening();
OnRecognitionResultCompleted(SpeechToTextResult.Failed(new Exception(err.LocalizedDescription)));
}
else
{
if (result.Final)
{
currentIndex = 0;
InternalStopListening();
OnRecognitionResultCompleted(SpeechToTextResult.Success(result.BestTranscription.FormattedString));
}
else
{
RestartTimer();
if (currentIndex <= 0)
{
OnSpeechToTextStateChanged(CurrentState);
}
currentIndex++;
OnRecognitionResultUpdated(result.BestTranscription.FormattedString);
}
}
});
}
async Task<IDispatcherTimer> CreateSilenceTimer(SpeechToTextOptions options, CancellationToken cancellationToken)
{
var timer = await MainThread.InvokeOnMainThreadAsync(() => Dispatcher.GetForCurrentThread()?.CreateTimer()
?? throw new InvalidOperationException($"{nameof(IDispatcherTimer)} must be retrieved from the main UI Thread"))
.WaitAsync(cancellationToken);
if (options.AutoStopSilenceTimeout >= SpeechToTextOptionsDefaults.AutoStopSilenceTimeout)
{
return timer;
}
timer.Tick += OnSilenceTimerTick;
timer.Interval = options.AutoStopSilenceTimeout;
timer.Start();
return timer;
}
void RestartTimer()
{
silenceTimer?.Stop();
silenceTimer?.Start();
}
}