Next.js+WebSpeechAPIで超簡単音声認識をしてみよう
WebSpeechAPIを使うことで、新しくPythonのコンテナを作ることも、ライブラリを導入しなくても、 APIを叩くこともなく、無料で簡単に音声認識をすることができます。
この記事では、Web上で簡単に自分のしゃべった内容を認識する方法を、Next.js+WebSpeechAPIを用いて解説します。
WebSpeechAPIとは
ウェブ音声 API (Web Speech API) で、音声データをウェブアプリに組み入れることができます。 ウェブ音声 API は、SpeechSynthesis (音声合成、Text-to-Speech)と SpeechRecognition (非同期音声認識、Asynchronous Speech Recognition)の 2 つの部分から成り立っています。
WebSpeechAPIは少し前まではChromeでしか対応していませんでしたが、現在はChromeやEdge、Safariなど主要ブラウザの多くでサポートしています。(互換性リスト)
現在、OpenAIなども音声認識機能を提供していますが、多少金がかかってしまいます。もし認識部分をフロントで完結させるのであれば、WebSpeechAPIを使ってみてもいいのではないでしょうか・
完成例
Repository
実際に確かめられるサイト
これをこの記事では実際に作ることができます。
環境構築
ただの環境構築なので、既知の方は飛ばしてください。
Next.jsの構築
ディレクトリ名は任意の好きな名前にしてください。
本記事はtest
としています
mkdir test
cd test
Next.js環境を作ります
npx create-next-app . --typescript
上記コマンド実行時、色々設定をしますが好きに設定してください。
私の場合は
✔ Would you like to use App Router? (recommended) … No / Yes
をNoに、それ以外はそのままエンターキーを押して次に進みます。
これで基本の環境構築は完了です。以下のコマンドでhttp://localhost:3000/
を開くと現状を確認できます。
npm run dev
ファイルを整理
デフォルトの画面をまっさらにするために以下のファイルを修正します。
export default function Home() {
return (
<main>
<div>
<p>テスト</p>
</div>
</main>
);
}
@tailwind base;
@tailwind components;
@tailwind utilities;
必要なpackageのインストール
TypeScirptで進める関係上、WebSpeechAPIの型定義ファイルをもらってきます。
npm i -D @types/webspeechapi
いざ音声認識
コード全体
下のコードをsrc/pages/index.ts
に貼り付けるだけで機能します。簡単ですね。
import { useState, useEffect } from "react";
export default function Home() {
const [isRecording, setIsRecording] = useState(false);
const [text, setText] = useState<string>("");
const [transcript, setTranscript] = useState<string>("");
const [recognition, setRecognition] = useState<SpeechRecognition | null>(
null
);
useEffect(() => {
if (typeof window !== "undefined") {
const recognition = new webkitSpeechRecognition();
recognition.lang = "ja-JP";
recognition.continuous = true;
recognition.interimResults = true;
setRecognition(recognition);
}
}, []);
useEffect(() => {
if (!recognition) return;
if (isRecording) {
recognition.start();
} else {
recognition.stop();
setText("");
}
}, [isRecording]);
useEffect(() => {
if (!recognition) return;
recognition.onresult = (event) => {
const results = event.results;
for (let i = event.resultIndex; i < results.length; i++) {
if (results[i].isFinal) {
setText((prevText) => prevText + results[i][0].transcript);
setTranscript("");
} else {
setTranscript(results[i][0].transcript);
}
}
};
}, [recognition]);
return (
<main>
<button
onClick={() => {
setIsRecording((prev) => !prev);
}}
>
{isRecording ? "停止" : "録音開始"}
</button>
<div>
<p>途中経過:{transcript}</p>
<p>解析:{text}</p>
</div>
</main>
);
}
各部分の解説
必要な状態を宣言
const [isRecording, setIsRecording] = useState(false);
const [text, setText] = useState<string>("");
const [transcript, setTranscript] = useState<string>("");
const [recognition, setRecognition] = useState<SpeechRecognition | null>(
null
);
変数名 | 説明 |
---|---|
isRecording | 録音を開始しているかどうか |
text | 録音済みのテキスト |
transcript | 録音中のテキスト |
recognition | 生成したSpeechRecognitionを格納する |
SpeechRecognitionを生成
useEffect(() => {
if (typeof window !== "undefined") {
const recognition = new webkitSpeechRecognition();
recognition.lang = "ja-JP";
recognition.continuous = true;
recognition.interimResults = true;
setRecognition(recognition);
}
}, []);
ページの初期読み込み時に、SpeechRecognitionを生成してもろもろ設定してます。
日本語以外の言語もいけちゃいます。
ボタン押下によって録音を開始する
useEffect(() => {
if (!recognition) return;
if (isRecording) {
recognition.start();
} else {
recognition.stop();
setText("");
}
}, [isRecording]);
ボタンを押すとisRecording
のbool値が反転するので、その値によって音声認識のオンオフをしています。本記事ではオフにしたときにtext
を削除していますが、これを消すことでずっと認識した内容を保持し続けることができます。
解析結果を取得する
useEffect(() => {
if (!recognition) return;
recognition.onresult = (event) => {
const results = event.results;
for (let i = event.resultIndex; i < results.length; i++) {
if (results[i].isFinal) {
setText((prevText) => prevText + results[i][0].transcript);
setTranscript("");
} else {
setTranscript(results[i][0].transcript);
}
}
};
}, [recognition]);
recognition.onresult
によって結果を取得することができます。
results[i].isFinal
がtrue
なら認識した一文の最終結果が格納されており、そうでないならその時々の途中の認識結果が格納されています。
結果を表示する
return (
<main>
<button
onClick={() => {
setIsRecording((prev) => !prev);
}}
>
{isRecording ? "停止" : "録音開始"}
</button>
<div>
<p>途中経過:{transcript}</p>
<p>解析:{text}</p>
</div>
</main>
);
アルティメットガバガバデザインです。
あとからみなさんの方でCSSバチクソに決めてもらうためにめちゃシンプルにしました。
button
タグでボタンを生成し、クリックするとisRecording
が切り替わります。
あとは途中結果と解析結果を表示して完成です。
精度
音声認識の精度は非常に高いと思います。と言うのもWebSpeechAPIは音声情報をGoogleのCloud Speech-to-Textに送って音声認識をしてルタめ最高水準の精度で認識できていると思います(参考)。
細かいカスタマイズはできないので、専門的な用語や漢字の誤変換等がたまにみられることがありますが、一般的な利用では最適ではないでしょうか。
注意点
まずfirefoxなど一部のブラウザに対応してないので、ちゃんと使うなら前置きがいりそうです。
また、少し離れた(1.5mくらい)場所での会話、生声ではなく機器越しの音声ではあんまりいい精度は出ないような印象でした。そのため、本格的にアプリケーションに組み込むのは慎重になる必要がありそうです。
まとめと宣伝
とりあえず音声認識機能が欲しかったり、趣味開発で導入したい場合はめっちゃ楽に組み込むことができるのでおすすめです。
宣伝ですが、音を認識して波形を生成するパッケージを作りました。React環境で簡単に導入することができます。ぜひ使ってみてください。
Discussion