2026年02月08日

バスクラ動画の作成手順

 「バスクラ練習記録」を更新しました。一進一退というところです。アンブシュアがなかなか固まりません。

 今回は、動画を作る手順をだいぶ見直しました。採用した手順を記録しておこうと思います。

 機材のセットアップは、こんな風になっています。演奏者(私)は、右手前にある椅子に座ります。マイクスタンドにマイクを固定して、演奏者から見て譜面台の奥に配置しています。写真の中央奥に MacBook Pro があり、この内蔵カメラを使って、主にバスクラの手の動きを撮影しています。口元まで撮影した方が記録としては良さそうですが、顔が写っていると公開しづらいので、手元だけを写す設定です。譜面台にはモバイルモニターが載っていて、MacBook Pro の画面をミラーリングしています。

20260208-1.jpg

 録画には OBS Studio を使います。シーンの設定はこのようになっています。出力画面のサイズを 1280x720 として、左半分に内蔵カメラの映像、右半分にメディアソースを配置して、伴奏動画を再生しながら録画・録音します。

20260208-3.jpg

 メディアソースの設定はこの通りです。伴奏動画のファイルをローカルファイルとして指定します。「ソースがアクティブになった時に再生を再開する」にチェックを入れておきます。

20260208-4.jpg

 メディアソースは録画の時以外は非アクティブにしておきます。常時表示させておくと、OBS Studio を立ち上げた時に必ず再生が始まるので、ちょっとめんどくさいのです。

20260208-9.jpg

 内蔵カメラは「映像キャプチャ」ソースとして設定します。サイズを1280x720にして、「フィルタ」で左右をトリミングして縦長にしてあります。

20260208-5.jpg

 音声入力のデバイスを、オーディオインターフェイスのUS-2x2に設定します。

20260208-6.jpg

 入力レベルの調整はまだ試行錯誤中です。今のところ、ゲインつまみを真ん中あたりにして録音していますが、あきらかに音が小さいのです。ただ、右一杯に回すと音が割れてしまうし、途中の角度に設定すると、何かの拍子につまみが動いてしまった時に同じ場所に戻すのが難しい。シールを貼ろうかな?(追記:「PEAK」LEDの角に合わせることにしました。)

20260208-10.jpg

 「音声ミキサー」で右クリックして「オーディオの詳細プロパティ」を開きます。「デスクトップ音声」と「マイク」は使わないので、「モニターオフ」として、すべてのトラックのチェックを外します。「メディアソース」は「モニターと出力」として、トラック2をオンにします。「音声入力-マイク」は「モニターのみ」として、トラック1をオンにします。これで、OBS からのモニター出力にはメディアソース(伴奏動画)の音声が流れ、録画出力にはトラック1にマイク入力(演奏)、トラック2にメディアソース(伴奏)の音声が出ます。

20260208-12.jpg

 OBS からのモニター出力は、「設定」→「音声」で選択します。Bluetoothヘッドホンを使っていますが、OBS Studio を立ち上げる時に接続済みになっていないと、あとから接続してもうまく認識されないようです。

20260208-7.jpg

 これで、伴奏を聴きながら録音・録画する準備ができました。あとは、録画ボタンを押して、メディアソースをアクティブにして、ヘッドホンに流れる伴奏に合わせて演奏すればよいわけです。指揮と楽譜はメディアソースの画面に出てきますが、ヘッドホンの音声がちょっと遅れているので、慣れが必要です。(追記:メディアソースのフィルタで「レンダリング遅延」をかけて、230 ms[ヘッドホンの遅れ時間]遅らせると、うまくいきました。)

 録画ボタンを押すだけで、メディアソースを自動的にアクティブにするようにしました。OBS は Lua スクリプトで動作の自動化ができます。こんなところで Lua の知識が役に立つとは思いませんでした。

local is_playing = false

function script_description()
    return "録画とメディアソース再生を同期させる"
end

function script_load(settings)
    obslua.obs_frontend_add_event_callback(on_event)
end

function handle_media_sources(action)
    local scene = obslua.obs_frontend_get_current_scene() -- 現在のシーンを取得
    local scene1 = obslua.obs_scene_from_source(scene)    -- シーンオブジェクトを取得
    local sourceItems = obslua.obs_scene_enum_items(scene1) -- シーン中のソース要素を取得
    if sourceItems then
        for _, sourceItem in ipairs(sourceItems) do
            local sourceSrc = obslua.obs_sceneitem_get_source(sourceItem) -- ソース要素オブジェクトを取得
            local source_id = obslua.obs_source_get_id(sourceSrc) -- ソース要素のIDを取得
            if source_id == "ffmpeg_source" then  -- メディアソース
                if action == "restart" then
                    obslua.obs_source_media_restart(sourceSrc)
                elseif action == "play" then
                    obslua.obs_source_media_play_pause(sourceSrc, false)
                elseif action == "pause" then
                    obslua.obs_source_media_play_pause(sourceSrc, true)
                elseif action == "stop" then
                    obslua.obs_source_media_stop(sourceSrc)
                elseif action == "show" then
                    obslua.obs_sceneitem_set_visible(sourceItem, true)
                elseif action == "hide" then
                    obslua.obs_sceneitem_set_visible(sourceItem, false)
                end
                obslua.script_log(obslua.LOG_INFO, string.format("Media %s: %s.", action, obslua.obs_source_get_name(sourceSrc)))
            end
        end
    end
    obslua.sceneitem_list_release(sourceItems)
    obslua.obs_source_release(scene)
end

function on_event(added_event)
    if added_event == obslua.OBS_FRONTEND_EVENT_RECORDING_STARTING then
        obslua.script_log(obslua.LOG_INFO, "録画が開始されました。")
        if not is_playing then
            handle_media_sources("show")
            is_playing = true
        end
    elseif added_event == obslua.OBS_FRONTEND_EVENT_RECORDING_UNPAUSED then
        obslua.script_log(obslua.LOG_INFO, "録画が再開されました。")
        if not is_playing then
            handle_media_sources("play")
            is_playing = true
        end
    elseif added_event == obslua.OBS_FRONTEND_EVENT_RECORDING_STOPPED or added_event == obslua.OBS_FRONTEND_EVENT_RECORDING_PAUSED then
        if added_event == obslua.OBS_FRONTEND_EVENT_RECORDING_STOPPED then
            obslua.script_log(obslua.LOG_INFO, "録画が停止されました。")
            handle_media_sources("hide")
        else
            obslua.script_log(obslua.LOG_INFO, "録画が中断されました。")
            handle_media_sources("pause")
        end
        if is_playing then
            is_playing = false
        end
    end
end

function script_save(settings)
end

 録画結果は mp4 ファイルで保存されます。公開用の動画は、下の bash スクリプトで加工しています。ffmpeg の filter_complex は何でもできますね。(追記:US-2x2のゲイン変更と、メディアソースのレンダリング遅延設定に対応して一部変更しました。伴奏の音声を 230 ms でなく 300 ms 遅らせているのは、こうしないとマイク録音と合わなかったためです。マイク録音が実際の音より 70 ms 遅れている、ということのようです)

#!/bin/bash
#  Usage: sh record_bcl.sh [-t duration] filename
while (( $# > 0 ))
do
  case $1 in
  -t)
    shift
    duration=$1
    ;;
  *)
    FILE=$1
    break
    ;;
  esac
  shift
done
if [ "$duration" == "" ]; then
  duration=`ffprobe -i $FILE -show_entries stream=codec_type,duration -of compact=p=0:nk=1 2>/dev/null | grep video | awk -F'|' '{print \$2-1}'`
fi
fdout=`echo "$duration-1" | bc`

OUTFILE=${FILE/.mp4/_out.mp4}

#  オーディオの 0 がマイク録音、1 が伴奏。
#  マイク録音:8dB 増幅する [a0]
#  伴奏:300 ms 遅らせて、2 dB 抑える [a1]
#  ビデオ(1280x720)の540:720:0:0を切り出す [v01]
#  ビデオの740:240:540:480を切り出して[v11]540:175に縮小[v12]、
#    #000032色を透過にして[v13]、70 ms 遅らせて[v14]、[v01]の0:546位置に合成[v20]
#  最後の1秒をフェードアウト[vout]
#  録画時に、伴奏のビデオを 230 ms 遅らせているものとする
#  (Bluetooth ヘッドホンによる音声の遅れに対応するため)
ffmpeg -t $duration -ss 1 -i "$FILE" -filter_complex "[0:0]split[v00][v10];\
[v00]crop=w=540:h=720:x=0:y=0[v01];\
[v10]crop=w=740:h=240:x=540:y=480[v11];\
[v11]scale=w=540:h=175[v12];\
[v12]colorkey=000032:0.01:0.1[v13];\
[v13]tpad=start_duration=0.07:start_mode=clone[v14];\
[v01][v14]overlay=x=0:y=546[v20];\
[v20]fade=t=out:st=$fdout:d=1[vout];\
[0:a:0]adelay=0|0,volume=8dB[a0];\
[0:a:1]adelay=300|300,volume=-2dB[a1];\
[a0][a1]amix[aout]" -map [vout] -map [aout] -y "$OUTFILE"
#  2秒のフレームのスナップショットを撮る
ffmpeg -ss 2 -i "$OUTFILE" -vframes 1 -y ${OUTFILE/.mp4/.jpg} 2>/dev/null

open -a "QuickTime Player" "$OUTFILE"
タグ:音楽
Posted at 2026年02月08日 11:52:08
email.png