summaryrefslogtreecommitdiff
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.
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*/