summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonas Häggqvist <rasher@rasher.dk>2006-11-03 21:47:52 +0000
committerJonas Häggqvist <rasher@rasher.dk>2006-11-03 21:47:52 +0000
commitda071d0d7c0067be55b2a6177ef01ee1ec5e88b8 (patch)
treeef33236850e37dd5595a53cb767f54311ccf4a9c
parentb403c6007ac1db68f3732952a12fbebe1ad7a6ea (diff)
downloadrockbox-da071d0d7c0067be55b2a6177ef01ee1ec5e88b8.tar.gz
rockbox-da071d0d7c0067be55b2a6177ef01ee1ec5e88b8.zip
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
-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.
50uclpack: 50uclpack:
51 $(SILENT)$(MAKE) -C ucl 51 $(SILENT)$(MAKE) -C ucl
52 52
53wavtrim: wavtrim.c
54 $(SILENT)$(CC) -g $+ -o $@
55
56voicefont: voicefont.c
57 $(SILENT)$(CC) -g $+ -o $@
58
53clean: 59clean:
54 @echo "Cleaning tools" 60 @echo "Cleaning tools"
55 $(SILENT)rm -f $(CLEANALL) $(shell for f in $(CLEANALL) ; do echo $$f.exe $$f.o $$f.obj ; done) *.ajf *~ 61 $(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
366fi 366fi
367} 367}
368 368
369voiceconfig () {
370 echo "Building voice for $archos"
371 echo ""
372
373 if [ `which flite` ]; then
374 FLITE="F(l)ite "
375 FLITE_OPTS="FLITE_OPTS=\"\""
376 DEFAULT_TTS="flite"
377 DEFAULT_TTS_OPTS=$FLITE_OPTS
378 DEFAULT_NOISEFLOOR="500"
379 DEFAULT_CHOICE="L"
380 fi
381 if [ `which speak` ]; then
382 ESPEAK="(e)Speak "
383 ESPEAK_OPTS="ESPEAK_OPTS=\"\""
384 DEFAULT_TTS="espeak"
385 DEFAULT_TTS_OPTS=$ESPEAK_OPTS
386 DEFAULT_NOISEFLOOR="500"
387 DEFAULT_CHOICE="e"
388 fi
389 if [ `which festival` ]; then
390 FESTIVAL="(F)estival "
391 FESTIVAL_OPTS="FLITE_OPTS=\"\""
392 DEFAULT_TTS="festival"
393 DEFAULT_TTS_OPTS=$FESTIVAL_OPTS
394 DEFAULT_NOISEFLOOR="500"
395 DEFAULT_CHOICE="F"
396 fi
397
398 if [ "$FESTIVAL" == "$FLITE" ] && [ "$FLITE" == "$ESPEAK" ]; then
399 echo "You need Festival, eSpeak or Flite in your path to build voice files"
400 exit
401 fi
402
403 echo "TTS engine to use: ${FLITE}${FESTIVAL}${ESPEAK}(${DEFAULT_CHOICE})?"
404 option=`input`
405 case "$option" in
406 [Ll])
407 TTS_ENGINE="flite"
408 NOISEFLOOR="500" # TODO: check this value
409 TTS_OPTS=$FLITE_OPTS
410 ;;
411 [Ee])
412 TTS_ENGINE="espeak"
413 NOISEFLOOR="500"
414 TTS_OPTS=$ESPEAK_OPTS
415 ;;
416 [Ff])
417 TTS_ENGINE="festival"
418 NOISEFLOOR="500"
419 TTS_OPTS=$FESTIVAL_OPTS
420 ;;
421 *)
422 TTS_ENGINE=$DEFAULT_TTS
423 TTS_OPTS=$DEFAULT_TTS_OPTS
424 NOISEFLOOR=$DEFAULT_NOISEFLOOR
425 esac
426 echo "Using $TTS_ENGINE for TTS"
427
428 echo ""
429
430 if [ `which oggenc` ]; then
431 OGGENC="(O)ggenc "
432 DEFAULT_ENC="oggenc"
433 VORBIS_OPTS="VORBIS_OPTS=\"-q0 --downmix\""
434 DEFAULT_ENC_OPTS=$VORBIS_OPTS
435 DEFAULT_CHOICE="O"
436 fi
437 if [ `which speexenc` ]; then
438 SPEEXENC="(S)peexenc "
439 DEFAULT_ENC="speexenc"
440 SPEEX_OPTS="" # TODO: find appropriate options for speex
441 DEFAULT_ENC_OPTS=$SPEEX_OPTS
442 DEFAULT_CHOICE="S"
443 fi
444 if [ `which lame` ]; then
445 LAME="(L)ame "
446 DEFAULT_ENC="lame"
447 LAME_OPTS="LAME_OPTS=\"--resample 12 -t -m m -h -V 9 -S\""
448 DEFAULT_ENC_OPTS=$LAME_OPTS
449 DEFAULT_CHOICE="L"
450 fi
451
452 if [ "$LAME" == "" ]; then
453 echo "You need to have Lame installed to build voice files"
454 fi
455
456 echo "Encoder to use: ${LAME}${OGGENC}${SPEEXENC}(${DEFAULT_CHOICE})?"
457 echo ""
458 echo "Note: Use Lame - the other options won't work"
459 option=`input`
460 case "$option" in
461 [Oo])
462 ENCODER="oggenc"
463 ENC_OPTS=$VORBIS_OPTS
464 ;;
465 [Ss])
466 ENCODER="speexenc"
467 ENC_OPTS=$SPEEX_OPTS
468 ;;
469 [Ll])
470 ENCODER="lame"
471 ENC_OPTS=$LAME_OPTS
472 ;;
473 *)
474 ENCODER=$DEFAULT_ENC
475 ENC_OPTS=$DEFAULT_ENC_OPTS
476 esac
477 echo "Using $ENCODER for encoding voice clips"
478
479 cat > voicesettings.sh <<EOF
480TTS_ENGINE="${TTS_ENGINE}"
481ENCODER="${ENCODER}"
482TEMPDIR="${pwd}"
483NOISEFLOOR="${NOISEFLOOR}"
484${TTS_OPTS}
485${ENC_OPTS}
486EOF
487}
488
489picklang() {
490 # figure out which languages that are around
491 for file in $rootdir/apps/lang/*.lang; do
492 clean=`echo $file | sed -e 's:.*/::g' | cut "-d." -f1`
493 langs="$langs $clean"
494 done
495
496 num=1
497 for one in $langs; do
498 echo "$num. $one"
499 num=`expr $num + 1`
500 done
501
502 read pick
503 return $pick;
504}
505
506whichlang() {
507 num=1
508 for one in $langs; do
509 if [ "$num" = "$pick" ]; then
510 echo $one
511 return
512 fi
513 num=`expr $num + 1`
514 done
515}
516
369target=$1 517target=$1
370 518
371if test "$target" = "--help"; then 519if test "$target" = "--help"; then
@@ -1144,7 +1292,7 @@ fi
1144 esac 1292 esac
1145 1293
1146 echo "" 1294 echo ""
1147 echo "Build (N)ormal, (D)evel, (S)imulator, (B)ootloader, $gdbstub(M)anual? (N)" 1295 echo "Build (N)ormal, (D)evel, (S)imulator, (B)ootloader, $gdbstub(M)anual, (V)oice? (N)"
1148 1296
1149 option=`input`; 1297 option=`input`;
1150 1298
@@ -1202,6 +1350,11 @@ fi
1202 apps="manual" 1350 apps="manual"
1203 echo "Manual build selected" 1351 echo "Manual build selected"
1204 ;; 1352 ;;
1353 [Vv])
1354 echo "Voice build selected"
1355 voiceconfig
1356 voice="yes"
1357 ;;
1205 *) 1358 *)
1206 debug="" 1359 debug=""
1207 echo "Normal build selected" 1360 echo "Normal build selected"
@@ -1238,6 +1391,20 @@ echo "Using source code root directory: $rootdir"
1238# this was once possible to change at build-time, but no more: 1391# this was once possible to change at build-time, but no more:
1239language="english" 1392language="english"
1240 1393
1394# Ask about language if building voice
1395if [ "yes" == "$voice" ]; then
1396 echo "Select a number for the language to use (default is english)"
1397
1398 picklang
1399 language=`whichlang`
1400
1401 if [ -z "$language" ]; then
1402 # pick a default
1403 language="english"
1404 fi
1405 echo "Language set to $language"
1406fi
1407
1241uname=`uname` 1408uname=`uname`
1242 1409
1243if [ "yes" = "$simulator" ]; then 1410if [ "yes" = "$simulator" ]; then
@@ -1516,6 +1683,9 @@ clean:
1516 @ARCHOSROM@ @FLASHFILE@ UI256.bmp rockbox-full.zip \ 1683 @ARCHOSROM@ @FLASHFILE@ UI256.bmp rockbox-full.zip \
1517 html txt rockbox-manual*.zip 1684 html txt rockbox-manual*.zip
1518 1685
1686voice: tools
1687 \$(TOOLSDIR)/genvoice.sh \$(ROOTDIR) \$(LANGUAGE) \$(ARCHOS) voicesettings.sh
1688
1519tools: 1689tools:
1520 \$(SILENT)\$(MAKE) -C \$(TOOLSDIR) CC=\$(HOSTCC) @TOOLSET@ 1690 \$(SILENT)\$(MAKE) -C \$(TOOLSDIR) CC=\$(HOSTCC) @TOOLSET@
1521 1691
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 @@
1#!/bin/sh
2# __________ __ ___.
3# Open \______ \ ____ ____ | | _\_ |__ _______ ___
4# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7# \/ \/ \/ \/ \/
8# $Id$
9#
10# Copyright (c) 2004 Daniel Gudlat
11# - http://www.rockbox.org/tracker/task/2131
12# Copyright (c) 2006 Jonas Häggqvist
13# - This version, only dirwalk and the following comments remains
14#
15# All files in this archive are subject to the GNU General Public License.
16# See the file COPYING in the source tree root for full license agreement.
17#
18# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19# KIND, either express or implied.
20#
21# Note: You may wish to change some of the settings below.
22#
23# A script to automatically generate audio clips containing the names of all
24# folders in a directory tree for use with the "Talkbox" feature available to
25# users of the Rockbox open source firmware for Archos MP3 players and
26# recorders as well as several other devices. Talkbox permits the device to
27# speak the names of the folders as one navigates the directory structure on
28# the device, thus permitting "eyes-free" use for those for whom the usual
29# visual navigation is difficult or simply inadvisable.
30#
31# Audio clips are captured and stored in wave format, then converted into MP3
32# format by a third party application (lame). If you execute the script,
33# passing it the top level of your music file hierarchy as an argument while
34# your device is connected to your PC, then the resulting audio clips will be
35# generated and stored in the correct location for use with the Talkbox
36# feature. Alternatively, if you mirror your music folder structure from your
37# PC to your Archos device, you can just run the script on the PC and then
38# update the files on your Archos with your usual synchronization routine.
39#
40# NOTE: If you don't already have them installed, you may obtain the Festival
41# text to speech system and several voices at:
42#
43# http://www.cstr.ed.ac.uk/projects/festival.html
44# http://festvox.org/festival/
45#
46# The most pleasant freely available Festival voice I know of is the slt_arctic
47# voice from HST at http://hts.ics.nitech.ac.jp/
48#
49# Known bugs
50# - This script generates talk clips for all files, Rockbox only uses talk clips
51# for music files it seems.
52
53# Include voicecommon.sh from the same dir as this script
54# Any settings from voicecommon can be overridden if added below the following
55# line.
56source `dirname $0`'/voicecommon.sh'
57
58####################
59# General settings #
60####################
61
62# which TTS engine to use. Available: festival, flite, espeak
63TTS_ENGINE=festival
64# which encoder to use, available: lame, speex, vorbis (only lame will produce
65# functional voice clips)
66ENCODER=lame
67# whether to overwrite existing mp3 files or only create missing ones (Y/N)
68OVERWRITE_TALK=N
69# whether, when overwriting mp3 files, also to regenerate all the wav files
70OVERWRITE_WAV=N
71# whether to remove the intermediary wav files after creating the mp3 files
72REMOVE_WAV=Y
73# whether to recurse into subdirectories
74RECURSIVE=Y
75# whether to strip extensions from filenames
76STRIP_EXTENSIONS=Y
77
78###################
79# End of settings #
80###################
81
82strip_extension() {
83 TO_SPEAK=$1
84 # XXX: add any that needs adding
85 for ext in mp3 ogg flac mpc sid; do
86 TO_SPEAK=`echo "$TO_SPEAK" |sed "s/\.$ext//i"`
87 done
88}
89
90# Walk directory $1, creating talk files if necessary, descend into
91# subdirecotries if specified
92dirwalk() {
93 if [ -d "$1" ]; then
94 for i in "$1"/*; do
95 # Do not generate talk clip for talk(.wav) files
96 if [ `echo "$i" | grep -c "\.talk$"` -ne 0 ] || \
97 [ `echo "$i" | grep -c "\.talk\.wav$"` -ne 0 ]; then
98 echo "Notice: Skipping file \"$i\""
99 continue
100 fi
101
102 TO_SPEAK=`basename "$i"`
103 if [ X$STRIP_EXTENSIONS = XY ]; then
104 strip_extension "$TO_SPEAK"
105 fi
106
107 if [ -d "$i" ]; then
108 # $i is a dir:
109 SAVE_AS="$i"/_dirname.talk
110 WAV_FILE="$SAVE_AS".wav
111
112 # If a talk clip already exists, only generate a new one if
113 # specified
114 if [ ! -f "$SAVE_AS" ] || [ X$OVERWRITE_TALK = XY ]; then
115 voice "$TO_SPEAK" "$WAV_FILE"
116 encode "$WAV_FILE" "$SAVE_AS"
117 fi
118
119 # Need to be done lastly, or all variables will be dirty
120 if [ X$RECURSIVE = XY ]; then
121 dirwalk "$i"
122 fi
123 else
124 # $i is a file:
125 SAVE_AS="$i".talk
126 WAV_FILE="$SAVE_AS".wav
127
128 # If a talk clip already exists, only generate a new one if
129 # specified
130 if [ ! -f "$i.talk" ] || [ X$OVERWRITE_TALK != XY ]; then
131 voice "$TO_SPEAK" "$WAV_FILE"
132 encode "$WAV_FILE" "$SAVE_AS"
133 fi
134 fi
135 # Remove wav file if specified
136 if [ X$REMOVEWAV = XY ]; then
137 rm -f "$WAV_FILE"
138 fi
139 done
140 else
141 echo "Warning: $1 is not a directory"
142 fi
143}
144
145init_tts
146init_encoder
147if [ $# -gt 0 ]; then
148 dirwalk "$*"
149else
150 dirwalk .
151fi
152stop_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 @@
1#!/bin/sh
2# __________ __ ___.
3# Open \______ \ ____ ____ | | _\_ |__ _______ ___
4# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7# \/ \/ \/ \/ \/
8# $Id$
9#
10# Copyright 2006 Jonas Häggqvist, some parts Copyright 2004 Daniel Gudlat
11#
12# All files in this archive are subject to the GNU General Public License.
13# See the file COPYING in the source tree root for full license agreement.
14#
15# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16# KIND, either express or implied.
17
18# Include voicecommon.sh from the same dir as this script
19# Any settings from voicecommon can be overridden if added below the following
20# line.
21source `dirname $0`'/voicecommon.sh'
22
23####################
24# General settings #
25####################
26
27# These settings can be overridden by passing a file with definitions as
28# the fourth parameter to this script
29
30# which TTS engine to use. Available: festival, flite, espeak
31TTS_ENGINE=festival
32# which encoder to use, available: lame, speex, vorbis (only lame will produce
33# functional voice clips at this point)
34ENCODER=lame
35# Where to save temporary files
36TEMPDIR=/tmp
37
38###################
39# End of settings #
40###################
41
42createvoicefile() {
43 $VOICEFONT "$LANG_FILE" "$TEMPDIR/" "./$RLANG.voice"
44}
45
46deletefiles() {
47 # XXX: might be unsafe depending on the value of TEMPDIR
48 rm -f "${TEMPDIR}"/LANG_*
49 rm -f "${TEMPDIR}"/VOICE_*
50}
51
52generateclips() {
53 ROCKBOX_DIR="$1"
54 RLANG="$2"
55 TARGET="$3"
56 GENLANG="$ROCKBOX_DIR"/tools/genlang
57 ENGLISH="$ROCKBOX_DIR"/apps/lang/english.lang
58 LANG_FILE="$ROCKBOX_DIR"/apps/lang/$RLANG.lang
59
60 $GENLANG -e=$ENGLISH -o -t=$TARGET $LANG_FILE |(
61 i=0
62 while read line; do
63 case `expr $i % 3` in
64 0)
65 # String ID no.
66 NUMBER=`echo $line |cut -b 2-`
67 ;;
68 1)
69 # String ID
70 ID=`echo $line |cut -b 5-`
71 ;;
72 2)
73 # String
74 STRING=`echo $line |cut -b 8-`
75
76 # Now generate the file
77 voice "$STRING" "$TEMPDIR/$ID".wav
78 encode "$TEMPDIR/$ID".wav "$TEMPDIR/$ID".mp3
79 ;;
80 esac
81 i=`expr $i + 1`
82 done
83 )
84}
85
86if [ -z "$3" ]; then
87 echo "Usage: $0 rockboxdirectory language target [settingsfile]";
88 exit 32
89else
90 if [ ! -d "$1" ] || [ ! -f "$1/tools/genlang" ]; then
91 echo "Error: $1 is not a Rockbox directory"
92 exit 33
93 fi
94 if [ ! -f "$1/apps/lang/$2.lang" ]; then
95 echo "Error: $2 is not a valid language"
96 exit 34
97 fi
98 if [ ! -z "$4" ]; then
99 if [ -f "$4" ]; then
100 # Read settings from file
101 source "$4"
102 else
103 echo "Error: $4 does not exist"
104 exit 36
105 fi
106 fi
107 # XXX: check for valid $TARGET?
108fi
109
110VOICEFONT=`dirname $0`/voicefont
111if [ ! -x $VOICEFONT ]; then
112 echo "Error: $VOICEFONT does not exist or is not executable"
113 exit 35
114fi
115
116init_tts
117init_encoder
118generateclips "$1" "$2" "$3"
119stop_tts
120createvoicefile
121#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 @@
1#!/bin/sh
2# __________ __ ___.
3# Open \______ \ ____ ____ | | _\_ |__ _______ ___
4# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7# \/ \/ \/ \/ \/
8# $Id$
9#
10# Copyright (c) 2006 Jonas Häggqvist
11#
12# All files in this archive are subject to the GNU General Public License.
13# See the file COPYING in the source tree root for full license agreement.
14#
15# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16# KIND, either express or implied.
17#
18# A selection of functions common to creating voicefiles for Rockbox.
19#
20# You may wish to change some of the settings below.
21
22#####################
23# Program locations #
24#####################
25
26# Leave any you're not using untouched, enter full path if the program is
27# not found
28
29# the festival main executable
30FESTIVAL_BIN=festival
31# the festival_client binary
32FESTIVAL_CLIENT=festival_client
33
34# The flite executable
35FLITE_BIN=flite
36
37# The eSpeak executable
38ESPEAK_BIN=speak
39
40# The lame executable
41LAME_BIN=lame
42
43# The speexenc executable
44SPEEX_BIN=speexenc
45
46# The oggenc executable
47VORBIS_BIN=oggenc
48
49# The wavtrim executable
50WAVTRIM=`dirname $0`/wavtrim
51
52#####################
53# Festival settings #
54#####################
55
56# If you're not using festival, leave untouched
57
58# whether to start the Festival server locally (Y/N)
59FESTIVAL_START=Y
60# the host of the Festival server
61# this is set to localhost automatically when FESTIVAL_START is Y
62FESTIVAL_HOST=localhost
63# the port of the Festival server
64FESTIVAL_PORT=1314
65# where to log the Festival client output
66FESTIVAL_LOG=/dev/null
67# other options to the festival client
68FESTIVAL_OPTS=""
69
70##################
71# Flite settings #
72##################
73
74# If you're not using flite, leave untouched
75FLITE_OPTS=""
76
77###################
78# eSpeak settings #
79###################
80
81# If you're not using eSpeak, leave untouched
82ESPEAK_OPTS=""
83
84####################
85# Wavtrim settings #
86####################
87
88# The maximum sample value that will be treated as silence by the wavtrim tool.
89# The value is expressed as an absolute 16 bit integer sample value (0 dB equals
90# 32767).
91#
92# 500 is a good guess - at least for Festival
93
94NOISEFLOOR='500'
95
96#####################
97# Encoding settings #
98#####################
99# where to log the encoder output
100ENC_LOG=/dev/null
101
102# Suggested: --vbr-new -t --nores -S
103# VBR, independent frames, silent mode
104LAME_OPTS="--vbr-new -t --nores -S"
105
106# Suggested:
107# XXX: suggest a default
108SPEEX_OPTS=""
109
110# Suggested: -q0 --downmix
111# Low quality, mono
112VORBIS_OPTS="-q0 --downmix"
113
114###################
115# End of settings #
116###################
117
118# Check if executables exist and perform any necessary initialisation
119init_tts() {
120 case $TTS_ENGINE in
121 festival)
122 # Check for festival_client
123 if [ ! `which $FESTIVAL_CLIENT` ]; then
124 echo "Error: $FESTIVAL_CLIENT not found"
125 exit 4
126 fi
127
128 # Check for, and start festival server if specified
129 if [ X$FESTIVAL_START = XY ]; then
130 if [ ! `which $FESTIVAL_BIN` ]; then
131 echo "Error: $FESTIVAL_BIN not found"
132 exit 3
133 fi
134 FESTIVAL_HOST='localhost'
135 $FESTIVAL_BIN --server 2>&1 > /dev/null &
136 FESTIVAL_SERVER_PID=$!
137 sleep 3
138 if [ `ps | grep -c "^\ *$FESTIVAL_SERVER_PID"` -ne 1 ]; then
139 echo "Error: Festival not started"
140 exit 9
141 fi
142 fi
143 # Test connection to festival server
144 output=`echo -E "Rockbox" | $FESTIVAL_CLIENT --server \
145 $FESTIVAL_HOST --otype riff --ttw --output \
146 /dev/null 2>&1`
147 if [ $? -ne 0 ]; then
148 echo "Error: Couldn't connect to festival server at" \
149 "$FESTIVAL_HOST ($output)"
150 exit 8
151 fi
152 ;;
153 flite)
154 # Check for flite
155 if [ ! `which $FLITE_BIN` ]; then
156 echo "Error: $FLITE_BIN not found"
157 exit 5
158 fi
159 ;;
160 espeak)
161 # Check for flite
162 if [ ! `which $ESPEAK_BIN` ]; then
163 echo "Error: $ESPEAK_BIN not found"
164 exit 5
165 fi
166 ;;
167 *)
168 echo "Error: no valid TTS engine selected: $TTS_ENGINE"
169 exit 2
170 ;;
171 esac
172 if [ ! -x $WAVTRIM ]; then
173 echo "Error: $WAVTRIM is not available"
174 exit 11
175 fi
176}
177
178# Perform any necessary shutdown for TTS engine
179stop_tts() {
180 case $TTS_ENGINE in
181 festival)
182 if [ X$FESTIVAL_START = XY ]; then
183 # XXX: This is probably possible to do using festival_client
184 kill $FESTIVAL_SERVER_PID > /dev/null 2>&1
185 fi
186 ;;
187 esac
188}
189
190# Check if executables exist and perform any necessary initialisation
191init_encoder() {
192 case $ENCODER in
193 lame)
194 # Check for lame binary
195 if [ ! `which $LAME_BIN` ]; then
196 echo "Error: $LAME_BIN not found"
197 exit 6
198 fi
199 ;;
200 speex)
201 # Check for speexenc binary
202 if [ ! `which $SPEEX_BIN` ]; then
203 echo "Error: $SPEEX_BIN not found"
204 exit 7
205 fi
206 ;;
207 vorbis)
208 # Check for vorbis encoder binary
209 if [ ! `which $VORBIS_BIN` ]; then
210 echo "Error: $VORBIS_BIN not found"
211 exit 10
212 fi
213 ;;
214 *)
215 echo "Error: no valid encoder selected: $ENCODER"
216 exit 1
217 ;;
218 esac
219
220}
221
222# Encode file $1 with ENCODER and save the result in $2, delete $1 if specified
223encode() {
224 INPUT=$1
225 OUTPUT=$2
226
227 if [ ! -f "$INPUT" ]; then
228 echo "Warning: missing input file: \"$INPUT\""
229 else
230 echo "Action: Encode $OUTPUT with $ENCODER"
231 case $ENCODER in
232 lame)
233 $LAME_BIN $LAME_OPTS "$WAV_FILE" "$OUTPUT" >>$ENC_LOG 2>&1
234 ;;
235 speex)
236 $SPEEX_BIN $SPEEX_OPTS "$WAV_FILE" "$OUTPUT" >>$ENC_LOG 2>&1
237 ;;
238 vorbis)
239 $VORBIS_BIN $VORBIS_OPTS "$WAV_FILE" -o "$OUTPUT" >>$ENC_LOG 2>&1
240 esac
241 if [ ! -f "$OUTPUT" ]; then
242 echo "Warning: missing output file \"$OUTPUT\""
243 fi
244 fi
245}
246
247# Generate file $2 containing $1 spoken by TTS_ENGINE, trim silence
248voice() {
249 TO_SPEAK=$1
250 WAV_FILE=$2
251 if [ ! -f "$WAV_FILE" ] || [ X$OVERWRITE_WAV = XY ]; then
252 if [ "${TO_SPEAK}" == "" ]; then
253 touch "$WAV_FILE"
254 else
255 case $TTS_ENGINE in
256 festival)
257 echo "Action: Generate $WAV_FILE with festival"
258 echo -E "$TO_SPEAK" | $FESTIVAL_CLIENT $FESTIVAL_OPTS \
259 --server $FESTIVAL_HOST \
260 --otype riff --ttw --output "$WAV_FILE" 2>"$WAV_FILE"
261 ;;
262 espeak)
263 echo "Action: Generate $WAV_FILE with eSpeak"
264 echo $ESPEAK_BIN $ESPEAK_OPTS -w "$WAV_FILE"
265 echo -E "$TO_SPEAK" | $ESPEAK_BIN $ESPEAK_OPTS -w "$WAV_FILE"
266 ;;
267 flite)
268 echo "Action: Generate $WAV_FILE with flite"
269 echo -E "$TO_SPEAK" | $FLITE_BIN $FLITE_OPTS -o "$WAV_FILE"
270 ;;
271 esac
272 fi
273 fi
274 trim "$WAV_FILE"
275}
276
277# Trim wavefile $1
278trim() {
279 WAVEFILE="$1"
280 echo "Action: Trim $WAV_FILE"
281 $WAVTRIM "$WAVEFILE" $NOISEFLOOR
282}
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 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2004 by Jörg Hohensohn
11 *
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 * A tool to generate the Rockbox "voicefont", a collection of all the UI
19 * strings.
20 *
21 * Details at http://www.rockbox.org/twiki/bin/view/Main/VoiceBuilding
22 *
23 ****************************************************************************/
24
25#include <stdio.h>
26#include <string.h>
27
28/* endian conversion macros */
29#define SWAP2(x) ((((unsigned)(x)>>8) & 0x00ff) | (((unsigned)(x)<<8) & 0xff00))
30#define SWAP4(x) ((((unsigned)(x)>>24) & 0x000000ff) |\
31 (((unsigned)(x)>>8) & 0x0000ff00) |\
32 (((unsigned)(x)<<8) & 0x00ff0000) |\
33 (((unsigned)(x)<<24) & 0xff000000))
34
35
36/* bitswap audio bytes, LSB becomes MSB and vice versa */
37int BitswapAudio (unsigned char* pDest, unsigned char* pSrc, size_t len)
38{
39 static const unsigned char Lookup[256] =
40 {
41 0x00,0x80,0x40,0xC0,0x20,0xA0,0x60,0xE0,0x10,0x90,0x50,0xD0,0x30,0xB0,0x70,0xF0,
42 0x08,0x88,0x48,0xC8,0x28,0xA8,0x68,0xE8,0x18,0x98,0x58,0xD8,0x38,0xB8,0x78,0xF8,
43 0x04,0x84,0x44,0xC4,0x24,0xA4,0x64,0xE4,0x14,0x94,0x54,0xD4,0x34,0xB4,0x74,0xF4,
44 0x0C,0x8C,0x4C,0xCC,0x2C,0xAC,0x6C,0xEC,0x1C,0x9C,0x5C,0xDC,0x3C,0xBC,0x7C,0xFC,
45 0x02,0x82,0x42,0xC2,0x22,0xA2,0x62,0xE2,0x12,0x92,0x52,0xD2,0x32,0xB2,0x72,0xF2,
46 0x0A,0x8A,0x4A,0xCA,0x2A,0xAA,0x6A,0xEA,0x1A,0x9A,0x5A,0xDA,0x3A,0xBA,0x7A,0xFA,
47 0x06,0x86,0x46,0xC6,0x26,0xA6,0x66,0xE6,0x16,0x96,0x56,0xD6,0x36,0xB6,0x76,0xF6,
48 0x0E,0x8E,0x4E,0xCE,0x2E,0xAE,0x6E,0xEE,0x1E,0x9E,0x5E,0xDE,0x3E,0xBE,0x7E,0xFE,
49 0x01,0x81,0x41,0xC1,0x21,0xA1,0x61,0xE1,0x11,0x91,0x51,0xD1,0x31,0xB1,0x71,0xF1,
50 0x09,0x89,0x49,0xC9,0x29,0xA9,0x69,0xE9,0x19,0x99,0x59,0xD9,0x39,0xB9,0x79,0xF9,
51 0x05,0x85,0x45,0xC5,0x25,0xA5,0x65,0xE5,0x15,0x95,0x55,0xD5,0x35,0xB5,0x75,0xF5,
52 0x0D,0x8D,0x4D,0xCD,0x2D,0xAD,0x6D,0xED,0x1D,0x9D,0x5D,0xDD,0x3D,0xBD,0x7D,0xFD,
53 0x03,0x83,0x43,0xC3,0x23,0xA3,0x63,0xE3,0x13,0x93,0x53,0xD3,0x33,0xB3,0x73,0xF3,
54 0x0B,0x8B,0x4B,0xCB,0x2B,0xAB,0x6B,0xEB,0x1B,0x9B,0x5B,0xDB,0x3B,0xBB,0x7B,0xFB,
55 0x07,0x87,0x47,0xC7,0x27,0xA7,0x67,0xE7,0x17,0x97,0x57,0xD7,0x37,0xB7,0x77,0xF7,
56 0x0F,0x8F,0x4F,0xCF,0x2F,0xAF,0x6F,0xEF,0x1F,0x9F,0x5F,0xDF,0x3F,0xBF,0x7F,0xFF,
57 };
58
59 while (len--)
60 *pDest++ = Lookup[*pSrc++];
61
62 return 0;
63}
64
65
66int main (int argc, char** argv)
67{
68 FILE* pFile;
69
70 int i,j;
71
72 /* two tables, one for normal strings, one for voice-only (>0x8000) */
73 static char names[1000][80]; /* worst-case space */
74 char name[80]; /* one string ID */
75 static int pos[1000]; /* position of sample */
76 static int size[1000]; /* length of clip */
77 int voiceonly[1000]; /* flag if this is voice only */
78 int count = 0;
79 int count_voiceonly = 0;
80 unsigned int value; /* value to be written to file */
81 static unsigned char buffer[65535]; /* clip buffer, allow only 64K */
82 int fields;
83 char line[255]; /* one line from the .lang file */
84 char mp3filename1[1024];
85 char mp3filename2[1024];
86 char* mp3filename;
87 FILE* pMp3File;
88
89
90 if (argc < 2)
91 {
92 printf("Makes a Rockbox voicefont from a collection of mp3 clips.\n");
93 printf("Usage: voicefont <language file> <mp3 path> <output file>\n");
94 printf("\n");
95 printf("Example: \n");
96 printf("voicefont english.lang voice\\ voicefont.bin\n");
97 return -1;
98 }
99
100 pFile = fopen(argv[1], "r");
101 if (pFile == NULL)
102 {
103 printf("Error opening language file %s\n", argv[1]);
104 return -2;
105 }
106
107 memset(voiceonly, 0, sizeof(voiceonly));
108 while (!feof(pFile))
109 {
110 fgets(line, sizeof(line), pFile);
111 if (line[0] == '#') /* comment */
112 continue;
113
114 fields = sscanf(line, " id: %s", name);
115 if (fields == 1)
116 {
117 count++; /* next entry started */
118 strcpy(names[count-1], name);
119 if (strncmp("VOICE_", name, 6) == 0) /* voice-only id? */
120 voiceonly[count-1] = 1;
121 continue;
122 }
123 }
124 fclose(pFile);
125
126 pFile = fopen(argv[3], "wb");
127 if (pFile == NULL)
128 {
129 printf("Error opening output file %s\n", argv[3]);
130 return -2;
131 }
132 fseek(pFile, 16 + count*8, SEEK_SET); /* space for header */
133
134 for (i=0; i<count; i++)
135 {
136 if (voiceonly[i] == 1)
137 count_voiceonly++;
138
139 pos[i] = ftell(pFile);
140 sprintf(mp3filename1, "%s%s.mp3", argv[2], names[i]);
141 sprintf(mp3filename2, "%s%s.wav.mp3", argv[2], names[i]);
142 mp3filename = mp3filename1;
143 pMp3File = fopen(mp3filename, "rb");
144 if (pMp3File == NULL)
145 { /* alternatively, try the lame default filename */
146 mp3filename = mp3filename2;
147 pMp3File = fopen(mp3filename, "rb");
148 if (pMp3File == NULL)
149 {
150 printf("mp3 file %s not found!\n", mp3filename1);
151 size[i] = 0;
152 continue;
153 }
154 }
155 printf("processing %s\n", mp3filename);
156
157 size[i] = fread(buffer, 1, sizeof(buffer), pMp3File);
158 fclose(pMp3File);
159 BitswapAudio(buffer, buffer, size[i]);
160 fwrite(buffer, 1, size[i], pFile);
161
162 printf("%d %s %d\n", i, names[i], size[i]); /* debug */
163 } /* for i */
164
165
166 fseek(pFile, 0, SEEK_SET);
167
168 /* Create the file format: */
169
170 /* 1st 32 bit value in the file is the version number */
171 value = SWAP4(200); /* 2.00 */
172 fwrite(&value, sizeof(value), 1, pFile);
173
174 /* 2nd 32 bit value in the file is the header size (= 1st table position) */
175 value = SWAP4(16); /* 16 bytes: for version, header size, number1, number2 */
176 fwrite(&value, sizeof(value), 1, pFile);
177
178 /* 3rd 32 bit value in the file is the number of clips in 1st table */
179 value = SWAP4(count-count_voiceonly);
180 fwrite(&value, sizeof(value), 1, pFile);
181
182 /* 4th bit value in the file is the number of clips in 2nd table */
183 value = SWAP4(count_voiceonly);
184 fwrite(&value, sizeof(value), 1, pFile);
185
186 /* then followed by offset/size pairs for each clip */
187 for (j=0; j<2; j++) /* now 2 tables */
188 {
189 for (i=0; i<count; i++)
190 {
191 if (j == 0) /* first run, skip the voice only ones */
192 {
193 if (voiceonly[i] == 1)
194 continue;
195 }
196 else /* second run, skip the non voice only ones */
197 {
198 if (!voiceonly[i] == 1)
199 continue;
200 }
201
202 value = SWAP4(pos[i]); /* position */
203 fwrite(&value, sizeof(value), 1, pFile);
204 value = SWAP4(size[i]); /* size */
205 fwrite(&value, sizeof(value), 1, pFile);
206 } /* for i */
207 } /* for j */
208
209
210 /*
211 * after this the actual bitswapped mp3 data follows,
212 * which we already have written, see above.
213 */
214
215 fclose(pFile);
216
217 return 0;
218}
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 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2004 by Jörg Hohensohn
11 *
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 * Details at http://www.rockbox.org/twiki/bin/view/Main/VoiceBuilding
19 *
20 ****************************************************************************/
21
22
23#include <stdio.h> /* for file I/O */
24#include <stdlib.h> /* for malloc */
25
26/* place a 32 bit value into memory, little endian */
27void Write32(unsigned char* pByte, unsigned long value)
28{
29 pByte[0] = (unsigned char)value;
30 pByte[1] = (unsigned char)(value >> 8);
31 pByte[2] = (unsigned char)(value >> 16);
32 pByte[3] = (unsigned char)(value >> 24) ;
33}
34
35
36/* read a 32 bit value from memory, little endian */
37unsigned long Read32(unsigned char* pByte)
38{
39 unsigned long value = 0;
40
41 value |= (unsigned long)pByte[0];
42 value |= (unsigned long)pByte[1] << 8;
43 value |= (unsigned long)pByte[2] << 16;
44 value |= (unsigned long)pByte[3] << 24;
45
46 return value;
47}
48
49
50/* place a 16 bit value into memory, little endian */
51void Write16(unsigned char* pByte, unsigned short value)
52{
53 pByte[0] = (unsigned char)value;
54 pByte[1] = (unsigned char)(value >> 8);
55}
56
57
58/* read a 16 bit value from memory, little endian */
59unsigned long Read16(unsigned char* pByte)
60{
61 unsigned short value = 0;
62
63 value |= (unsigned short)pByte[0];
64 value |= (unsigned short)pByte[1] << 8;
65
66 return value;
67}
68
69
70int main (int argc, char** argv)
71{
72 FILE* pFile;
73 long lFileSize, lGot;
74 unsigned char* pBuf;
75 int bps; /* byte per sample */
76 int sps; /* samples per second */
77 int datapos; /* where the payload starts */
78 int skip_head, skip_tail, pad_head, pad_tail;
79 int i;
80 int max_silence = 0;
81 signed char sample8;
82 short sample16;
83
84 if (argc < 2)
85 {
86 printf("wavtrim removes silence at the begin and end of a WAV file.\n");
87 printf("usage: wavtrim <filename.wav> [<max_silence>]\n");
88 return 0;
89 }
90
91 if (argc == 3)
92 {
93 max_silence = atoi(argv[2]);
94 }
95
96 pFile = fopen(argv[1], "rb");
97 if (pFile == NULL)
98 {
99 printf("Error opening file %s for reading\n", argv[1]);
100 return -1;
101 }
102
103 fseek(pFile, 0, SEEK_END);
104 lFileSize = ftell(pFile);
105 fseek(pFile, 0, SEEK_SET);
106
107 pBuf = malloc(lFileSize);
108 if (pBuf == NULL)
109 {
110 printf("Out of memory to allocate %ld bytes for file.\n", lFileSize);
111 fclose(pFile);
112 return -1;
113 }
114
115 lGot = fread(pBuf, 1, lFileSize, pFile);
116 fclose(pFile);
117 if (lGot != lFileSize)
118 {
119 printf("File read error, got only %ld bytes out of %ld.\n", lGot, lFileSize);
120 free(pBuf);
121 return -1;
122 }
123
124 bps = Read16(pBuf + 32);
125 datapos = 28 + Read16(pBuf + 16);
126
127 if (Read32(pBuf) != 0x46464952 /* "RIFF" */
128 || Read32(pBuf+8) != 0x45564157 /* "WAVE" */
129 || Read32(pBuf+12) != 0x20746d66 /* "fmt " */
130 || Read32(pBuf+datapos-8) != 0x61746164) /* "data" */
131 {
132 printf("No valid input WAV file?\n", lGot, lFileSize);
133 free(pBuf);
134 return -1;
135 }
136
137 sps = Read32(pBuf + 24);
138 pad_head = sps * 10 / 1000; /* 10 ms */
139 pad_tail = sps * 10 / 1000; /* 10 ms */
140
141 if (bps == 1) /* 8 bit samples */
142 {
143
144 max_silence >>= 8;
145
146 /* clip the start */
147 for (i=datapos; i<lFileSize; i++)
148 {
149 sample8 = pBuf[i] - 0x80;
150 if (abs(sample8) > max_silence)
151 break;
152 }
153 skip_head = i - datapos;
154 skip_head = (skip_head > pad_head) ? skip_head - pad_head : 0;
155
156 /* clip the end */
157 for (i=lFileSize-1; i>datapos+skip_head; i--)
158 {
159 sample8 = pBuf[i] - 0x80;
160 if (abs(sample8) > max_silence)
161 break;
162 }
163 skip_tail = lFileSize - 1 - i;
164 skip_tail = (skip_tail > pad_tail) ? skip_tail - pad_tail : 0;
165 }
166 else if (bps == 2) /* 16 bit samples */
167 {
168
169 /* clip the start */
170 for (i=datapos; i<lFileSize; i+=2)
171 {
172 sample16 = *(short *)(pBuf + i);
173 if (abs(sample16) > max_silence)
174 break;
175 }
176 skip_head = i - datapos;
177 skip_head = (skip_head > 2 * pad_head) ?
178 skip_head - 2 * pad_head : 0;
179
180 /* clip the end */
181 for (i=lFileSize-2; i>datapos+skip_head; i-=2)
182 {
183 sample16 = *(short *)(pBuf + i);
184 if (abs(sample16) > max_silence)
185 break;
186 }
187 skip_tail = lFileSize - 2 - i;
188 skip_tail = (skip_tail > 2 * pad_tail) ?
189 skip_tail - 2 * pad_tail : 0;
190 }
191
192 /* update the size in the headers */
193 Write32(pBuf+4, Read32(pBuf+4) - skip_head - skip_tail);
194 Write32(pBuf+datapos-4, Read32(pBuf+datapos-4) - skip_head - skip_tail);
195
196 pFile = fopen(argv[1], "wb");
197 if (pFile == NULL)
198 {
199 printf("Error opening file %s for writing\n", argv[1]);
200 return -1;
201 }
202
203 /* write the new file */
204 fwrite(pBuf, 1, datapos, pFile); /* write header */
205 fwrite(pBuf + datapos + skip_head, 1, lFileSize - datapos - skip_head - skip_tail, pFile);
206 fclose(pFile);
207
208 free(pBuf);
209 return 0;
210}
211
212/*
213RIFF Chunk (12 bytes in length total)
2140 - 3 "RIFF" (ASCII Characters)
2154 - 7 Total Length Of Package To Follow (Binary, little endian)
2168 - 11 "WAVE" (ASCII Characters)
217
218
219FORMAT Chunk (24 or 26 bytes in length total) Byte Number
22012 - 15 "fmt_" (ASCII Characters)
22116 - 19 Length Of FORMAT Chunk (Binary, 0x10 or 0x12 seen)
22220 - 21 Always 0x01
22322 - 23 Channel Numbers (Always 0x01=Mono, 0x02=Stereo)
22424 - 27 Sample Rate (Binary, in Hz)
22528 - 31 Bytes Per Second
22632 - 33 Bytes Per Sample: 1=8 bit Mono, 2=8 bit Stereo or 16 bit Mono, 4=16 bit Stereo
22734 - 35 Bits Per Sample
228
229
230DATA Chunk Byte Number
23136 - 39 "data" (ASCII Characters)
23240 - 43 Length Of Data To Follow
23344 - end
234 Data (Samples)
235*/