summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--tools/Makefile6
-rwxr-xr-xtools/configure172
-rwxr-xr-xtools/gentalkclips.sh152
-rwxr-xr-xtools/genvoice.sh121
-rw-r--r--tools/voicecommon.sh282
-rw-r--r--tools/voicefont.c218
-rw-r--r--tools/wavtrim.c235
7 files changed, 1185 insertions, 1 deletions
diff --git a/tools/Makefile b/tools/Makefile
index 26cdcab230..1f8be87370 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -50,6 +50,12 @@ generate_rocklatin: generate_rocklatin.c ../firmware/drivers/lcd-player-charset.
uclpack:
$(SILENT)$(MAKE) -C ucl
+wavtrim: wavtrim.c
+ $(SILENT)$(CC) -g $+ -o $@
+
+voicefont: voicefont.c
+ $(SILENT)$(CC) -g $+ -o $@
+
clean:
@echo "Cleaning tools"
$(SILENT)rm -f $(CLEANALL) $(shell for f in $(CLEANALL) ; do echo $$f.exe $$f.o $$f.obj ; done) *.ajf *~
diff --git a/tools/configure b/tools/configure
index e7562f824b..60dc49cbc8 100755
--- a/tools/configure
+++ b/tools/configure
@@ -366,6 +366,154 @@ if [ -z "$simver" ]; then
fi
}
+voiceconfig () {
+ echo "Building voice for $archos"
+ echo ""
+
+ if [ `which flite` ]; then
+ FLITE="F(l)ite "
+ FLITE_OPTS="FLITE_OPTS=\"\""
+ DEFAULT_TTS="flite"
+ DEFAULT_TTS_OPTS=$FLITE_OPTS
+ DEFAULT_NOISEFLOOR="500"
+ DEFAULT_CHOICE="L"
+ fi
+ if [ `which speak` ]; then
+ ESPEAK="(e)Speak "
+ ESPEAK_OPTS="ESPEAK_OPTS=\"\""
+ DEFAULT_TTS="espeak"
+ DEFAULT_TTS_OPTS=$ESPEAK_OPTS
+ DEFAULT_NOISEFLOOR="500"
+ DEFAULT_CHOICE="e"
+ fi
+ if [ `which festival` ]; then
+ FESTIVAL="(F)estival "
+ FESTIVAL_OPTS="FLITE_OPTS=\"\""
+ DEFAULT_TTS="festival"
+ DEFAULT_TTS_OPTS=$FESTIVAL_OPTS
+ DEFAULT_NOISEFLOOR="500"
+ DEFAULT_CHOICE="F"
+ fi
+
+ if [ "$FESTIVAL" == "$FLITE" ] && [ "$FLITE" == "$ESPEAK" ]; then
+ echo "You need Festival, eSpeak or Flite in your path to build voice files"
+ exit
+ fi
+
+ echo "TTS engine to use: ${FLITE}${FESTIVAL}${ESPEAK}(${DEFAULT_CHOICE})?"
+ option=`input`
+ case "$option" in
+ [Ll])
+ TTS_ENGINE="flite"
+ NOISEFLOOR="500" # TODO: check this value
+ TTS_OPTS=$FLITE_OPTS
+ ;;
+ [Ee])
+ TTS_ENGINE="espeak"
+ NOISEFLOOR="500"
+ TTS_OPTS=$ESPEAK_OPTS
+ ;;
+ [Ff])
+ TTS_ENGINE="festival"
+ NOISEFLOOR="500"
+ TTS_OPTS=$FESTIVAL_OPTS
+ ;;
+ *)
+ TTS_ENGINE=$DEFAULT_TTS
+ TTS_OPTS=$DEFAULT_TTS_OPTS
+ NOISEFLOOR=$DEFAULT_NOISEFLOOR
+ esac
+ echo "Using $TTS_ENGINE for TTS"
+
+ echo ""
+
+ if [ `which oggenc` ]; then
+ OGGENC="(O)ggenc "
+ DEFAULT_ENC="oggenc"
+ VORBIS_OPTS="VORBIS_OPTS=\"-q0 --downmix\""
+ DEFAULT_ENC_OPTS=$VORBIS_OPTS
+ DEFAULT_CHOICE="O"
+ fi
+ if [ `which speexenc` ]; then
+ SPEEXENC="(S)peexenc "
+ DEFAULT_ENC="speexenc"
+ SPEEX_OPTS="" # TODO: find appropriate options for speex
+ DEFAULT_ENC_OPTS=$SPEEX_OPTS
+ DEFAULT_CHOICE="S"
+ fi
+ if [ `which lame` ]; then
+ LAME="(L)ame "
+ DEFAULT_ENC="lame"
+ LAME_OPTS="LAME_OPTS=\"--resample 12 -t -m m -h -V 9 -S\""
+ DEFAULT_ENC_OPTS=$LAME_OPTS
+ DEFAULT_CHOICE="L"
+ fi
+
+ if [ "$LAME" == "" ]; then
+ echo "You need to have Lame installed to build voice files"
+ fi
+
+ echo "Encoder to use: ${LAME}${OGGENC}${SPEEXENC}(${DEFAULT_CHOICE})?"
+ echo ""
+ echo "Note: Use Lame - the other options won't work"
+ option=`input`
+ case "$option" in
+ [Oo])
+ ENCODER="oggenc"
+ ENC_OPTS=$VORBIS_OPTS
+ ;;
+ [Ss])
+ ENCODER="speexenc"
+ ENC_OPTS=$SPEEX_OPTS
+ ;;
+ [Ll])
+ ENCODER="lame"
+ ENC_OPTS=$LAME_OPTS
+ ;;
+ *)
+ ENCODER=$DEFAULT_ENC
+ ENC_OPTS=$DEFAULT_ENC_OPTS
+ esac
+ echo "Using $ENCODER for encoding voice clips"
+
+ cat > voicesettings.sh <<EOF
+TTS_ENGINE="${TTS_ENGINE}"
+ENCODER="${ENCODER}"
+TEMPDIR="${pwd}"
+NOISEFLOOR="${NOISEFLOOR}"
+${TTS_OPTS}
+${ENC_OPTS}
+EOF
+}
+
+picklang() {
+ # figure out which languages that are around
+ for file in $rootdir/apps/lang/*.lang; do
+ clean=`echo $file | sed -e 's:.*/::g' | cut "-d." -f1`
+ langs="$langs $clean"
+ done
+
+ num=1
+ for one in $langs; do
+ echo "$num. $one"
+ num=`expr $num + 1`
+ done
+
+ read pick
+ return $pick;
+}
+
+whichlang() {
+ num=1
+ for one in $langs; do
+ if [ "$num" = "$pick" ]; then
+ echo $one
+ return
+ fi
+ num=`expr $num + 1`
+ done
+}
+
target=$1
if test "$target" = "--help"; then
@@ -1144,7 +1292,7 @@ fi
esac
echo ""
- echo "Build (N)ormal, (D)evel, (S)imulator, (B)ootloader, $gdbstub(M)anual? (N)"
+ echo "Build (N)ormal, (D)evel, (S)imulator, (B)ootloader, $gdbstub(M)anual, (V)oice? (N)"
option=`input`;
@@ -1202,6 +1350,11 @@ fi
apps="manual"
echo "Manual build selected"
;;
+ [Vv])
+ echo "Voice build selected"
+ voiceconfig
+ voice="yes"
+ ;;
*)
debug=""
echo "Normal build selected"
@@ -1238,6 +1391,20 @@ echo "Using source code root directory: $rootdir"
# this was once possible to change at build-time, but no more:
language="english"
+# Ask about language if building voice
+if [ "yes" == "$voice" ]; then
+ echo "Select a number for the language to use (default is english)"
+
+ picklang
+ language=`whichlang`
+
+ if [ -z "$language" ]; then
+ # pick a default
+ language="english"
+ fi
+ echo "Language set to $language"
+fi
+
uname=`uname`
if [ "yes" = "$simulator" ]; then
@@ -1516,6 +1683,9 @@ clean:
@ARCHOSROM@ @FLASHFILE@ UI256.bmp rockbox-full.zip \
html txt rockbox-manual*.zip
+voice: tools
+ \$(TOOLSDIR)/genvoice.sh \$(ROOTDIR) \$(LANGUAGE) \$(ARCHOS) voicesettings.sh
+
tools:
\$(SILENT)\$(MAKE) -C \$(TOOLSDIR) CC=\$(HOSTCC) @TOOLSET@
diff --git a/tools/gentalkclips.sh b/tools/gentalkclips.sh
new file mode 100755
index 0000000000..8da3b37704
--- /dev/null
+++ b/tools/gentalkclips.sh
@@ -0,0 +1,152 @@
+#!/bin/sh
+# __________ __ ___.
+# Open \______ \ ____ ____ | | _\_ |__ _______ ___
+# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+# \/ \/ \/ \/ \/
+# $Id$
+#
+# Copyright (c) 2004 Daniel Gudlat
+# - http://www.rockbox.org/tracker/task/2131
+# Copyright (c) 2006 Jonas Häggqvist
+# - This version, only dirwalk and the following comments remains
+#
+# 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.
+#
+# Note: You may wish to change some of the settings below.
+#
+# A script to automatically generate audio clips containing the names of all
+# folders in a directory tree for use with the "Talkbox" feature available to
+# users of the Rockbox open source firmware for Archos MP3 players and
+# recorders as well as several other devices. Talkbox permits the device to
+# speak the names of the folders as one navigates the directory structure on
+# the device, thus permitting "eyes-free" use for those for whom the usual
+# visual navigation is difficult or simply inadvisable.
+#
+# Audio clips are captured and stored in wave format, then converted into MP3
+# format by a third party application (lame). If you execute the script,
+# passing it the top level of your music file hierarchy as an argument while
+# your device is connected to your PC, then the resulting audio clips will be
+# generated and stored in the correct location for use with the Talkbox
+# feature. Alternatively, if you mirror your music folder structure from your
+# PC to your Archos device, you can just run the script on the PC and then
+# update the files on your Archos with your usual synchronization routine.
+#
+# NOTE: If you don't already have them installed, you may obtain the Festival
+# text to speech system and several voices at:
+#
+# http://www.cstr.ed.ac.uk/projects/festival.html
+# http://festvox.org/festival/
+#
+# The most pleasant freely available Festival voice I know of is the slt_arctic
+# voice from HST at http://hts.ics.nitech.ac.jp/
+#
+# Known bugs
+# - This script generates talk clips for all files, Rockbox only uses talk clips
+# for music files it seems.
+
+# Include voicecommon.sh from the same dir as this script
+# Any settings from voicecommon can be overridden if added below the following
+# line.
+source `dirname $0`'/voicecommon.sh'
+
+####################
+# General settings #
+####################
+
+# which TTS engine to use. Available: festival, flite, espeak
+TTS_ENGINE=festival
+# which encoder to use, available: lame, speex, vorbis (only lame will produce
+# functional voice clips)
+ENCODER=lame
+# whether to overwrite existing mp3 files or only create missing ones (Y/N)
+OVERWRITE_TALK=N
+# whether, when overwriting mp3 files, also to regenerate all the wav files
+OVERWRITE_WAV=N
+# whether to remove the intermediary wav files after creating the mp3 files
+REMOVE_WAV=Y
+# whether to recurse into subdirectories
+RECURSIVE=Y
+# whether to strip extensions from filenames
+STRIP_EXTENSIONS=Y
+
+###################
+# End of settings #
+###################
+
+strip_extension() {
+ TO_SPEAK=$1
+ # XXX: add any that needs adding
+ for ext in mp3 ogg flac mpc sid; do
+ TO_SPEAK=`echo "$TO_SPEAK" |sed "s/\.$ext//i"`
+ done
+}
+
+# Walk directory $1, creating talk files if necessary, descend into
+# subdirecotries if specified
+dirwalk() {
+ if [ -d "$1" ]; then
+ for i in "$1"/*; do
+ # Do not generate talk clip for talk(.wav) files
+ if [ `echo "$i" | grep -c "\.talk$"` -ne 0 ] || \
+ [ `echo "$i" | grep -c "\.talk\.wav$"` -ne 0 ]; then
+ echo "Notice: Skipping file \"$i\""
+ continue
+ fi
+
+ TO_SPEAK=`basename "$i"`
+ if [ X$STRIP_EXTENSIONS = XY ]; then
+ strip_extension "$TO_SPEAK"
+ fi
+
+ if [ -d "$i" ]; then
+ # $i is a dir:
+ SAVE_AS="$i"/_dirname.talk
+ WAV_FILE="$SAVE_AS".wav
+
+ # If a talk clip already exists, only generate a new one if
+ # specified
+ if [ ! -f "$SAVE_AS" ] || [ X$OVERWRITE_TALK = XY ]; then
+ voice "$TO_SPEAK" "$WAV_FILE"
+ encode "$WAV_FILE" "$SAVE_AS"
+ fi
+
+ # Need to be done lastly, or all variables will be dirty
+ if [ X$RECURSIVE = XY ]; then
+ dirwalk "$i"
+ fi
+ else
+ # $i is a file:
+ SAVE_AS="$i".talk
+ WAV_FILE="$SAVE_AS".wav
+
+ # If a talk clip already exists, only generate a new one if
+ # specified
+ if [ ! -f "$i.talk" ] || [ X$OVERWRITE_TALK != XY ]; then
+ voice "$TO_SPEAK" "$WAV_FILE"
+ encode "$WAV_FILE" "$SAVE_AS"
+ fi
+ fi
+ # Remove wav file if specified
+ if [ X$REMOVEWAV = XY ]; then
+ rm -f "$WAV_FILE"
+ fi
+ done
+ else
+ echo "Warning: $1 is not a directory"
+ fi
+}
+
+init_tts
+init_encoder
+if [ $# -gt 0 ]; then
+ dirwalk "$*"
+else
+ dirwalk .
+fi
+stop_tts
diff --git a/tools/genvoice.sh b/tools/genvoice.sh
new file mode 100755
index 0000000000..5b667ea3ba
--- /dev/null
+++ b/tools/genvoice.sh
@@ -0,0 +1,121 @@
+#!/bin/sh
+# __________ __ ___.
+# Open \______ \ ____ ____ | | _\_ |__ _______ ___
+# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+# \/ \/ \/ \/ \/
+# $Id$
+#
+# Copyright 2006 Jonas Häggqvist, some parts Copyright 2004 Daniel Gudlat
+#
+# 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.
+
+# Include voicecommon.sh from the same dir as this script
+# Any settings from voicecommon can be overridden if added below the following
+# line.
+source `dirname $0`'/voicecommon.sh'
+
+####################
+# General settings #
+####################
+
+# These settings can be overridden by passing a file with definitions as
+# the fourth parameter to this script
+
+# which TTS engine to use. Available: festival, flite, espeak
+TTS_ENGINE=festival
+# which encoder to use, available: lame, speex, vorbis (only lame will produce
+# functional voice clips at this point)
+ENCODER=lame
+# Where to save temporary files
+TEMPDIR=/tmp
+
+###################
+# End of settings #
+###################
+
+createvoicefile() {
+ $VOICEFONT "$LANG_FILE" "$TEMPDIR/" "./$RLANG.voice"
+}
+
+deletefiles() {
+ # XXX: might be unsafe depending on the value of TEMPDIR
+ rm -f "${TEMPDIR}"/LANG_*
+ rm -f "${TEMPDIR}"/VOICE_*
+}
+
+generateclips() {
+ ROCKBOX_DIR="$1"
+ RLANG="$2"
+ TARGET="$3"
+ GENLANG="$ROCKBOX_DIR"/tools/genlang
+ ENGLISH="$ROCKBOX_DIR"/apps/lang/english.lang
+ LANG_FILE="$ROCKBOX_DIR"/apps/lang/$RLANG.lang
+
+ $GENLANG -e=$ENGLISH -o -t=$TARGET $LANG_FILE |(
+ i=0
+ while read line; do
+ case `expr $i % 3` in
+ 0)
+ # String ID no.
+ NUMBER=`echo $line |cut -b 2-`
+ ;;
+ 1)
+ # String ID
+ ID=`echo $line |cut -b 5-`
+ ;;
+ 2)
+ # String
+ STRING=`echo $line |cut -b 8-`
+
+ # Now generate the file
+ voice "$STRING" "$TEMPDIR/$ID".wav
+ encode "$TEMPDIR/$ID".wav "$TEMPDIR/$ID".mp3
+ ;;
+ esac
+ i=`expr $i + 1`
+ done
+ )
+}
+
+if [ -z "$3" ]; then
+ echo "Usage: $0 rockboxdirectory language target [settingsfile]";
+ exit 32
+else
+ if [ ! -d "$1" ] || [ ! -f "$1/tools/genlang" ]; then
+ echo "Error: $1 is not a Rockbox directory"
+ exit 33
+ fi
+ if [ ! -f "$1/apps/lang/$2.lang" ]; then
+ echo "Error: $2 is not a valid language"
+ exit 34
+ fi
+ if [ ! -z "$4" ]; then
+ if [ -f "$4" ]; then
+ # Read settings from file
+ source "$4"
+ else
+ echo "Error: $4 does not exist"
+ exit 36
+ fi
+ fi
+ # XXX: check for valid $TARGET?
+fi
+
+VOICEFONT=`dirname $0`/voicefont
+if [ ! -x $VOICEFONT ]; then
+ echo "Error: $VOICEFONT does not exist or is not executable"
+ exit 35
+fi
+
+init_tts
+init_encoder
+generateclips "$1" "$2" "$3"
+stop_tts
+createvoicefile
+#deletefiles
diff --git a/tools/voicecommon.sh b/tools/voicecommon.sh
new file mode 100644
index 0000000000..d6759d834b
--- /dev/null
+++ b/tools/voicecommon.sh
@@ -0,0 +1,282 @@
+#!/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=speak
+
+# The lame executable
+LAME_BIN=lame
+
+# The speexenc executable
+SPEEX_BIN=speexenc
+
+# The oggenc executable
+VORBIS_BIN=oggenc
+
+# The wavtrim executable
+WAVTRIM=`dirname $0`/wavtrim
+
+#####################
+# 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 client
+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 [ ! `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 [ ! `which $FESTIVAL_BIN` ]; then
+ echo "Error: $FESTIVAL_BIN not found"
+ exit 3
+ fi
+ FESTIVAL_HOST='localhost'
+ $FESTIVAL_BIN --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 [ ! `which $FLITE_BIN` ]; then
+ echo "Error: $FLITE_BIN not found"
+ exit 5
+ fi
+ ;;
+ espeak)
+ # Check for flite
+ if [ ! `which $ESPEAK_BIN` ]; then
+ echo "Error: $ESPEAK_BIN not found"
+ 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 [ ! `which $LAME_BIN` ]; then
+ echo "Error: $LAME_BIN not found"
+ exit 6
+ fi
+ ;;
+ speex)
+ # Check for speexenc binary
+ if [ ! `which $SPEEX_BIN` ]; then
+ echo "Error: $SPEEX_BIN not found"
+ exit 7
+ fi
+ ;;
+ vorbis)
+ # Check for vorbis encoder binary
+ if [ ! `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 $FESTIVAL_OPTS \
+ --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"
+ ;;
+ esac
+ fi
+ fi
+ trim "$WAV_FILE"
+}
+
+# Trim wavefile $1
+trim() {
+ WAVEFILE="$1"
+ echo "Action: Trim $WAV_FILE"
+ $WAVTRIM "$WAVEFILE" $NOISEFLOOR
+}
diff --git a/tools/voicefont.c b/tools/voicefont.c
new file mode 100644
index 0000000000..0a6d0a2121
--- /dev/null
+++ b/tools/voicefont.c
@@ -0,0 +1,218 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2004 by Jrg Hohensohn
+ *
+ * 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 tool to generate the Rockbox "voicefont", a collection of all the UI
+ * strings.
+ *
+ * Details at http://www.rockbox.org/twiki/bin/view/Main/VoiceBuilding
+ *
+ ****************************************************************************/
+
+#include <stdio.h>
+#include <string.h>
+
+/* endian conversion macros */
+#define SWAP2(x) ((((unsigned)(x)>>8) & 0x00ff) | (((unsigned)(x)<<8) & 0xff00))
+#define SWAP4(x) ((((unsigned)(x)>>24) & 0x000000ff) |\
+ (((unsigned)(x)>>8) & 0x0000ff00) |\
+ (((unsigned)(x)<<8) & 0x00ff0000) |\
+ (((unsigned)(x)<<24) & 0xff000000))
+
+
+/* bitswap audio bytes, LSB becomes MSB and vice versa */
+int BitswapAudio (unsigned char* pDest, unsigned char* pSrc, size_t len)
+{
+ static const unsigned char Lookup[256] =
+ {
+ 0x00,0x80,0x40,0xC0,0x20,0xA0,0x60,0xE0,0x10,0x90,0x50,0xD0,0x30,0xB0,0x70,0xF0,
+ 0x08,0x88,0x48,0xC8,0x28,0xA8,0x68,0xE8,0x18,0x98,0x58,0xD8,0x38,0xB8,0x78,0xF8,
+ 0x04,0x84,0x44,0xC4,0x24,0xA4,0x64,0xE4,0x14,0x94,0x54,0xD4,0x34,0xB4,0x74,0xF4,
+ 0x0C,0x8C,0x4C,0xCC,0x2C,0xAC,0x6C,0xEC,0x1C,0x9C,0x5C,0xDC,0x3C,0xBC,0x7C,0xFC,
+ 0x02,0x82,0x42,0xC2,0x22,0xA2,0x62,0xE2,0x12,0x92,0x52,0xD2,0x32,0xB2,0x72,0xF2,
+ 0x0A,0x8A,0x4A,0xCA,0x2A,0xAA,0x6A,0xEA,0x1A,0x9A,0x5A,0xDA,0x3A,0xBA,0x7A,0xFA,
+ 0x06,0x86,0x46,0xC6,0x26,0xA6,0x66,0xE6,0x16,0x96,0x56,0xD6,0x36,0xB6,0x76,0xF6,
+ 0x0E,0x8E,0x4E,0xCE,0x2E,0xAE,0x6E,0xEE,0x1E,0x9E,0x5E,0xDE,0x3E,0xBE,0x7E,0xFE,
+ 0x01,0x81,0x41,0xC1,0x21,0xA1,0x61,0xE1,0x11,0x91,0x51,0xD1,0x31,0xB1,0x71,0xF1,
+ 0x09,0x89,0x49,0xC9,0x29,0xA9,0x69,0xE9,0x19,0x99,0x59,0xD9,0x39,0xB9,0x79,0xF9,
+ 0x05,0x85,0x45,0xC5,0x25,0xA5,0x65,0xE5,0x15,0x95,0x55,0xD5,0x35,0xB5,0x75,0xF5,
+ 0x0D,0x8D,0x4D,0xCD,0x2D,0xAD,0x6D,0xED,0x1D,0x9D,0x5D,0xDD,0x3D,0xBD,0x7D,0xFD,
+ 0x03,0x83,0x43,0xC3,0x23,0xA3,0x63,0xE3,0x13,0x93,0x53,0xD3,0x33,0xB3,0x73,0xF3,
+ 0x0B,0x8B,0x4B,0xCB,0x2B,0xAB,0x6B,0xEB,0x1B,0x9B,0x5B,0xDB,0x3B,0xBB,0x7B,0xFB,
+ 0x07,0x87,0x47,0xC7,0x27,0xA7,0x67,0xE7,0x17,0x97,0x57,0xD7,0x37,0xB7,0x77,0xF7,
+ 0x0F,0x8F,0x4F,0xCF,0x2F,0xAF,0x6F,0xEF,0x1F,0x9F,0x5F,0xDF,0x3F,0xBF,0x7F,0xFF,
+ };
+
+ while (len--)
+ *pDest++ = Lookup[*pSrc++];
+
+ return 0;
+}
+
+
+int main (int argc, char** argv)
+{
+ FILE* pFile;
+
+ int i,j;
+
+ /* two tables, one for normal strings, one for voice-only (>0x8000) */
+ static char names[1000][80]; /* worst-case space */
+ char name[80]; /* one string ID */
+ static int pos[1000]; /* position of sample */
+ static int size[1000]; /* length of clip */
+ int voiceonly[1000]; /* flag if this is voice only */
+ int count = 0;
+ int count_voiceonly = 0;
+ unsigned int value; /* value to be written to file */
+ static unsigned char buffer[65535]; /* clip buffer, allow only 64K */
+ int fields;
+ char line[255]; /* one line from the .lang file */
+ char mp3filename1[1024];
+ char mp3filename2[1024];
+ char* mp3filename;
+ FILE* pMp3File;
+
+
+ if (argc < 2)
+ {
+ printf("Makes a Rockbox voicefont from a collection of mp3 clips.\n");
+ printf("Usage: voicefont <language file> <mp3 path> <output file>\n");
+ printf("\n");
+ printf("Example: \n");
+ printf("voicefont english.lang voice\\ voicefont.bin\n");
+ return -1;
+ }
+
+ pFile = fopen(argv[1], "r");
+ if (pFile == NULL)
+ {
+ printf("Error opening language file %s\n", argv[1]);
+ return -2;
+ }
+
+ memset(voiceonly, 0, sizeof(voiceonly));
+ while (!feof(pFile))
+ {
+ fgets(line, sizeof(line), pFile);
+ if (line[0] == '#') /* comment */
+ continue;
+
+ fields = sscanf(line, " id: %s", name);
+ if (fields == 1)
+ {
+ count++; /* next entry started */
+ strcpy(names[count-1], name);
+ if (strncmp("VOICE_", name, 6) == 0) /* voice-only id? */
+ voiceonly[count-1] = 1;
+ continue;
+ }
+ }
+ fclose(pFile);
+
+ pFile = fopen(argv[3], "wb");
+ if (pFile == NULL)
+ {
+ printf("Error opening output file %s\n", argv[3]);
+ return -2;
+ }
+ fseek(pFile, 16 + count*8, SEEK_SET); /* space for header */
+
+ for (i=0; i<count; i++)
+ {
+ if (voiceonly[i] == 1)
+ count_voiceonly++;
+
+ pos[i] = ftell(pFile);
+ sprintf(mp3filename1, "%s%s.mp3", argv[2], names[i]);
+ sprintf(mp3filename2, "%s%s.wav.mp3", argv[2], names[i]);
+ mp3filename = mp3filename1;
+ pMp3File = fopen(mp3filename, "rb");
+ if (pMp3File == NULL)
+ { /* alternatively, try the lame default filename */
+ mp3filename = mp3filename2;
+ pMp3File = fopen(mp3filename, "rb");
+ if (pMp3File == NULL)
+ {
+ printf("mp3 file %s not found!\n", mp3filename1);
+ size[i] = 0;
+ continue;
+ }
+ }
+ printf("processing %s\n", mp3filename);
+
+ size[i] = fread(buffer, 1, sizeof(buffer), pMp3File);
+ fclose(pMp3File);
+ BitswapAudio(buffer, buffer, size[i]);
+ fwrite(buffer, 1, size[i], pFile);
+
+ printf("%d %s %d\n", i, names[i], size[i]); /* debug */
+ } /* for i */
+
+
+ fseek(pFile, 0, SEEK_SET);
+
+ /* Create the file format: */
+
+ /* 1st 32 bit value in the file is the version number */
+ value = SWAP4(200); /* 2.00 */
+ fwrite(&value, sizeof(value), 1, pFile);
+
+ /* 2nd 32 bit value in the file is the header size (= 1st table position) */
+ value = SWAP4(16); /* 16 bytes: for version, header size, number1, number2 */
+ fwrite(&value, sizeof(value), 1, pFile);
+
+ /* 3rd 32 bit value in the file is the number of clips in 1st table */
+ value = SWAP4(count-count_voiceonly);
+ fwrite(&value, sizeof(value), 1, pFile);
+
+ /* 4th bit value in the file is the number of clips in 2nd table */
+ value = SWAP4(count_voiceonly);
+ fwrite(&value, sizeof(value), 1, pFile);
+
+ /* then followed by offset/size pairs for each clip */
+ for (j=0; j<2; j++) /* now 2 tables */
+ {
+ for (i=0; i<count; i++)
+ {
+ if (j == 0) /* first run, skip the voice only ones */
+ {
+ if (voiceonly[i] == 1)
+ continue;
+ }
+ else /* second run, skip the non voice only ones */
+ {
+ if (!voiceonly[i] == 1)
+ continue;
+ }
+
+ value = SWAP4(pos[i]); /* position */
+ fwrite(&value, sizeof(value), 1, pFile);
+ value = SWAP4(size[i]); /* size */
+ fwrite(&value, sizeof(value), 1, pFile);
+ } /* for i */
+ } /* for j */
+
+
+ /*
+ * after this the actual bitswapped mp3 data follows,
+ * which we already have written, see above.
+ */
+
+ fclose(pFile);
+
+ return 0;
+}
diff --git a/tools/wavtrim.c b/tools/wavtrim.c
new file mode 100644
index 0000000000..86434235ba
--- /dev/null
+++ b/tools/wavtrim.c
@@ -0,0 +1,235 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2004 by Jrg Hohensohn
+ *
+ * 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.
+ *
+ * Details at http://www.rockbox.org/twiki/bin/view/Main/VoiceBuilding
+ *
+ ****************************************************************************/
+
+
+#include <stdio.h> /* for file I/O */
+#include <stdlib.h> /* for malloc */
+
+/* place a 32 bit value into memory, little endian */
+void Write32(unsigned char* pByte, unsigned long value)
+{
+ pByte[0] = (unsigned char)value;
+ pByte[1] = (unsigned char)(value >> 8);
+ pByte[2] = (unsigned char)(value >> 16);
+ pByte[3] = (unsigned char)(value >> 24) ;
+}
+
+
+/* read a 32 bit value from memory, little endian */
+unsigned long Read32(unsigned char* pByte)
+{
+ unsigned long value = 0;
+
+ value |= (unsigned long)pByte[0];
+ value |= (unsigned long)pByte[1] << 8;
+ value |= (unsigned long)pByte[2] << 16;
+ value |= (unsigned long)pByte[3] << 24;
+
+ return value;
+}
+
+
+/* place a 16 bit value into memory, little endian */
+void Write16(unsigned char* pByte, unsigned short value)
+{
+ pByte[0] = (unsigned char)value;
+ pByte[1] = (unsigned char)(value >> 8);
+}
+
+
+/* read a 16 bit value from memory, little endian */
+unsigned long Read16(unsigned char* pByte)
+{
+ unsigned short value = 0;
+
+ value |= (unsigned short)pByte[0];
+ value |= (unsigned short)pByte[1] << 8;
+
+ return value;
+}
+
+
+int main (int argc, char** argv)
+{
+ FILE* pFile;
+ long lFileSize, lGot;
+ unsigned char* pBuf;
+ int bps; /* byte per sample */
+ int sps; /* samples per second */
+ int datapos; /* where the payload starts */
+ int skip_head, skip_tail, pad_head, pad_tail;
+ int i;
+ int max_silence = 0;
+ signed char sample8;
+ short sample16;
+
+ if (argc < 2)
+ {
+ printf("wavtrim removes silence at the begin and end of a WAV file.\n");
+ printf("usage: wavtrim <filename.wav> [<max_silence>]\n");
+ return 0;
+ }
+
+ if (argc == 3)
+ {
+ max_silence = atoi(argv[2]);
+ }
+
+ pFile = fopen(argv[1], "rb");
+ if (pFile == NULL)
+ {
+ printf("Error opening file %s for reading\n", argv[1]);
+ return -1;
+ }
+
+ fseek(pFile, 0, SEEK_END);
+ lFileSize = ftell(pFile);
+ fseek(pFile, 0, SEEK_SET);
+
+ pBuf = malloc(lFileSize);
+ if (pBuf == NULL)
+ {
+ printf("Out of memory to allocate %ld bytes for file.\n", lFileSize);
+ fclose(pFile);
+ return -1;
+ }
+
+ lGot = fread(pBuf, 1, lFileSize, pFile);
+ fclose(pFile);
+ if (lGot != lFileSize)
+ {
+ printf("File read error, got only %ld bytes out of %ld.\n", lGot, lFileSize);
+ free(pBuf);
+ return -1;
+ }
+
+ bps = Read16(pBuf + 32);
+ datapos = 28 + Read16(pBuf + 16);
+
+ if (Read32(pBuf) != 0x46464952 /* "RIFF" */
+ || Read32(pBuf+8) != 0x45564157 /* "WAVE" */
+ || Read32(pBuf+12) != 0x20746d66 /* "fmt " */
+ || Read32(pBuf+datapos-8) != 0x61746164) /* "data" */
+ {
+ printf("No valid input WAV file?\n", lGot, lFileSize);
+ free(pBuf);
+ return -1;
+ }
+
+ sps = Read32(pBuf + 24);
+ pad_head = sps * 10 / 1000; /* 10 ms */
+ pad_tail = sps * 10 / 1000; /* 10 ms */
+
+ if (bps == 1) /* 8 bit samples */
+ {
+
+ max_silence >>= 8;
+
+ /* clip the start */
+ for (i=datapos; i<lFileSize; i++)
+ {
+ sample8 = pBuf[i] - 0x80;
+ if (abs(sample8) > max_silence)
+ break;
+ }
+ skip_head = i - datapos;
+ skip_head = (skip_head > pad_head) ? skip_head - pad_head : 0;
+
+ /* clip the end */
+ for (i=lFileSize-1; i>datapos+skip_head; i--)
+ {
+ sample8 = pBuf[i] - 0x80;
+ if (abs(sample8) > max_silence)
+ break;
+ }
+ skip_tail = lFileSize - 1 - i;
+ skip_tail = (skip_tail > pad_tail) ? skip_tail - pad_tail : 0;
+ }
+ else if (bps == 2) /* 16 bit samples */
+ {
+
+ /* clip the start */
+ for (i=datapos; i<lFileSize; i+=2)
+ {
+ sample16 = *(short *)(pBuf + i);
+ if (abs(sample16) > max_silence)
+ break;
+ }
+ skip_head = i - datapos;
+ skip_head = (skip_head > 2 * pad_head) ?
+ skip_head - 2 * pad_head : 0;
+
+ /* clip the end */
+ for (i=lFileSize-2; i>datapos+skip_head; i-=2)
+ {
+ sample16 = *(short *)(pBuf + i);
+ if (abs(sample16) > max_silence)
+ break;
+ }
+ skip_tail = lFileSize - 2 - i;
+ skip_tail = (skip_tail > 2 * pad_tail) ?
+ skip_tail - 2 * pad_tail : 0;
+ }
+
+ /* update the size in the headers */
+ Write32(pBuf+4, Read32(pBuf+4) - skip_head - skip_tail);
+ Write32(pBuf+datapos-4, Read32(pBuf+datapos-4) - skip_head - skip_tail);
+
+ pFile = fopen(argv[1], "wb");
+ if (pFile == NULL)
+ {
+ printf("Error opening file %s for writing\n", argv[1]);
+ return -1;
+ }
+
+ /* write the new file */
+ fwrite(pBuf, 1, datapos, pFile); /* write header */
+ fwrite(pBuf + datapos + skip_head, 1, lFileSize - datapos - skip_head - skip_tail, pFile);
+ fclose(pFile);
+
+ free(pBuf);
+ return 0;
+}
+
+/*
+RIFF Chunk (12 bytes in length total)
+0 - 3 "RIFF" (ASCII Characters)
+4 - 7 Total Length Of Package To Follow (Binary, little endian)
+8 - 11 "WAVE" (ASCII Characters)
+
+
+FORMAT Chunk (24 or 26 bytes in length total) Byte Number
+12 - 15 "fmt_" (ASCII Characters)
+16 - 19 Length Of FORMAT Chunk (Binary, 0x10 or 0x12 seen)
+20 - 21 Always 0x01
+22 - 23 Channel Numbers (Always 0x01=Mono, 0x02=Stereo)
+24 - 27 Sample Rate (Binary, in Hz)
+28 - 31 Bytes Per Second
+32 - 33 Bytes Per Sample: 1=8 bit Mono, 2=8 bit Stereo or 16 bit Mono, 4=16 bit Stereo
+34 - 35 Bits Per Sample
+
+
+DATA Chunk Byte Number
+36 - 39 "data" (ASCII Characters)
+40 - 43 Length Of Data To Follow
+44 - end
+ Data (Samples)
+*/