#!/bin/sh
# Copyright (C) 1999--2001 Chris Vaill
# This file is part of normalize.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.


#######################################################################
# These variables may be customized for local setup
#######################################################################

# %m becomes name of mp3 or ogg file
# %w becomes name of temporary WAV file
# %b becomes bitrate of re-encoded file, as specified by the -b option
# Example: OGGENCODE="oggenc -b %b -o %m %w"

MP3DECODE="REPLACE_WITH_MP3_DECODER -o %w %m"
MP3ENCODE="REPLACE_WITH_MP3_ENCODER %w %m"
OGGDECODE="REPLACE_WITH_OGG_DECODER -d wav -f %w %m"
OGGENCODE="REPLACE_WITH_OGG_ENCODER -b %b -o %m %w"

# to disable preserving of info tags, set the following two to "true"
MP3INFO="REPLACE_WITH_id3cp_OR_mp3info"
OGGINFO="REPLACE_WITH_OGG_TAG_READER"

# change this if normalize is not on your path
NORMALIZE="normalize"


#######################################################################
# No user serviceable parts below
#######################################################################

test -n "$BASH_VERSION" && set -o posix

PROGNAME=`basename "$0"`
VERSION=0.7

usage () {
    cat << EOF
Usage: $PROGNAME [OPTION]... [FILE]...
  Normalize volume of mp3 or ogg files by decoding, running normalize,
  and re-encoding.  Batch mode and mix mode cannot be used, but this
  only requires as much extra disk space as the largest mp3 or ogg
  file, decoded.

  -a AMP         \\
  -g ADJ          |
  -n              |_ These arguments are passed as arguments to normalize.
  -T THR          |  Run "normalize --help" for more info.
  -v              |
  -q             /
  --ogg          Convert files to OGG, regardless of original format
  --mp3          Convert files to MP3, regardless of original format
  -b BR          Set bitrate of re-encoded file (default 128)
  --tmpdir TMP   Put temporary WAV files in temp directory TMP
  --notags       Do not copy ID3 or OGG tags to the output file
  -h             Display this help and exit.
  -V             Display version information and exit.

Report bugs to <cvaill@cs.columbia.edu>.
EOF
}

if test x"$1" = x; then
    usage
    exit 0
fi

# sed script to escape \"$` chars
SED_ESC='s/\\/\\\\/g;s/"/\\"/g;s/\$/\\\$/g;s/`/\\`/g'
# sed script to double-escape \"$` chars
SED_ESC_ESC='s/\\/\\\\\\\\/g;
	     s/"/\\\\"/g;
	     s/\$/\\\\\$/g;
	     s/`/\\\\`/g'

NORMALIZE_ARGS=
NOMOREARGS=false
ALL_TO_MP3=false
ALL_TO_OGG=false
BITRATE=128
COPY_TAGS_OPT=true
TMPDIR=
DO_ADJUST=true
# we track verbosity separately for this script
VERBOSE=1

TAG_ARTIST=
TAG_TITLE=
TAG_ALBUM=
TAG_COMM=
TAG_TRACK=
TAG_GENRE=
TAG_YEAR=
TAG_TEMPFILE=

read_tags () {
    FNAME="$1"

    case "$FNAME" in
    *.ogg|*.OGG)
	TAG_CMD=`"$OGGINFO" "$FNAME" \
		| sed 's/^artist=/TAG_ARTIST=/;
		       s/^title=/TAG_TITLE=/;
		       s/^album=/TAG_ALBUM=/;
		       s/^tracknumber=/TAG_TRACK=/;
		       s/^date=/TAG_YEAR=/;
		       s/^genre=/TAG_GENRE=/;
		       s/^comment=/TAG_COMM=/;
		       s/\\\/\\\\\\\/g;
		       s/\\"/\\\\"/g;
		       s/\\$/\\\\$/g;
		       s/\`/\\\\\`/g;
		       s/=\(.*\)/=\"\1\"/'`
	eval "$TAG_CMD"
	;;
    *.mp3|*.MP3)
	if test x"$MP3INFO" = xid3cp; then
	    n=$$
	    while test -f /tmp/normalize-mp3-$n.tag; do
		n=`expr $n + 1`
	    done
	    TAG_TEMPFILE=/tmp/normalize-mp3-$n.tag
	    # copy the tag somewhere safe
	    "$MP3INFO" "$FNAME" "$TAG_TEMPFILE" > /dev/null
	elif test x"$MP3INFO" = xmp3info; then
	    TAG_CMD=`"$MP3INFO" -p 'TAG_ARTIST=%a\n\
				    TAG_TITLE=%t\n\
				    TAG_ALBUM=%l\n\
				    TAG_COMM=%c\n\
				    TAG_TRACK=%n\n\
				    TAG_GENRE=%g\n\
				    TAG_YEAR=%y\n' \
				"$FNAME" 2> /dev/null \
		    | sed 's/\\\/\\\\\\\/g;
			   s/\\"/\\\\"/g;
			   s/\\$/\\\\$/g;
			   s/\`/\\\\\`/g;
			   s/=\(.*\)/=\"\1\"/'`
	    eval "$TAG_CMD"
	else
	    TAG_ARTIST=
	    TAG_TITLE=
	    TAG_ALBUM=
	    TAG_COMM=
	    TAG_TRACK=
	    TAG_GENRE=
	    TAG_YEAR=
	fi
	;;
    *)
	TAG_ARTIST=
	TAG_TITLE=
	TAG_ALBUM=
	TAG_COMM=
	TAG_TRACK=
	TAG_GENRE=
	TAG_YEAR=
	;;
    esac
}

write_tags () {
    FNAME="$1"

    case "$FNAME" in
    *.ogg|*.OGG)
	# FIXME when there's a command-line ogg tag writer available
	# until then, we do it with oggenc, later
	;;
    *.mp3|*.MP3)
	if test x"$MP3INFO" = xid3cp; then
	    if test -f "$TAG_TEMPFILE"; then
		# copy the tags back from the tempfile
		"$MP3INFO" "$TAG_TEMPFILE" "$FNAME" > /dev/null \
		&& rm -f "$TAG_TEMPFILE"
	    else
		echo "$PROGNAME: lost temporary tag file $TAG_TEMPFILE"
	    fi
	elif test x"$MP3INFO" = xmp3info; then
	    "$MP3INFO" -a "$TAG_ARTIST" -t "$TAG_TITLE" -l "$TAG_ALBUM" \
		       -y "$TAG_YEAR" -c "$TAG_COMM" -n "$TAG_TRACK" \
		       -g "$TAG_GENRE" "$FNAME"
	fi
	;;
    esac
}

# set MP3INFO and OGGINFO to empty strings if they haven't been set
case "$MP3INFO" in
    REPLACE*) MP3INFO= ;;
esac
case "$OGGINFO" in
    REPLACE*) OGGINFO= ;;
esac

# read arguments
while test x"$1" != x; do
    if test $NOMOREARGS != true; then
	case "$1" in
	-*) case "$1" in
	    -a|--amplitude)
		NORMALIZE_ARGS="$NORMALIZE_ARGS -a $2"
		shift 2 ; continue ;;
	    -b|--bitrate)
		BITRATE="$2"
		shift 2 ; continue ;;
	    -g|--gain)
		NORMALIZE_ARGS="$NORMALIZE_ARGS -g $2"
		shift 2 ; continue ;;
	    -n|--no-adjust)
		NORMALIZE_ARGS="$NORMALIZE_ARGS -n"
		DO_ADJUST=false
		shift ; continue ;;
	    -T|--adjust-threshold)
		NORMALIZE_ARGS="$NORMALIZE_ARGS -T $2"
		shift 2 ; continue ;;
	    --tmp|--tmpdir)
		TMPDIR="$2"
		case "$TMPDIR" in
		    */) ;;
		    *) TMPDIR="$TMPDIR/" ;;
		esac
		shift 2 ; continue ;;
	    -v|--verbose)
		NORMALIZE_ARGS="$NORMALIZE_ARGS -v"
		VERBOSE=2
		shift ; continue ;;
	    -q|--quiet)
		NORMALIZE_ARGS="$NORMALIZE_ARGS -q"
		VERBOSE=0
		shift ; continue ;;
	    --ogg)
		ALL_TO_OGG=true
		ALL_TO_MP3=false
		shift ; continue ;;
	    --mp3)
		ALL_TO_MP3=true
		ALL_TO_OGG=false
		shift ; continue ;;
	    --notags|--noid3)
		COPY_TAGS_OPT=false
		shift ; continue ;;
	    -h|--help)
		usage ; exit 0 ;;
	    -V|--version)
		echo "$PROGNAME, from the normalize $VERSION distribution"
		exit 0 ;;
	    --) NOMOREARGS=true
		shift ; continue ;;
	    *) echo "Unrecognized option \"$1\"" ; usage ; exit 1 ;;
	    esac
	    ;;
	esac
    fi

    INPUT_FILE="$1"

    case "$INPUT_FILE" in
	*.mp3|*.MP3)
	    INPUT_FMT=mp3; OUTPUT_FMT=mp3
	    DECODER="$MP3DECODE"; ENCODER="$MP3ENCODE"
	    ;;
	*.ogg|*.OGG)
	    INPUT_FMT=ogg; OUTPUT_FMT=ogg
	    DECODER="$OGGDECODE"; ENCODER="$OGGENCODE"
	    ;;
	*)
	    echo "$PROGNAME: file \"$INPUT_FILE\" has unrecognized extension"
	    echo "$PROGNAME: Recognized extensions are mp3 and ogg"
	    continue
	    ;;
    esac

    # construct temporary and output file names,
    # and escape commas in filenames for sed
    if test -n "$TMPDIR"; then
	FILEBASE=`echo "$INPUT_FILE" \
	    | sed 's,.*/,,;s,\..*,,;s/,/\\,/g'`
	FILEBASE="$TMPDIR$FILEBASE"
    else
	FILEBASE=`echo "$INPUT_FILE" | sed 's,\..*,,;s/,/\\,/g'`
    fi
    TMP_FILE="$FILEBASE.wav"
    INPUT_FILE=`echo "$INPUT_FILE" | sed 's/,/\\,/g'`
    BITRATE=`echo "$BITRATE" | sed 's/,/\\,/g'` # hey, you never know
    if test $ALL_TO_MP3 = true; then
	OUTPUT_FILE="$FILEBASE.mp3"
	OUTPUT_FMT=mp3
	ENCODER="$MP3ENCODE"
    elif test $ALL_TO_OGG = true; then
	OUTPUT_FILE="$FILEBASE.ogg"
	OUTPUT_FMT=ogg
	ENCODER="$OGGENCODE"
    else
	OUTPUT_FILE="$INPUT_FILE"
    fi

    # construct encode and decode commands
    TMP_FILE_ESC=`echo "$TMP_FILE" | sed "$SED_ESC_ESC"`
    INPUT_FILE_ESC=`echo "$INPUT_FILE" | sed "$SED_ESC_ESC"`
    OUTPUT_FILE_ESC=`echo "$OUTPUT_FILE" | sed "$SED_ESC_ESC"`
    DECODE_CMD=`echo $DECODER \
	| sed "s,%w,\"$TMP_FILE_ESC\",g;\
	       s,%m,\"$INPUT_FILE_ESC\",g;\
	       s,%b,\"$BITRATE\",g"`
    ENCODE_CMD=`echo $ENCODER \
	| sed "s,%w,\"$TMP_FILE_ESC\",g;\
	       s,%m,\"$OUTPUT_FILE_ESC\",g;\
	       s,%b,\"$BITRATE\",g"`
    if test ! $VERBOSE = 2; then
	DECODE_CMD="$DECODE_CMD > /dev/null 2>&1"
	ENCODE_CMD="$ENCODE_CMD > /dev/null 2>&1"
    fi

    # should we try to preserve info tags?
    COPY_TAGS=true
    if test $COPY_TAGS_OPT = false; then COPY_TAGS=false
    elif test x"$MP3INFO" = x \
      && test $INPUT_FMT = mp3 -o $OUTPUT_FMT = mp3; then
	echo "$PROGNAME: unable to preserve tags; edit script to fix" >&2
	COPY_TAGS=false
    elif test x"$OGGINFO" = x \
      && test $INPUT_FMT = ogg -o $OUTPUT_FMT = ogg; then
	echo "$PROGNAME: unable to preserve tags; edit script to fix" >&2
	COPY_TAGS=false
    fi

    # save tags, if necessary
    test $COPY_TAGS = true && read_tags "$INPUT_FILE"

    # Since there's no common standalone ogg tag setting program,
    # we have to set the tags as we encode, so we change the encode
    # command to add in the tag setting options.
    if test $COPY_TAGS = true -a $OUTPUT_FMT = ogg; then
	if test -n "$TAG_ARTIST"; then
	    TAG_ARTIST=`echo "$TAG_ARTIST" | sed "$SED_ESC"`
	    ENCODE_CMD="$ENCODE_CMD -a \"$TAG_ARTIST\""
	fi
	if test -n "$TAG_TITLE"; then
	    TAG_TITLE=`echo "$TAG_TITLE" | sed "$SED_ESC"`
	    ENCODE_CMD="$ENCODE_CMD -t \"$TAG_TITLE\""
	fi
	if test -n "$TAG_ALBUM"; then
	    TAG_ALBUM=`echo "$TAG_ALBUM" | sed "$SED_ESC"`
	    ENCODE_CMD="$ENCODE_CMD -l \"$TAG_ALBUM\""
	fi
	if test -n "$TAG_COMM"; then
	    TAG_COMM=`echo "$TAG_COMM" | sed "$SED_ESC"`
	    ENCODE_CMD="$ENCODE_CMD -c \"comment=$TAG_COMM\""
	fi
	if test -n "$TAG_TRACK"; then
	    TAG_TRACK=`echo "$TAG_TRACK" | sed "$SED_ESC"`
	    ENCODE_CMD="$ENCODE_CMD -N \"$TAG_TRACK\""
	fi
	if test -n "$TAG_GENRE"; then
	    TAG_GENRE=`echo "$TAG_GENRE" | sed "$SED_ESC"`
	    ENCODE_CMD="$ENCODE_CMD -c \"genre=$TAG_GENRE\""
	fi
	if test -n "$TAG_YEAR"; then
	    TAG_YEAR=`echo "$TAG_YEAR" | sed "$SED_ESC"`
	    ENCODE_CMD="$ENCODE_CMD -d \"$TAG_YEAR\""
	fi
    fi

    # run decoder
    #test ! $VERBOSE = 0 && echo "Running \`$DECODE_CMD'..." >&2
    test ! $VERBOSE = 0 && echo "Running decoder..." >&2
    eval "$DECODE_CMD"

    if test $? != 0; then echo "$PROGNAME: error while decoding"; exit 1; fi

    # run normalize
    TMP_FILE_ESC=`echo "$TMP_FILE" | sed "$SED_ESC"`
    test ! $VERBOSE = 0 && echo "Running normalize..." >&2
    eval "$NORMALIZE $NORMALIZE_ARGS \"$TMP_FILE_ESC\""

    # if normalize returns 1, there was an error
    # if normalize returns 2, there was no error, just no volume was changed
    NORM_RET=$?
    if test x$NORM_RET != x0 -a x$NORM_RET != x2; then
	echo "$PROGNAME: error while running normalize"
	exit 1
    fi

    # run encoder, if necessary
    if test $DO_ADJUST = true; then
	if test x$NORM_RET = x0 -o x"$INPUT_FILE" != x"$OUTPUT_FILE"; then
	    # run encoder
	    #test ! $VERBOSE = 0 && echo "Running \`$ENCODE_CMD'..." >&2
	    test ! $VERBOSE = 0 && echo "Running encoder..." >&2
	    eval "$ENCODE_CMD"
	    if test $? != 0; then
		echo "$PROGNAME: error while encoding"
		exit 1
	    fi
	else
	    test ! $VERBOSE = 0 \
		&& echo "$INPUT_FILE is already normalized, not re-encoding..."
	fi
    fi

    # restore tags, if necessary
    test $COPY_TAGS = true && write_tags "$OUTPUT_FILE"

    # delete temp file
    rm -f "$TMP_FILE"

    shift
done
