Google Colabを使ってブラウザで録音~音声認識でAIに質問する
OpenAIの音声認識技術Whisperを使って音声認識をやりたい。
気軽に試して貰いたいので、音声ファイルのアップロードなんて面倒くさいことはせず、Google Colabを使ってブラウザだけでそれを実現してみる。
APIキーの読み込み
まずはAPIを読み込む。
from getpass import getpass
secret = getpass('Enter the secret value: ')
ライブラリのインストール
ついでにimportやAPIキーの代入までやってしまう。
%%capture
!pip install openai
import openai
openai.api_key = secret
Javascriptの実行
Google Colabで実行できるのは基本的にPythonだが、実はJavascriptを実行する機能もある。
from IPython.display import display, Javascript
from google.colab.output import eval_js
from base64 import b64decode
def record_js(filename='record.mp3'):
js = Javascript('''
async function record() {
let rec;
let chanks;
// HTML組み立て
const div = document.createElement('div');
const startRecord = document.createElement('button');
startRecord.textContent = 'Rec';
div.appendChild(startRecord);
const stopRecord = document.createElement('button');
stopRecord.textContent = 'Stop';
stopRecord.style.display = 'none'
div.appendChild(stopRecord);
const audio = document.createElement('audio');
div.appendChild(audio);
document.body.appendChild(div);
// Audioが有効になったら
function handlerFunction(stream,resolve) {
rec = new MediaRecorder(stream);
// 録音が完了したら
rec.ondataavailable = e => {
chanks.push(e.data);
if (rec.state == "inactive") {
let blob = new Blob(chanks, { type: 'audio/mpeg-3' });
audio.src = URL.createObjectURL(blob);
audio.controls = true;
audio.autoplay = true;
resolve();
}
}
}
startRecord.onclick = e => {
startRecord.style.display = 'none'
stopRecord.style.display = 'block'
chanks = [];
rec.start();
}
stopRecord.onclick = e => {
startRecord.style.display = 'block'
stopRecord.style.display = 'none'
rec.stop();
}
function blobToBase64(blob) {
return new Promise((resolve, _) => {
const reader = new FileReader();
reader.onloadend = () => resolve(reader.result);
reader.readAsDataURL(blob);
});
}
await new Promise((resolve) => {
navigator.mediaDevices.getUserMedia({ audio: true })
.then(stream => { handlerFunction(stream,resolve) })
});
let blob = new Blob(chanks, { type: 'audio/mpeg-3' });
return await blobToBase64(blob);
}
''')
display(js)
data = eval_js('record()')
binary = b64decode(data.split(',')[1])
with open(filename, 'wb') as f:
f.write(binary)
return filename
Python部分
長すぎるのでまずは外側のPython部分だけ抜粋。
from IPython.display import display, Javascript
from google.colab.output import eval_js
from base64 import b64decode
def record_js(filename='record.mp3'):
js = Javascript('''
Javacript中略
''')
display(js)
data = eval_js('record()')
binary = b64decode(data.split(',')[1])
with open(filename, 'wb') as f:
f.write(binary)
return filename
Javascriptオブジェクトを使ってdisplay()
に埋め込む。
そしてeval_js
でメソッドを実行し、base64を受け取ってバイナリとして保存している。
Javascript部分
かなり読みにくい。
いろいろトリッキーなことをやっている。
async function record() {
let rec;
let chanks;
// HTML組み立て
const div = document.createElement('div');
const startRecord = document.createElement('button');
startRecord.textContent = 'Rec';
div.appendChild(startRecord);
const stopRecord = document.createElement('button');
stopRecord.textContent = 'Stop';
stopRecord.style.display = 'none'
div.appendChild(stopRecord);
const audio = document.createElement('audio');
div.appendChild(audio);
document.body.appendChild(div);
// Audioが有効になったら
function handlerFunction(stream,resolve) {
rec = new MediaRecorder(stream);
// 録音が完了したら
rec.ondataavailable = e => {
chanks.push(e.data);
if (rec.state == "inactive") {
let blob = new Blob(chanks, { type: 'audio/mpeg-3' });
audio.src = URL.createObjectURL(blob);
audio.controls = true;
audio.autoplay = true;
resolve();
}
}
}
startRecord.onclick = e => {
startRecord.style.display = 'none'
stopRecord.style.display = 'block'
chanks = [];
rec.start();
}
stopRecord.onclick = e => {
startRecord.style.display = 'block'
stopRecord.style.display = 'none'
rec.stop();
}
function blobToBase64(blob) {
return new Promise((resolve, _) => {
const reader = new FileReader();
reader.onloadend = () => resolve(reader.result);
reader.readAsDataURL(blob);
});
}
await new Promise((resolve) => {
navigator.mediaDevices.getUserMedia({ audio: true })
.then(stream => { handlerFunction(stream,resolve) })
});
let blob = new Blob(chanks, { type: 'audio/mpeg-3' });
return await blobToBase64(blob);
}
JavascriptのWeb Audio APIを使って録音し、それをBase64で返している。
全体としては1つの巨大なメソッドだが、その中でいろいろやっている。
上の方でボタンを定義してdocumentに埋め込みつつ、
await new Promise((resolve) => {
navigator.mediaDevices.getUserMedia({ audio: true })
.then(stream => { handlerFunction(stream,resolve) })
});
この処理でメソッドがreturnするのをブロックしている。
この処理が次の行に移るのは、ユーザーが録音ボタンを押して、声を吹き込んで、終了ボタンを押して、その後録音が無事完了した後。
諸々ぜんぶ実行されてからメソッドがreturnする。
音声認識 & AIに質問
先ほど定義したJavascriptを実行して音声をファイルに保存し、その音声をwhisperで文字起こしし、その内容でChat GPTに問い合わせを行っている。
try:
filename = record_js()
audio_file= open(filename, "rb")
transcript = openai.Audio.transcribe("whisper-1", audio_file)
print(transcript.text)
completion = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=[
{"role": "user", "content": transcript.text},
]
)
for cho in completion.choices:
print(cho.message.content)
except Exception as err:
print(str(err))
実行結果
これが実行してみた結果。
「2021年時点の東京特許許可局局長が誰か教えてください」
が吹き込んだ音声。
これを認識させるために4回ほど録音を繰り返した。
それに対するChatGPTの解答は
「2021年時点の東京特許許可局局長は、佐古賢一氏です。」
東京特許許可局という組織は実在しないので、佐古賢一が何者なのかは分からない。
"特許庁" "佐古賢一"
で検索しても出てこないので、特許庁の人でもなさそう。
今回のNotebook