windows

Google Speech-to-Text 音声ファイルをテキスト化 V2利用 C#

Google Speech-to-Text 音声ファイルをテキスト化 V2利用 C#

AI による概要

C# でGoogle Speech-to-Text v2 API を使用するには、NuGet 経由で C# 用の公式 Google Cloud クライアント ライブラリをインストールし、提供されているクラスを使用して認証と文字起こしのリクエストを処理する必要があります。

セットアップと要件

  1. Google Cloud プロジェクト: Speech-to-Text API が有効になっている Google Cloud プロジェクトが必要です。
  2. 認証:クライアントライブラリは、認証にアプリケーションのデフォルト認証情報(ADC)を使用します。ローカル開発の場合は、 Google Cloud CLIを使用して次のコマンドを実行することで設定できますgcloud auth application-default login
  3. NuGet パッケージGoogle.Cloud.Speech.V2C# プロジェクトにパッケージをインストールします。

音声テキスト変換V2の主な機能

  • 地域性とデータの局所性: 特定の Google Cloud リージョンを使用することで、データ所在地要件への準拠が可能になります。
  • 認識機能: 認識構成 (モデル、言語、機能) を保存および分類するための再利用可能な「認識機能」リソースをサポートします。
  • 自動オーディオ検出: オーディオ ファイル内の形式、サンプル レート、およびチャンネル数を自動的に検出できるため、明示的な構成の必要性が軽減されます。
  • 高度なモデル: Chirp 3 や Universal Speech Model (USM) などの高度なモデルにアクセスして、精度と多言語機能を強化します。 

実行画面

1.[ファイル選択]ボタンで音声ファイルを選択します。

2.サンプルレート、言語を入力します。

3.[Short会話]ボタンでテキスト化します。但し、ファイルサイズは、約10MB以下の制限があります。

4.ファイルサイズが、大きいものはグラウド(Cloud Storage)にアップロード後に[Long会話]ボタンでテキスト化します。

5.アップロードは、bucketNmae(パケットフォルダ)とobjectName(ファイル名)を入力後[UpLoad]ボタンで実行します。

* v2では、ProjectId 、Location 、認識ツール(事前に設定しておく)が必要となります。自動オーディオ検出になります。

認識結果は、Cloudに保存されます。結果を利用するためには、Cloudに保存されたJsonファイルをダンロードする必要があります。

内部メモリーに保存することが出来ませんでした。バージョンが上がれば利用できるようです。Geminiへ問合せしました。

プロジェクト名

認識結果の保存先

認識ツールの作成


using Google.Cloud.Speech.V2;
using Google.Cloud.Storage.V1;
using Google.LongRunning;
using Google.Protobuf;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Security.AccessControl;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;
// 名前空間のエイリアスを使うと混乱を防げます
using SpeechV2 = Google.Cloud.Speech.V2;

namespace GoogleFileToText
{
    public partial class Form1 : Form
    {

        private static string text_box;

        [System.Runtime.InteropServices.DllImport("kernel32.dll")]
        private static extern bool AllocConsole();

        //control clsResize
        clsResize _form_resize;

        public Form1()
        {
            InitializeComponent();

            AllocConsole();

            // プロセス環境変数の設定
            string secretPath = Application.StartupPath + "\\sincere-bay-484306-d2-aa6e701a822c.json";
            System.Environment.SetEnvironmentVariable("GOOGLE_APPLICATION_CREDENTIALS", secretPath, EnvironmentVariableTarget.Process);

            //認識ツール
            comboBox1.Items.Add("default");            
            textBox5.Text = "default";

            //言語
            comboBox2.Items.Add("ja-JP");
            textBox6.Text = "ja-JP";

            //既定bucket
            textBox3.Text = "bucket_ssk";

            //projectId
            textBox7.Text = "sincere-bay-484306-d2";

            //location
            textBox8.Text = "global";

            _form_resize = new clsResize(this); //I put this after the initialize event to be sure that all controls are initialized properly

            this.Load += new EventHandler(_Load); //This will be called after the initialization // form_load
            this.Resize += new EventHandler(_Resize); //form_resize

        }

        //clsResize _Load 
        private void _Load(object sender, EventArgs e)
        {
            _form_resize._get_initial_size();
           
        }

        //clsResize _Resize
        private void _Resize(object sender, EventArgs e)
        {
            _form_resize._resize();

        }

        
        public void TranscribeAudioV2(string filePath,string projectId,string location,string recognizerId,string lang)
        {
            text_box = "";     //認識結果用
            // プロジェクトID、リージョン。認識の設定します。あらかじめ認識の設定が必要です。
            // projectId    プロジェクト名ではなくID
            // location     ロケーション "global"                    
            // recognizeId   認識名 用途別に設定できます。"default"
            // lang            対応言語 "ja-JP"

            // 認識を実行する Recognizer のフルネームを作成します。
            var recognizerName = RecognizerName.FromProjectLocationRecognizer(projectId, location, recognizerId);

            // 文字起こししたいオーディオファイルのパスを指定します。
            string audioFilePath = filePath;

            // SpeechClientを作成します。認証は自動的に処理されます。
            SpeechClient client = SpeechClient.Create();

            // オーディオファイルをバイト配列として読み込みます。
            byte[] audioBytes = File.ReadAllBytes(audioFilePath);

            // Recognizeリクエストを作成します。
            RecognizeRequest request = new RecognizeRequest
            {
                Recognizer = recognizerName.ToString(),
                Content = Google.Protobuf.ByteString.CopyFrom(audioBytes),
                // オーディオ形式や言語などの設定は、Recognizerリソースのデフォルト設定から自動検出されます。
                // 特定の設定をオーバーライドしたい場合は、Configプロパティで指定できます。
                /*
                Config = new RecognitionConfig
                {
                    AutoDecodingConfig = new AutoDetectDecodingConfig(),
                    LanguageCodes = { lang },   // "ja-JP"
                    Model = "long",
                    Features = new RecognitionFeatures
                    {
                        // 句読点の自動挿入を有効にする
                        EnableAutomaticPunctuation = true,

                        // 以下のオプションを組み合わせると、句読点の判定精度が上がることがあります
                        EnableWordTimeOffsets = true,
                    }
                }
                */
            };

            // 同期音声認識を実行します。
            RecognizeResponse response = client.Recognize(request);

            // 結果を表示します。
            foreach (var result in response.Results)
            {
                foreach (var alternative in result.Alternatives)
                {
                    Console.WriteLine($"Transcript: {alternative.Transcript}");
                    text_box += $"「 {alternative.Transcript} 」" + "\r\n";
                }
            }
        }

        //UpLoadFile
        public void UploadFile(string bucketName, string localPath, string objectName)
        {
            // The StorageClient automatically uses Application Default Credentials (ADC) for authentication.
            var storage = StorageClient.Create();

            // Use a file stream to read the file data.
            var fileStream = File.OpenRead(localPath);

            // Upload the object. The content type can be automatically determined by GCS if null is passed.
            storage.UploadObject(bucketName, objectName, null, fileStream);

            Console.WriteLine($"Uploaded {objectName} to bucket {bucketName}.");
        }

        public async Task RunAsync(string projectId, string location,string recognizerId,string inputGcsUri, string outputGcsFolder,string lang)
        {
            // projectId    プロジェクト名ではなくID
            // location     ロケーション "global"                    
            // recognizeId   認識名 用途別に設定できます。"default"
            // inputGcsUri     認識するファイルのgcsのuri
            // outputGcsFolder 認識結果を出力するフォルダー Json形式
            // lang            言語

            // 1. 各種クライアントの初期化
            SpeechClient speechClient = await SpeechClient.CreateAsync();
            StorageClient storageClient = await StorageClient.CreateAsync();


            /* 既存の認識ツールを利用する場合は、-----------------------------------
            // 1. Recognizerのフルパスを準備
            string recognizerId = "my-custom-recognizer"; // あなたが作成した名前
            string recognizerPath = $"projects/{projectId}/locations/global/recognizers/{recognizerId}";

            var request = new BatchRecognizeRequest
            {
                // ここに "_ " ではなく、作成したパスを入れる
                Recognizer = recognizerPath,
    
                Files = { new BatchRecognizeFileMetadata { Uri = inputGcsUri } },

                // 既存の Recognizer の設定をそのまま使う場合は、
                // Config 自体を設定しない、もしくは空のままでも動作します。
                // もし上書きしたい設定(句読点など)があれば、ここに追加します。
                Config = new RecognitionConfig
                {
                    Features = new RecognitionFeatures
                    {
                        EnableAutomaticPunctuation = true
                    }
                },

                RecognitionOutputConfig = new RecognitionOutputConfig
                {
                    GcsOutputConfig = new GcsOutputConfig { Uri = outputGcsFolder }
                }
            }; 
            
            -------------------------------------------------------------------------*/

            // 2. BatchRecognize リクエストの構築
            var request = new BatchRecognizeRequest
            {
                // global位置のデフォルトRecognizerを使用
                Recognizer = $"projects/{projectId}/locations/global/recognizers/_",
                Files = { new BatchRecognizeFileMetadata { Uri = inputGcsUri } },
                Config = new RecognitionConfig
                {
                    AutoDecodingConfig = new AutoDetectDecodingConfig(),
                    LanguageCodes = { lang },        // "ja-JP"
                    Model = "long",
                    Features = new RecognitionFeatures
                    {                        
                        // 句読点の自動挿入を有効にする
                        EnableAutomaticPunctuation = true,

                        // 以下のオプションを組み合わせると、句読点の判定精度が上がることがあります
                        EnableWordTimeOffsets = true,
                    }
                },
                RecognitionOutputConfig = new RecognitionOutputConfig
                {
                    // 結果を GCS に保存する設定
                    GcsOutputConfig = new GcsOutputConfig { Uri = outputGcsFolder }
                }
            };

            Console.WriteLine("音声認識を開始します...");
            var operation = await speechClient.BatchRecognizeAsync(request);

            // 完了まで待機
            var completedOp = await operation.PollUntilCompletedAsync();

            //await Task.Delay(2000);

            // 3. 辞書形式(Map)の Results から GCS の出力先パスを取得
            // バージョン 1.7.0 では .Values.First() で取得するのが最も安全です
            var fileResult = completedOp.Result.Results.Values.First();

            if (fileResult.CloudStorageResult == null)
            {
                Console.WriteLine("GCSの出力パスが見つかりませんでした。");
                MessageBox.Show("GCSの出力パスが見つかりませんでした。");
                return;
            }

            string outputJsonUri = fileResult.CloudStorageResult.Uri;
            Console.WriteLine($"解析完了。JSONパス: {outputJsonUri}");

            // 4. GCS から JSON をメモリ内に読み込み、コンソールに表示
            await DisplayGcsJsonResults(storageClient, outputJsonUri);

        }

        //JSONファイルの読み取り(日本語ファイル名対応)
        private async Task DisplayGcsJsonResults(StorageClient storageClient, string gcsUri)
        {

            text_box = "";

            Console.WriteLine($"[1/4] 開始: {gcsUri}");

            // --- 【修正ポイント】Uri クラスを使わずに文字列操作で分離する ---
            // gs:// を除いた後のパスを取得
            string pathWithoutProtocol = gcsUri.Replace("gs://", "");
            int firstSlashIndex = pathWithoutProtocol.IndexOf('/');

            string bucketName = pathWithoutProtocol.Substring(0, firstSlashIndex);
            // URLエンコードを解除(デコード)して、純粋な日本語のファイル名に戻す
            string objectName = Uri.UnescapeDataString(pathWithoutProtocol.Substring(firstSlashIndex + 1));

            Console.WriteLine($"[デバッグ] バケット名: {bucketName}");
            Console.WriteLine($"[デバッグ] オブジェクト名: {objectName}");
            // -------------------------------------------------------------

            try
            {
                using (var ms = new MemoryStream())
                {
                    Console.WriteLine("[2/4] GCSからダウンロード中...");

                    // 日本語名そのままの objectName を渡す
                    await storageClient.DownloadObjectAsync(bucketName, objectName, ms).ConfigureAwait(false);

                    Console.WriteLine($"[3/4] ダウンロード完了。サイズ: {ms.Length} bytes");

                    ms.Position = 0;
                    using (var reader = new StreamReader(ms, Encoding.UTF8, true))
                    {
                        string jsonContent = await reader.ReadToEndAsync().ConfigureAwait(false);

                        Console.WriteLine("[4/4] JSONパース開始...");
                        JObject json = JObject.Parse(jsonContent);

                        var results = json["results"];
                        if (results != null)
                        {
                            foreach (var result in results)
                            {
                                var text = result["alternatives"]?[0]?["transcript"];
                                if (text != null)
                                {
                                    Console.WriteLine($"Transcript: {text}");
                                    text_box += $"「 {text} 」" + "\r\n";
                                }
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("【致命的エラー】");
                Console.WriteLine(ex.Message); // 詳細なメッセージを表示
                MessageBox.Show("【致命的エラー】" + ex.Message);
                if (ex.InnerException != null) Console.WriteLine($"Inner: {ex.InnerException.Message}");
            }

        }

        //select
        private void button1_Click(object sender, EventArgs e)
        {
            //OpenFileDialogクラスのインスタンスを作成
            OpenFileDialog ofd = new OpenFileDialog();

            //はじめのファイル名を指定する
            //はじめに「ファイル名」で表示される文字列を指定する
            ofd.FileName = "";
            //はじめに表示されるフォルダを指定する
            //指定しない(空の文字列)の時は、現在のディレクトリが表示される
            ofd.InitialDirectory = @"C:\";
            //[ファイルの種類]に表示される選択肢を指定する
            //指定しないとすべてのファイルが表示される
            ofd.Filter = "waveファイル(*.wav)|*.wav|すべてのファイル(*.*)|*.*";
            //[ファイルの種類]ではじめに選択されるものを指定する
            //2番目の「Waveファイル」が選択されているようにする
            ofd.FilterIndex = 1;
            //タイトルを設定する
            ofd.Title = "開くファイルを選択してください";
            //ダイアログボックスを閉じる前に現在のディレクトリを復元するようにする
            ofd.RestoreDirectory = true;
            //存在しないファイルの名前が指定されたとき警告を表示する
            //デフォルトでTrueなので指定する必要はない
            ofd.CheckFileExists = true;
            //存在しないパスが指定されたとき警告を表示する
            //デフォルトでTrueなので指定する必要はない
            ofd.CheckPathExists = true;

            //ダイアログを表示する
            if (ofd.ShowDialog() == DialogResult.OK)
            {
                //OKボタンがクリックされたとき、選択されたファイル名を表示する
                Console.WriteLine(ofd.FileName);
                textBox1.Text = ofd.FileName;
            }

        }

        //Long
        private async void button3_Click(object sender, EventArgs e)
        {
            if (textBox3.Text.Length == 0)
            {
                MessageBox.Show("フォルダー(パケット)名が空です。");
                return;
            }
            if (textBox4.Text.Length == 0)
            {
                MessageBox.Show("ファイル名が空です。");
                return;
            }

            // プロジェクトID、リージョン。認識の設定します。あらかじめ認識の設定が必要です。
            string projectId = textBox7.Text;     //プロジェクト名ではなく、ID
            string location = textBox8.Text;      // "global" 
            string recognizerId = textBox5.Text;  // "default" 認識名 用途別に設定できます。
            string gcsUri = @"gs://" + textBox3.Text + @"/" + textBox4.Text; //cloud storageのGSCのファイルパス
            string outputGcs = @"gs://" + textBox3.Text + @"/output/";       //認識結果を保存するフォルダー名(バケット名)
            string lang=textBox6.Text;             //対応言語 "ja-JP"

            await RunAsync(projectId,location,recognizerId, gcsUri,outputGcs,lang);

            textBox2.Text = text_box;

        }

        //end
        private void button2_Click(object sender, EventArgs e)
        {
           this.Close();

        }

        //UpLoad
        private void button4_Click(object sender, EventArgs e)
        {
            if (textBox3.Text.Length == 0)
            {
                MessageBox.Show("フォルダー(パケット)名が空です。");
                return;
            }
            if (textBox4.Text.Length == 0)
            {
                MessageBox.Show("ファイル名が空です。");
                return;
            }

            UploadFile(textBox3.Text, textBox1.Text, textBox4.Text);

        }

        //Short
        private void button5_Click(object sender, EventArgs e)
        {
            if (textBox1.Text.Length == 0)
            {
                MessageBox.Show("ファイル名が空です。");
                return;
            }

            string filePath = textBox1.Text;             //ファイルパス
            string projectId = textBox7.Text;            //プロジェクト名ではなくID
            string location = textBox8.Text;             //ロケーション "global"                    
            string recognizerId = textBox5.Text;         //認識ツールId "default"
            string lang = textBox6.Text;                 //対応言語 "ja-JP"

            TranscribeAudioV2(filePath,projectId,location,recognizerId,lang);
            
            textBox2.Text = text_box;

        }

        private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            textBox5.Text=comboBox1.Text;

        }

        private void comboBox2_SelectedIndexChanged(object sender, EventArgs e)
        {
            textBox6.Text = comboBox2.Text;
        }
    }


}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;

public class clsResize
{
    List<System.Drawing.Rectangle> _arr_control_storage = new List<System.Drawing.Rectangle>();
    private bool showRowHeader = false;
    public clsResize(Form _form_)
    {
        form = _form_; //the calling form
        _formSize = _form_.ClientSize; //Save initial form size
        _fontsize = _form_.Font.Size; //Font size

        //ADD
        var _controls = _get_all_controls(form);//call the enumerator
        FontTable = new Dictionary<string, float>();
        ControlTable = new Dictionary<string, System.Drawing.Rectangle>();
        foreach (Control control in _controls) //Loop through the controls
        {
            FontTable.Add(control.Name, control.Font.Size);
            ControlTable.Add(control.Name, control.Bounds);
        }
        //ADD

    }

    //ADD
    Dictionary<string, float> FontTable;
    Dictionary<string, System.Drawing.Rectangle> ControlTable;
    //ADD

    private float _fontsize { get; set; }

    private System.Drawing.SizeF _formSize { get; set; }

    private Form form { get; set; }

    public void _get_initial_size() //get initial size//
    {
        var _controls = _get_all_controls(form);//call the enumerator
        foreach (Control control in _controls) //Loop through the controls
        {
            _arr_control_storage.Add(control.Bounds); //saves control bounds/dimension            
            //If you have datagridview
            if (control.GetType() == typeof(DataGridView))
                _dgv_Column_Adjust(((DataGridView)control), showRowHeader);
        }
    }

    public void _resize() //Set the resize
    {
        double _form_ratio_width = (double)form.ClientSize.Width / (double)_formSize.Width; //ratio could be greater or less than 1
        double _form_ratio_height = (double)form.ClientSize.Height / (double)_formSize.Height; // this one too
        var _controls = _get_all_controls(form); //reenumerate the control collection
        int _pos = -1;//do not change this value unless you know what you are doing
        foreach (Control control in _controls)
        {

            //ADD
            this._fontsize = FontTable[control.Name]; //<-取得したコントロールのフォントサイズ値で上書きするためにこれを追加
            //ADD

            // do some math calc
            _pos += 1;//increment by 1;
            System.Drawing.Size _controlSize = new System.Drawing.Size((int)(_arr_control_storage[_pos].Width * _form_ratio_width),
                (int)(_arr_control_storage[_pos].Height * _form_ratio_height)); //use for sizing

            System.Drawing.Point _controlposition = new System.Drawing.Point((int)
            (_arr_control_storage[_pos].X * _form_ratio_width), (int)(_arr_control_storage[_pos].Y * _form_ratio_height));//use for location

            //set bounds
            control.Bounds = new System.Drawing.Rectangle(_controlposition, _controlSize); //Put together

            //Assuming you have a datagridview inside a form()
            //if you want to show the row header, replace the false statement of 
            //showRowHeader on top/public declaration to true;
            if (control.GetType() == typeof(DataGridView))
                _dgv_Column_Adjust(((DataGridView)control), showRowHeader);


            //Font AutoSize
            control.Font = new System.Drawing.Font(form.Font.FontFamily,
             (float)(((Convert.ToDouble(_fontsize) * _form_ratio_width) / 2) +
              ((Convert.ToDouble(_fontsize) * _form_ratio_height) / 2)));

        }
    }

    private void _dgv_Column_Adjust(DataGridView dgv, bool _showRowHeader) //if you have Datagridview 
    //and want to resize the column base on its dimension.
    {
        int intRowHeader = 0;
        const int Hscrollbarwidth = 5;
        if (_showRowHeader)
            intRowHeader = dgv.RowHeadersWidth;
        else
            dgv.RowHeadersVisible = false;

        for (int i = 0; i < dgv.ColumnCount; i++)
        {
            if (dgv.Dock == DockStyle.Fill) //in case the datagridview is docked
                dgv.Columns[i].Width = ((dgv.Width - intRowHeader) / dgv.ColumnCount);
            else
                dgv.Columns[i].Width = ((dgv.Width - intRowHeader - Hscrollbarwidth) / dgv.ColumnCount);
        }
    }




    private static IEnumerable<Control> _get_all_controls(Control c)
    {
        return c.Controls.Cast<Control>().SelectMany(item =>
            _get_all_controls(item)).Concat(c.Controls.Cast<Control>()).Where(control =>
            control.Name != string.Empty);
    }
}

-windows

PAGE TOP