💭

AIさんに手伝ってもらって、音声をテキストに

2024/07/01に公開

はじめに

調子に乗って、今度はマイクで音声を拾って、テキストに変換する実装です。第1回目の逆ですね。

Claudさんにやり方を聞いてみる

最近お気に入りのClaudeさんです。PytonとかReactの実装のイメージですが、C#でも書いてくれます。
質問

パソコンについているマイクを使用して、使用者の音声を文字に変換するソフトをC#で作成して

回答

以下に、基本的な音声認識プログラムの例を示します:
C# Speech-to-Text ProgramClick to open code
このプログラムを実行するには、以下の手順が必要です:

Visual Studioなどの開発環境でC#プロジェクトを作成します。
System.Speech名前空間を使用するために、プロジェクトの参照に "System.Speech" を追加します。
上記のコードをプロジェクトのメインファイルにコピーします。
プログラムをビルドして実行します。

もちろん、コードも教えてくれてるんですが、VisualStudioでの手順も教えてくれてるので、この通りに進めていきます。

VisualStudioでプロジェクトの作成

新規プロジェクトにて、「コンソールアプリ」を選択しました。

System.Speechを追加

Nugetで「System.Speech」を選択して、インストールします。

コードをコピー

Program.cs

using System;
using System.Speech.Recognition;

class SpeechToTextProgram
{
    static void Main()
    {
        // 音声認識エンジンを作成
        using (SpeechRecognitionEngine recognizer = new SpeechRecognitionEngine())
        {
            // 音声認識の文法を作成(ここでは自由発話を許可)
            recognizer.LoadGrammar(new DictationGrammar());

            // デフォルトのオーディオデバイス(マイク)を使用
            recognizer.SetInputToDefaultAudioDevice();

            // 音声認識のイベントハンドラを設定
            recognizer.SpeechRecognized += new EventHandler<SpeechRecognizedEventArgs>(Recognizer_SpeechRecognized);

            // 音声認識を開始
            recognizer.RecognizeAsync(RecognizeMode.Multiple);

            Console.WriteLine("音声認識を開始しました。話してください。終了するには 'exit' と言ってください。");

            // プログラムを実行し続ける
            while (true)
            {
                string input = Console.ReadLine();
                if (input.ToLower() == "exit")
                {
                    break;
                }
            }
        }
    }

    // 音声認識イベントのハンドラ
    static void Recognizer_SpeechRecognized(object sender, SpeechRecognizedEventArgs e)
    {
        if (e.Result.Text == "exit")
        {
            Environment.Exit(0);
        }
        Console.WriteLine("認識された文字列: " + e.Result.Text);
    }
}

なんかいっぱいワーニング出てますが、気にせずにビルドしましょう

動作するも改善

動いていはいるみたいですが、精度が良くないみたいです。
そのままAIさんにぶつけてみましょう
質問

動作していますが、認識精度が良くないです。精度を上げてください

回答

はい、音声認識の精度を向上させるためにいくつかの改善策を実装できます。以下に、精度を向上させるための修正版プログラムを示します。
この改良版プログラムでは、以下の変更を行いました:

1.日本語の認識を指定:
new SpeechRecognitionEngine(new CultureInfo("ja-JP")) を使用して、日本語の音声認識エンジンを明示的に指定しました。
2.タイムアウト設定の調整:
InitialSilenceTimeout, BabbleTimeout, EndSilenceTimeout, EndSilenceTimeoutAmbiguous を設定して、音声入力のタイミングを調整しました。
3.信頼度のチェック:
認識結果の信頼度(Confidence)をチェックし、高信頼度(0.8以上)の結果のみを採用するようにしました。
4.中間結果の表示:
SpeechHypothesized イベントを使用して、認識中の中間結果を表示するようにしました。
5.結果の表示改善:
認識結果に信頼度を含めて表示するようにしました。

日本語の認識を指定ってのは重要な気がします。最初から実装しろよ、とも思いますが。

using System;
using System.Globalization;
using System.Speech.Recognition;

class ImprovedSpeechToTextProgram
{
    static void Main()
    {
        // 音声認識エンジンを作成
        using (SpeechRecognitionEngine recognizer = new SpeechRecognitionEngine(new CultureInfo("ja-JP")))
        {
            // 音声認識の文法を作成(ここでは自由発話を許可)
            recognizer.LoadGrammar(new DictationGrammar());

            // デフォルトのオーディオデバイス(マイク)を使用
            recognizer.SetInputToDefaultAudioDevice();

            // 音声認識の設定を調整
            recognizer.InitialSilenceTimeout = TimeSpan.FromSeconds(2);
            recognizer.BabbleTimeout = TimeSpan.FromSeconds(1);
            recognizer.EndSilenceTimeout = TimeSpan.FromSeconds(0.5);
            recognizer.EndSilenceTimeoutAmbiguous = TimeSpan.FromSeconds(0.5);

            // 音声認識のイベントハンドラを設定
            recognizer.SpeechRecognized += new EventHandler<SpeechRecognizedEventArgs>(Recognizer_SpeechRecognized);
            recognizer.SpeechHypothesized += new EventHandler<SpeechHypothesizedEventArgs>(Recognizer_SpeechHypothesized);

            // 音声認識を開始
            recognizer.RecognizeAsync(RecognizeMode.Multiple);

            Console.WriteLine("音声認識を開始しました。話してください。終了するには 'exit' と言ってください。");

            // プログラムを実行し続ける
            while (true)
            {
                string input = Console.ReadLine();
                if (input.ToLower() == "exit")
                {
                    break;
                }
            }
        }
    }

    // 音声認識イベントのハンドラ
    static void Recognizer_SpeechRecognized(object sender, SpeechRecognizedEventArgs e)
    {
        if (e.Result.Confidence > 0.8)
        {
            if (e.Result.Text.ToLower() == "exit")
            {
                Environment.Exit(0);
            }
            Console.WriteLine($"認識された文字列 (信頼度: {e.Result.Confidence:F2}): {e.Result.Text}");
        }
        else
        {
            Console.WriteLine($"低信頼度の認識結果 (信頼度: {e.Result.Confidence:F2}): {e.Result.Text}");
        }
    }

    // 音声認識の中間結果のハンドラ
    static void Recognizer_SpeechHypothesized(object sender, SpeechHypothesizedEventArgs e)
    {
        Console.WriteLine($"認識中: {e.Result.Text}");
    }
}

ただ、これでもまだまだです。
更に改善してもらいましょう

質問

更に精度向上したいです。

回答

はい、音声認識の精度をさらに向上させるために、より高度な手法を導入することができます。以下に、さらに改良を加えたプログラムを示します。
この高度なバージョンでは、以下の改良を加えています:
1.カスタム辞書機能:
・ユーザーが独自の単語や言い回しを登録できるカスタム辞書を実装しました。
・プログラム起動時に custom_dictionary.txt から辞書を読み込みます。
・実行中に add:原文=置換後 の形式で新しい単語を追加できます。

他にもいろいろと機能追加してくれたんだけど、とりあえず1つだけ抜粋しました。
カスタム辞書で、前もって日本語を登録しておく作戦らしいです。

using System;
using System.Globalization;
using System.Speech.Recognition;
using System.Collections.Generic;
using System.IO;

class AdvancedSpeechToTextProgram
{
    private static SpeechRecognitionEngine recognizer;
    private static Dictionary<string, string> customDictionary = new Dictionary<string, string>();

    static void Main()
    {
        InitializeRecognizer();
        LoadCustomDictionary("custom_dictionary.txt");

        Console.WriteLine("音声認識を開始しました。話してください。終了するには 'exit' と言ってください。");

        while (true)
        {
            string input = Console.ReadLine();
            if (input.ToLower() == "exit")
            {
                break;
            }
            else if (input.StartsWith("add:"))
            {
                AddWordToDictionary(input.Substring(4));
            }
        }

        recognizer.Dispose();
    }

    static void InitializeRecognizer()
    {
        recognizer = new SpeechRecognitionEngine(new CultureInfo("ja-JP"));
        
        // 音声認識の文法を作成(自由発話と特定のコマンドを組み合わせる)
        DictationGrammar dictationGrammar = new DictationGrammar();
        GrammarBuilder commandGrammar = new GrammarBuilder();
        commandGrammar.Append(new Choices("終了", "追加", "ヘルプ"));
        
        recognizer.LoadGrammar(dictationGrammar);
        recognizer.LoadGrammar(new Grammar(commandGrammar));

        // デフォルトのオーディオデバイス(マイク)を使用
        recognizer.SetInputToDefaultAudioDevice();

        // 音声認識の設定を調整
        recognizer.InitialSilenceTimeout = TimeSpan.FromSeconds(3);
        recognizer.BabbleTimeout = TimeSpan.FromSeconds(2);
        recognizer.EndSilenceTimeout = TimeSpan.FromSeconds(1);
        recognizer.EndSilenceTimeoutAmbiguous = TimeSpan.FromSeconds(0.8);

        // 音声認識のイベントハンドラを設定
        recognizer.SpeechRecognized += Recognizer_SpeechRecognized;
        recognizer.SpeechHypothesized += Recognizer_SpeechHypothesized;
        recognizer.SpeechRecognitionRejected += Recognizer_SpeechRecognitionRejected;

        // 音声認識を開始
        recognizer.RecognizeAsync(RecognizeMode.Multiple);
    }

    static void Recognizer_SpeechRecognized(object sender, SpeechRecognizedEventArgs e)
    {
        string recognizedText = e.Result.Text;

        // カスタム辞書による置換
        foreach (var entry in customDictionary)
        {
            recognizedText = recognizedText.Replace(entry.Key, entry.Value);
        }

        if (e.Result.Confidence > 0.8)
        {
            if (recognizedText.ToLower() == "exit" || recognizedText == "終了")
            {
                Console.WriteLine("プログラムを終了します。");
                Environment.Exit(0);
            }
            else if (recognizedText == "ヘルプ")
            {
                ShowHelp();
            }
            else
            {
                Console.WriteLine($"認識結果 (信頼度: {e.Result.Confidence:F2}): {recognizedText}");
            }
        }
        else
        {
            Console.WriteLine($"低信頼度の認識結果 (信頼度: {e.Result.Confidence:F2}): {recognizedText}");
        }
    }

    static void Recognizer_SpeechHypothesized(object sender, SpeechHypothesizedEventArgs e)
    {
        Console.Write($"\r認識中: {e.Result.Text}");
    }

    static void Recognizer_SpeechRecognitionRejected(object sender, SpeechRecognitionRejectedEventArgs e)
    {
        Console.WriteLine("\n音声認識に失敗しました。もう一度お試しください。");
    }

    static void LoadCustomDictionary(string filename)
    {
        if (File.Exists(filename))
        {
            foreach (var line in File.ReadLines(filename))
            {
                var parts = line.Split('=');
                if (parts.Length == 2)
                {
                    customDictionary[parts[0].Trim()] = parts[1].Trim();
                }
            }
            Console.WriteLine($"カスタム辞書を読み込みました: {customDictionary.Count} エントリ");
        }
    }

    static void AddWordToDictionary(string input)
    {
        var parts = input.Split('=');
        if (parts.Length == 2)
        {
            customDictionary[parts[0].Trim()] = parts[1].Trim();
            File.AppendAllText("custom_dictionary.txt", $"{parts[0].Trim()}={parts[1].Trim()}\n");
            Console.WriteLine($"辞書に追加しました: {parts[0].Trim()} = {parts[1].Trim()}");
        }
        else
        {
            Console.WriteLine("無効な形式です。'原文=置換後' の形式で入力してください。");
        }
    }

    static void ShowHelp()
    {
        Console.WriteLine("使用方法:");
        Console.WriteLine("- 通常の発話: そのまま話してください");
        Console.WriteLine("- プログラム終了: '終了' と言うか 'exit' と入力");
        Console.WriteLine("- 辞書に単語を追加: 'add:原文=置換後' と入力");
        Console.WriteLine("- このヘルプを表示: 'ヘルプ' と言う");
    }
}

このカスタム辞書とやらも作ってもらいます。
質問

custom_dictionaly.txtファイルも作ってください。一般的な会話で使用する文言を過不足なくフォローしてください

回答

はい、了解しました。一般的な会話で使用される文言をカバーする custom_dictionary.txt ファイルを作成します。このファイルには、日常会話でよく使われる表現や、音声認識で間違いやすい言葉の組み合わせを含めます。

作ってもらったファイルを、exeと同じ場所に置いて、実行です。
いまいち、きれいにテキスト化しないんですよね。雑音が多いからかな。
AIさんだけでは、ここまでですかね。

Discussion