openai の whisper を node で触ってみた



## はじめに

openai の whisper の文字起こし(Create transcription) を node で触ってみたので書いてみました！

whisper を触っている人が大体が python なので、node で書いてみようかなと思い投稿しました。

詳しい内容は下記の公式を見てください！

### [公式](https://platform.openai.com/docs/api-reference/audio)

## まず nextjs でプロジェクトを作成

今回は nextjs のプロジェクトで起きたので nextjs で書きます。

- [公式](https://nextjs.org/docs/pages/api-reference/create-next-app)

```
npx create-next-app@latest
```

## ブラウザで録音の準備

- 今回は MediaRecorder で録音して行います。
- 今回は簡易的に下記のように作成しました。

hooks.ts

```
import { useRef, useState } from "react";

type Hooks = {
  startRecording: () => void;
  stopRecording: () => void;
  isAudio: boolean;
};

export const useHooks = (): Hooks => {
  const mediaRecorder = useRef<MediaRecorder | null>(null);
  const [audioFile, setAudioFile] = useState<File | null>(null);
  const [isAudio, setIsAudio] = useState<boolean>(false);

  const handleDataAvailable = (event: BlobEvent) => {
    // 音声ファイル生成
    const file = new File([event.data], "audio.mp3", {
      type: event.data.type,
      lastModified: Date.now(),
    });
    setAudioFile(file);
  };

  const startRecording = async() => {
    // 録音開始
    const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
    mediaRecorder.current = new MediaRecorder(stream);
    mediaRecorder.current.start();
    mediaRecorder.current.addEventListener(
      "dataavailable",
      handleDataAvailable
    );
    setIsAudio(true);
  };

  const stopRecording = () => {
    // 録音停止
    mediaRecorder.current?.stop();
    setIsAudio(false);
  };

  return {
    startRecording,
    stopRecording,
    isAudio,
  };
};

```

index.tsx

- 部分だけ書いてますので、変更して使用してください。

```
  const {
    startRecording,
    stopRecording,
    isAudio,
  } = useHooks();
  <button
    type="button"
    onClick={startRecording}
    disabled={isAudio}
  >
    録音スタート
  </button>
  <button
    type="button"
    onClick={startRecording}
    disabled={isAudio}
  >
    録音ストップ
  </button>
```

## openai key 取得

openai にアクセスして、[apikey](https://platform.openai.com/account/api-keys)を取得してください。
わからない方は色んな方が解説しているので、そこを参考にして下さい。

## openai whisper の設定

- model は`whisper-1`しかないため、`whisper-1`を採用。(2023 年 5 月 15 日)
- file は先ほどの録音した file を使用。
- language は今回は日本語の精度を高めたかったので`ja`を使用。
- 他の設定は今回使ってないが公式を見てプロジェクトにあったものを採用すればいいと思います。

## .env or .env.local に apiKey を置く

.env か.env.local に下記を置いて、呼び出すのが無難だと思います。

`NEXT_PUBLIC_OPENAI_API_KEY=Your Api Key`

## エンドポイントを選択

- `https://api.openai.com/v1/audio/transcriptions`

## openai whisper を叩く

今回は useEffect を使用して、audioFile が更新されたら、openai api を叩く実装にしています。

hooks.ts の続き

```
  useEffect(() => {
    const uploadAudio = async () => {
      if (!audioFile) return;
      const endPoint = https://api.openai.com/v1/audio/transcriptions

      const formData = new FormData();
      // fileを指定
      formData.append("file", audioFile, "audio.mp3");
      // modelを指定
      formData.append("model", "whisper-1");
      // languageを指定
      formData.append("language", "ja");
      setIsLoading(true);
      const response = await fetch(endPoint, {
        method: "POST",
        headers: {
          Authorization: `Bearer ${process.env.NEXT_PUBLIC_OPENAI_API_KEY}`,
        },
        body: formData,
      });
      const responseData = await response.json();
      if (responseData.text) {
        // 文字起こしされたテキスト
        setTranscript(responseData.text);
      }
      setAudioFile(null);
      setIsLoading(false);
    };
    uploadAudio();
  }, [audioFile]);
```

## 終わりに

実際に触ってみると、簡単に作成が出来ました。
色んなカスタマイズをして、プロジェクトに合った選択をしてください！

## 補足

- 無音やノイズで予期しない文字起こしをされるため、無音除去やノイズ除去は必須かも(node で完全に除去が出来たらいいなぁ)
  - ご質問ありがとうございます。などなど
  - [予期しない文字起こしの議論](https://github.com/openai/whisper/discussions/29)