diff options
Diffstat (limited to 'tools')
-rw-r--r-- | tools/VOICE_PAUSE.wav | bin | 0 -> 26612 bytes | |||
-rwxr-xr-x | tools/configure | 58 | ||||
-rwxr-xr-x | tools/sapi5_voice_new.vbs | 67 | ||||
-rwxr-xr-x | tools/voice.pl | 360 |
4 files changed, 464 insertions, 21 deletions
diff --git a/tools/VOICE_PAUSE.wav b/tools/VOICE_PAUSE.wav new file mode 100644 index 0000000000..90d07f0ff6 --- /dev/null +++ b/tools/VOICE_PAUSE.wav | |||
Binary files differ | |||
diff --git a/tools/configure b/tools/configure index 03182cfde4..6be536e109 100755 --- a/tools/configure +++ b/tools/configure | |||
@@ -288,7 +288,8 @@ whichadvanced () { | |||
288 | 288 | ||
289 | # Ask about languages to build | 289 | # Ask about languages to build |
290 | echo "Select a number for the language to use (default is english)" | 290 | echo "Select a number for the language to use (default is english)" |
291 | echo "You may enter a comma-separated list of languages to build" | 291 | # The multiple-language feature is currently broken |
292 | # echo "You may enter a comma-separated list of languages to build" | ||
292 | 293 | ||
293 | picklang | 294 | picklang |
294 | voicelanguage=`whichlang` | 295 | voicelanguage=`whichlang` |
@@ -329,7 +330,7 @@ voiceconfig () { | |||
329 | 330 | ||
330 | if [ -f "`which flite`" ]; then | 331 | if [ -f "`which flite`" ]; then |
331 | FLITE="F(l)ite " | 332 | FLITE="F(l)ite " |
332 | FLITE_OPTS="FLITE_OPTS=\"\"" | 333 | FLITE_OPTS="" |
333 | DEFAULT_TTS="flite" | 334 | DEFAULT_TTS="flite" |
334 | DEFAULT_TTS_OPTS=$FLITE_OPTS | 335 | DEFAULT_TTS_OPTS=$FLITE_OPTS |
335 | DEFAULT_NOISEFLOOR="500" | 336 | DEFAULT_NOISEFLOOR="500" |
@@ -337,7 +338,7 @@ voiceconfig () { | |||
337 | fi | 338 | fi |
338 | if [ -f "`which espeak`" ]; then | 339 | if [ -f "`which espeak`" ]; then |
339 | ESPEAK="(e)Speak " | 340 | ESPEAK="(e)Speak " |
340 | ESPEAK_OPTS="ESPEAK_OPTS=\"\"" | 341 | ESPEAK_OPTS="" |
341 | DEFAULT_TTS="espeak" | 342 | DEFAULT_TTS="espeak" |
342 | DEFAULT_TTS_OPTS=$ESPEAK_OPTS | 343 | DEFAULT_TTS_OPTS=$ESPEAK_OPTS |
343 | DEFAULT_NOISEFLOOR="500" | 344 | DEFAULT_NOISEFLOOR="500" |
@@ -345,7 +346,23 @@ voiceconfig () { | |||
345 | fi | 346 | fi |
346 | if [ -f "`which festival`" ]; then | 347 | if [ -f "`which festival`" ]; then |
347 | FESTIVAL="(F)estival " | 348 | FESTIVAL="(F)estival " |
348 | FESTIVAL_OPTS="FESTIVAL_OPTS=\"\"" | 349 | case "$thislang" in |
350 | "italiano") | ||
351 | FESTIVAL_OPTS="--language italian" | ||
352 | ;; | ||
353 | "espanol") | ||
354 | FESTIVAL_OPTS="--language spanish" | ||
355 | ;; | ||
356 | "finnish") | ||
357 | FESTIVAL_OPTS="--language finnish" | ||
358 | ;; | ||
359 | "czech") | ||
360 | FESTIVAL_OPTS="--language czech" | ||
361 | ;; | ||
362 | *) | ||
363 | FESTIVAL_OPTS="" | ||
364 | ;; | ||
365 | esac | ||
349 | DEFAULT_TTS="festival" | 366 | DEFAULT_TTS="festival" |
350 | DEFAULT_TTS_OPTS=$FESTIVAL_OPTS | 367 | DEFAULT_TTS_OPTS=$FESTIVAL_OPTS |
351 | DEFAULT_NOISEFLOOR="500" | 368 | DEFAULT_NOISEFLOOR="500" |
@@ -354,7 +371,7 @@ voiceconfig () { | |||
354 | # Allow SAPI if Windows is in use | 371 | # Allow SAPI if Windows is in use |
355 | if [ -f "`which winver`" ]; then | 372 | if [ -f "`which winver`" ]; then |
356 | SAPI5="(S)API5 " | 373 | SAPI5="(S)API5 " |
357 | SAPI5_OPTS="SAPI5_OPTS=\"\"" | 374 | SAPI5_OPTS="" |
358 | DEFAULT_TTS="sapi5" | 375 | DEFAULT_TTS="sapi5" |
359 | DEFAULT_TTS_OPTS=$SAPI5_OPTS | 376 | DEFAULT_TTS_OPTS=$SAPI5_OPTS |
360 | DEFAULT_NOISEFLOOR="500" | 377 | DEFAULT_NOISEFLOOR="500" |
@@ -397,10 +414,10 @@ voiceconfig () { | |||
397 | echo "Using $TTS_ENGINE for TTS" | 414 | echo "Using $TTS_ENGINE for TTS" |
398 | 415 | ||
399 | # Allow the user to input manual commandline options | 416 | # Allow the user to input manual commandline options |
400 | printf "Enter $TTS_ENGINE options (enter for defaults `echo $TTS_OPTS |sed 's/.*=//'`): " | 417 | printf "Enter $TTS_ENGINE options (enter for defaults \"$TTS_OPTS\"): " |
401 | USER_TTS_OPTS=`input` | 418 | USER_TTS_OPTS=`input` |
402 | if [ -n "$USER_TTS_OPTS" ]; then | 419 | if [ -n "$USER_TTS_OPTS" ]; then |
403 | TTS_OPTS="`echo $TTS_OPTS | sed 's/=.*//'`=\"$USER_TTS_OPTS\"" | 420 | TTS_OPTS="$USER_TTS_OPTS" |
404 | fi | 421 | fi |
405 | 422 | ||
406 | echo "" | 423 | echo "" |
@@ -408,7 +425,7 @@ voiceconfig () { | |||
408 | if [ -f "`which oggenc`" ]; then | 425 | if [ -f "`which oggenc`" ]; then |
409 | OGGENC="(O)ggenc " | 426 | OGGENC="(O)ggenc " |
410 | DEFAULT_ENC="oggenc" | 427 | DEFAULT_ENC="oggenc" |
411 | VORBIS_OPTS="VORBIS_OPTS=\"-q0 --downmix\"" | 428 | VORBIS_OPTS="-q0 --downmix" |
412 | DEFAULT_ENC_OPTS=$VORBIS_OPTS | 429 | DEFAULT_ENC_OPTS=$VORBIS_OPTS |
413 | DEFAULT_CHOICE="O" | 430 | DEFAULT_CHOICE="O" |
414 | fi | 431 | fi |
@@ -422,7 +439,7 @@ voiceconfig () { | |||
422 | if [ -f "`which lame`" ]; then | 439 | if [ -f "`which lame`" ]; then |
423 | LAME="(L)ame " | 440 | LAME="(L)ame " |
424 | DEFAULT_ENC="lame" | 441 | DEFAULT_ENC="lame" |
425 | LAME_OPTS="LAME_OPTS=\"--resample 12 -t -m m -h -V 9 -S\"" | 442 | LAME_OPTS="--resample 12 -t -m m -h -V 9 -S -B 64 --vbr-new" |
426 | DEFAULT_ENC_OPTS=$LAME_OPTS | 443 | DEFAULT_ENC_OPTS=$LAME_OPTS |
427 | DEFAULT_CHOICE="L" | 444 | DEFAULT_CHOICE="L" |
428 | fi | 445 | fi |
@@ -456,25 +473,16 @@ voiceconfig () { | |||
456 | echo "Using $ENCODER for encoding voice clips" | 473 | echo "Using $ENCODER for encoding voice clips" |
457 | 474 | ||
458 | # Allow the user to input manual commandline options | 475 | # Allow the user to input manual commandline options |
459 | printf "Enter $ENCODER options (enter for defaults `echo $ENC_OPTS |sed 's/.*=//'`): " | 476 | printf "Enter $ENCODER options (enter for defaults \"$ENC_OPTS\"): " |
460 | USER_ENC_OPTS=`input` | 477 | USER_ENC_OPTS=`input` |
461 | if [ -n "$USER_ENC_OPTS" ]; then | 478 | if [ -n "$USER_ENC_OPTS" ]; then |
462 | ENC_OPTS="`echo $ENC_OPTS | sed 's/=.*//'`=\"$USER_ENC_OPTS\"" | 479 | ENC_OPTS=$USER_ENC_OPTS |
463 | fi | 480 | fi |
464 | 481 | ||
465 | TEMPDIR="${pwd}" | 482 | TEMPDIR="${pwd}" |
466 | if [ -f "`which cygpath`" ]; then | 483 | if [ -f "`which cygpath`" ]; then |
467 | TEMPDIR=`cygpath . -a -w` | 484 | TEMPDIR=`cygpath . -a -w` |
468 | fi | 485 | fi |
469 | |||
470 | cat > voicesettings-$thislang.sh <<EOF | ||
471 | TTS_ENGINE="${TTS_ENGINE}" | ||
472 | ENCODER="${ENCODER}" | ||
473 | TEMPDIR="$TEMPDIR" | ||
474 | NOISEFLOOR="${NOISEFLOOR}" | ||
475 | ${TTS_OPTS} | ||
476 | ${ENC_OPTS} | ||
477 | EOF | ||
478 | } | 486 | } |
479 | 487 | ||
480 | picklang() { | 488 | picklang() { |
@@ -1664,6 +1672,10 @@ sed > Makefile \ | |||
1664 | -e "${simmagic1}" \ | 1672 | -e "${simmagic1}" \ |
1665 | -e "${simmagic2}" \ | 1673 | -e "${simmagic2}" \ |
1666 | -e "s,@MANUALDEV@,${manualdev},g" \ | 1674 | -e "s,@MANUALDEV@,${manualdev},g" \ |
1675 | -e "s,@ENCODER@,${ENCODER},g" \ | ||
1676 | -e "s,@ENC_OPTS@,${ENC_OPTS},g" \ | ||
1677 | -e "s,@TTS_ENGINE@,${TTS_ENGINE},g" \ | ||
1678 | -e "s,@TTS_OPTS@,${TTS_OPTS},g" \ | ||
1667 | <<EOF | 1679 | <<EOF |
1668 | ## Automaticly generated. http://www.rockbox.org/ | 1680 | ## Automaticly generated. http://www.rockbox.org/ |
1669 | 1681 | ||
@@ -1732,6 +1744,10 @@ export GCCVER=@GCCVER@ | |||
1732 | export GCCNUM=@GCCNUM@ | 1744 | export GCCNUM=@GCCNUM@ |
1733 | export UNAME=@UNAME@ | 1745 | export UNAME=@UNAME@ |
1734 | export MANUALDEV=@MANUALDEV@ | 1746 | export MANUALDEV=@MANUALDEV@ |
1747 | export TTS_OPTS=@TTS_OPTS@ | ||
1748 | export TTS_ENGINE=@TTS_ENGINE@ | ||
1749 | export ENC_OPTS=@ENC_OPTS@ | ||
1750 | export ENCODER=@ENCODER@ | ||
1735 | 1751 | ||
1736 | # Do not print "Entering directory ..." | 1752 | # Do not print "Entering directory ..." |
1737 | MAKEFLAGS += --no-print-directory | 1753 | MAKEFLAGS += --no-print-directory |
@@ -1866,7 +1882,7 @@ if [ "yes" = "$voice" ]; then | |||
1866 | 1882 | ||
1867 | voice: tools features | 1883 | voice: tools features |
1868 | \$(SILENT)for f in \`cat \$(BUILDDIR)/${apps}/features\`; do feat="\$\$feat:\$\$f" ; done ; \\ | 1884 | \$(SILENT)for f in \`cat \$(BUILDDIR)/${apps}/features\`; do feat="\$\$feat:\$\$f" ; done ; \\ |
1869 | for lang in \`echo \$(VOICELANGUAGE) |sed "s/,/ /g"\`; do \$(TOOLSDIR)/genvoice.sh \$(ROOTDIR) \$\$lang \$(ARCHOS)\$\$feat \$(TARGET_ID) voicesettings-\$\$lang.sh ; done \\ | 1885 | for lang in \`echo \$(VOICELANGUAGE) |sed "s/,/ /g"\`; do \$(TOOLSDIR)/voice.pl -V -l=\$\$lang -t=\$(ARCHOS)\$\$feat -i=\$(TARGET_ID) -e=\$(ENCODER) -E="\$(ENC_OPTS)" -s=\$(TTS_ENGINE) -S="\$(TTS_OPTS)"; done \\ |
1870 | EOF | 1886 | EOF |
1871 | 1887 | ||
1872 | fi | 1888 | fi |
diff --git a/tools/sapi5_voice_new.vbs b/tools/sapi5_voice_new.vbs new file mode 100755 index 0000000000..96c6e2a720 --- /dev/null +++ b/tools/sapi5_voice_new.vbs | |||
@@ -0,0 +1,67 @@ | |||
1 | '*************************************************************************** | ||
2 | ' __________ __ ___. | ||
3 | ' Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | ' Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | ' Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | ' Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | ' \/ \/ \/ \/ \/ | ||
8 | ' $Id: sapi5_voice.vbs$ | ||
9 | ' | ||
10 | ' Copyright (C) 2007 Steve Bavin, Jens Arnold, Mesar Hameed | ||
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 | '*************************************************************************** | ||
19 | ' Purpose: Make a voice clip file for the given text on stdin | ||
20 | |||
21 | 'To be done: | ||
22 | ' - Allow user to override voice, speed and/or format (currently uses Control Panel defaults for voice/speed) | ||
23 | ' - Voice specific replacements/corrections for pronounciation (this should be at a higher level really) | ||
24 | |||
25 | Const SSFMCreateForWrite = 3 | ||
26 | |||
27 | Const SPSF_8kHz16BitMono = 6 | ||
28 | Const SPSF_11kHz16BitMono = 10 | ||
29 | Const SPSF_12kHz16BitMono = 14 | ||
30 | Const SPSF_16kHz16BitMono = 18 | ||
31 | Const SPSF_22kHz16BitMono = 22 | ||
32 | Const SPSF_24kHz16BitMono = 26 | ||
33 | Const SPSF_32kHz16BitMono = 30 | ||
34 | Const SPSF_44kHz16BitMono = 34 | ||
35 | Const SPSF_48kHz16BitMono = 38 | ||
36 | |||
37 | Dim oSpVoice, oSpFS, nAudioFormat, sText, sOutputFile | ||
38 | |||
39 | nAudioFormat = SPSF_22kHz16BitMono 'Audio format to use, recommended settings: | ||
40 | '- for AT&T natural voices, use SPSF_32kHz16BitMono | ||
41 | '- for MS voices, use SPSF_22kHz16BitMono | ||
42 | |||
43 | Set oSpVoice = CreateObject("SAPI.SpVoice") | ||
44 | If Err.Number <> 0 Then | ||
45 | WScript.Echo "Error - could not get SpVoice object. " & _ | ||
46 | "SAPI 5 not installed?" | ||
47 | Err.Clear | ||
48 | WScript.Quit 1 | ||
49 | End If | ||
50 | |||
51 | While 1 > 0 | ||
52 | sText = WScript.StdIn.ReadLine | ||
53 | sOutputFile = WScript.StdIn.ReadLine | ||
54 | If sOutputFile = "" Then | ||
55 | Set oSpFS = Nothing | ||
56 | Set oSpVoice = Nothing | ||
57 | Set oArgs = Nothing | ||
58 | WScript.Quit 0 | ||
59 | End If | ||
60 | ' WScript.Echo "Saying " + sText + " in " + sOutputFile | ||
61 | Set oSpFS = CreateObject("SAPI.SpFileStream") | ||
62 | oSpFS.Format.Type = nAudioFormat | ||
63 | oSpFS.Open sOutputFile, SSFMCreateForWrite, False | ||
64 | Set oSpVoice.AudioOutputStream = oSpFS | ||
65 | oSpVoice.Speak sText | ||
66 | oSpFS.Close | ||
67 | Wend | ||
diff --git a/tools/voice.pl b/tools/voice.pl new file mode 100755 index 0000000000..1635b701f1 --- /dev/null +++ b/tools/voice.pl | |||
@@ -0,0 +1,360 @@ | |||
1 | #!/usr/bin/perl -s | ||
2 | # __________ __ ___. | ||
3 | # Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | # Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | # Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | # Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | # \/ \/ \/ \/ \/ | ||
8 | # $Id: | ||
9 | # | ||
10 | # Copyright (C) 2007 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 | use strict; | ||
19 | use warnings; | ||
20 | use File::Basename; | ||
21 | use File::Copy; | ||
22 | use Switch; | ||
23 | use vars qw($V $C $t $l $e $E $s $S $i $v); | ||
24 | use IPC::Open3; | ||
25 | use Digest::MD5 qw(md5_hex); | ||
26 | |||
27 | sub printusage { | ||
28 | print <<USAGE | ||
29 | |||
30 | Usage: voice.pl [options] [path to dir] | ||
31 | -V | ||
32 | Create voice file. You must also specify -t and -l. | ||
33 | |||
34 | -C | ||
35 | Create .talk clips. | ||
36 | |||
37 | -t=<target> | ||
38 | Specify which target you want to build voicefile for. Must include | ||
39 | any features that target supports. | ||
40 | |||
41 | -i=<target_id> | ||
42 | Numeric target id. Needed for voice building. | ||
43 | |||
44 | -l=<language> | ||
45 | Specify which language you want to build. Without .lang extension. | ||
46 | |||
47 | -e=<encoder> | ||
48 | Which encoder to use for voice strings | ||
49 | |||
50 | -E=<encoder options> | ||
51 | Which encoder options to use when compressing voice strings. Enclose | ||
52 | in double quotes if the options include spaces. | ||
53 | |||
54 | -s=<TTS engine> | ||
55 | Which TTS engine to use. | ||
56 | |||
57 | -S=<TTS engine options> | ||
58 | Options to pass to the TTS engine. Enclose in double quotes if the | ||
59 | options include spaces. | ||
60 | |||
61 | -v | ||
62 | Be verbose | ||
63 | USAGE | ||
64 | ; | ||
65 | } | ||
66 | |||
67 | # Initialize TTS engine. May return an object or value which will be passed | ||
68 | # to voicestring and shutdown_tts | ||
69 | sub init_tts { | ||
70 | our $verbose; | ||
71 | my ($tts_engine, $tts_engine_opts, $language) = @_; | ||
72 | my $ret = undef; | ||
73 | switch($tts_engine) { | ||
74 | case "festival" { | ||
75 | print("> festival $tts_engine_opts --server\n") if $verbose; | ||
76 | my $pid = open(FESTIVAL_SERVER, "| festival $tts_engine_opts --server > /dev/null 2>&1"); | ||
77 | $ret = *FESTIVAL_SERVER; | ||
78 | $ret = $pid; | ||
79 | $SIG{INT} = sub { kill TERM => $pid; print("foo"); panic_cleanup(); }; | ||
80 | $SIG{KILL} = sub { kill TERM => $pid; print("boo"); panic_cleanup(); }; | ||
81 | } | ||
82 | case "sapi5" { | ||
83 | my $toolsdir = dirname($0); | ||
84 | my $path = `cygpath $toolsdir -a -w`; | ||
85 | chomp($path); | ||
86 | $path = $path . "\\sapi5_voice_new.vbs $language $tts_engine_opts"; | ||
87 | $path =~ s/\\/\\\\/g; | ||
88 | print("> cscript /B $path\n") if $verbose; | ||
89 | my $pid = open(F, "| cscript /B $path"); | ||
90 | $ret = *F; | ||
91 | $SIG{INT} = sub { print($ret "\r\n\r\n"); panic_cleanup(); }; | ||
92 | $SIG{KILL} = sub { print($ret "\r\n\r\n"); panic_cleanup(); }; | ||
93 | } | ||
94 | } | ||
95 | return $ret; | ||
96 | } | ||
97 | |||
98 | # Shutdown TTS engine if necessary. | ||
99 | sub shutdown_tts { | ||
100 | my ($tts_engine, $tts_object) = @_; | ||
101 | switch($tts_engine) { | ||
102 | case "festival" { | ||
103 | # Send SIGTERM to festival server | ||
104 | kill TERM => $tts_object; | ||
105 | } | ||
106 | case "sapi5" { | ||
107 | print($tts_object "\r\n\r\n"); | ||
108 | close($tts_object); | ||
109 | } | ||
110 | } | ||
111 | } | ||
112 | |||
113 | # Apply corrections to a voice-string to make it sound better | ||
114 | sub correct_string { | ||
115 | our $verbose; | ||
116 | my ($string, $language, $tts_engine) = @_; | ||
117 | my $orig = $string; | ||
118 | switch($language) { | ||
119 | # General for all engines and languages (perhaps - just an example) | ||
120 | $string =~ s/USB/U S B/; | ||
121 | |||
122 | case ("deutsch") { | ||
123 | switch($tts_engine) { | ||
124 | $string =~ s/alphabet/alfabet/; | ||
125 | $string =~ s/alkaline/alkalein/; | ||
126 | $string =~ s/ampere/amper/; | ||
127 | $string =~ s/byte(s?)\b/beit$1/; | ||
128 | $string =~ s/\bdezibel\b/de-zibell/; | ||
129 | $string =~ s/energie\b/ener-gie/; | ||
130 | $string =~ s/\bflash\b/fläsh/g; | ||
131 | $string =~ s/\bfirmware(s?)\b/firmwer$1/; | ||
132 | $string =~ s/\bid3 tag\b/id3 täg/g; # can't just use "tag" here | ||
133 | $string =~ s/\bloudness\b/laudness/; | ||
134 | $string =~ s/\bnumerisch\b/numehrisch/; | ||
135 | $string =~ s/\brücklauf\b/rück-lauf/; | ||
136 | $string =~ s/\bsuchlauf\b/such-lauf/; | ||
137 | } | ||
138 | } | ||
139 | } | ||
140 | if ($orig ne $string) { | ||
141 | printf("%s -> %s\n", $orig, $string) if $verbose; | ||
142 | } | ||
143 | return $string; | ||
144 | } | ||
145 | |||
146 | # Produce a wav file of the text given | ||
147 | sub voicestring { | ||
148 | our $verbose; | ||
149 | my ($string, $output, $tts_engine, $tts_engine_opts, $tts_object) = @_; | ||
150 | my $cmd; | ||
151 | printf("Generate \"%s\" with %s in file %s\n", $string, $tts_engine, $output) if $verbose; | ||
152 | switch($tts_engine) { | ||
153 | case "festival" { | ||
154 | # festival_client lies to us, so we have to do awful soul-eating | ||
155 | # work with IPC::open3() | ||
156 | $cmd = "festival_client --server localhost --otype riff --ttw --output \"$output\""; | ||
157 | print("> $cmd\n") if $verbose; | ||
158 | # Open command, and filehandles for STDIN, STDOUT, STDERR | ||
159 | my $pid = open3(*CMD_IN, *CMD_OUT, *CMD_ERR, $cmd); | ||
160 | # Put the string to speak into STDIN and close it | ||
161 | print(CMD_IN $string); | ||
162 | close(CMD_IN); | ||
163 | # Read all output from festival_client (because it LIES TO US) | ||
164 | while (<CMD_ERR>) { | ||
165 | } | ||
166 | close(CMD_OUT); | ||
167 | close(CMD_ERR); | ||
168 | } | ||
169 | case "flite" { | ||
170 | $cmd = "flite $tts_engine_opts -t \"$string\" \"$output\""; | ||
171 | print("> $cmd\n") if $verbose; | ||
172 | `$cmd`; | ||
173 | } | ||
174 | case "espeak" { | ||
175 | # xxx: $tts_engine_opts isn't used | ||
176 | $cmd = "espeak $tts_engine_opts -w $output"; | ||
177 | print("> $cmd\n") if $verbose; | ||
178 | open(ESPEAK, "| $cmd"); | ||
179 | print ESPEAK $string . "\n"; | ||
180 | close(ESPEAK); | ||
181 | } | ||
182 | case "sapi5" { | ||
183 | print($tts_object sprintf("%s\r\n%s\r\n", $string, $output)); | ||
184 | } | ||
185 | } | ||
186 | } | ||
187 | |||
188 | # Encode a wav file into the given destination file | ||
189 | sub encodewav { | ||
190 | our $verbose; | ||
191 | my ($input, $output, $encoder, $encoder_opts) = @_; | ||
192 | printf("Encode \"%s\" with %s in file %s\n", $input, $encoder, $output) if $verbose; | ||
193 | switch ($encoder) { | ||
194 | case 'lame' { | ||
195 | my $cmd = "lame $encoder_opts \"$input\" \"$output\""; | ||
196 | print("> $cmd\n") if $verbose; | ||
197 | `lame $encoder_opts "$input" "$output"`; | ||
198 | `$cmd`; | ||
199 | } | ||
200 | case 'vorbis' { | ||
201 | `oggenc $encoder_opts "$input" -o "$output"`; | ||
202 | } | ||
203 | case 'speexenc' { | ||
204 | `speexenc $encoder_opts "$input" "$output"`; | ||
205 | } | ||
206 | } | ||
207 | } | ||
208 | |||
209 | sub wavtrim { | ||
210 | our $verbose; | ||
211 | my ($file) = @_; | ||
212 | my $cmd = dirname($0) . "/wavtrim \"$file\""; | ||
213 | print("> $cmd\n") if $verbose; | ||
214 | `$cmd`; | ||
215 | } | ||
216 | |||
217 | # Run genlang and create voice clips for each string | ||
218 | sub generateclips { | ||
219 | our $verbose; | ||
220 | my ($language, $target, $encoder, $encoder_opts, $tts_engine, $tts_engine_opts) = @_; | ||
221 | my $genlang = dirname($0) . '/genlang'; | ||
222 | my $english = dirname($0) . '/../apps/lang/english.lang'; | ||
223 | my $langfile = dirname($0) . '/../apps/lang/' . $language . '.lang'; | ||
224 | my $id = ''; | ||
225 | my $voice = ''; | ||
226 | my $cmd = "$genlang -o -t=$target -e=$english $langfile 2>/dev/null"; | ||
227 | my $pool_file; | ||
228 | open(VOICEFONTIDS, "> voicefontids"); | ||
229 | my $i = 0; | ||
230 | |||
231 | my $tts_object = init_tts($tts_engine, $tts_engine_opts, $language); | ||
232 | print("Generating voice clips"); | ||
233 | print("\n") if $verbose; | ||
234 | for (`$cmd`) { | ||
235 | my $line = $_; | ||
236 | print(VOICEFONTIDS $line); | ||
237 | if ($line =~ /^id: (.*)$/) { | ||
238 | $id = $1; | ||
239 | } | ||
240 | elsif ($line =~ /^voice: "(.*)"$/) { | ||
241 | $voice = $1; | ||
242 | if ($id !~ /^NOT_USED_.*$/ && $voice ne "") { | ||
243 | my $wav = $id . '.wav'; | ||
244 | my $mp3 = $id . '.mp3'; | ||
245 | |||
246 | # Print some progress information | ||
247 | if (++$i % 10 == 0 and !$verbose) { | ||
248 | print("."); | ||
249 | } | ||
250 | |||
251 | # Apply corrections to the string | ||
252 | $voice = correct_string($voice); | ||
253 | |||
254 | # If we have a pool of snippes, see if the string exists there first | ||
255 | if (defined($ENV{'POOL'})) { | ||
256 | $pool_file = sprintf("%s/%s-%s-%s.mp3", $ENV{'POOL'}, md5_hex($voice), $language, $tts_engine); | ||
257 | if (-f $pool_file) { | ||
258 | printf("Re-using %s (%s) from pool\n", $id, $voice) if $verbose; | ||
259 | copy($pool_file, $mp3); | ||
260 | } | ||
261 | } | ||
262 | |||
263 | # Don't generate MP3 if it already exists (probably from the POOL) | ||
264 | if (! -f $mp3) { | ||
265 | if ($id eq "VOICE_PAUSE") { | ||
266 | print("Use distributed $wav\n") if $verbose; | ||
267 | copy(dirname($0)."/VOICE_PAUSE.wav", $wav); | ||
268 | } | ||
269 | else { | ||
270 | voicestring($voice, $wav, $tts_engine, $tts_engine_opts, $tts_object); | ||
271 | wavtrim($wav, 500); # 500 seems to be a reasonable default for now | ||
272 | } | ||
273 | |||
274 | encodewav($wav, $mp3, $encoder, $encoder_opts); | ||
275 | if (defined($ENV{'POOL'})) { | ||
276 | copy($mp3, $pool_file); | ||
277 | } | ||
278 | unlink($wav); | ||
279 | } | ||
280 | $voice = ""; | ||
281 | $id = ""; | ||
282 | } | ||
283 | } | ||
284 | } | ||
285 | print("\n"); | ||
286 | close(VOICEFONTIDS); | ||
287 | shutdown_tts($tts_engine, $tts_object); | ||
288 | } | ||
289 | |||
290 | # Assemble the voicefile | ||
291 | sub createvoice { | ||
292 | our $verbose; | ||
293 | my ($language, $target_id) = @_; | ||
294 | my $voicefont = dirname($0) . '/voicefont'; | ||
295 | my $outfile = ""; | ||
296 | my $i = 0; | ||
297 | do { | ||
298 | $outfile = sprintf("%s%s.voice", $language, ($i++ == 0 ? '' : '-'.$i)); | ||
299 | } while (-f $outfile); | ||
300 | printf("Saving voice file to %s\n", $outfile) if $verbose; | ||
301 | my $cmd = "$voicefont 'voicefontids' $target_id ./ $outfile"; | ||
302 | print("> $cmd\n") if $verbose; | ||
303 | my $output = `$cmd`; | ||
304 | print($output) if $verbose; | ||
305 | } | ||
306 | |||
307 | sub deletemp3s() { | ||
308 | for (glob('*.mp3')) { | ||
309 | unlink($_); | ||
310 | } | ||
311 | for (glob('*.wav')) { | ||
312 | unlink($_); | ||
313 | } | ||
314 | } | ||
315 | |||
316 | sub panic_cleanup { | ||
317 | deletemp3s(); | ||
318 | die "moo"; | ||
319 | } | ||
320 | |||
321 | # Check parameters | ||
322 | my $printusage = 0; | ||
323 | unless (defined($V) or defined($C)) { print("Missing either -V or -C\n"); $printusage = 1; } | ||
324 | if (defined($V)) { | ||
325 | unless (defined($t)) { print("Missing -t argument\n"); $printusage = 1; } | ||
326 | unless (defined($l)) { print("Missing -l argument\n"); $printusage = 1; } | ||
327 | unless (defined($i)) { print("Missing -i argument\n"); $printusage = 1; } | ||
328 | } | ||
329 | elsif (defined($C)) { | ||
330 | unless (defined($ARGV[0])) { print "Missing path argument\n"; $printusage = 1; } | ||
331 | } | ||
332 | unless (defined($e)) { print("Missing -e argument\n"); $printusage = 1; } | ||
333 | unless (defined($E)) { print("Missing -E argument\n"); $printusage = 1; } | ||
334 | unless (defined($s)) { print("Missing -s argument\n"); $printusage = 1; } | ||
335 | unless (defined($S)) { print("Missing -S argument\n"); $printusage = 1; } | ||
336 | if ($printusage == 1) { printusage(); exit 1; } | ||
337 | |||
338 | $SIG{INT} = \&panic_cleanup; | ||
339 | $SIG{KILL} = \&panic_cleanup; | ||
340 | |||
341 | if (defined($v) or defined($ENV{'V'})) { | ||
342 | our $verbose = 1; | ||
343 | } | ||
344 | |||
345 | |||
346 | # Do what we're told | ||
347 | if ($V == 1) { | ||
348 | printf("Generating voice\n Target: %s\n Language: %s\n Encoder (options): %s (%s)\n TTS Engine (options): %s (%s)\n", | ||
349 | $t, $l, $e, $E, $s, $S); | ||
350 | generateclips($l, $t, $e, $E, $s, $S); | ||
351 | createvoice($l, $i); | ||
352 | deletemp3s(); | ||
353 | } | ||
354 | elsif ($C) { | ||
355 | # xxx: Implement .talk clip generation | ||
356 | } | ||
357 | else { | ||
358 | printusage(); | ||
359 | exit 1; | ||
360 | } | ||