さて、前回は音声認識と音声ファイルの作成についてpyaudioを使ったpyファイルの説明をしてきました。
今回は残りのpyコード、と言ってもメインとなるSpeech to text APIを利用した文字起こしの部分を紹介していきます。
前回記事をご確認いただけていない方は、以下の記事をご確認ください。
おさらい:ファイル構成概要
まずはファイル構成を再度掲載していきますね。
-speech_to_text.dir |-functions.dir |- __init__.py |- audio_input.py |- cloud_speech_sample.py |- templatelike.py |- pysimplegui.py |- result.wav
前回説明したのはaudio_input.pyでした。今回は残りのpyファイル、functions.dirに含まれているpyのコード群を説明していきます。
cloud_speech_sample.py: wavファイルをAPIに渡して文字起こしをする
さて、本丸となる文字起こしの部分ですが、第2回でGCPで利用可能にしたspeech to text APIを利用していきます。この部分を動作させるためには、GCP側でAPIを有効にしていく必要がありますので、第2回の設定が終わっていない方は、下記記事を参考に設定をお願いします。
実際のコード掲載
まずは何より、実際のコードを貼っていきますので、みていきましょう。
from google.cloud import speech_v1
from google.cloud.speech_v1 import enums
import io
def time_mesurement(local_file_path):
import wave
wr = wave.open(local_file_path, "rb")
ch = wr.getnchannels()
width = wr.getsampwidth()
fr = wr.getframerate()
fn = wr.getnframes()
total_time = 1.0 * fn / fr
wr.close()
print("byte数:"+ str(width))
print("total_time:" + str(1.0 * fn / fr))
if total_time > 60:
return True
def audio_recognize(local_file_path, short_long):
#constructing instance
client = speech_v1.SpeechClient()
# 言語設定
language_code = "ja-JP"
# データのsample_rate
sample_rate_hertz = 44100
# WAV前提で作成、もしMP3とうに変更の場合はライブラリの追加が必要
encoding = enums.RecognitionConfig.AudioEncoding.LINEAR16
config = {
"language_code": language_code,
"sample_rate_hertz": sample_rate_hertz,
"encoding": encoding,
}
with io.open(local_file_path, "rb") as f:
content = f.read()
audio = {"content": content}
if short_long == True:
operation = client.long_running_recognize(config, audio)
response = operation.result()
else:
response = client.recognize(config, audio)
transcript = ""
confidence = 0
for result in response.results:
alternative = result.alternatives[0]
transcript = alternative.transcript
confidence = alternative.confidence
print(transcript)
print(confidence)
return transcript
作成したメソッドは2つです。1つが、音声ファイルの長さが1分超か否かを判定するtime_mesurementメソッド、もう一つが、実際に文字起こしをリクエストするaudio_recognizeメソッドになります。2つのメソッドを順々にみていきましょう。
time_mesurementメソッド:音声ファイルの時間を測る方法
time_mesurementメソッドにおいては、ファイルパスを受け取り、そのファイルが60秒未満(1分未満)か否かを判定して、結果をTrue/Falseで返す役割を担っています。これは、次に説明するspeech_to_text APIがファイルボリュームが60秒未満かどうかでレスポンスの返ってき方が同期・非同期で異なってくるためです。
メインの機能は正直以下だけです。
total_time = 1.0 * fn / fr
total_time(総時間)変数に、frameレートとframe数を渡して総時間を計算しています。変数の役割は以下のようになっています。
wr = wave.open(local_file_path, “rb”) #wavファイルをread_バイナリーで取得
fr = wr.getframerate() #frameレートを取得
fn = wr.getnframes() #frame数を取得
音声ファイルの時間の計算方法のメジャーなやり方の一つかと思っていますが、あまり詳しくないので、ここら辺は突っ込まれても回答できません。。
音声ファイルの長さが1分未満か否かでSpeech to text APIで利用するメソッドが変わる
先ほどtime_mesurementメソッドで説明した、なぜ音声ファイルの長さを測る必要があるのかという点ですが、実はSpeech to text APIの仕様で以下のような分岐が存在するためです。
1分未満:同期処理ですぐに文字起こし結果が返ってくる(responseが受けられる)
1分以上:非同期処理で音声文字起こしの結果が返される(responseがすぐには返ってくるとは限らない、requestを投げて返ってくるのは成功した場合はaccept要素が返される。そして、実行後transcript結果が返される)
ここら辺は実際のドキュメントに記載があるので、そちらをご覧ください。

この処理の分岐のために、time_mesurementメソッドを利用しているだけで、もともと1分以上の入力を許容しないor 1ぷん以上の入力しか許容しないという前提があればここら辺は悩む必要ありません。
audio_recognizeメソッド:speech to text apiを利用して文字起こしtranscriptを返す
さてやっとメインディッシュの文字起こしをするためのメソッドの説明です。と言っても、ここは基本speech to text APIの例題に乗っているものをほぼそのまま利用していますので、先ほど紹介したドキュメントをご参照いただければ把握できると思いますが、ざっくり解説していきます。
ざっくりとした流れは以下です。
①SpeechClientインスタンスを作成
②変数を定義(config)
③音声ファイルを取得
④APIに音声ファイルが1分未満かどうかを判定して投げ込む
⑤返ってきた結果を文字化する
①SpeechClientインスタンスを作成
対象コードは以下の部分です。
client = speech_v1.SpeechClient()
文字起こしを呼ぶためのクライアントインスタンスを作成しています。それ以上でもそれ以下でもありません。
②変数を定義(config)
以下の部分で使用するconfig設定を作成するためのデータを集めていきます。利用しているconfigは以下のようなものです。
config = { "language_code": language_code, "sample_rate_hertz": sample_rate_hertz, "encoding": encoding, }
言語種類(今回は日本語のためja-JP)、サンプルレート(44100, これはwavファイル音声入力時に利用されているレートです)、encodingの種類を指定する必要があります。
③音声ファイルを取得
openでメソッド呼び出し時に引数で渡しているファイルパスからwavファイルを開いて、中身をaudio変数として、content要素に入れています。このaudio変数がAPIに受け渡す音声データのバイナリーデータとなります。渡し方は④で説明。
with io.open(local_file_path, "rb") as f: content = f.read() audio = {"content": content}
④APIに音声ファイルが1分未満かどうかを判定して投げ込む
①で作成しているインスタンスでそれぞれ1分未満かどうかで呼び出すメソッドが異なります。非同期・同期の違いにより、一旦別変数に実行処理結果を渡すか否かが異なります。
if short_long == True: #1分以上の場合 operation = client.long_running_recognize(config, audio) #long_running_recognizeに渡して、オペレーション結果をまつ response = operation.result() else: #1分未満の場合 response = client.recognize(config, audio) #recgonizeに渡してすぐ結果が返ってくる
⑤返ってきた結果を文字化する
④で取得したresponseのresults要素にA,文字起こしテキスト B.confidence(信頼度)が含まれていますので、それから文字起こしテキストを取得していきます。confidenceはgoogleさんが文字起こししてどの程度の信頼性と考えているかを返してくれています。正直10秒くらいだといい精度だと思いますが、もちろん長くなったり、音声ファイルの質が悪かったりすれば低くなります。
transcript = "" confidence = 0 for result in response.results: alternative = result.alternatives[0] transcript = alternative.transcript confidence = alternative.confidence print(transcript) print(confidence)
まとめ
今回のコードは全体の中でメインとなる文字起こしAPIを利用する部分でした。やっと5回目にしてメインにたどり着きました。実際にコード書くよりブログに起こす方に時間がかかってしまいました。。ちなみに、変数名やスクリプトの構成などかなりリファクタリング要素の多いコードですので、遊びで使うレベルでないと後々保守が大変になりますのでご容赦ください。
ドキュメント読めるようになれば、比較的簡単にAPIを利用して文字起こしが可能になっています。いい時代ですね。ただし、専門用語や業界独自用語なんかにはまだまだ弱いイメージです。
業界独自用語などが多く存在する場合は、自分でdictを事前に準備しておいて、mecabみたいな形態素分解してくれるライブラリを利用して置き換えが必要かと思います。ただ、mecabも文字登録・アップデート共に止まって久しいので、何か新しいライブラリ作る必要あるかもですが、簡易なものではまだまだやくにたつと思いますよ。
関連記事: python speech to text APIを利用して音声認識ファイルを文字起こししてみよう
pythonでGoogleが提供するspeech to text APIを利用して音声認識ファイルを文字起こししてみます。speech to text APIの基本的な使い方、Macでのマイクを利用したwavファイルの作成方法、pythonでのメジャーGUIツールを利用した感想などを紹介していきます。