CommunityToolkit.SpeechToText在.NET MAUI中性能异常缓慢求助
.NET MAUI CommunityToolkit 语音转文本超时问题
问题现象
基于官方示例实现的.NET MAUI语音转文本功能,使用CommunityToolkit的ISpeechToText接口时,无论是调用ListenAsync还是事件驱动方式,都无法自动检测语音结束,必须等待1分钟超时后才返回识别结果。在iPad测试时,说出"This is a test"后,调试窗口需等待1分钟才输出:
[0:] Success This is a test Time 00:01:00.9459623
且无论何时说话均存在该问题,同时ISpeechToText方法未提供超时设置项。
测试代码
async Task StartListener(CancellationToken cancellationToken) { var isGranted = await speechToText.RequestPermissions(cancellationToken); if (!isGranted) { await Toast.Make("Permission not granted").Show(CancellationToken.None); return; } do { Stopwatch sw = Stopwatch.StartNew(); var recognitionResult = await speechToText.ListenAsync( CultureInfo.GetCultureInfo(Language), new Progress<string>(partialText => { RecognitionText += partialText + " "; }), cancellationToken); sw.Stop(); if (recognitionResult.IsSuccessful) { RecognitionText = recognitionResult.Text; Debug.WriteLine("Success " + RecognitionText + " Time " + sw.Elapsed); RecognitionText = string.Empty; } else { Debug.WriteLine("failed - Time " + sw.Elapsed); } } while (!cancellationToken.IsCancellationRequested); }
问题原因
- iOS平台底层默认配置:CommunityToolkit的
ISpeechToText在iOS端依赖SFSpeechRecognizer,系统默认无语音超时时间为60秒,Toolkit封装时未暴露该配置项。 - 识别模式特性:默认的
ListenAsync采用连续识别模式,不会自动在用户停止说话时终止,必须等待超时或手动取消。
优化方案
方案1:手动检测静音,主动取消识别
通过监听部分识别结果的更新时间,结合静音时长判断,手动取消识别任务:
async Task StartListener(CancellationToken cancellationToken) { var isGranted = await speechToText.RequestPermissions(cancellationToken); if (!isGranted) { await Toast.Make("Permission not granted").Show(CancellationToken.None); return; } do { Stopwatch sw = Stopwatch.StartNew(); var lastUpdateTime = DateTime.Now; var listenCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); var recognitionResult = await speechToText.ListenAsync( CultureInfo.GetCultureInfo(Language), new Progress<string>(partialText => { RecognitionText += partialText + " "; lastUpdateTime = DateTime.Now; // 后台检测静音 _ = Task.Run(async () => { while (!listenCts.Token.IsCancellationRequested) { if ((DateTime.Now - lastUpdateTime).TotalSeconds >= 2) { listenCts.Cancel(); break; } await Task.Delay(500); } }, listenCts.Token); }), listenCts.Token); sw.Stop(); if (recognitionResult.IsSuccessful) { RecognitionText = recognitionResult.Text; Debug.WriteLine("Success " + RecognitionText + " Time " + sw.Elapsed); RecognitionText = string.Empty; } else if (recognitionResult.Status == SpeechToTextStatus.Canceled) { Debug.WriteLine("Recognition canceled (silence detected) - Time " + sw.Elapsed); } else { Debug.WriteLine("failed - Time " + sw.Elapsed); } } while (!cancellationToken.IsCancellationRequested); }
方案2:自定义iOS平台实现,修改系统超时参数
由于Toolkit未暴露SFSpeechRecognizer的超时配置,可通过自定义平台代码修改:
- 在iOS项目中创建自定义实现,继承Toolkit的
SpeechToTextImplementation:
[assembly: Dependency(typeof(CustomSpeechToText))] namespace YourApp.iOS { public class CustomSpeechToText : SpeechToTextImplementation { protected override async Task<SpeechToTextResult> StartListeningAsync(CultureInfo culture, IProgress<string>? recognitionProgress, CancellationToken cancellationToken) { var recognizer = new SFSpeechRecognizer(new NSLocale(culture.Name)); if (recognizer == null) { return new SpeechToTextResult(SpeechToTextStatus.NotSupported, null); } var request = new SFSpeechAudioBufferRecognitionRequest(); request.ShouldReportPartialResults = true; // 设置无语音超时为2秒 request.EndSilenceTimeout = TimeSpan.FromSeconds(2); return await base.StartListeningAsync(culture, recognitionProgress, cancellationToken); } } }
- 在
MauiProgram.cs中替换默认注册:
builder.Services.AddSingleton<ISpeechToText, CustomSpeechToText>();
方案3:事件驱动模式,主动停止识别
使用ListenWithEventsAsync方法,结合静音检测主动调用StopListenAsync:
async Task StartListener(CancellationToken cancellationToken) { var isGranted = await speechToText.RequestPermissions(cancellationToken); if (!isGranted) { await Toast.Make("Permission not granted").Show(CancellationToken.None); return; } speechToText.RecognitionResultUpdated += (s, e) => { RecognitionText += e.RecognitionResult + " "; lastUpdateTime = DateTime.Now; }; do { Stopwatch sw = Stopwatch.StartNew(); var lastUpdateTime = DateTime.Now; var listenTask = speechToText.ListenWithEventsAsync(CultureInfo.GetCultureInfo(Language), cancellationToken); // 静音检测任务 var silenceTask = Task.Run(async () => { while (!cancellationToken.IsCancellationRequested) { if ((DateTime.Now - lastUpdateTime).TotalSeconds >= 2) { await speechToText.StopListenAsync(); break; } await Task.Delay(500); } }, cancellationToken); await listenTask; sw.Stop(); var result = speechToText.CurrentResult; if (result != null && result.IsSuccessful) { Debug.WriteLine("Success " + result.Text + " Time " + sw.Elapsed); RecognitionText = string.Empty; } else { Debug.WriteLine("failed - Time " + sw.Elapsed); } } while (!cancellationToken.IsCancellationRequested); }
内容的提问来源于stack exchange,提问作者Gratzy
相关产品推荐
相关产品推荐

