PythonでSpeechRecognitionを使ってリアルタイム文字起こし【Windows】

# 概要

GoogleのSpeechRecognitionというライブラリを使ってリアルタイム文字起こしをしました。
用途としてはリモート会議や授業での音声をリアルタイム文字起こしして、議事録作成やメモの省力化などに使えると思います。

## 利用するライブラリ
- リアルタイムで文字起こしするライブラリ：SpeechRecognition
- パソコンからの音声出力をキャッチするライブラリ：PyAudio


```
wave
time
datetime
speech_recognition
pyaudio
```

### ライブラリの入れ方
```pip install SpeechRecognition```で入れられます。
注意点は、ライブラリのインストールの時とインポートする時とでスペルの大文字小文字が異なる点です。
- インストールの時：大文字小文字で"_"が不要：```SpeechRecognition```
- インポートの時：全部小文字で"_"が必要：```speech_recognition```


### デバイスから出力される音声を使えるようにするための下準備
リアルタイムで音声を文字起こしするためにはまず、音声が無ければいけません。Windowsではパソコンから出力される音声をPythonで使えるようにするために初期設定が必要です。
やり方については[こちらの記事](https://qiita.com/KENTAROSZK/items/8d82a495b7cffec69862) に記載してありますので、まずはそちらの設定をしてから次に進んでください。

## コード
作成したコードがこちらです。

``` python
import speech_recognition as sr
import wave
import time
from datetime import datetime

import pyaudio

FORMAT        = pyaudio.paInt16
SAMPLE_RATE   = 44100        # サンプリングレート
CHANNELS      = 1            # モノラルかバイラルか
INPUT_DEVICE_INDEX = 0       # マイクのチャンネル
CALL_BACK_FREQUENCY = 3      # コールバック呼び出しの周期[sec]


OUTPUT_TXT_FILE = "./" + datetime.now().strftime('%Y%m%d_%H_%M') +".txt" # テキストファイルのファイル名を日付のtxtファイルにする


def look_for_audio_input():
    """
    デバイスうえでのオーディオ系の機器情報を表示する
    """
    pa = pyaudio.PyAudio()

    for i in range(pa.get_device_count()):
        print(pa.get_device_info_by_index(i))
        print()

    pa.terminate()


def callback(in_data, frame_count, time_info, status):
    """
    コールバック関数の定義
    """
    
    global sprec # speech_recognitionオブジェクトを毎回作成するのではなく、使いまわすために、グローバル変数で定義しておく

    try:
        audiodata  = sr.AudioData(in_data, SAMPLE_RATE, 2)
        sprec_text = sprec.recognize_google(audiodata, language='ja-JP')
        
        with open(OUTPUT_TXT_FILE,'a') as f: #ファイルの末尾に追記していく
            f.write("\n" + sprec_text)
    
    except sr.UnknownValueError:
        pass
    
    except sr.RequestError as e:
        pass
    
    finally:
        return (None, pyaudio.paContinue)


def realtime_textise():
    """
    リアルタイムで音声を文字起こしする
    """

    with open(OUTPUT_TXT_FILE, 'w') as f: #txtファイルの新規作成
        DATE = datetime.now().strftime('%Y%m%d_%H:%M:%S')
        f.write("日時 : " + DATE + "\n") # 最初の一行目に日時を記載する

    global sprec # speech_recognitionオブジェクトを毎回作成するのではなく、使いまわすために、グローバル変数で定義しておく
    
    # speech recogniserインスタンスを生成
    sprec = sr.Recognizer() 
    
    # Audio インスタンス取得
    audio  = pyaudio.PyAudio() 
    
    # ストリームオブジェクトを作成
    stream = audio.open(format             = FORMAT,
                        rate               = SAMPLE_RATE,
                        channels           = CHANNELS,
                        input_device_index = INPUT_DEVICE_INDEX,
                        input              = True, 
                        frames_per_buffer  = SAMPLE_RATE*CALL_BACK_FREQUENCY, # CALL_BACK_FREQUENCY 秒周期でコールバック
                        stream_callback    = callback)
    
    stream.start_stream()
    
    while stream.is_active():
        time.sleep(0.1)
    
    stream.stop_stream()
    stream.close()
    audio.terminate()


def main():
    look_for_audio_input()
    realtime_textise()


if __name__ == '__main__':
    main()
```

## コードの解説

最初にパラメータを設定しています。

- ```FORMAT```：pyaudioで録音、再生する時のフォーマットを指定しています。ここは、そういうものだと思って使う形で良いと思います。
- ```SAMPLE_RATE```：サンプリングレートです。サンプリングレートについて説明するのはここの記事の本筋から外れてしまうのですが、コンピュータがアナログデータを取り扱うにあたってどのくらいの頻度でデータを取得するのか、を設定する数値です。大きくすればするほどより高音質に録音/再生できますが、ここをいじるのはあまりお勧めしません。
- ```CHANNELS```：モノラルかバイラルかを設定します。
- ```INPUT_DEVICE_INDEX```：ループバック録音※のデバイスのインデックスです。これは利用している端末毎に違うため、自分で探す必要があります。多分インデックスがゼロかイチのものがデバイスとなっているはずです。やりながら音声をちゃんと取得できているか確認して手作業で確認しないといけないです。筆者の環境では、ループバック録音のデバイスのインデックスがゼロだったので、```INPUT_DEVICE_INDEX=0```としています。
- ```CALL_BACK_FREQUENCY```：コールバック関数の呼び出し頻度です。単位は秒です。

※ループバック録音とは、ラフに説明すると、PCから出力される音声をプログラムの中でキャッチして録音することです。「普段イヤホンで聞いている音声をPythonで使えるようにする」させるために必要な設定、という認識で良いと思います。


関数```look_for_audio_input()```はループバック録音のデバイスのインデックスを探すために実行させます。

```callback```関数はコールバック関数です。パソコンから発せられる音声をリアルタイムで取得するために、pyaudioではコールバック関数を定義する必要があります。
``` python
try:
   audiodata  = sr.AudioData(in_data, SAMPLE_RATE, 2)
   sprec_text = sprec.recognize_google(audiodata, language='ja-JP')
        
   with open(OUTPUT_TXT_FILE,'a') as f: #ファイルの末尾に追記していく
       f.write("\n" + sprec_text)
```
処理の流れとしては、```try```の中で、pyaudioで取得した音声データをspeech_recognitionで読み込んで、```audiodata```変数に格納します。それを```recognize_google()```メソッドに引数として渡してあげます。今回は日本語のリアルタイム文字起こしをさせるので、言語を日本語に設定（```language=ja-JP```）しています。
```recognize_google()```メソッドは音声データを引数として、文字列を返してくれるので、それを```sprec_text```変数に保存させ、これをtxtファイルとして追記させています。



```realtime_textise()```がリアルタイム文字起こしさせるためのメインの処理を担っています。

``` python
# speech recogniserインスタンスを生成
sprec = sr.Recognizer() 
    
# Audio インスタンス取得
audio  = pyaudio.PyAudio() 
    
# ストリームオブジェクトを作成
stream = audio.open(format             = FORMAT,
                    rate               = SAMPLE_RATE,
                    channels           = CHANNELS,
                    input_device_index = INPUT_DEVICE_INDEX,
                    input              = True, 
                    frames_per_buffer  = SAMPLE_RATE*CALL_BACK_FREQUENCY, # CALL_BACK_FREQUENCY 秒周期でコールバック
                    stream_callback    = callback)
    
stream.start_stream()
    
while stream.is_active():
     time.sleep(0.1)

```
処理の流れとしては、```sprec = sr.Recognizer() ```でspeech recognitionをインスタンス化し、そのあとにpyaudioのインスタンスを作成しています。

``` python
stream = audio.open(format             = FORMAT,
                    rate               = SAMPLE_RATE,
                    channels           = CHANNELS,
                    input_device_index = INPUT_DEVICE_INDEX,
                    input              = True, 
                    frames_per_buffer  = SAMPLE_RATE*CALL_BACK_FREQUENCY, # CALL_BACK_FREQUENCY 秒周期でコールバック
                    stream_callback    = callback)
```
ここでストリームオブジェクトを作成して、pyaudioの諸々の設定をしています。先ほどの```callback()```関数もここで指定しています。

```while```の無限ループは、作成したストリームオブジェクトがアクティブである限りずっと実行し続ける（```CTR+C```で強制終了させるなどしない限りはずっと文字起こしをさせ続ける）ためにループさせているパートです。これが無いと、実行後速攻でプログラムが終了してしまいます。


## 結果とまとめ
今回はSpeechRecognitionというライブラリを使ってパソコンから出力される音声（システム音声）をリアルタイムに文字起こしさせることができました。ただ、文字起こしの精度については正直ビミョーなところだと思います。精度についてはローカルの非力なPCでも動けるようにライトなモデルを使っているのが原因なのかもしれないと推測しています（実際のところは確認していないので、実はかなり大規模なモデルを使っているのかもしれませんが）。

精度については他にある音声認識エンジンを使う方が良いかもしれません。調べてみると他にも音声認識エンジン/APIは以下のものがあるようです。Google系のサービスを使うのであれば、Google Cloud Speech APIというのを使えばGCP側の馬力を使ったより高精度な文字起こしができるかもしれませんが、いつか試してみようと思います。

- CMU Sphinx（オフラインで動作）
- Google Cloud Speech API
- Wit.ai
- Microsoft Bing Voice Recognition
- Houndify API
- IBM Speech to Text
- Snowboy Hotword Detection（オフラインで動作）