#!/bin/sh
#             __________               __   ___.
#   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
#   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
#   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
#   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
#                     \/            \/     \/    \/            \/
# $Id$
#
# Copyright (c) 2006 Jonas Häggqvist
#
# All files in this archive are subject to the GNU General Public License.
# See the file COPYING in the source tree root for full license agreement.
#
# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
# KIND, either express or implied.
#
# A selection of functions common to creating voicefiles for Rockbox.
# 
# You may wish to change some of the settings below.

#####################
# Program locations #
#####################

# Leave any you're not using untouched, enter full path if the program is
# not found

# the festival main executable
FESTIVAL_BIN=festival
# the festival_client binary
FESTIVAL_CLIENT=festival_client

# The flite executable
FLITE_BIN=flite

# The eSpeak executable
ESPEAK_BIN=espeak

# The lame executable
LAME_BIN=lame

# The speexenc executable
SPEEX_BIN=speexenc

# The oggenc executable
VORBIS_BIN=oggenc

# Tools directory
TOOLSDIR=`dirname $0`

# The wavtrim executable
WAVTRIM=$TOOLSDIR/wavtrim

# The SAPI5 script directory
if [ -f "`which cygpath`" ]; then
    SAPI5DIR=`cygpath $TOOLSDIR -a -w`
fi

#####################
# Festival settings #
#####################

# If you're not using festival, leave untouched

# whether to start the Festival server locally (Y/N)
FESTIVAL_START=Y
# the host of the Festival server
# this is set to localhost automatically when FESTIVAL_START is Y
FESTIVAL_HOST=localhost
# the port of the Festival server
FESTIVAL_PORT=1314
# where to log the Festival client output
FESTIVAL_LOG=/dev/null
# other options to the festival server
FESTIVAL_OPTS=""

##################
# Flite settings #
##################

# If you're not using flite, leave untouched
FLITE_OPTS=""

###################
# eSpeak settings #
###################

# If you're not using eSpeak, leave untouched
ESPEAK_OPTS=""

####################
# Wavtrim settings #
####################

# The maximum sample value that will be treated as silence by the wavtrim tool.
# The value is expressed as an absolute 16 bit integer sample value (0 dB equals
# 32767).
#
# 500 is a good guess - at least for Festival

NOISEFLOOR='500'

#####################
# Encoding settings #
#####################
# where to log the encoder output
ENC_LOG=/dev/null

# Suggested: --vbr-new -t --nores -S
#            VBR, independent frames, silent mode
LAME_OPTS="--vbr-new -t --nores -S"

# Suggested:
# XXX: suggest a default
SPEEX_OPTS=""

# Suggested: -q0 --downmix
#            Low quality, mono
VORBIS_OPTS="-q0 --downmix"

###################
# End of settings #
###################

# Check if executables exist and perform any necessary initialisation
init_tts() {
    case $TTS_ENGINE in
        festival)
            # Check for festival_client
            if [ ! -f "`which $FESTIVAL_CLIENT`" ]; then
                echo "Error: $FESTIVAL_CLIENT not found"
                exit 4
            fi

            # Check for, and start festival server if specified
            if [ X$FESTIVAL_START = XY ]; then
                if [ ! -f "`which $FESTIVAL_BIN`" ]; then
                    echo "Error: $FESTIVAL_BIN not found"
                    exit 3
                fi
                FESTIVAL_HOST='localhost'
                $FESTIVAL_BIN $FESTIVAL_OPTS --server 2>&1 > /dev/null &
                FESTIVAL_SERVER_PID=$!
                sleep 3
                if [ `ps | grep -c "^\ *$FESTIVAL_SERVER_PID"` -ne 1 ]; then
                    echo "Error: Festival not started"
                    exit 9
                fi
            fi
            # Test connection to festival server
            output=`echo -E "Rockbox" | $FESTIVAL_CLIENT --server \
                   $FESTIVAL_HOST --otype riff --ttw --output \
                   /dev/null 2>&1`
            if [ $? -ne 0 ]; then
                echo "Error: Couldn't connect to festival server at" \
                     "$FESTIVAL_HOST ($output)"
                exit 8
            fi
            ;;
        flite)
            # Check for flite
            if [ ! -f "`which $FLITE_BIN`" ]; then
                echo "Error: $FLITE_BIN not found"
                exit 5
            fi
            ;;
        espeak)
            # Check for espeak
            if [ ! -f "`which $ESPEAK_BIN`" ]; then
                echo "Error: $ESPEAK_BIN not found"
                exit 5
            fi
            ;;
        sapi5)
            # Check for SAPI5
            cscript /B $SAPI5DIR/sapi5_init_tts.vbs
            if [ $? -ne 0 ]; then
                echo "Error: SAPI 5 not available"
                exit 5
            fi
            ;;
        *)
            echo "Error: no valid TTS engine selected: $TTS_ENGINE"
            exit 2
            ;;
    esac
    if [ ! -x $WAVTRIM ]; then
        echo "Error: $WAVTRIM is not available"
        exit 11
    fi
}

# Perform any necessary shutdown for TTS engine
stop_tts() {
    case $TTS_ENGINE in
        festival)
            if [ X$FESTIVAL_START = XY ]; then
                # XXX: This is probably possible to do using festival_client
                kill $FESTIVAL_SERVER_PID > /dev/null 2>&1
            fi
            ;;
    esac
}

# Check if executables exist and perform any necessary initialisation
init_encoder() {
    case $ENCODER in
        lame)
            # Check for lame binary
            if [ ! -f "`which $LAME_BIN`" ]; then
                echo "Error: $LAME_BIN not found"
                exit 6
            fi
            ;;
        speex)
            # Check for speexenc binary
            if [ ! -f "`which $SPEEX_BIN`" ]; then
                echo "Error: $SPEEX_BIN not found"
                exit 7
            fi
            ;;
        vorbis)
            # Check for vorbis encoder binary
            if [ ! -f "`which $VORBIS_BIN`" ]; then
                echo "Error: $VORBIS_BIN not found"
                exit 10
            fi
            ;;
        *)
            echo "Error: no valid encoder selected: $ENCODER"
            exit 1
            ;;
    esac

}

# Encode file $1 with ENCODER and save the result in $2, delete $1 if specified
encode() {
    INPUT=$1
    OUTPUT=$2

    if [ ! -f "$INPUT" ]; then
        echo "Warning: missing input file: \"$INPUT\""
    else
        echo "Action: Encode   $OUTPUT with $ENCODER"
        case $ENCODER in
            lame)
                $LAME_BIN $LAME_OPTS "$WAV_FILE" "$OUTPUT" >>$ENC_LOG 2>&1
                ;;
            speex)
                $SPEEX_BIN $SPEEX_OPTS "$WAV_FILE" "$OUTPUT" >>$ENC_LOG 2>&1
                ;;
            vorbis)
                $VORBIS_BIN $VORBIS_OPTS "$WAV_FILE" -o "$OUTPUT" >>$ENC_LOG 2>&1
        esac
        if [ ! -f "$OUTPUT" ]; then
            echo "Warning: missing output file \"$OUTPUT\""
        fi
    fi
}

# Generate file $2 containing $1 spoken by TTS_ENGINE, trim silence 
voice() {
    TO_SPEAK=$1
    WAV_FILE=$2
    if [ ! -f "$WAV_FILE" ] || [ X$OVERWRITE_WAV = XY ]; then
        if [ "${TO_SPEAK}" = "" ]; then
            touch "$WAV_FILE"
        else
            case $TTS_ENGINE in
                festival)
                    echo "Action: Generate $WAV_FILE with festival"
                    echo -E "$TO_SPEAK" | $FESTIVAL_CLIENT \
                         --server $FESTIVAL_HOST \
                         --otype riff --ttw --output "$WAV_FILE" 2>"$WAV_FILE"
                    ;;
                espeak)
                    echo "Action: Generate $WAV_FILE with eSpeak"
                    echo $ESPEAK_BIN $ESPEAK_OPTS -w "$WAV_FILE"
                    echo -E "$TO_SPEAK" | $ESPEAK_BIN $ESPEAK_OPTS -w "$WAV_FILE"
                    ;;
                flite)
                    echo "Action: Generate $WAV_FILE with flite"
                    echo -E "$TO_SPEAK" | $FLITE_BIN $FLITE_OPTS -o "$WAV_FILE"
                    ;;
                sapi5)
                    cscript /B "$SAPI5DIR\sapi5_voice.vbs" ""$TO_SPEAK"" "$WAV_FILE"
                    ;;
            esac
        fi
    fi
    trim "$WAV_FILE"
}

# Trim wavefile $1
trim() {
    WAVEFILE="$1"
    echo "Action: Trim     $WAV_FILE"
    $WAVTRIM "$WAVEFILE" $NOISEFLOOR
}
