Skip to content

Instantly share code, notes, and snippets.

@jethrojones
Last active September 19, 2025 20:26
Show Gist options
  • Save jethrojones/f4f6f930e3bb62971b607dcafb4b6080 to your computer and use it in GitHub Desktop.
Save jethrojones/f4f6f930e3bb62971b607dcafb4b6080 to your computer and use it in GitHub Desktop.
One-drop splitter for RØDECaster Pro (fw 2.x) poly-WAVs. Outputs mono stems for selected mics (defaults: Mic1=ch2, Mic2=ch3). macOS Shortcuts-friendly; logs to /tmp/rode_split.log. Includes a “channel finder” mode to discover which channel index your mics are on.
#!/bin/bash
# Split RØDECaster Pro multitrack poly-WAVs into per-channel stems (macOS Shortcuts friendly)
# -----------------------------------------------------------------------------
# REQUIREMENTS
# - ffmpeg & ffprobe installed (e.g., Homebrew: `brew install ffmpeg`)
#
# DEFAULTS (tuned for RØDECaster Pro 1, fw 2.1.x)
# - Mic 1 is on channel index 2 (zero-based; i.e., 3rd channel in the file)
# - Mic 2 is on channel index 3
# - Outputs go to a single sibling folder named "split"
#
# QUICK USE
# bash rodecaster_split.sh /path/to/file.wav
#
# TWEAKS (set as environment variables, or edit below)
# MIC1_CH_INDEX : default 2 (e.g., MIC1_CH_INDEX=0 for first channel)
# MIC2_CH_INDEX : default 3
# NAME1 / NAME2 : default mic1 / mic2 (e.g., NAME1=host NAME2=guest)
# OUTDIR_NAME : default split
# INCLUDE_STEREO : default false (set to true to also export stereo mix as one stereo file)
# FIND_CHANNELS : default false (set to true to export 3-second previews for *every* channel)
# NORM : default false (set to true to add EBU R128 loudness normalization per mic)
#
# EXAMPLES
# # Use different mapping (e.g., mics on ch0 and ch1), and friendly names:
# MIC1_CH_INDEX=0 MIC2_CH_INDEX=1 NAME1=host NAME2=guest bash rodecaster_split.sh file.wav
#
# # Also export the stereo mix and run loudness normalization:
# INCLUDE_STEREO=true NORM=true bash rodecaster_split.sh file.wav
#
# # Not sure which channels your mics are on? Generate 3s previews of every channel:
# FIND_CHANNELS=true bash rodecaster_split.sh file.wav
#
# NOTES
# - Works inside macOS Shortcuts: set shell to /bin/bash, pass input "as arguments".
# - Logs to /tmp/rode_split.log
# - Uses ffmpeg pan filter (works on more builds than -map_channel).
# -----------------------------------------------------------------------------
set -euo pipefail
# Ensure Homebrew binaries are visible (important when run via Shortcuts)
export PATH="/opt/homebrew/bin:/usr/local/bin:$PATH"
FFMPEG="${FFMPEG_PATH:-$(command -v ffmpeg || true)}"
FFPROBE="${FFPROBE_PATH:-$(command -v ffprobe || true)}"
LOG="/tmp/rode_split.log"
# ---- CONFIG ----
MIC1_CH_INDEX="${MIC1_CH_INDEX:-2}" # zero-based channel index for Mic 1
MIC2_CH_INDEX="${MIC2_CH_INDEX:-3}" # zero-based channel index for Mic 2
NAME1="${NAME1:-mic1}" # suffix label for Mic 1 output
NAME2="${NAME2:-mic2}" # suffix label for Mic 2 output
OUTDIR_NAME="${OUTDIR_NAME:-split}" # shared output folder
INCLUDE_STEREO="${INCLUDE_STEREO:-false}"# true|false -> also export stereo mix
FIND_CHANNELS="${FIND_CHANNELS:-false}" # true|false -> export 3s previews for each channel
NORM="${NORM:-false}" # true|false -> apply EBU loudness normalization
# Loudness settings (used if NORM=true)
# Target dialog norm around podcast standards; tweak as desired.
LOUD_I="${LOUD_I:--16}"
LOUD_TP="${LOUD_TP:--1.5}"
LOUD_LRA="${LOUD_LRA:-11}"
if [[ -z "$FFMPEG" || -z "$FFPROBE" ]]; then
echo "ffmpeg/ffprobe not found on PATH" >> "$LOG"
echo "Install with: brew install ffmpeg"
exit 1
fi
for f in "$@"; do
[[ -f "$f" ]] || { echo "Skip (not a file): $f" >> "$LOG"; continue; }
# Case-insensitive .wav check (Bash 3.2 compatible)
ext="${f##*.}"
shopt -s nocasematch
if [[ ! "$ext" =~ ^wav$ ]]; then
shopt -u nocasematch
echo "Skip non-WAV: $f" >> "$LOG"
continue
fi
shopt -u nocasematch
dir="$(cd "$(dirname "$f")" && pwd)"
base="$(basename "$f")"
name="${base%.*}"
outdir="$dir/$OUTDIR_NAME"
mkdir -p "$outdir"
# Probe channel count
channels="$("$FFPROBE" -v error -select_streams a:0 -show_entries stream=channels \
-of default=nw=1:nk=1 "$f" | tr -d '[:space:]' || true)"
echo "[$(date)] $base → channels=$channels" >> "$LOG"
if [[ ! "$channels" =~ ^[0-9]+$ ]]; then
echo "No channel info from ffprobe, skipping: $f" >> "$LOG"
continue
fi
# Optional: FIND_CHANNELS preview (3s mono WAV for each channel)
if [[ "$FIND_CHANNELS" == "true" ]]; then
i=0
while [ "$i" -lt "$channels" ]; do
"$FFMPEG" -hide_banner -loglevel error -y -t 3 -i "$f" \
-filter_complex "[0:a]pan=mono|c0=c${i}[ch]" \
-map "[ch]" -c:a pcm_s16le "$outdir/${name}_ch${i}_preview.wav"
i=$((i+1))
done
echo "Preview stems written under: $outdir" >> "$LOG"
# Continue to full export as well (comment 'continue' in if you only want previews)
fi
# Guard against out-of-range channel indexes
if (( MIC1_CH_INDEX >= channels || MIC2_CH_INDEX >= channels )); then
echo "Not enough channels ($channels) for indexes $MIC1_CH_INDEX/$MIC2_CH_INDEX in $base" >> "$LOG"
continue
fi
# Optional: export stereo mix as a single stereo file (uses c0/c1)
if [[ "$INCLUDE_STEREO" == "true" && "$channels" -ge 2 ]]; then
"$FFMPEG" -hide_banner -loglevel error -y -i "$f" \
-filter_complex "[0:a]pan=stereo|c0=c0|c1=c1[st]" \
-map "[st]" -c:a pcm_s24le "$outdir/${name}_stereo_mix.wav"
fi
# Build optional loudness filter
norm_filter=""
if [[ "$NORM" == "true" ]]; then
norm_filter=",loudnorm=I=$LOUD_I:TP=$LOUD_TP:LRA=$LOUD_LRA"
fi
# MIC 1
"$FFMPEG" -hide_banner -loglevel error -y -i "$f" \
-filter_complex "[0:a]pan=mono|c0=c${MIC1_CH_INDEX}${norm_filter}[m1]" \
-map "[m1]" -c:a pcm_s24le "$outdir/${name}_${NAME1}.wav"
# MIC 2
"$FFMPEG" -hide_banner -loglevel error -y -i "$f" \
-filter_complex "[0:a]pan=mono|c0=c${MIC2_CH_INDEX}${norm_filter}[m2]" \
-map "[m2]" -c:a pcm_s24le "$outdir/${name}_${NAME2}.wav"
echo "Wrote: $outdir/${name}_${NAME1}.wav, ${name}_${NAME2}.wav" >> "$LOG"
done
echo "OK"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment