mcpField
M
maui-speech-to-text

maui-speech-to-text

github.com/davidortinau開発17642 回インストール16 週間前に更新
リポジトリ

maui-speech-to-text について

CommunityToolkit.Maui を使う .NET MAUI 開発者向けの音声テキスト変換ガイドです。権限、認識中の制御、イベント処理の注意点や、プラットフォームごとの問題とサービス化の方法を扱います。

主な機能

  1. 01開始前にマイクと音声の権限を確認
  2. 02タイムアウトとキャンセルトークンを設定
  3. 03イベントハンドラーを確実に解除
  4. 04部分結果でリアルタイム文字起こしを表示
  5. 05音声認識をサービスとしてラップ

活用シーン

  1. 01権限説明の不足による iOS クラッシュの回避
  2. 02Android のマイク権限拒否への対応
  3. 03バッテリー消費とイベント重複処理の防止
  4. 04MAUI UI でのリアルタイム文字起こし表示

インストール

コマンドをコピーしてターミナルで実行します。

$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 権限を2回拒否すると、OS はプロンプトを表示しなくなります。ユーザーを手動で設定画面に誘導する必要があります。

よくある間違い

❌ タイムアウトなし — 無期限のリスニング

バッテリーを消耗する無期限のリスニングセッションを防ぐため、必ずタイムアウトを設定してください。

// ❌ 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;
}

プラットフォームごとの注意点

プラットフォーム注意点
iOSplist キーのどちらかが欠けている → 実行時クラッシュ
Androidユーザーが権限を2回拒否 → OS がプロンプトを表示しなくなる。設定画面への誘導が必要
すべてタイムアウトなし → 無期限のリスニングによるバッテリー消耗
すべてリスニング中に 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 のいずれでも同じように機能します。3つのパターンすべてについては、references/speech-to-text-api.md を参照してください。

チェックリスト

  • CommunityToolkit.Maui NuGet をインストール済み(最新バージョンを確認)
  • MauiProgram.cs で UseMauiCommunityToolkit() を呼び出している
  • DI 経由で ISpeechToText をシングルトンとして登録している
  • iOS:Info.plist に NSSpeechRecognitionUsageDescription と NSMicrophoneUsageDescription の両方がある
  • Android:AndroidManifest.xml に RECORD_AUDIO 権限がある
  • StartListeningAsync を呼び出す前に毎回権限を確認している
  • タイムアウトを設定している(最大60秒を推奨)
  • finally ブロックでイベントハンドラーの登録を解除している
  • 使用後に CancellationTokenSource を破棄している

コメント