OpenAIのWhisperを使用して文字起こし[Python]

## はじめに
普段はビジネスサイドでBizDev、なんでも屋をやっています。
Whisper使ってみたかったのでapiに触ってみたんですが、ファイル制限とか少しつまづくことがあったので共有がてら記事にしました。

## OpenAIのWhisperとは

OpenAIのWhisperは、音声認識モデルの1つで、様々な環境での音声の自然さや安定性に優れています。Whisperは、英語、日本語、中国語など、多数の言語に対応しています。Whisperを使用するには、OpenAIのAPIキーが必要なので各自取得して下さい。

## 作り方
### 必要なライブラリのインストール


- openai
- pydub

```python
pip install openai pydub
```
### APIキーの設定

OpenAIのWhisperを使用するには、APIキーの設定が必要です。APIキーは、OpenAIのWebサイトで取得できます。取得したAPIキーを、プログラム内に設定します。ここではconfig.pyという別ファイルに変数openai_api_keyとして入れてます。
```python
import openai
import config

openai.api_key = config.openai_api_key
```

### ファイルから音声を抽出する

ファイルから音声を抽出するために、ffmpegを使用します。以下のコードでは、入力ファイルのパスを指定し、音声ファイルを一時ファイルとして保存します。
```python
import pathlib
import subprocess
import tempfile

input_file = "/path/to/input_file.mp4"
original_file = pathlib.Path(input_file)

with tempfile.NamedTemporaryFile(suffix=original_file.suffix) as audio_file:
    subprocess.run(["ffmpeg", "-y", "-i", str(original_file), "-codec:a", "copy", "-vn", audio_file.name])
```
### オーディオファイルの圧縮

Whisperに音声を送信する前に、オーディオファイルを圧縮する必要があります。以下のコードでは、ファイルサイズを目標値に調整し、適切なビットレートを計算しています。ビットレートを下げすぎると音声の品質が低下するため、8kbps未満には設定できません。

```python
import math
from pydub import AudioSegment

TARGET_FILE_SIZE = "25000000"

audio_size = pathlib.Path(audio_file.name).stat().st_size

if audio_size > TARGET_FILE_SIZE:
    audio_segment = AudioSegment.from_file(audio_file.name)
    audio_length_sec = len(audio_segment) / 1000
    target_kbps = int(math.floor(TARGET_FILE_SIZE * 8 / audio_length_sec / 1000 * 0.95))

    if target_kbps < 8:
        assert f"{target_kbps=} is not supported."

    with tempfile.NamedTemporaryFile(suffix=".mp4") as converted_file:
        subprocess.run(["ffmpeg", "-y", "-i", audio_file.name, "-codec:a", "aac", "-ar", "16000", "-ac", "1",
                        "-b:a", f"{target_kbps}k", converted_file.name])
```
### Whisperを使用して音声認識を行う

以下のコードでは、Whisperを使用して音声認識を行います。オーディオファイルを読み込み、適切なプロンプトを指定します。この場合、会話内容に応じてプロンプトを調整する必要があります。
```python
import datetime

prompt = "プロンプトを入力してください。"
with open(converted_file.name, "rb") as f:
    response = openai.Audio.transcribe("whisper-1", f, prompt=prompt, language="ja")
transcription = str(response["text"])
```

### 認識結果をファイルに書き出す

以下のコードでは、認識結果をテキストファイルに書き出します。ファイル名には、現在の日時が含まれています。

```python
current_time_str = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
output_file = f"/path/to/output_file_{current_time_str}.txt"
with open(output_file, "wt", encoding="utf-8") as f:
    f.writelines([transcription])
```

### 完成型
```python
import openai
import config
import pathlib
import subprocess
import math
from pydub import AudioSegment
import tempfile
import datetime


# APIキーを設定します
openai.api_key = config.openai_api_key

# ファイルのパスを指定します
input_file = ""
original_file = pathlib.Path(input_file)

with tempfile.NamedTemporaryFile(suffix=original_file.suffix) as audio_file:
    # ffmpegを使ってビデオからオーディオファイルを抽出します
    subprocess.run(["ffmpeg", "-y", "-i", str(original_file)
        , "-codec:a", "copy", "-vn", audio_file.name])

    TARGET_FILE_SIZE = 25000000

    # オーディオファイルのサイズを表示します
    audio_size = pathlib.Path(audio_file.name).stat().st_size
    print(f"{audio_size=}")
    if audio_size > TARGET_FILE_SIZE:
        print("This file needs to be converted.")

    audio_segment = AudioSegment.from_file(audio_file.name)

    audio_length_sec = len(audio_segment)/1000

    target_kbps = int(math.floor(TARGET_FILE_SIZE * 8 / audio_length_sec / 1000 * 0.95))

    if target_kbps < 8:
        assert f"{target_kbps=} is not supported."

    with tempfile.NamedTemporaryFile(suffix=".mp4") as converted_file:
        subprocess.run(["ffmpeg", "-y", "-i", audio_file.name
            , "-codec:a", "aac", "-ar", "16000", "-ac", "1", "-b:a", f"{target_kbps}k"
            , converted_file.name])

        print(f"{pathlib.Path(converted_file.name).stat().st_size=}")

        # プロンプトを指定
        prompt = ""

        with open(converted_file.name, "rb") as f:
            response = openai.Audio.transcribe("whisper-1", f
            ,prompt=prompt
            ,language = "ja")
        transcription = str(response["text"])

        # 現在の日時を取得し、指定された形式で文字列に変換します
        current_time_str = datetime.datetime.now().strftime("%Y%m%d%H%M%S")

        # ファイル名に日時文字列を追加して、新しいファイル名を作成します
        output_file = f"/path/to/output_file_{current_time_str}.txt"

        # ファイルを指定された名前で保存します
        with open(output_file, "wt", encoding="utf-8") as f:
            f.writelines([transcription])
```
## おわりに

ミーティングが多い営業職などは、Whisperは、有効活用したいところです。
拙いコードですがコピペで使えると思うのでよかったらどうぞ。
それにしてもChatGPTのおかげでコーディングが楽になりました。

## 参考記事
OpenAIのWhisper APIの25MB制限に合うような調整を検討する
https://dev.classmethod.jp/articles/openai-api-whisper-about-data-limit/