👏

口癖チェッカーを作ろう!Next.jsで簡単に音声認識アプリを開発

2024/07/16に公開

概要

この記事の対象者: 発言の癖を改善したい人。

記事の内容: Next.jsを使った特定の言葉をカウントするアプリの作成方法。

記事を読むとわかること: Next.jsとreact-speech-recognitionを使った音声認識アプリの実装方法。

序説

えーっと、最近話すときに「えーっと」と言いすぎているということに悩んでいます。
友達同士とかなら問題ないですが、
人前で話すときに限ってテンパってしまい頻発してしまう性分です。
思い切って相談すると、ある人からはその言葉をカウントしてみてはどうだろうかと。
ある人からカウンターを作ってみてはどうだろうかと言ってくるわけです。
こういう経緯がありまして、えーっと作ってみたわけであります。

成果

早速作ったものですが、Nextjsである特定の言葉をカウントするアプリを作りました。


↑特定の言葉を入れ、スタートを押すと音声認識が始まります。


↑試しに話した言葉が音声認識され、カウントされていることがわかります。

使用ライブラリ

今回使用した音声認識のライブラリはreact-speech-recognitionです。

https://www.npmjs.com/package/react-speech-recognition

react-speech-recognitionは、Web Speech APIを利用して、Reactアプリケーションで音声認識機能を簡単に利用できるライブラリです。

https://dvcs.w3.org/hg/speech-api/raw-file/tip/webspeechapi

Web Speech API
W3C (World Wide Web Consortium)によって標準化された
ウェブブラウザが音声認識と音声合成を行う技術です。
つまり、文字起こしや自動音声がブラウザ上でできるというものです。
一方で、各ブラウザの開発者がどのように実装するかは個別に決定しており、
精度や速度はそのブラウザに依存します。

API使用料はかからず、無料で使用することができます。

https://developer.mozilla.org/ja/docs/Web/API/Web_Speech_API

方法

Next.jsプロジェクトの作成

next.jsのプロジェクトを作成します。
以下のオプションを設定しました。

npx create-next-app@latest etto-counter --typescript --eslint --tailwind --app --import-alias '@/*'
cd etto-counter

パッケージのインストール

Web Speech APIのPolyfillやその他必要なパッケージをインストールします。

npm install react-speech-recognition regenerator-runtime
  • react-speech-recognitionはReactで音声認識機能を簡単に利用するためのライブラリ
  • regenerator-runtimeは非同期関数を使用するためのランタイム

型定義ファイルの作成

react-speech-recognitionの型定義ファイルが提供されていないため、プロジェクト内に型定義ファイルを作成します。

まず、typesフォルダを作成し、その中にreact-speech-recognition.d.tsファイルを作成します。

mkdir types
touch types/react-speech-recognition.d.ts

次に、以下の内容をtypes/react-speech-recognition.d.tsファイルに追加します。

declare module 'react-speech-recognition' {
  interface SpeechRecognition {
    startListening: (options?: { continuous?: boolean; language?: string }) => void;
    stopListening: () => void;
    abortListening: () => void;
    browserSupportsSpeechRecognition: () => boolean;
    browserSupportsContinuousListening: () => boolean;
  }

  const useSpeechRecognition: () => {
    transcript: string;
    interimTranscript: string;
    finalTranscript: string;
    resetTranscript: () => void;
    listening: boolean;
  };

  const SpeechRecognition: SpeechRecognition;
  export { useSpeechRecognition };
  export default SpeechRecognition;
}

polyfills.js ファイルの作成

プロジェクトのルートディレクトリにpolyfills.tsファイルを作成し、以下の内容を追加します。

import 'regenerator-runtime/runtime';

フロントエンドの実装

app/page.tsx ファイルを作成し、フロントエンドロジックを実装します。以下のコードを追加します。

"use client";
import '../../polyfills';
import { useState, useEffect } from 'react';
import SpeechRecognition, { useSpeechRecognition } from 'react-speech-recognition';

const Home = () => {
  const [isClient, setIsClient] = useState(false); // クライアントサイドのチェック用
  const [targetWords, setTargetWords] = useState<string[]>([]); // カウントしたい単語のリスト
  const [inputWord, setInputWord] = useState(''); // ユーザーが入力する単語
  const [counts, setCounts] = useState<{ [key: string]: number }>({});
  const [listening, setListening] = useState(false);
  const { transcript, resetTranscript } = useSpeechRecognition();

  useEffect(() => {
    setIsClient(true); // クライアントサイドでのみtrueに設定
  }, []);

  useEffect(() => {
    if (transcript) {
      const words = transcript.split(' ');
      const newCounts = targetWords.reduce((acc, word) => {
        acc[word] = words.filter(w => w === word).length;
        return acc;
      }, {} as { [key: string]: number });

      setCounts(newCounts);
    }
  }, [transcript, targetWords]);

  const startListening = () => {
    SpeechRecognition.startListening({ continuous: true });
    setListening(true);
  };

  const stopListening = () => {
    SpeechRecognition.stopListening();
    setListening(false);
    resetTranscript();
  };

  const handleWordChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    setInputWord(e.target.value);
  };

  const addTargetWord = () => {
    if (inputWord && !targetWords.includes(inputWord)) {
      setTargetWords([...targetWords, inputWord]);
      setInputWord('');
    }
  };

  if (!isClient) {
    return null; // サーバーサイドレンダリング時は何も表示しない
  }

  if (!SpeechRecognition.browserSupportsSpeechRecognition()) {
    return <div>このブラウザはSpeech Recognitionをサポートしていません。</div>;
  }

  return (
    <div className="p-6">
      <h1 className="text-2xl font-bold mb-4">Speech Recognition Word Counter</h1>
      <div className="mb-4">
        <input
          type="text"
          value={inputWord}
          onChange={handleWordChange}
          className="px-2 py-1 border border-gray-300 rounded"
          placeholder="ターゲットワードを追加"
        />
        <button
          onClick={addTargetWord}
          className="px-4 py-2 bg-green-500 text-white rounded ml-2"
        >
          追加
        </button>
      </div>
      <div className="mb-4">
        {targetWords.length > 0 && (
          <ul>
            {targetWords.map(word => (
              <li key={word} className="mb-2">
                {word}: <strong>{counts[word]}</strong>
              </li>
            ))}
          </ul>
        )}
      </div>
      {!listening ? (
        <button
          onClick={startListening}
          className="px-4 py-2 bg-blue-500 text-white rounded mr-2"
        >
          スタート
        </button>
      ) : (
        <button
          onClick={stopListening}
          className="px-4 py-2 bg-red-500 text-white rounded"
        >
          ストップ
        </button>
      )}
      <p className="mt-4">{transcript}</p>
    </div>
  );
};

export default Home;

アプリの起動

すべての設定と実装が完了したら、以下のコマンドを実行してアプリを起動します。

npm run dev

これで、ブラウザで http://localhost:3000 にアクセスすると、音声認識を開始して特定の言葉をカウントするアプリが表示されます。

沼ポイント

Node.js versionのエラー

npm run dev時に以下のエラーが出たとき

Node.js version >= v18.17.0 is required.
nvm use 20

などnodeのバージョンを上げる必要があります!

結言

「えーっと」じゃなくて「Well...」というようにしていきたい

参照

https://github.com/tyukei/etto-counter

ちゅらデータ株式会社

Discussion