Voice update, most useful for non-Windows systems. Voice file building using Festival, Flite or eSpeak from the configure script. Compressing with Speex and Vorbis possible, but won't create useable voicefiles. Adds voicefont.c and wavtrim.c by Jörg Hohensohn. Also includes a script to generate talk clips.

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@11426 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Jonas Häggqvist 2006-11-03 21:47:52 +00:00
parent b403c6007a
commit da071d0d7c
7 changed files with 1185 additions and 1 deletions

View file

@ -50,6 +50,12 @@ generate_rocklatin: generate_rocklatin.c ../firmware/drivers/lcd-player-charset.
uclpack: uclpack:
$(SILENT)$(MAKE) -C ucl $(SILENT)$(MAKE) -C ucl
wavtrim: wavtrim.c
$(SILENT)$(CC) -g $+ -o $@
voicefont: voicefont.c
$(SILENT)$(CC) -g $+ -o $@
clean: clean:
@echo "Cleaning tools" @echo "Cleaning tools"
$(SILENT)rm -f $(CLEANALL) $(shell for f in $(CLEANALL) ; do echo $$f.exe $$f.o $$f.obj ; done) *.ajf *~ $(SILENT)rm -f $(CLEANALL) $(shell for f in $(CLEANALL) ; do echo $$f.exe $$f.o $$f.obj ; done) *.ajf *~

172
tools/configure vendored
View file

@ -366,6 +366,154 @@ if [ -z "$simver" ]; then
fi 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 target=$1
if test "$target" = "--help"; then if test "$target" = "--help"; then
@ -1144,7 +1292,7 @@ fi
esac esac
echo "" 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`; option=`input`;
@ -1202,6 +1350,11 @@ fi
apps="manual" apps="manual"
echo "Manual build selected" echo "Manual build selected"
;; ;;
[Vv])
echo "Voice build selected"
voiceconfig
voice="yes"
;;
*) *)
debug="" debug=""
echo "Normal build selected" 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: # this was once possible to change at build-time, but no more:
language="english" 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` uname=`uname`
if [ "yes" = "$simulator" ]; then if [ "yes" = "$simulator" ]; then
@ -1516,6 +1683,9 @@ clean:
@ARCHOSROM@ @FLASHFILE@ UI256.bmp rockbox-full.zip \ @ARCHOSROM@ @FLASHFILE@ UI256.bmp rockbox-full.zip \
html txt rockbox-manual*.zip html txt rockbox-manual*.zip
voice: tools
\$(TOOLSDIR)/genvoice.sh \$(ROOTDIR) \$(LANGUAGE) \$(ARCHOS) voicesettings.sh
tools: tools:
\$(SILENT)\$(MAKE) -C \$(TOOLSDIR) CC=\$(HOSTCC) @TOOLSET@ \$(SILENT)\$(MAKE) -C \$(TOOLSDIR) CC=\$(HOSTCC) @TOOLSET@

152
tools/gentalkclips.sh Executable file
View file

@ -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

121
tools/genvoice.sh Executable file
View file

@ -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

282
tools/voicecommon.sh Normal file
View file

@ -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
}

218
tools/voicefont.c Normal file
View file

@ -0,0 +1,218 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2004 by Jörg 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;
}

235
tools/wavtrim.c Normal file
View file

@ -0,0 +1,235 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2004 by Jörg 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)
*/