From 515a9f479a717c80ad7fe800d6f9d13f32c7e601 Mon Sep 17 00:00:00 2001 From: Michael Campagnaro Date: Thu, 22 Jun 2023 15:55:57 -0400 Subject: [PATCH] Improve video audio normalization scripts --- dotfiles/bin/extract-16bit-wav-from-video | 1 + dotfiles/bin/fix-audio-in-one-channel | 2 +- dotfiles/bin/normalize-video-volume | 33 ++++++++++++++--------- dotfiles/bin/remove-audio-from-video | 2 +- dotfiles/bin/trim-accurate-video | 2 ++ dotfiles/bin/trim-video | 2 ++ 6 files changed, 27 insertions(+), 15 deletions(-) diff --git a/dotfiles/bin/extract-16bit-wav-from-video b/dotfiles/bin/extract-16bit-wav-from-video index 32802c9..b92d68f 100644 --- a/dotfiles/bin/extract-16bit-wav-from-video +++ b/dotfiles/bin/extract-16bit-wav-from-video @@ -45,6 +45,7 @@ fi printf "\n${YELLOW}${BOLD}Extracting 16-bit WAV from $input | output: $output_name${NORMAL}\n" +# -ac 1 mixes audio to a single channel. ffmpeg -i "$input" -ar 16000 -ac 1 -c:a pcm_s16le "$output_name" printf "${GREEN}${BOLD}Done extracting 16-bit WAV from $input | output: $output_name${NORMAL}\n" diff --git a/dotfiles/bin/fix-audio-in-one-channel b/dotfiles/bin/fix-audio-in-one-channel index 17c5e00..c795523 100644 --- a/dotfiles/bin/fix-audio-in-one-channel +++ b/dotfiles/bin/fix-audio-in-one-channel @@ -45,7 +45,7 @@ fi printf "\n${YELLOW}${BOLD}Repairing audio in $filename.$extension | output: $output${NORMAL}\n" -ffmpeg -i "$filename.$extension" -c:v copy -ac 1 "$output" +ffmpeg -i "$filename.$extension" -c:v copy -ac 1 -map 0 "$output" printf "\n${GREEN}${BOLD}Done repairing audio in $filename.$extension | output: $output${NORMAL}\n\n" diff --git a/dotfiles/bin/normalize-video-volume b/dotfiles/bin/normalize-video-volume index 2580a25..0628636 100644 --- a/dotfiles/bin/normalize-video-volume +++ b/dotfiles/bin/normalize-video-volume @@ -1,6 +1,8 @@ #!/usr/bin/env bash -# Use this to normalize the audio of a video using the average loudness, or RMS-based normalization. +# Use this to normalize the audio of a video using the average loudness, or RMS-based normalization. It does a pretty good job! +# If you want to modify the volume yourself then checkout the analyze-video-volume and change-video-volume scripts. +# # This does not re-encode the video. # # Inspired by https://superuser.com/a/323127 and https://superuser.com/a/1312885 @@ -50,21 +52,26 @@ printf "\n${YELLOW}${BOLD}Normalizing audio in $filename.$extension | output: $o # This is done in two passes. The first pass will compute the mean loudness and # the second pass will normalize the audio using the mean as the target. -# -vn, -sn, and -dn tells ffmpeg to ignore non-audio streams during the analysis. This speeds things up. -ffmpeg -i "$filename.$extension" -af "volumedetect" -vn -sn -dn -f null /dev/null +temp_file="/tmp/_audio_info_$RANDOM" -#ffmpeg -i "$filename.$extension" -c:v copy -ac 1 "$output" +# 1st pass: +cmd="ffmpeg -i \"$filename.$extension\" -pass 1 -filter:a loudnorm=print_format=json -vn -sn -dn -f null /dev/null 2>&1 | sed -n '/{/,/}/p' > $temp_file" +printf "\n${BOLD}Running 1st pass:\n$cmd\n\n${NORMAL}" +eval $cmd -printf "\n${GREEN}${BOLD}Done normalizing audio in $filename.$extension | output: $output${NORMAL}\n\n" +printf "\n${BOLD}${GREEN}Done.\n\n${NORMAL}" +ii=`grep \"input_i\" $temp_file | cut -d: -f2 | tr -cd [:digit:].-` +itp=`grep \"input_tp\" $temp_file | cut -d: -f2 | tr -cd [:digit:].-` +ilra=`grep \"input_lra\" $temp_file | cut -d: -f2 | tr -cd [:digit:].-` +it=`grep \"input_thresh\" $temp_file | cut -d: -f2 | tr -cd [:digit:].-` +to=`grep \"target_offset\" $temp_file | cut -d: -f2 | tr -cd [:digit:].-` +# 2nd pass: +cmd="ffmpeg -i \"$filename.$extension\" -c:v copy -pass 2 -filter:a loudnorm=linear=true:I=-16:LRA=11:tp=-1.5:measured_I=$ii:measured_LRA=$ilra:measured_tp=$itp:measured_thresh=$it:offset=$to:print_format=summary -map 0 \"$output\"" +printf "${BOLD}Re-encoding audio:\n$cmd\n\n${NORMAL}" +eval $cmd -#--------------------------------------- -# This seems better. 2 pass using loudnorm filter. +printf "\n${GREEN}${BOLD}Done normalizing volume in $filename.$extension | output: $output${NORMAL}\n" +rm $temp_file -# 1st pass: ffmpeg -i "$filename.$extension" -pass 1 -filter:a loudnorm=print_format=json -vn -sn -dn -f null /dev/null -# 2nd pass: ffmpeg -i "$filename.$extension" -c:v copy -pass 2 -filter:a loudnorm=linear=true:measured_I=$input_i:measured_LRA=$input_lra:measured_tp=$input_tp:measured_thresh=$input_thresh "$output" - -# TODO: extract the $input_i, input_lra, etc from the 1st pass output so that this can be automated. -# TODO: stackoverflow said if there are subtitles or multiple vid streams then add "-map 0" before the output name. Test this. -# TODO: disable the log file or just delete it after normalizing. diff --git a/dotfiles/bin/remove-audio-from-video b/dotfiles/bin/remove-audio-from-video index a331e68..94d3c57 100644 --- a/dotfiles/bin/remove-audio-from-video +++ b/dotfiles/bin/remove-audio-from-video @@ -43,7 +43,7 @@ fi printf "\n${YELLOW}${BOLD}Removing audio from '$filename.$extension' | output: '$output'${NORMAL}\n" # -an removes the audio. -ffmpeg -i "$filename.$extension" -c:v copy -an "$output" +ffmpeg -i "$filename.$extension" -c:v copy -an -map 0 "$output" printf "\n${GREEN}${BOLD}Done removing audio from '$filename.$extension' | output: '$output'${NORMAL}\n\n" diff --git a/dotfiles/bin/trim-accurate-video b/dotfiles/bin/trim-accurate-video index 6bd29c6..a2538cf 100644 --- a/dotfiles/bin/trim-accurate-video +++ b/dotfiles/bin/trim-accurate-video @@ -42,6 +42,8 @@ timing_args="-ss $start_time -to $end_time" printf "\n${YELLOW}${BOLD}Trimming '$filename.$extension' | output: $output | start: $start_time | end: $end_time${NORMAL}\n" +# You might have issues if the file has multiple video streams or embedded subtitles. The -map 0 arg is typically given +# when copying a video stream, but I'm not sure if it's appropriate to use here. ffmpeg -y -stats -loglevel level+error $timing_args -accurate_seek -i "$filename.$extension" -c:v libx264 -c:a copy "$output" printf "\n${GREEN}${BOLD}Finished trimming${NORMAL}\n\n" diff --git a/dotfiles/bin/trim-video b/dotfiles/bin/trim-video index 7d54049..7388b11 100644 --- a/dotfiles/bin/trim-video +++ b/dotfiles/bin/trim-video @@ -40,6 +40,8 @@ timing_args="-ss $start_time -to $end_time" printf "\n${YELLOW}${BOLD}Trimming '$filename.$extension' | output: $output | start: $start_time | end: $end_time${NORMAL}\n" +# You might have issues if the file has multiple video streams or embedded subtitles. The -map 0 arg is typically given +# when copying a video stream, but I'm not sure if it's appropriate to use here. ffmpeg -y -stats -loglevel level+error $timing_args -i "$filename.$extension" -c copy "$output" printf "\n${GREEN}${BOLD}Finished trimming${NORMAL}\n\n"