🐙

Google Colabで録音したい

2025/02/02に公開

Google Colab上でマイク録音したい

Google Colab上でSST(Speech To Text)のデモを作りたい。
音声ファイルをいちいち作ってアップロードするのも面倒なのでマイクから録音できないものかと方法を調べてみました。

録音はブラウザを介して行うことになるので javascript関数を呼び出す必要があります。

IPythonモジュールのdisplay関数でjavascriptの関数を定義しておき、eval_js関数で定義したjavascript関数を呼び出すという流れになります。

シーケンス図を描くとこんな感じ

フローの詳細説明:

録音開始時

ユーザーが録音開始ボタンをクリック
Python側でon_start_clicked()が呼び出される
JavaScriptのstartRecording()関数を呼び出し
ブラウザがユーザーにマイク使用の許可を求める
許可後、MediaRecorderで録音開始

録音中

MediaRecorderが継続的に音声データをrecordedChunks配列に蓄積

録音停止時

ユーザーが停止ボタンをクリック
Python側でon_stop_clicked()が呼び出される
JavaScriptのstopRecording()関数を呼び出し
録音停止とマイクアクセスの解放
録音データをBlobに変換しDataURLとして返す
Python側でデータをデコードしファイルに保存

このように、Python・JavaScript・ブラウザAPIが連携して録音機能を実現しています。

import ipywidgets as widgets
from IPython.display import display, Javascript
from google.colab.output import eval_js
import base64

# 1. JavaScript の初期化:グローバル関数として startRecording と stopRecording を定義
def init_js():
    display(Javascript('''
    // グローバル変数の初期化
    window.recorder = null;
    window.recordedChunks = [];
    window.stream = null;

    // 録音開始関数をグローバルに定義
    window.startRecording = async function() {
      // マイクから音声を取得(ユーザーの許可が必要です)
      window.stream = await navigator.mediaDevices.getUserMedia({ audio: true });
      // MediaRecorder の作成
      window.recorder = new MediaRecorder(window.stream);
      window.recordedChunks = [];
      // データが利用可能になったら recordedChunks に追加
      window.recorder.ondataavailable = event => {
        if (event.data.size > 0) {
          window.recordedChunks.push(event.data);
        }
      };
      // 録音開始
      window.recorder.start();
      return "録音を開始しました!";
    };

    // 録音停止関数をグローバルに定義(マイクアクセスの解除も実施)
    window.stopRecording = async function() {
      // 録音停止
      window.recorder.stop();
      // 録音停止が完了するのを待つ
      await new Promise(resolve => window.recorder.onstop = resolve);
      
      // マイクのアクセスを解除
      if (window.stream) {
        window.stream.getTracks().forEach(track => track.stop());
      }
      
      // 録音データを Blob に変換
      let blob = new Blob(window.recordedChunks, { type: 'audio/webm' });
      // Blob を Data URL に変換して返す
      let reader = new FileReader();
      let promise = new Promise(resolve => {
        reader.onloadend = () => resolve(reader.result);
      });
      reader.readAsDataURL(blob);
      return promise;
    };
    '''))
    print("JavaScript の初期化が完了しました!")

# 初期化を実行
init_js()

# 2. GUI の作成:録音開始と停止のボタンを用意
start_button = widgets.Button(description="録音開始", button_style='success')
stop_button = widgets.Button(description="停止", button_style='danger')
output_area = widgets.Output()

# 録音開始ボタンのクリックイベント
def on_start_clicked(b):
    with output_area:
        print("録音開始ボタンが押されました。マイクへのアクセス許可が求められたら「許可」を選んでくださいね!")
    # JavaScript の startRecording() を呼び出す
    result = eval_js("startRecording()")
    with output_area:
        print(result)

# 録音停止ボタンのクリックイベント
def on_stop_clicked(b):
    with output_area:
        print("停止ボタンが押されました。録音停止とマイクのアクセス解除を行います…")
    # JavaScript の stopRecording() を呼び出し、Data URL を受け取る
    data_url = eval_js("stopRecording()")
    if data_url is None:
        with output_area:
            print("エラー:録音データが取得できませんでした。再度試してください。")
        return
    header, encoded = data_url.split(',', 1)
    audio_data = base64.b64decode(encoded)
    # ファイルに保存(WebM 形式)
    filename = 'output.webm'
    with open(filename, 'wb') as f:
        f.write(audio_data)
    with output_area:
        print("録音が完了しました! ファイルが保存されました: " + filename)

# ボタンにイベントハンドラを登録
start_button.on_click(on_start_clicked)
stop_button.on_click(on_stop_clicked)

# GUI を表示
display(widgets.HBox([start_button, stop_button]))
display(output_area)


colabサンプル

参考

https://colab.research.google.com/github/ektaarora3501/tensorflow/blob/master/advanced_outputs.ipynb#scrollTo=jcAaZiyh47Il

Discussion