初心者がRubyでSpeech-to-Text APIをつかってみた話

備忘録を兼ねて書くので、先輩諸氏は適度に飛ばし読みしてくださいませ。
実行環境：MacOS, Ruby2.6.5
# Speech-to-Text APIとは？
Google社のクラウドコンピューティングサービス、**GCP**（Google Cloud Platform）にて提供されているAPIです。
名前のとおり、音声をテキストに変換してくれます。Googleドキュメントの音声入力機能（あれ便利ですよね）的なものをアプリケーションに組み込めるらしい、ということでやってみました。
# 準備作業
### GCPの設定
・ Googleアカウントを準備
・ GCPに初回登録
　クレジットカードや住所など入力
・ [GCPコンソール](https://console.cloud.google.com/)にアクセス
・ プロジェクト作成
　初回登録をすると「My first project」が自動作成されているので好きな名前に変更。
> Google Cloud プロジェクトは、API の管理、課金の有効化、共同編集者の追加と削除、Google Cloud リソースに対する権限の管理など、すべての Google Cloud サービスの作成、有効化、使用の基礎となります。-- Google公式ガイドより

・ Speech-to-Text APIを有効に設定
　GCPコンソールのサイドバーから「APIとサービス」→「ライブラリ」を選択。
　機械学習の項目にある「Cloud Speech-to-Text API」を選び、有効にするボタンを押す。
![スクリーンショット 2021-03-26 15.22.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1238651/0adf853f-389e-d569-69ef-3ec877dcfaa3.png)

・ サービスアカウント作成
> サービス アカウントは、ユーザーではなく、アプリケーションや仮想マシン（VM）インスタンスで使用される特別なアカウントです。アプリケーションはサービス アカウントを使用して、承認された API 呼び出しを行います。-- Google公式ガイドより

　このとき、JSONで受け取るを選択して秘密鍵ファイルをダウンロード。

### コンピュータ側
・ サービスアカウントの秘密鍵を環境変数に設定

```
　export GOOGLE_APPLICATION_CREDENTIALS="秘密鍵ファイルのパス"
```
・ [Google Cloud SDK](https://cloud.google.com/sdk/docs/install)をインストール
　GCに対してコマンド操作ができるようになる。
・ テスト環境を作成（Ruby on Rails 6.0.0）
・ [google-cloud-speech(Gem)](https://rubygems.org/gems/google-cloud-speech?locale=ja)をインストール


# 音声文字変換の方式について
[Speech-to-Textドキュメント](https://cloud.google.com/speech-to-text/docs?hl=ja)を読んだところ、音声入力の方式については大きく分けて以下２つがあるとのこと。

|音声入力方式|説明|音声に関する制限|
|---|---|---|
|バッチ型|録音した音声ファイルを渡す|ローカルファイルを渡す場合は**長さ1分未満 & 10MBまで**。GC Storageのバケットに保存すればそれ以上も可。|10MBまで|10MBまで|
|ストリーミング型|会話をリアルタイムで認識|基本、1リクエスト（会話のひと区切り）あたり長さ約5分まで。それより長いリクエストを送る場合の[サンプルコード](https://cloud.google.com/speech-to-text/docs/streaming-recognize?hl=ja#performing_streaming_speech_recognition_on_an_audio_stream)あり。<br>（でも何故だろうRubyは無かった...）|

さらに、バッチ型については同期・非同期で扱える音声ファイルに差がある。

|変換方式|ローカルファイルを渡す場合の制限|GC Storageのバケットに保存する場合の制限|
|---|---|---|
|同期変換|長さ1分未満 & 10MBまで|長さ1分未満|
|非同期変換|長さ1分未満 & 10MBまで|長さ480分（8時間）まで|

ということで、使い勝手を知るべく
　1. 同期変換（ローカルに保存、１分未満）
　2. 非同期変換（GCStorageに保存、1分以上）
を試すことにしました。

## 1. 同期変換

```controller.rb
  def speech_sync_recognize
    # [START speech_transcribe_sync] ローカルファイルの同期音声文字変換

    require "google/cloud/speech"
    speech = Google::Cloud::Speech.speech

    # ローカルファイルのパス
    audio_file_path = "/hoge/hoge/sounds/test_short.flac"
  
    # [START speech_ruby_migration_sync_response]
    audio_file = File.binread audio_file_path
    config     = { encoding:          :FLAC,    #FLAC、LINEAR16が対応
                   sample_rate_hertz: 44_100,   #サンプリングレート
                   language_code:     "ja-JP" } #日本語に設定
    audio      = { content: audio_file }
  
    response = speech.recognize config: config, audio: audio
    results = response.results
  
    alternatives = results.first.alternatives
    alternatives.each do |alternative|
      puts "Transcription: #{alternative.transcript}"
    end
    # [END speech_ruby_migration_sync_response]
    # [END speech_transcribe_sync]
  end
```

出力結果は...

``Transcription: 
皆さんおはようございますえっとキャンプメンバーの＊＊です10時になりましたので朝礼を始めます
``
``元の音声:
皆さんおはようございます。テックキャンプメンターの＊＊です。10時になりましたので朝礼を始めます。
``

ほぼ拾えてる！（一部、個人情報を含んでおりましたので割愛しました）

## 2. 非同期変換

```controller.rb
  def speech_async_recognize_gcs
    # [START speech_transcribe_async_gcs] Google Cloud Storageファイルの非同期音声文字変換
  
    require "google/cloud/speech"
    speech = Google::Cloud::Speech.speech

    storage_path = "gs://hoge_bucket/test_long.flac"
   
    config = { encoding:          :FLAC,    #FLAC、LINEAR16が対応
               sample_rate_hertz: 44_100,   #サンプリングレート
               language_code:     "ja-JP" } #日本語に設定
    audio = { uri: storage_path }
  
    operation = speech.long_running_recognize config: config, audio: audio
  
    puts "Operation started"
  
    operation.wait_until_done!
  
    raise operation.results.message if operation.error?
  
    results = operation.response.results
    
    @transcriptions = "Transcription: "
    results.each do |result|
      alternatives = result.alternatives
      @transcriptions += "#{alternatives.first.transcript}"
    end
    # [END speech_transcribe_async_gcs]
  end
```

出力結果

``Transcription: 
屋さんおはようございますキャンプメンバーの雨で中止になりましたので朝礼を始めますまずは面接を行います面積はストレスの低減メタ認知力集中力の向上です瞑想は前頭葉の筋トレです日々の積み重ねで頭の形に変わってきて自制心や洞察力が高まると言われております是非し午後は1時から高等部までピンと伸ばし腹式呼吸をしやすくします5秒吸って口で10秒拍腹式呼吸継続します閉じても閉じなくても中です意識の全てを自分の呼吸に集中します着きます集中がそれて他の事を考え出したら再度呼吸に集中を戻します瞑想中は音が入らないように必ず夢舞妓ミュートにしてくださいそれでは始めます用意スタート
``

``元の音声：
皆さんおはようございます。テックキャンプメンターの＊＊です。10時になりましたので朝礼を始めます。まずは瞑想を行います。瞑想の目的は、ストレスの低減、メタ認知力、集中力の向上です。瞑想は前頭葉の筋トレです。日々の積み重ねで、脳の形が物理的に変わってきて、自制心や洞察力が高まると言われております。是非、毎日の習慣にしてみましょう。実施方法は、背筋から後頭部までピンと伸ばし、腹式呼吸をしやすくします。鼻で5秒吸って口で10秒吐く、腹式呼吸を5分継続します。目は閉じても閉じなくても自由です。意識の全てを自分の呼吸に集中します。口と鼻の神経に意識を向けます。集中が逸れて他のことを考え出したら、再度呼吸に集中を戻します。瞑想中は音が入らないように、必ずZoomのマイクをミュートにしてください。それでは始めます。用意スタート。
``

結構抜け落ちました。
たまに１文まるっと抜けたり、日本語と英語が混ざるところで改善が必要ですね。

# まとめ
今回は、基本的な使い方を知るためにもっともプレーンな機能で音声文字変換をしました。
Googleさんの提供する高度な機能がたくさんあるようなので（句読点挿入とか、モデル適応とか、言語自動検出とか）、色々試してみたいと思います。

## 参考にさせていただきました
[エンジニア必読！今さら聞けない、Google Cloud Platform (GCP) とは？]
(https://www.topgate.co.jp/gcp01-what-is-google-cloud-platform)
[Google Cloud Speech-to-Text APIをいろいろ調査してみる]
(https://tech-blog.optim.co.jp/entry/2020/02/21/163000)
[Google Cloud Speech APIのリアルタイム音声認識は使い物になる精度なのか？]
(https://qiita.com/hamham/items/9b553d0759a2319ea211)
[google-cloud-speech のGitHubリポジトリ]
(https://github.com/googleapis/google-cloud-ruby/blob/master/google-cloud-speech/samples/speech_samples.rb)