Lambdaカクテル

京都在住Webエンジニアの日記です

Invite link for Scalaわいわいランド

OpenAIのAudio Transcription APIで遊んだ

最近OpenAIがオーディオ系のAPIをいくつか出していた。といってもけっこう前の話だ。

そんな中、最近OpenAIが色々なサービスを展開している。自分も触って習熟しておいたほうが、面白いアイデアを思い付いたときにすぐにプロダクトを作れて役に立つはずだ。そういうわけで色々APIを見ていたところ、その中でも面白そうだった書き起こしAPIを使うことにした。自分が喋った内容を書き起こしてもらいたい。上手くいくだろうか?ボソボソ喋るオタクボイスでも?

openai.itshinan.jp

このAPIを使うと、オーディオ音声(wavとかflacとか色々な入力形式を使える)から各言語の書き起こしをやってくれる。イクゾー

素材音声

今回用意した自分の音声はこれ。過去の自分の記事の内容の一部をボソボソ読み上げている。お世辞にも上手ではないし、APIの能力を見てみたいので意図的にけっこう早口に読み上げている。マイクは良いので音質は良いという状態。FLACフォーマット(48000 Hz, stereo, s32 (24 bit))で保存してある。

soundcloud.com

今回はこれを文字起こしてもらう。

小銭を払う

あらかじめOpenAIに登録し、10ドルくらいクレカで前払いしておいた。遊ぶくらいならこの程度あれば十分。APIキーを払出しておき、手元に控えておく。大人はクレカがあるから良いが、クレカがない学生はどうするのだろう?と思った。

また状態が反映されるのに少しだけ時間がかかるため、スクリプト実行直前に決済するとクオータエラーになるようだ。あらかじめ払っておいてからコードを書くとよい。

Scalaから呼び出す

Scala用のAPIインターフェイスが非公式ながら用意されている(といってもOpenAPIから自動的に生成しているので、ほぼリアルタイムで公式のAPIに追従している)のでこれを使ってみた。

github.com

Scala Scriptを作成する。ファイル名はなんでもよい。Scala Scriptについては以下の記事を参照のこと。

blog.3qe.us

//> using scala "3.3.0"
//> using dep "io.cequence::openai-scala-client:0.5.0"
// ↑必要なライブラリをここに書いておけば勝手に拾ってくれる

// もろもろインポート
import scala.concurrent.Future
import akka.stream.Materializer
import akka.actor.ActorSystem
import java.io.File
import io.cequence.openaiscala._
import io.cequence.openaiscala.service._
import io.cequence.openaiscala.domain.response._
import io.cequence.openaiscala.domain.settings._

object WhisperTranscription {
  // 非同期処理に必要なやつ
  import scala.concurrent.ExecutionContext.Implicits.global
  implicit val materializer: Materializer = Materializer(ActorSystem())

  def main(args: Array[String]): Unit = {
    val service = OpenAIServiceFactory()

    val audioFilePath = File(args(0))

    val prompt = Some("日本語のまま書き出して。")

   // デフォルトで設定は必要ないが、日本語を強制するために設定を用意する
    val setting =
      CreateTranscriptionSettings("whisper-1", None, None, Some("ja"))

    // APIを呼び出す
    val transcription: Future[TranscriptResponse] =
      service.createAudioTranscription(audioFilePath, prompt, setting)

    // 終わったら表示してもらう
    transcription.andThen(println)
  }
}

WhisperTranscription.main(args)

まぁ、見ての通り単にファイルをAPIに渡しているだけである。追加で、「この音声は日本語です」とか「日本語のまま書き出して」といった指示を与えているにすぎない。

そしてスクリプトを実行してみる。控えておいたAPIキーを使う。

% OPENAI_SCALA_CLIENT_API_KEY="..." scala-cli openai-whisper.scala.sc -- transcript.flac

すると自動的に必要な依存ライブラリが全て降ってきて(キャッシュされる)数秒でコードがコンパイルされ、文字起こしされたデータが表示される。このコードだと起動しっぱなしになるので、Ctrl+cで終了させた。

Success(TranscriptResponse(Airframe RPCは、その名の通りScalaでRPC、 つまりRemote Procedure Callを行うためのライブラリなんだけど、 RPCとは大雑把に言うと、 変革値にあるマシンのメソッドを呼び出すことであって、 例えばREST APIもHTTPを使ったRPC 
技術のうちの一つだと考えると、 分かりやすいと思います。 Airframe RPCを使うと、Scalaのトレイトで定義したAPIに対して、 自動的にRPC用のインターフェースを実装してくれるんだけど、 これによってほぼ透過的にScala間で通信できるようになるんだよね。 しかも、同じScalaを使っているScala.jsにも対応していて、 APIインターフェースをクロスコンパイルすることで、サーバークライアント通信も行えるんだよね。 Airframe RPCは基盤技術としてREST APIとGRPCとの2つの通信手段を利用できるようになっていて、  例えばJVM間の通信ではGRPCを使ったり、 Scala.jsからはフラウザーで楽に利用できるREST APIを使い分けができるんだけど、 この設定はbuild.svtを使えばできて、 REST APIは簡素でどこからでも呼び出せる一方で、 GRPCはHTTP2を使っていて、 より高速で高効 率な通信を行えるという特徴があるんだよね。,None))

所見

誤字がほぼ無い(「遠隔地」を「変革値」と聞き違えただけ?)のがすごいと思う。意図的に専門用語が多い箇所を使って文字起こしさせたのだけれど、殆んどの用語を正確に書くことができている。gRPCをGRPCと書いているのが唯一の書き違いになっていると思う。「trait」を「トレイト」と書いているなどの細やかな気になりはあるが、これはプロンプトを追加して指示するだけで改善するだろう。

レスポンス形式はいくつか選択できて、字幕に使われるsrtやvttといったフォーマットも選択できるのも便利なポイントだ(CreateTranscriptionSettingsの第2引数にSome(TranscriptResponseFormatType.srt)などを渡せばよい)。 ffmpegで字幕をつけるとこんな感じで見える(ついでにYouTube側でも字幕を設定してみた)。

youtu.be

以前AWSのTranscribeを使ったことがあるのだけれど、その時は専門用語や口語的表現にめっぽう弱かったので、音声認識の精度がここまで良くなったのかと感銘を受けている。

まとめ

ちょっとした用途で使うのにも良いし、普通に仕事とかでも使えるレベルだと思う。

★記事をRTしてもらえると喜びます
Webアプリケーション開発関連の記事を投稿しています.読者になってみませんか?