🎼

fastapiで音声を測定するAPIを作ってみた

2024/09/01に公開

音のテンポを測定するAPIを作ってみた

昔、Flutterの案件でしたが、音声の音のテンポBPMというらしい?
を測定する案件に携わったことがありました。しかし、Flutter, Dartには音声を測定するパッケージはありませんでした😵

音声の再生時間は、14〜20秒ぐらいが良いと思われます。短いサンプロを用意して、試してみてください。
無料の音声サンプルサイト

当時は、ChatGPTに質問すると、C++, Pythonで作れると教えてくれました?
本当か😅

Pythonは、経験があったので試しに作ってみた。当時は、librosaというライブラリを使っていました。作った後は、Cloud Runにデプロイして、Flutterから、file_picker, http packageを使って、HTTP POSTして、MP3ファイルの音のテンポを測定していました。123BPMとか測定した数値が返ってきていました。

https://librosa.org/doc/latest/index.html

当時は、Flaskの経験があったので、使ってましたが、最近は、fastapiが流行ってるので、同じの作れないかなと試しにやってみたら、作れました!

できてるか怪しいが....

Flutterのコードは、仕事で作ったものなので、公開できませんが、fastapiのコードは、趣味で作ったものなので、公開できます。仕事のは、Flaskなので笑

ファイルは、3つあればOKです

  1. app.py
  2. requirements.txt
  3. Dockerfile

Google Cloudに、デプロイしてみたい人は試してみてください。結構躓きます笑

fastapiのファイルを作成。これだけあればOK。今回は、ローカル環境でしか動かしません。

app.py
import os
import tempfile
from fastapi import FastAPI, File, UploadFile, HTTPException
from fastapi.middleware.cors import CORSMiddleware
import librosa
from pydantic import BaseModel

app = FastAPI()

# CORSミドルウェアの設定
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

class BPMResponse(BaseModel):
    bpm: float

@app.get("/")
async def home():
    return "FastAPI BPM API"

async def estimate_tempo(audio_file: UploadFile):
    with tempfile.NamedTemporaryFile(suffix=".mp3", delete=True) as temp_file:
        content = await audio_file.read()
        temp_file.write(content)
        temp_file.flush()

        y, sr = librosa.load(temp_file.name, sr=None)
        onset_env = librosa.onset.onset_strength(y=y, sr=sr)
        tempo, _ = librosa.beat.beat_track(onset_envelope=onset_env, sr=sr)

        if tempo == 0:

            tempo = librosa.feature.tempo(onset_envelope=onset_env, sr=sr)[0]

        return tempo

@app.post("/estimate_bpm", response_model=BPMResponse)
async def upload_file(audio: UploadFile = File(...)):
    try:
        bpm = await estimate_tempo(audio)
        return {"bpm": bpm}
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

if __name__ == "__main__":
    import uvicorn
    port = int(os.environ.get('PORT', 8080))
    uvicorn.run(app, host="0.0.0.0", port=port)

パッケージをインストールするファイルを作成。私、Pythonエンジニアを4ヶ月やってた頃に、この書き方覚えました。コマンド打ったら、インストールしたパッケージをファイルに追加してくれるコマンドもあったりします。

fastapi
uvicorn
python-multipart
librosa

Dockerfileを作成。これは正直いうと参考にしながら、作ったので、いいものではないかも?
Flaskの時も急いで作ったから、あれも良いものか?

Dockerfile
FROM python:3.9-slim

# システムの依存関係をインストール
RUN apt-get update && apt-get install -y \
    libsndfile1 \
    libasound2-dev \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8080"]

BPM測定APIの使い方

Flaskと違って、Swaggerが使えるのが、良かった笑
fastapi最高です。Flutter, Reactから、音声ファイルを送信するのですが、Swagger使えば、file uploadが使えるので、バックエンドだけでも動作検証はできるようになりました。

  1. Dockerイメージをビルド
docker build -t fastapi-bpm-api .
  1. Dockerコンテナを実行
docker run -p 8080:8080 fastapi-bpm-api

Hello URL

http://localhost:8080/

Swagger URL

http://localhost:8080/docs

こんな感じで動きます。お試しあれ。Dockerは、起動しておいてくださいね。



最後に

解析系のツールとかを作るなら、ライブラリが豊富なPythonを使うと良いそうで、Web APIを自作できるのではと、試しにやってました。意外とできた。他にも趣味で解析系のAPI作りたいですね。

Flaskで、デプロイした時は、こんな感じでやりました。コマンドは一緒ですが、プロジェクトIDとかが入力の時に必要でしたね。IAMの設定とかするので、クラウドを操作するときは、難易度上がるかも?
Cloud Runは無料だそうですが、私には、毎月10〜15円の請求が来てました笑
コンテナのイメージで、料金がかかるとか?
 
https://zenn.dev/joo_hashi/articles/4f3b96f9589b59
https://zenn.dev/joo_hashi/articles/d8dc44e32d91e2

Discussion