Skip to content

Instantly share code, notes, and snippets.

@blakek
Created March 30, 2023 14:43
Show Gist options
  • Save blakek/195afec017a3083196b94671521935f8 to your computer and use it in GitHub Desktop.
Save blakek/195afec017a3083196b94671521935f8 to your computer and use it in GitHub Desktop.
Music file manipulation
#!/usr/bin/env bash
set -eo pipefail
##
# Transpose a song from one key to another.
##
version='0.0.1'
# Formatting functions
bold() {
format_bold='\033[1m'
format_reset='\033[0m'
echo -e "${format_bold}$1${format_reset}"
}
error() {
format_red='\033[0;31m'
format_reset='\033[0m'
echo -e "${format_red}$1${format_reset}" >&2
}
panic() {
error "$1"
exit 1
}
# Floating point calculations
calculate() {
# `bc` cannot do fractional exponents but `awk` can
awk "BEGIN { print $1 }"
}
show_usage() {
cat <<-EOF
$(bold transpose) - Transpose a song from one key to another
$(bold USAGE)
$ transpose --help
$ transpose --version
$ transpose input_file output_file semitones
For example, to transpose a song from C to E:
$ transpose input_file output_file 4
or, to transpose a song from E to C:
$ transpose input_file output_file -4
$(bold OPTIONS)
-h, --help
Show this help message and exit
-v, --version
Show the version of this script and exit
EOF
}
transpose() {
# Check if usage/version was requested
if [[ $# -ne 3 ]]; then
show_usage
exit 0
fi
for arg in "$@"; do
if [[ $arg == "--help" || $arg == "-h" ]]; then
show_usage
exit 0
elif [[ $arg == "--version" || $arg == "-v" ]]; then
echo "$version"
exit 0
fi
done
local input_file="$1"
local output_file="$2"
local semitones="$3"
# Validate arguments
if [[ ! -r $input_file ]]; then
panic "Could not read input file"
fi
if [[ $semitones -lt -12 || $semitones -gt 12 ]]; then
panic "Semitones must be between -12 and 12"
fi
# Find the difference between the start and end keys, picking the closest one
local pitch_difference
pitch_difference=$(calculate "2 ^ ($semitones / 12)")
# Show progress if pv is installed
if [[ $(hash pv 2>/dev/null) ]]; then
pv -i 0.15 "$input_file" | ffmpeg -i - -y -af "rubberband=pitch=$pitch_difference" -v quiet "$output_file"
else
ffmpeg -i "$input_file" -y -af "rubberband=pitch=$pitch_difference" -v quiet -stats "$output_file"
fi
}
transpose "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment