mcpField
M
maui-speech-to-text

maui-speech-to-text

github.com/davidortinau开发17642 次安装16 周前更新
代码仓库

关于 maui-speech-to-text

面向 .NET MAUI 开发者的语音转文字实践技能,涵盖 CommunityToolkit.Maui 的权限、监听和事件处理注意事项。提供跨平台常见问题与服务封装建议。

主要功能

  1. 01识别前检查麦克风和语音权限
  2. 02为监听设置超时与取消令牌
  3. 03及时取消订阅事件处理程序
  4. 04通过部分结果显示实时转写
  5. 05将语音识别封装为服务

适用场景

  1. 01避免 iOS 缺少权限说明导致崩溃
  2. 02处理 Android 麦克风权限拒绝
  3. 03防止监听耗电及重复事件处理
  4. 04在 MAUI 界面中展示实时转写

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/davidortinau/maui-skills --skill maui-speech-to-text

安装后在 Claude Code 中输入/maui-speech-to-text确认已加载。

附带文件references/speech-to-text-api.md

详细介绍

语音转文本 — 注意事项与最佳实践

有关完整的服务实现、类型和 UI 集成模式,请参阅 references/speech-to-text-api.md。

关键事项:权限处理

开始语音识别之前,务必先请求权限。麦克风和语音识别权限都必须获得。

// ❌ Starting recognition without checking permissions
var result = await _speechService.StartListeningAsync();

// ✅ Always check permissions first
if (!await _speechService.RequestPermissionsAsync())
    return; // Gracefully handle denial
var result = await _speechService.StartListeningAsync(cancellationToken);
⚠️ iOS 需要同时配置两项权限说明

如果 Info.plist 中缺少 NSSpeechRecognitionUsageDescription 或 NSMicrophoneUsageDescription,就会导致运行时崩溃,而不是优雅地失败。

⚠️ Android:重新请求权限

在 Android 上,如果用户两次拒绝 RECORD_AUDIO 权限,操作系统就不会再显示权限提示。你必须引导用户手动前往设置。

常见错误

❌ 未设置超时 — 无限时长聆听

务必设置超时,以免聆听会话无限持续并耗尽电池电量:

// ❌ No timeout — listens forever if no speech detected
await _speechToText.StartListenAsync(options, CancellationToken.None);

// ✅ Use a combined timeout + user cancellation token
using var timeoutCts = new CancellationTokenSource(TimeSpan.FromSeconds(60));
using var combinedCts = CancellationTokenSource.CreateLinkedTokenSource(
    userCancellationToken, timeoutCts.Token);
await _speechToText.StartListenAsync(options, combinedCts.Token);
❌ 未取消订阅事件处理程序

事件订阅未清理会导致重复处理和内存泄漏:

// ❌ Subscribe without unsubscribe
_speechToText.RecognitionResultUpdated += OnRecognitionResultUpdated;

// ✅ Always unsubscribe in finally block
try
{
    _speechToText.RecognitionResultUpdated += OnRecognitionResultUpdated;
    // ... listen ...
}
finally
{
    _speechToText.RecognitionResultUpdated -= OnRecognitionResultUpdated;
}
❌ 未释放 CancellationTokenSource
// ❌ Leaked CTS
_currentCts = new CancellationTokenSource();

// ✅ Dispose in finally
try { /* ... */ }
finally
{
    _currentCts?.Dispose();
    _currentCts = null;
}

平台注意事项

平台注意事项
iOS缺少任一 plist 键 → 运行时崩溃
Android用户两次拒绝权限 → 操作系统不再显示提示;必须引导用户前往设置
所有平台未设置超时 → 无限时长聆听会耗尽电池电量
所有平台已在聆听时调用 StartListeningAsync → 返回错误,而不是抛出异常

架构建议

  1. 将 ISpeechToText 封装在服务中 — 不要在 ViewModel 中直接使用 SpeechToText.Default。将其封装为 ISpeechRecognitionService,以便进行测试和状态管理。

  2. 使用部分结果改善 UX — 订阅 PartialResultReceived,以实时反馈转录结果。用户期望看到文字随着说话逐渐出现。

  3. 连续聆听 = 带延迟的循环 — 在对话模式下,通过带短暂延迟(Task.Delay(100))的循环调用 StartListeningAsync。

  4. 防止重复启动 — 启动前检查状态:

    if (State == SpeechRecognitionState.Listening)
        return new SpeechRecognitionResultDto { Success = false, ErrorMessage = "Already listening" };
    
  5. 自然语言输出 — CommunityToolkit.Maui 返回经过规范化并带有标点的文本,而不是原始音素。基本用例无需后处理。

  6. 与 UI 无关的服务 — ISpeechRecognitionService 模式在 XAML/MVVM、C# Markup 和 MauiReactor 中的用法完全相同。有关这三种模式,请参阅 references/speech-to-text-api.md。

检查清单

  • 已安装 CommunityToolkit.Maui NuGet 包(查找当前版本)
  • 已在 MauiProgram.cs 中调用 UseMauiCommunityToolkit()
  • 已通过 DI 将 ISpeechToText 注册为 singleton
  • iOS:Info.plist 中同时包含 NSSpeechRecognitionUsageDescription 和 NSMicrophoneUsageDescription
  • Android:AndroidManifest.xml 中包含 RECORD_AUDIO 权限
  • 每次调用 StartListeningAsync 前都检查权限
  • 已配置超时(建议最长 60 秒)
  • 已在 finally 块中取消订阅事件处理程序
  • 使用后已释放 CancellationTokenSource

评论

支持 Markdown