pulseaudio: アプリの出力を時間量だけ遅延する

pulseaudio: アプリの出力を時間量だけ遅延する

オーディオがビデオより先に再生されるアプリが 1 つあるので、オーディオを 0.5 秒遅らせたいと思っています。現在、アプリのオーディオを pulseaudio の null モジュールにリダイレクトし、parec を使用してモニター出力を外部バッファリング プログラムに送信しています。外部バッファリング プログラムでは、データが 0.5 秒後に pulse に出力されます。pulseaudio 内でこれを行うより簡単な方法はありますか?

答え1

ループバックモジュールソースとシンクの間に最大 2 秒の遅延を追加できます。

答え2

私は自分のbashスクリプト(「pa遅延”)は、すでに述べたループバックモジュールとヌルシンクモジュールを使用します。イグナシオ・バスケス・エイブラムス任意の長さの仮想シンクとループバックのチェーンを確立します。私はこのスクリプトを積極的に使用しています。シンクはDelay1、Delay2、…Delayと呼ばれます。そしてスクリプトは最初にその名前のシンクと、以前の実行で作成された可能性のあるこれらのシンクを使用するループバックをすべてアンロードします。次に、コマンドラインパラメータとしてミリ秒単位で指定される要求された遅延に必要な長さまでチェーンを構築します。(遅延が 0 の場合、スクリプトは以前のチェーンを削除してから終了します。) 最初の仮想シンクである Delay1 は、完全な遅延を得るために常に接続されるシンクです。中間のシンクも理論的には使用できますが、それらはすべて、接続によって生じる遅延の量を示す説明が与えられています。Delay1 が常に使用するシンクであることが重要です。なぜなら、(少なくとも私の XFCE の場合) システムはプログラムがどのシンクに接続されたかを「記憶」しているからです。Delay1 シンクに接続されたプログラムは、チェーンがしばらく存在していなかった場合でも (その間、プログラムはデフォルトの出力デバイスに接続されます)、そのシンクに戻ります。したがって、次のように呼び出すことができます。pa遅延遅延の長さを調整するために必要な回数だけ実行し、それ以上介入することなく、遅延を使用するプログラムは実行ごとに確立された新しいチェーンを使用します。これは、私が呼び出した後にも適用されます。pa遅延0遅延のない音声をより長く聞くことができます。コンピュータをサスペンドした後、遅延が指定よりも長くなる傾向があることがわかりました(これは警告そのレイテンシ_ミリ秒ループバック モジュールのパラメータは「フレンドリーな要求のみ」であるため、サスペンドする前に「pa-delay 0」状態にも変更します。

#! /bin/bash

delay_msec="$1"
if [[ ! $delay_msec =~ ^[0-9]+$ ]]; then
    echo "Usage: $( basename "$0" ) delay_milliseconds" >&2
    exit 2
fi
max_loopback_delay=2000

list_delay_loopback_modules() {
    pactl list modules short | grep -P '\tmodule-loopback\t(.*\s)?source=Delay[1-9][0-9]*[.]monitor(\s|$)' | cut -f1
}

list_delay_null_modules() {
    pactl list modules short | grep -P '\tmodule-null-sink\t(.*\s)?sink_name=Delay[1-9][0-9]*(\s|$)' | cut -f1
}

build_module_array() {
    local object_type="$1"
    local array="$( echo "$1" | sed 's/-/_/g' )"
    typeset -n array
    local object_list="$( 
        pactl list "$object_type" |
        perl -00 -p -e ' chomp; s{\s*\n\s*}{|}mg; s{$}{\n}; ' |
        sed -En -e 's/^[a-zA-Z ]* #([0-9]+)[|].*[|]Owner Module: ([0-9]+)[|].*/\1:\2/p'
    )"
    while IFS=: read object module; do
        array[$module]=$object
    done <<<"$object_list"
}

for module in $( list_delay_loopback_modules ); do
    pactl unload-module "$module"
done
for module in $( list_delay_null_modules ); do
    pactl unload-module "$module"
done

last_loopback_delay=$(( (delay_msec + max_loopback_delay - 1) % max_loopback_delay + 1 ))
loops=$(( (delay_msec - last_loopback_delay) / max_loopback_delay + 1 ))
(( loops > 0 )) || exit 0

nbsp="$( echo -e '\u00a0' )"
narrownbsp="$( echo -e '\u202f' )"
i=1
module="$( pactl load-module module-null-sink sink_name="Delay$i" sink_properties="device.description=\"Delay:${nbsp}${delay_msec}${narrownbsp}ms\"" )"
while (( ++i <= loops )); do
    module="$( pactl load-module module-null-sink sink_name="Delay$i" sink_properties="device.description=\"Delay:${nbsp}$(( delay_msec - (i - 1) * max_loopback_delay ))${narrownbsp}ms\"" )"
done
i=$loops
module="$( pactl load-module module-loopback source="Delay$i.monitor" latency_msec=$last_loopback_delay )"
last_loopback_module=$module
while (( --i > 0 )); do
    module="$( pactl load-module module-loopback source="Delay$i.monitor" sink=Delay$(( i + 1 )) latency_msec=$max_loopback_delay )"
done

build_module_array sinks
build_module_array sources
build_module_array sink-inputs
build_module_array source-outputs

for module in $( list_delay_null_modules ); do
    pactl set-source-volume ${sources[$module]} '100%'
    pactl set-sink-volume   ${sinks[$module]}   '100%'
done
for module in $( list_delay_loopback_modules ); do
    pactl set-source-output-volume ${source_outputs[$module]} '100%'
    test "$module" == "$last_loopback_module" && continue
    pactl set-sink-input-volume    ${sink_inputs[$module]}    '100%'
done

関連情報