LoginSignup
0
0

動画や音声ファイルの音量(ゲイン)を ffmpeg で調整するときはコマンド1発でやりたい

Posted at

Abstract

動画や音声ファイルの音量(ゲイン)を ffmpeg で調整するときのメモをPowerShellで一つのコマンドにしたかったが、当時(約1年前)苦労したので備忘録。

環境

  • Windows 10
  • PoweShell v7.4.0
  • ffmpeg version 6.1-full_build-www.gyan.dev

目的

mp3とかの音楽ファイルのmax_volumeを0dB位になるように調整すること。
0以外にもできるように引数を生やしたりして、次のようにコマンドで変換できるようにする。

Set-MaxVolume -InputFile hoge.mp3 -OutputFile fuga.mp3 -TargetVolume 0.0

方針

  1. ffmepgのログ出力から、max_volumeを抽出
  2. ゲインを上げたら$TargetVolumeになるように、上げるゲインX[dB]を計算
  3. ffmpeg -i input -af volume=XdB outputで調整

簡単ですね!

完成したもの

function Select-ffmpegLog([parameter(mandatory=$true)][string]$File, [string]$Pattern = "max_volume:") {
    # ffmpegは標準エラーに出力されてしまうので、標準出力に渡しなおす
    ffmpeg -i $File -vn -af volumedetect -f null - 2>&1 | Select-String $Pattern;
}

function Find-MaxVolume([parameter(mandatory=$true)][string]$File) {
    <#
        .SYNOPSIS
            対象ファイルのmax_volumeを'10 dB'の形式で取得して返す
    #>
    $pattern = "max_volume:";
    $raw = Select-ffmpegLog $File $pattern;
    $raw.Tostring().Substring($raw.Matches.Index + $pattern.Length);
}

function Remove-dB([parameter(mandatory=$true)][string]$dBString) {
    <#
        .SYNOPSIS
            '10 dB' -> '10'のように取り出す
    #>
    $dBString = $dBString.Replace(' ', '');
    $dBString -replace "(\d+)dB", "`$1";
}

function Get-VolumeGain([parameter(mandatory=$true)][string]$File, [float]$TargetVolume = 0.0) {
    $max_volume = [float](Remove-dB (Find-MaxVolume $File));
    $gain = [Math]::Truncate($TargetVolume - $max_volume * 10) / 10;     # 小数点第1位で切り捨て
    "${gain}dB";
}

function Set-MaxVolume([parameter(mandatory=$true)][string]$InputFile, [parameter(mandatory=$true)][string]$OutputFile, [float]$TargetVolume = 0.0) {
    $gain = Get-VolumeGain $InputFile $TargetVolume;
    ffmpeg -i $InputFile -af volume=$gain $OutputFile;
}

ハマりどころと解説

ffmpegのログ出力を受け取れない

A. ffmpegのログ出力は標準エラーに出るから。

初心者殺し。当時沼った。
ということで、次のようにしてログを得て、max_volumeの行をSelect-Stringで抽出します。

function Select-ffmpegLog([parameter(mandatory=$true)][string]$file, [string]$pattern = "max_volume:") {
    # ffmpegのログ出力は標準エラーに出力されてしまうので、標準出力に渡しなおす
    ffmpeg -i $file -vn -af volumedetect -f null - 2>&1 | Select-String $pattern;
}

取得したログからmax_volumeを抽出

Select-Stringの返り値から、Matchオブジェクトを取り出して、ゲインを取り出す。

function Find-MaxVolume([parameter(mandatory=$true)][string]$file) {
    <#
        .SYNOPSIS
            対象ファイルのmax_volumeを'10 dB'の形式で取得して返す
    #>
    $pattern = "max_volume:";
    $raw = Select-ffmpegLog $file $pattern;
    $raw.Tostring().Substring($raw.Matches.Index + $pattern.Length);
}

上げるゲインを計算する

得られたmax_volume'10 dB'から10を取り出して、上げるゲインを計算して、'2dB'みたいな形式で得る。
つまり文字列 -> float -> 計算 -> 文字列という処理が必要。
次のように[float]でキャストしたりして処理する。

ちなみにReplaceメソッドでは正規表現による置換ができない。

function Remove-dB([parameter(mandatory=$true)][string]$dBString) {
    <#
        .SYNOPSIS
            '10 dB' -> '10'のように取り出す
    #>
    $dBString = $dBString.Replace(' ', '');
    $dBString -replace "(\d+)dB", "`$1";
}

function Get-VolumeGain([parameter(mandatory=$true)][string]$File, [float]$TargetVolume = 0.0) {
    $max_volume = [float](Remove-dB (Find-MaxVolume $File));
    $gain = [Math]::Truncate($TargetVolume - $max_volume * 10) / 10;     # 小数点第1位で切り捨て
    "${gain}dB";
}
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0