通話内容を AI で文字起こしする、基本編
前回の記事で、Asterisk (IP-PBX) の機能を使って通話内容を mp3ファイルとして保存するところまでできた。
この記事では通話内容を録音した mp3ファイルを Google Cloud Speech-to-Text API で解析する。
音声データの渡し方
音声データを解析するためには API に対して音声データを渡す必要がある。Google Cloud Speech-to-Text API では主に 3つの方法が用意されている。
1つめが、gRPC を使う方法。gRPCサーバを仲介してストリーミングでデータを送る。長時間のスピーチなどをリアルタイムに解析したいときには有用だと思うが、録音済みデータを渡す場合は煩雑なだけになりそうなので今回は候補から除外する。
Google のドキュメントで例として使われいるのが、Google Cloud Storage というクラウドのストレージにファイルをアップロードし、その URI を指定する方法。すでに Google Cloud Storage を使っているのであればこれが一番カンタンと思う。Google Cloud Storage は API が用意されているのでこれを使ってもいいし、ツール類があるのでそれを使うこともできる。
最後に、音声データを BASE64 でエンコードして文字列として送信する方法。BASE64 にエンコードする手間、データサイズが概ね3分の4倍になるなどデメリットが目立つので Google のドキュメントでもほぼ扱いが無いが、通話の音声データ程度であればデータサイズはさほど大きくないし、BASE64 にエンコードするのも後述する node の fsクラスの関数で実装できるので開発のコストは最も低いと思う。今回は、この BASE64 で音声データをテキストデータとして送る方法を使う。
API の認証
Google の用意する API では細かいアクセス制御ができるように、多様な認証の仕組み(IAM、Identity and Access Management)を用意しているが、これが Google の API を扱う上での障壁の一つになっている感も否めない。
本来であれば OAuth 2.0 を使い、エンドユーザの認証まで行なった上で細かくアクセス制御するのが望ましいが、今回はデータの保存などは行わず、リクエストに対してレスポンスを得るだけの使い方をするので、扱いが容易なサービスアカウントの仕組みを使ってアクセスする。
サービスアカウントはあらかじめ Google Cloudコンソールで発行しておき、ローカルには認証情報の jsonファイルを保存しておく。
サービスアカウントを使った認証方法についてはドキュメントを参照していただく(これがめちゃくちゃ読みづらいのが難点ではあるものの)として、(2024年6月現在の)フローとしては Google Cloudコンソールにアクセス、上部のメニューバーからプロジェクトを選択(もしくは新しく作成しておく)、IAM と管理、サービスアカウント、サービスアカウントを作成、でサービスアカウントを作成しておく。
作成したサービスアカウントの詳細へアクセスし、キー、鍵を追加、新しい鍵を作成、キーのタイプを JSON として作成すると、認証情報の jsonファイルがダウンロードできるのでこれをローカルに保存しておく。認証情報の jsonファイルは、プロジェクト名-12文字のhex.json というファイル名になっている。このファイルを盗まれると、他人に API を使われてしまうのでぜったいに公開してはいけないし、誰かに渡してもいけない。他人がアクセスする場合は、その人に新しいサービスアカウント、もしくは新しい鍵を生成してもらってそれを使うようにする必要がある。(開発したアプリを公開する場合なども)
あとはプロジェクトに対して、API とサービスから、Cloud Speech-to-Text API を有効にしておく。
API を叩く
ここまでの API を使うまでの下準備が一番大変なところで、ここさえクリアできてしまえばこの先はあまり難しいところはない。
開発者は API へアクセスしてなんらかの計算を行い結果を得ることを「API を叩く」という。日常生活で考えればちょっと乱暴な言い回しに聞こえるが、開発者の脳内には暴力的な要素はまったくなくて、なぜ API を使うことを「叩く」というのかはわからない。
もしかしたら URI などにアクセスする際に、処理の実行で「エンターキーを押す」動作のことを「エンターキーを叩く」とも言うので、これが省略されて「API を叩く」になったのかもしれない。しかし、エンターキー以外のキーを押すときは「キーを押す」であって「キーを叩く」とはあまり言わないのでこれもちょっとモヤッたしたものがある。
日常生活で言えば電卓に限っては「電卓を叩く」と言わなくもない気がするので人間にとって指は叩くための部位なのかもしれない。
閑話休題。Google Cloud Speech-to-Text API の実態は http を使った REST API ではあるものの、各言語用のライブラリが用意されているのでそれを使うと便利に使える。
GO でも Python でもいいが、今回は node を使ってみた。npm を使って「@google-cloud/speech」をインストールしておく。
あとはドキュメントの例そのままでほぼ動くはずなのであまり解説するところはないが、ソースコードを見ながら解説していく。
まず、先程用意した JSON形式の API の認証情報の鍵ファイルについては環境変数 GOOGLE_APPLICATION_CREDENTIALS でファイルのパスを指定する形になる。アプリの実行のたびに環境変数を設定するのが面倒であれば、nodeファイルの冒頭で、
process.env.GOOGLE_APPLICATION_CREDENTIALS = '/home/username/projectname-123456790ab.json';
として埋め込んでしまうことができる。
非同期処理を使いたいので、function は async で記述する。
async function gcloudSpeechToText() {
音声ファイルの引き渡しは前述の通り、BASE64エンコードした文字列として渡すが、これは fs の toString関数にオプション 'base64' を指定するだけで実装できる。
const audio = {
content: fs.readFileSync(filename).toString('base64'),
};
以下が実際に動作する node の全ソースコード。
process.env.GOOGLE_APPLICATION_CREDENTIALS = '/home/username/projectname-1234567890ab.json';
async function gcloudSpeechToText() {
const filename = process.argv[2];
const fs = require('fs');
const { readFile } = require('node:fs/promises');
await readFile(filename + '.json', { encoding: "utf-8" }).then(file => {
console.log(file);
return true;
}).catch(async err => {
const speech = require('@google-cloud/speech');
const client = new speech.SpeechClient();
const config = {
encoding: 'MP3',
sampleRateHertz: 16000,
languageCode: 'ja-JP',
enableSpeakerDiarization: true,
diarizationSpeakerCount: 2,
minSpeakerCount: 2,
maxSpeakerCount: 2,
useEnhanced: true,
model: 'phone_call',
//model: 'telephony',
audioChannelCount: 2,
enableSeparateRecognitionPerChannel: true,
};
const audio = {
content: fs.readFileSync(filename).toString('base64'),
};
const request = {
config: config,
audio: audio,
};
const [response] = await client.recognize(request);
console.log(JSON.stringify(response));
await fs.writeFileSync(filename + '.json', JSON.stringify(response));
});
}
gcloudSpeechToText();
解析したい mp3ファイルのパスを引数として指定する。
解析にはとてもリソースを必要とするため、一度解析したものはキャッシュとして保存する。これは解析対象の mp3ファイルのパス + .json というファイル名で保存してある。
解析で使うオプションについて簡単に説明しておく。
encoding: 'MP3', sampleRateHertz: 16000, は引き渡す音声データが mp3形式で、サンプリングレート16kHz であることを明示している。wav形式でサンプリングレート 8kHz なら encoding: 'LINEAR16', sampleRateHertz: 8000, になる。
languageCode: 'ja-JP', は日本語が使われいることを明示している。API v2 からは複数の言語を指定できるので配列形式で指定する必要がある。
enableSpeakerDiarization: true, diarizationSpeakerCount: 2, minSpeakerCount: 2, maxSpeakerCount: 2, は話者が 2人居て(電話なので 2人が多い)、それぞれ分離して解析するように明示している。
useEnhanced: true, は拡張モデルを使用することを明示している。現在のところ、電話通話モデルと動画モデルが用意されており、今回は電話通話モデルを使いたいので指定する。
model: 'phone_call', で電話通話モデルを指定している。
audioChannelCount: 2, は音声データがステレオフォーマットになっていることを明示している。
enableSeparateRecognitionPerChanne: true, で各チャンネルごとにわけて解析するように明示している。
以上のコードを実行すると、Google Cloud Speech-to-Text API から解析結果の JSON が返ってくる。上記コードでは付属していくるメタデータは捨ててしまい、メインの解析結果のみを取り扱っている。
次の記事では解析結果を整形して表示する。
この記事が気に入ったらサポートをしてみませんか?