🤖

Cloudflare Workers AI 使ってみた「音声認識編」

に公開

CloudflareのAI APIでWhisperモデルを使った音声認識アプリの構築

こんにちは、ikechan こといけがわです。

Cloudflare Workers AIの新しいモデルを試す連載、今回は音声認識モデル Whisper を使ってみました。WhisperはOpenAIが開発した高精度な音声認識モデルで、多言語対応の音声からテキストへの変換(Speech-to-Text)を実現します。

今回はmacOSのターミナルからリアルタイムで音声を録音し、Whisperモデルでテキスト変換する実験的なアプリケーションを構築してみました。

前提条件

この記事を進めるにあたり、以下の準備が必要です。

  • Cloudflare アカウント
    Workers AI の利用に必須。Whisperモデルが有効になっているか確認してください。
  • Node.js / Bun 開発環境
    今回はHonoフレームワークとBunを使用してサーバーを構築します。
  • macOS 環境
    sox(Sound eXchange)ツールを使用するので、Homebrewでインストールが必要です。

実装手順

1. 環境のセットアップ

まず、Honoプロジェクトを作成し、必要なパッケージをインストールします。

bun create hono@latest voice-recognition-app
cd voice-recognition-app
bun add sox  # 音声録音用のパッケージ

macOSでは以下のコマンドで録音に必要なツールをインストールします。

brew install sox

2. APIリクエスト用のユーティリティ

まず、Cloudflare Workers AIへのリクエストを処理するコードを実装します。usecase.tsファイルを作成しましょう。

import { config } from "./constant";
import type {
  ApiRequestParams,
  AudioTranscriptionResult,
} from "./types";

export const makeApiRequest = async ({
  endpoint,
  data,
  contentType,
}: ApiRequestParams): Promise<Response> => {
  console.log("url", `${config.url}${endpoint}`);
  const response = await fetch(`${config.url}${endpoint}`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${config.key}`,
      "Content-Type": contentType,
    },
    body: data,
  });

  if (!response.ok) {
    const errorText = await response.text();
    throw new Error(
      `API Error: ${response.status} ${response.statusText} - ${errorText}`
    );
  }

  return response;
};

// 音声認識用の関数
export const transcribeAudio = async (audioData: Uint8Array): Promise<any> => {
  try {
    console.log("Whisperモデルにリクエスト送信中...");
    console.log(`送信データサイズ: ${audioData.length} バイト`);
    
    const response = await makeApiRequest({
      endpoint: "/@cf/openai/whisper",
      data: audioData,
      contentType: "audio/wav",
    });
    
    console.log("APIステータスコード:", response.status);
    
    return response.json();
  } catch (error) {
    console.error("API呼び出しエラー:", error);
    throw error;
  }
};

3. 設定ファイルの作成

constant.tsファイルにCloudflare APIの設定を記述します。

export const config = {
  url: "https://api.cloudflare.com/client/v4/accounts/あなたのアカウントID/ai/run",
  key: "あなたのAPIキー"
};

4. 音声録音とWhisperモデルの実行

record.tsファイルに、ターミナルからの音声録音とWhisperモデルでの認識を行うコードを実装します。

import { transcribeAudio } from "./usecase";
import fs from "fs";
import path from "path";
import { spawn } from "child_process";

// 録音用の一時ファイルパス
const TEMP_WAV_FILE = path.join(process.cwd(), "temp_recording.wav");

// macOSでの録音処理(soxを使用)
async function recordAudioWithSox(durationSeconds: number): Promise<Uint8Array> {
  console.log(`マイクから録音を開始します (${durationSeconds}秒間)...`);

  // 既存の録音ファイルを削除
  if (fs.existsSync(TEMP_WAV_FILE)) {
    fs.unlinkSync(TEMP_WAV_FILE);
  }

  // soxのrecコマンドで録音を実行
  return new Promise((resolve, reject) => {
    const rec = spawn("rec", [
      "-q",               // 静かモード
      TEMP_WAV_FILE,      // 出力ファイル
      "rate", "16k",      // サンプルレート16kHz
      "channels", "1",    // モノラル
      "trim", "0", `${durationSeconds}`  // 録音時間
    ]);

    rec.on("close", (code) => {
      if (code !== 0) {
        reject(new Error(`録音プロセスが異常終了しました (code: ${code})`));
        return;
      }

      try {
        // 録音ファイルを読み込み
        const audioData = fs.readFileSync(TEMP_WAV_FILE);
        resolve(new Uint8Array(audioData));
      } catch (error) {
        reject(error);
      }
    });

    rec.stderr.on("data", (data) => {
      console.error(`録音エラー: ${data}`);
    });
  });
}

// メイン処理
async function main() {
  console.log("Cloudflare Whisper音声認識ツール");
  console.log("================================");
  console.log(
    "マイクから音声を録音し、Whisperモデルを使って文字起こしを行います。"
  );

  while (true) {
    try {
      // 5秒間録音
      const audioData = await recordAudioWithSox(5);
      console.log("録音完了! 文字起こし処理中...");

      // Whisperモデルで文字起こし
      const response = await transcribeAudio(audioData);

      console.log("\n--- 文字起こし結果 ---");
      if (response && response.result && response.result.text) {
        console.log(response.result.text);
        
        if (response.result.word_count) {
          console.log(`単語数: ${response.result.word_count}`);
        }
      } else {
        console.log("結果が取得できませんでした。");
      }
    } catch (error) {
      console.error("エラーが発生しました:", error);
    }

    console.log(
      "\n続けるには Enter キーを押してください。終了するには Ctrl+C を押してください。"
    );
    await new Promise((resolve) => {
      process.stdin.once("data", () => resolve(null));
    });
  }
}

// スクリプトが直接実行された場合のみ実行
if (require.main === module) {
  main().catch(console.error);
}

export { recordAudioWithSox, transcribeAudio };

5. APIエンドポイントの実装

WebサーバーAPIからWhisperモデルを利用できるように、index.tsファイルにエンドポイントを追加します。

import { Hono } from "hono";
import { transcribeAudio } from "./usecase";

const app = new Hono();

// 音声認識エンドポイント
app.post("/transcribe", async (c) => {
  try {
    const audioBuffer = await c.req.arrayBuffer();
    if (!audioBuffer || audioBuffer.byteLength === 0) {
      return c.json({ success: false, error: "Audio data is required" }, 400);
    }

    const audioData = new Uint8Array(audioBuffer);
    const result = await transcribeAudio(audioData);

    return c.json({ success: true, result });
  } catch (error) {
    return c.json(
      {
        success: false, 
        error: error instanceof Error ? error.message : "Unknown error occurred"
      },
      400
    );
  }
});

Bun.serve({
  fetch: app.fetch,
  port: 8080
});

export default app;

動作確認

1. ターミナルからの音声認識

ターミナルからCLIモードでWhisperモデルを使って音声認識を行う場合は、以下のコマンドを実行します。

bun run record.ts

これを実行すると、5秒間の録音が始まり、録音終了後にWhisperモデルによる認識結果が表示されます。

2. APIを使った音声認識

サーバーを起動して、WebAPIとして音声認識を提供する場合は以下のコマンドを実行します。

bun run index.ts

その後、別のターミナルから以下のようにcurlコマンドで音声ファイルを送信することでAPIを利用できます。

curl -X POST http://localhost:8080/transcribe \
  --data-binary @path/to/your/audiofile.wav \
  -H "Content-Type: audio/wav"

実行結果

実際に「おはようございます」と言ってみた時の認識結果です:

マイクから録音を開始します (5秒間)...
録音完了! 文字起こし処理中...
Whisperモデルにリクエスト送信中...
送信データサイズ: 320080 バイト
APIステータスコード: 200

--- 文字起こし結果 ---
おはようございます
単語数: 4

一度録音した後、Enterキーを押すとまた録音が始まるので、様々な言葉で試してみることができます。日本語だけでなく、英語や他の言語でも認識可能です。

まとめ

今回はCloudflare Workers AIのWhisperモデルを使って、MacOSターミナルからリアルタイムで音声認識を行うアプリケーションを実装しました。

Whisperモデルは多言語対応の高精度な音声認識を提供し、APIを通じて簡単に利用できるため、様々な音声処理アプリケーションに組み込むことができます。

Cloudflare Workers AIは、このようなAI機能をサーバーレス環境で簡単に利用できる強力なツールであり、様々なシーンで活用できます。今後も他のモデルを試していきたいと思います。

お知らせ

toraco株式会社では、エンジニア向けコミュニティを 2024年11月1日 より開始しました。

Discord サーバーでは、

  • もくもく会・作業ラジオ・雑談部屋
  • オフラインイベントの案内
  • 副業や案件の紹介
  • フロントエンド・生成AI技術の情報共有

など、さまざまな活動を行っています!

ぜひ、こちらからご参加ください。

参考リンク

Discussion