Cloud Speech-to-Text を使って英語音声ファイルを文字変換する
- speech-to-text
はじめに
英語のポッドキャストで聞きたいエピソードがありました。聞いてみようと思ったものの、エピソードは 45 分あり、私にとっては長い時間でした。英語は読む方が速いため、文字起こしの方法はないものかと調べていたところ、Google Cloud Platform(以下、GCP) に Speech-to-Text という音声文字変換サービスがあったため、試してみました。
やったこと
音声文字変換を行う Node.js スクリプトを TypeScript で書きました。コードはこちらです。
https://github.com/mokajima/speech-to-text-demo
使い方
$ GOOGLE_APPLICATION_CREDENTIALS=./serviceAccount.json ts-node index.ts -e 'FLAC' -u 'gs://cloud-samples-tests/speech/brooklyn.flac'
$ GOOGLE_APPLICATION_CREDENTIALS=./serviceAccount.json node index.js -e 'FLAC' -u 'gs://cloud-samples-tests/speech/brooklyn.flac'
ts-node index.ts
か node index.js
を実行します。スクリプトの実行に成功すると、実行結果が transcription.txt
というファイル名で出力されます。
環境変数 GOOGLE_APPLICATION_CREDENTIALS
に GCP のサービス アカウントキーが含まれる JSON ファイルのパスを指定します。GCP のサービス アカウントキーの取得方法については後述します。
-e
は --encoding
の略で、音声ファイルのエンコードを指定します。-u
は --uri
の略で、音声ファイルの URI を指定します。1 分以上の音声ファイルの文字変換を行いたい場合、音声ファイルを Cloud Storage に置く必要があります。1 そのため、-u
は gs://バケット名/ファイル名
の形式で指定します。音声ファイルの Cloud Storage への格納方法については後述します。
オプションに -l
や -s
を指定することも可能です。指定可能なオプションの一覧は次の通りです。
オプション | オプション (省略系) |
必須 | 初期値 | |
---|---|---|---|---|
--encoding | -e | ✅ | 音声ファイルのエンコード 2 | |
--uri | -u | ✅ | 音声ファイルの URI | |
--languageCode | -l | en-US | 音声ファイルの言語 3 | |
--sampleRateHertz | -s | 16000 | 音声ファイルのサンプルレート |
GCP のサービス アカウントキーの取得
GCP のサービス アカウントキーを取得するには、GCP のプロジェクトを作成する必要があります。GCP のコンソールのヘッダーの「プロジェクトの選択」をクリックします。
「プロジェクトの選択」ダイアログの右上の「新しいプロジェクト」をクリックします。
「新しいプロジェクト」ページで「作成」をクリックします。「プロジェクト名」「場所」は初期値のままで問題ありません。
プロジェクトの作成が完了すると、プロジェクトのダッシュボードが表示されます。ナビゲーションメニューの「IAM と管理」>「サービス アカウント」をクリックします。
「プロジェクト『プロジェクト名』のサービス アカウント」ページで「サービス アカウントを作成」をクリックします。
「サービス アカウントの詳細」で「サービス アカウント名」と「サービス アカウント ID」を入力し、「作成」をクリックします。
「このサービス アカウントにプロジェクトへのアクセスを許可する (省略可)」でサービス アカウントに付与するアクセス権を指定します。ここでは「ロール」>「Cloud Storage」>「Storage オブジェクト閲覧者」を選択します。後ほど Cloud Storage に音声ファイルを格納するため、Cloud Storage の閲覧権限を付与しておく必要があります。
「Storage オブジェクト閲覧者」を選択したら、「続行」をクリックします。
「ユーザーにこのサービス アカウントへのアクセスを許可 (省略可)」で「完了」をクリックします。
サービス アカウントの作成が完了すると、「プロジェクト『プロジェクト名』のサービス アカウント」ページに遷移します。作成したサービスアカウント名(「メール」カラムの値)をクリックします。
「詳細」タブの内容が表示されます。「キー」タブをクリックします。
「鍵を追加」>「新しい鍵を作成」をクリックします。
「『サービス アカウント』の秘密鍵の作成」ダイアログで「キーのタイプ」>「JSON」を選択します。「作成」をクリックすると、秘密鍵をダウンロードします。この JSON ファイルがサービス アカウントキーで、使い方の serviceAccount.json
です。
音声ファイルの Cloud Storage への格納
ナビゲーションメニューの「Cloud Storage」をクリックします。
「ブラウザ」ページで「バケットを作成」をクリックします。
「バケットに名前を付ける」でバケット名を入力し、「続行」をクリックします。
「データの保存場所の選択」で「ロケーション タイプ」>「Region」、「ロケーション」>「asia-northeast1 (東京)」を選択し、「続行」をクリックします。
「データのデフォルトのストレージ クラスを選択する」で「Standard」を選択し、「続行」をクリックします。
「オブジェクトへのアクセスを制御する方法を選択する」で「アクセス制御」>「均一」を選択し、「続行」をクリックします。
「詳細設定(省略可)」で「暗号化」>「Google が管理する暗号鍵」を選択し、「作成」をクリックします。
バケットの作成が完了すると、「バケットの詳細」ページに遷移します。「ファイルをアップロード」をクリックし、音声ファイルをアップロードします。
コード
import { v1p1beta1 } from '@google-cloud/speech' // (1)
import { program } from 'commander' // (2)
import * as fs from 'fs'
type AudioEncoding =
| 'ENCODING_UNSPECIFIED'
| 'LINEAR16'
| 'FLAC'
| 'MULAW'
| 'AMR'
| 'AMR_WB'
| 'OGG_OPUS'
| 'SPEEX_WITH_HEADER_BYTE'
| 'MP3'
const DEFAULT_CONFIG = {
// 句読点の自動挿入を有効にする
enableAutomaticPunctuation: true,
}
const client = new v1p1beta1.SpeechClient()
program // (3)
.option('-l, --languageCode <languageCode>', undefined, 'en-US')
.option('-s, --sampleRateHertz <sampleRateHertz>', undefined, '16000')
.requiredOption('-e, --encoding <encoding>')
.requiredOption('-u, --uri <uri>')
.action(async () => { // (4)
const { encoding, languageCode, sampleRateHertz, uri } = program.opts() // (5)
const audio = { uri }
const config = {
...DEFAULT_CONFIG,
encoding,
languageCode,
sampleRateHertz,
}
try {
const [operation] = await client.longRunningRecognize({ audio, config }) // (6)
const [response] = await operation.promise() // (7)
const transcription = response.results // (8)
?.map((result) => result.alternatives?.[0].transcript)
.join('\n')
fs.writeFile('./transcription.txt', transcription ?? '', (error) => {
if (error) {
throw error
}
})
} catch (error) {
console.error(error)
}
})
program.parse(process.argv) // (9)
(1) v1p1beta1 のインポート
公式ドキュメントのサンプルコード 4 では const speech = require('@google-cloud/speech')
のようにデフォルトインポートしていますが、ここでは v1p1beta1
をインポートしています。これは、Cloud Speech-to-Text API の v1
が MP3 非対応のため v1p1beta1
を使う必要があるためです。5 6
(2) Commander.js
Commander.js は Node.js で Command Line Interface(以下、CLI)を作成するためのライブラリです。
(3) program
commander
からインポートした program
は Command
クラスのインスタンスです。commander
から Command
クラスをインポートして、new Command()
することも可能です。7
option()
と requiredOption()
はスクリプト実行時にオプションを渡せるようにするためのメソッドです。option()
で任意オプションを、requiredOption()
で必須オプションを渡せるようにします。
option()
と requiredOption()
の第一引数はフラグ名です。-e, --encoding <encoding>
や -u, --uri <uri>
のように フラグ名 <任意の名前>
とすることで、オプションが真偽値以外の値を取ることができます。なお、オプションの値は program.opts()
で取得可能ですが、<任意の名前>
は program.opts()
の返り値に影響しません。そのため、.requiredOption('-u, --uri <uri>')
を .requiredOption('-u, --uri <value>')
とすることも可能です。
option()
の第二引数はオプションの説明文です。第二引数は省略可能ですが、第三引数に値を渡したいため、ここでは undefined
を指定しています。
option()
の第三引数はオプションの初期値です。
(4) action()
action()
に CLI 実行時の処理を記述します。
(5) program.opts()
program.opts()
でオプションの値を取得し、client.longRunningRecognize()
に渡すための audio
と config
を定義しています。
(6) client.longRunningRecognize()
client.longRunningRecognize()
は音声文字変換を非同期で実行します。レスポンスはタプル型で、最初の要素が Operation のインスタンスとなっています。分割代入で operation
に代入しています。
(7) operation.promise()
operation.promise()
は音声文字変換の結果を Promise で返します。レスポンスはタプル型で、最初の要素に文字起こしが含まれます。分割代入で response
に代入しています。
たとえば、gs://cloud-samples-tests/speech/brooklyn.flac
を音声文字変換してみます。8
$ GOOGLE_APPLICATION_CREDENTIALS=./serviceAccount.json ts-node index.ts -e 'FLAC' -u 'gs://cloud-samples-tests/speech/brooklyn.flac'
このとき response
の値は次のようになります。
{
results: [
{
alternatives: [
{
transcript: "How old is the Brooklyn Bridge?",
confidence: 0.9822515845298767,
},
],
languageCode: "en-us",
},
],
},
(8) transcription.txt へ出力
response.results
を加工し、transcription
に代入した後、fs.writeFile()
で transcription.txt
として出力しています。
(9) program.parse(process.argv)
program.parse(process.argv)
は引数を処理し、オプションによって使われなかった引数を program.args
配列に残します。たとえば、次のコマンドを実行した場合、program.args
は ['foo', 'bar']
となります。
$ GOOGLE_APPLICATION_CREDENTIALS=./serviceAccount.json ts-node index.ts -e 'FLAC' -u 'gs://cloud-samples-tests/speech/brooklyn.flac' foo bar
実行時間
音声ファイルのエンコード | 音声ファイルの長さ | 実行時間 |
---|---|---|
FLAC | 約 10 秒 | 約 1 分 |
MP3 | 約 45 分 | 約 10 分 |
費用
GCP のコンソールの「お支払い」を確認したところ、約 45 分の音声ファイルの実行を 2 回、約 10 秒の音声ファイルの実行を何度か行った結果、約 90 円でした 💰
おわりに
GCP の Speech-to-Text を使って音声文字変換を行いました。音声文字変換の精度が高かったため、機会があればまた使ってみたいと思います。日本語音声についても試してみたいところです。
参考
- Speech-to-Text: 自動音声認識 | Google Cloud
- 長い音声ファイルの文字変換 | Cloud Speech-to-Text ドキュメント | Google Cloud
- 音声エンコードの概要 | Cloud Speech-to-Text ドキュメント | Google Cloud
- 言語サポート | Cloud Speech-to-Text ドキュメント | Google Cloud
- Method: speech.longrunningrecognize | Cloud Speech-to-Text ドキュメント
- ListOperationsResponse | Cloud Speech-to-Text ドキュメント | Google Cloud
- tj/commander.js: node.js command-line interfaces made easy
-
https://cloud.google.com/speech-to-text/docs/async-recognize
↩ -
https://cloud.google.com/speech-to-text/docs/encoding
↩ -
https://cloud.google.com/speech-to-text/docs/languages
↩ -
https://cloud.google.com/speech-to-text/docs/async-recognize#speech_transcribe_async_gcs-nodejs
↩ -
https://cloud.google.com/speech-to-text/docs/encoding
↩ -
なお、試してみたところ
↩v1
でも動作しました。 -
https://github.com/tj/commander.js/#declaring-program-variable
↩ -
https://cloud.google.com/speech-to-text/docs/quickstart-gcloud
↩