#!/usr/bin/env bash
#                       /usr/local/bin/speedup
# https://crystalfaeries.net/posix/bin/speedup
# celeste:crystalfaery SPEEDUP 2016-10-07 03:01:22+00:00
# http://hackerpublicradio.org/eps/hpr2135/full_shownotes.html
#===============================================================================
#
#         FILE: speedup
#
#        USAGE: ./speedup [-s ...] [-t ...] [-m] [-c] [-d] [-D] [-h] filename
#
#  DESCRIPTION: A script to perform a speedup and silence removal on a given
#               audio file
#
#      OPTIONS: ---
# REQUIREMENTS: ---
#         BUGS: ---
#        NOTES: ---
#       AUTHOR: Dave Morriss (djm), Dave.Morriss@gmail.com
#      VERSION: 0.0.4
#      CREATED: 2015-05-01 21:51:32
#     REVISION: 2016-04-22 11:35:08
#
#===============================================================================

set -o nounset                              # Treat unset variables as an error

SCRIPT=${0##*/}
VERSION="0.0.4"

#===  FUNCTION  ================================================================
#          NAME:  _usage
#   DESCRIPTION:  Report usage
#    PARAMETERS:  1 - the exit value (so it can be used to return an error
#                     value)
#       RETURNS:  Nothing
#===============================================================================
_usage () {
    local res="${1:-0}"

    cat <<-endusage

Usage: ${SCRIPT} [-s ...] [-t ...] [-m] [-c] [-d] [-D] [-h] filename

Speeds up and truncates silence in an audio file

Options:
  -s            This option if present causes the audio to be sped up.
                The option can be repeated and the speed up is increased for
                every -s given
  -t            This option if present causes the audio to have silences
                truncated. The option can be repeated to increase the
                sensitivity of the truncation
  -m            Mix-down multiple (stereo) tracks to mono
  -c            Delete the original file leaving the modified file behind with
                the same name as the original
  -d            Engage dry-run mode where the planned actions are reported
                but nothing is actually done
  -D            Run in DEBUG mode where more information is reported
                about what is going on
  -h            Print this help

Arguments:
  filename     The full path of the audio file containing the podcast episode.

Note:
  Unless deleted with the -c option the script will rename the original file
  and create the modified file with the same name as the original. The
  original file will have the name 'NAME_.EXT' with an underscore added after
  the original name.

Version: $VERSION
endusage
    exit "$res"
}

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

#
# Default settings
#
CLEANUP=0
DEBUG=0
DRYRUN=0
SPEEDUP=0
TRUNCATE=0
MIXDOWN=0

#
# Process options
#
while getopts :cDdhmst opt
do
    case "${opt}" in
        c) CLEANUP=1;;
        D) DEBUG=1;;
        d) DRYRUN=1;;
        s) ((SPEEDUP++));;
        t) ((TRUNCATE++));;
        m) MIXDOWN=1;;
        h) _usage 1;;
        *) _usage 1;;
    esac
done
shift $((OPTIND - 1))

#
# Check there is one argument
#
if [[ $# -ne 1 ]]; then
    echo "Error: filename missing"
    _usage 1
fi

#
# Does the file given as an argument exist?
#
if [[ ! -e "$1" ]]; then
    echo "File not found: $1"
    exit 1
fi

if [[ $DRYRUN -eq 1 ]]; then
    echo "Dry run: no changes will be made"
fi

#
# Work out the speed-up we want (if any) and generate the argument to sox
#
SPEEDS=( 1.05 1.1 1.2 1.3 1.4 1.5 1.6 1.7 )
if [[ $SPEEDUP -eq 0 ]]; then
    TEMPO=
else
    if [[ $SPEEDUP -gt ${#SPEEDS[@]} ]]; then
        SPEEDUP=${#SPEEDS[@]}
    fi
    ((SPEEDUP--))
    speed=${SPEEDS[$SPEEDUP]}
    TEMPO="tempo ${speed}"
fi

#
# Work out the silence truncation parameters (if any). The first set trims
# silence but ignores silences of 0.5 seconds in the middle (like pauses for
# breaths). The second set removes everything but can make a rather rushed
# result. See http://digitalcardboard.com/blog/2009/08/25/the-sox-of-silence/
# for some advice.
#
TRUNCS=( "1 0.1 1% -1 0.5 1%"  "1 0.1 1% -1 0.1 1%" )
if [[ $TRUNCATE -eq 0 ]]; then
    SILENCE=
else
    if [[ $TRUNCATE -gt ${#TRUNCS[@]} ]]; then
        TRUNCATE=${#TRUNCS[@]}
    fi
    ((TRUNCATE--))
    silence=${TRUNCS[$TRUNCATE]}
    SILENCE="silence ${silence}"
fi

if [[ $MIXDOWN == 0 ]]; then
    REMIX=
else
    REMIX="remix -"
fi

#
# Report some internals in debug mode
#
if [[ $DEBUG -eq 1 ]]; then
    echo "SPEEDUP  = $SPEEDUP"
    echo "TRUNCATE = $TRUNCATE"
    echo "MIXDOWN  = $MIXDOWN"
    echo "speed    = ${speed:-0}"
    echo "silence  = ${silence:-}"
    echo "TEMPO    = $TEMPO"
    echo "SILENCE  = $SILENCE"
    echo "REMIX    = $REMIX"
fi

#
# Is there anything to do?
#
if [[ -z $TEMPO && -z $SILENCE ]]; then
    echo "Nothing to do; exiting"
    exit 1
fi

#
# Divide up the path to the file
#
orig="$(realpath "$1")"
odir="${orig%/*}"
oname="${orig##*/}"
oext="${oname##*.}"

#
# The name of the original file will be changed to this
#
new="${odir}/${oname%.$oext}_.${oext}"

#
# Report the name of the input file
#
echo "Processing $orig"

#
# If the new name exists we already processed it
#
if [[ -e $new ]]; then
    echo "Oops! Looks like this file has already been sped up"
    exit 1
fi

#
# Rename the original file
#
if [[ $DRYRUN -eq 1 ]]; then
    printf "Dry run: rename %s to %s\n" "$orig" "$new"
else
    mv "$orig" "$new"
fi

#
# Speed up and remove long silences as requested
# -S   requests a progress display
# -v2  adjusts the volume of the file that follows it on the command line by
#      a factor of 2
# -V9  requests a very high (debug) level of verbosity (default -V2)
# remix - mixes all stereo to mono
#
#sox -S -v2 "${new}" "${orig}" -V9 ${TEMPO} remix - ${SILENCE}
if [[ $DRYRUN -eq 1 ]]; then
    printf "Dry run: %s\n" \
        "sox -S -v2 \"${new}\" \"${orig}\" ${TEMPO} ${REMIX} ${SILENCE}"
else
    # [We want TEMP, REMIX and SILENCE to be word-split etc]
    # shellcheck disable=SC2086
    sox -S -v2 "${new}" "${orig}" ${TEMPO} ${REMIX} ${SILENCE}
fi

#
# Delete the original file if asked. Note that the script can't detect that
# the audio has been sped up if this file is missing.
#
if [[ $CLEANUP -eq 1 ]]; then
    if [[ $DRYRUN -eq 1 ]]; then
        printf "Dry run: delete %s\n" "$new"
    else
        rm -f "$new"
    fi
fi

exit

# vim: syntax=sh:ts=8:sw=4:ai:et:tw=78:fo=tcrqn21


syntax highlighted by Code2HTML, v. 0.9.1