#!/usr/bin/env bash set -euo pipefail # trim_mp3_silence_copy.sh # # Usage: # ./trim_mp3_silence_copy.sh input.mp3 [output.mp3] # # Env vars you can override: # NOISE_DB (default: -40dB) threshold for silencedetect # MIN_DUR (default: 0.5) minimum silence duration to consider (seconds) in="${1:-}" out="${2:-}" if [[ -z "${in}" ]]; then echo "Usage: $0 input.mp3 [output.mp3]" >&2 exit 1 fi if [[ ! -f "${in}" ]]; then echo "Input file not found: ${in}" >&2 exit 1 fi if [[ -z "${out}" ]]; then base="${in%.*}" out="${base}.trimmed.mp3" fi NOISE_DB="${NOISE_DB:--40dB}" MIN_DUR="${MIN_DUR:-0.5}" # Get full duration (seconds, as float) duration="$(ffprobe -v error -show_entries format=duration -of default=nw=1:nk=1 "$in")" # Run silencedetect and capture lines sd="$( ffmpeg -hide_banner -nostats -i "$in" \ -af "silencedetect=noise=${NOISE_DB}:d=${MIN_DUR}" \ -f null - 2>&1 | grep -E 'silence_(start|end):' || true )" if [[ -z "$sd" ]]; then echo "No silence detected (noise=${NOISE_DB}, d=${MIN_DUR}). Copying original -> ${out}" >&2 cp -f -- "$in" "$out" exit 0 fi # First silence_end => start of audio start_keep="$(awk '/silence_end:/ {print $NF; exit}' <<<"$sd")" # Last silence_start => end of audio (trailing silence start) end_keep="$(awk '/silence_start:/ {v=$NF} END{if(v!="") print v}' <<<"$sd")" # Fallbacks / sanity if [[ -z "${start_keep:-}" ]]; then # No leading silence_end found; keep from 0 start_keep="0" fi if [[ -z "${end_keep:-}" ]]; then # No trailing silence_start found; keep to full duration end_keep="$duration" fi # Ensure start < end (handle edge cases) ok="$( awk -v s="$start_keep" -v e="$end_keep" 'BEGIN{ if (s+0 < e+0) print "1"; else print "0"; }' )" if [[ "$ok" != "1" ]]; then echo "Could not determine a valid trim window." >&2 echo "Detected start_keep=${start_keep}, end_keep=${end_keep}, duration=${duration}" >&2 echo "Silencedetect output:" >&2 echo "$sd" >&2 exit 2 fi echo "Input: $in" echo "Output: $out" echo "Keep: ${start_keep}s -> ${end_keep}s (duration ~ $(awk -v s="$start_keep" -v e="$end_keep" 'BEGIN{printf "%.3f", (e-s)}'))" echo "Detect: noise=${NOISE_DB}, min_silence=${MIN_DUR}s" # Stream-copy trim (no re-encode). Use -ss/-to after -i for more accurate seeking while still copying. ffmpeg -hide_banner -y -i "$in" -ss "$start_keep" -to "$end_keep" -c copy "$out"