diff options
author | Jens Arnold <amiconn@rockbox.org> | 2007-09-01 08:38:10 +0000 |
---|---|---|
committer | Jens Arnold <amiconn@rockbox.org> | 2007-09-01 08:38:10 +0000 |
commit | 080522f9173526c9ffca06bf12782ddf73cb577a (patch) | |
tree | 56c39cb41ef1bfe1f933e35cc11b7aa926d3e13f /tools/voice.pl | |
parent | 80e91c1af392c72cd72095c1cbfa21957462381b (diff) | |
download | rockbox-080522f9173526c9ffca06bf12782ddf73cb577a.tar.gz rockbox-080522f9173526c9ffca06bf12782ddf73cb577a.zip |
Voice file generation: * Significant speedup of SAPI5 voice generation by running lame and wavtrim from inside the VB script instead of the perl script, avoiding the large overhead of process generation within cygwin. Added proper synchronisation between perl script and VB script as the pipes are buffered. * Make wavtrim work as intended (threashold wasn't passed). * Set correct SVN properties for the VB script.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@14562 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'tools/voice.pl')
-rwxr-xr-x | tools/voice.pl | 97 |
1 files changed, 62 insertions, 35 deletions
diff --git a/tools/voice.pl b/tools/voice.pl index 109451f82e..88f3ba4744 100755 --- a/tools/voice.pl +++ b/tools/voice.pl | |||
@@ -21,6 +21,7 @@ use File::Basename; | |||
21 | use File::Copy; | 21 | use File::Copy; |
22 | use Switch; | 22 | use Switch; |
23 | use vars qw($V $C $t $l $e $E $s $S $i $v); | 23 | use vars qw($V $C $t $l $e $E $s $S $i $v); |
24 | use IPC::Open2; | ||
24 | use IPC::Open3; | 25 | use IPC::Open3; |
25 | use Digest::MD5 qw(md5_hex); | 26 | use Digest::MD5 qw(md5_hex); |
26 | 27 | ||
@@ -69,43 +70,44 @@ USAGE | |||
69 | sub init_tts { | 70 | sub init_tts { |
70 | our $verbose; | 71 | our $verbose; |
71 | my ($tts_engine, $tts_engine_opts, $language) = @_; | 72 | my ($tts_engine, $tts_engine_opts, $language) = @_; |
72 | my $ret = undef; | 73 | my %ret = ("name" => $tts_engine); |
73 | switch($tts_engine) { | 74 | switch($tts_engine) { |
74 | case "festival" { | 75 | case "festival" { |
75 | print("> festival $tts_engine_opts --server\n") if $verbose; | 76 | 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 | my $pid = open(FESTIVAL_SERVER, "| festival $tts_engine_opts --server > /dev/null 2>&1"); |
77 | $ret = *FESTIVAL_SERVER; | 78 | my $dummy = *FESTIVAL_SERVER; #suppress warning |
78 | $ret = $pid; | ||
79 | $SIG{INT} = sub { kill TERM => $pid; print("foo"); panic_cleanup(); }; | 79 | $SIG{INT} = sub { kill TERM => $pid; print("foo"); panic_cleanup(); }; |
80 | $SIG{KILL} = sub { kill TERM => $pid; print("boo"); panic_cleanup(); }; | 80 | $SIG{KILL} = sub { kill TERM => $pid; print("boo"); panic_cleanup(); }; |
81 | $ret{"pid"} = $pid; | ||
81 | } | 82 | } |
82 | case "sapi5" { | 83 | case "sapi5" { |
83 | my $toolsdir = dirname($0); | 84 | my $toolsdir = dirname($0); |
84 | my $path = `cygpath $toolsdir -a -w`; | 85 | my $path = `cygpath $toolsdir -a -w`; |
85 | chomp($path); | 86 | chomp($path); |
86 | $path = $path . "\\sapi5_voice_new.vbs $language $tts_engine_opts"; | 87 | $path = $path . '\\'; |
87 | $path =~ s/\\/\\\\/g; | 88 | my $cmd = $path . "sapi5_voice_new.vbs $language $tts_engine_opts"; |
88 | print("> cscript /B $path\n") if $verbose; | 89 | $cmd =~ s/\\/\\\\/g; |
89 | my $pid = open(F, "| cscript /B $path"); | 90 | print("> cscript //nologo $cmd\n") if $verbose; |
90 | $ret = *F; | 91 | my $pid = open2(*CMD_OUT, *CMD_IN, "cscript //nologo $cmd"); |
91 | $SIG{INT} = sub { print($ret "\r\n\r\n"); panic_cleanup(); }; | 92 | $SIG{INT} = sub { print(CMD_IN "QUIT\r\n"); panic_cleanup(); }; |
92 | $SIG{KILL} = sub { print($ret "\r\n\r\n"); panic_cleanup(); }; | 93 | $SIG{KILL} = sub { print(CMD_IN "QUIT\r\n"); panic_cleanup(); }; |
94 | %ret = (%ret, "stdin" => *CMD_IN, "stdout" => *CMD_OUT, "toolspath" => $path); | ||
93 | } | 95 | } |
94 | } | 96 | } |
95 | return $ret; | 97 | return \%ret; |
96 | } | 98 | } |
97 | 99 | ||
98 | # Shutdown TTS engine if necessary. | 100 | # Shutdown TTS engine if necessary. |
99 | sub shutdown_tts { | 101 | sub shutdown_tts { |
100 | my ($tts_engine, $tts_object) = @_; | 102 | my ($tts_object) = @_; |
101 | switch($tts_engine) { | 103 | switch($$tts_object{"name"}) { |
102 | case "festival" { | 104 | case "festival" { |
103 | # Send SIGTERM to festival server | 105 | # Send SIGTERM to festival server |
104 | kill TERM => $tts_object; | 106 | kill TERM => $$tts_object{"pid"}; |
105 | } | 107 | } |
106 | case "sapi5" { | 108 | case "sapi5" { |
107 | print($tts_object "\r\n\r\n"); | 109 | print({$$tts_object{"stdin"}} "QUIT\r\n"); |
108 | close($tts_object); | 110 | close($$tts_object{"stdin"}); |
109 | } | 111 | } |
110 | } | 112 | } |
111 | } | 113 | } |
@@ -113,14 +115,14 @@ sub shutdown_tts { | |||
113 | # Apply corrections to a voice-string to make it sound better | 115 | # Apply corrections to a voice-string to make it sound better |
114 | sub correct_string { | 116 | sub correct_string { |
115 | our $verbose; | 117 | our $verbose; |
116 | my ($string, $language, $tts_engine) = @_; | 118 | my ($string, $language, $tts_object) = @_; |
117 | my $orig = $string; | 119 | my $orig = $string; |
118 | switch($language) { | 120 | switch($language) { |
119 | # General for all engines and languages (perhaps - just an example) | 121 | # General for all engines and languages (perhaps - just an example) |
120 | $string =~ s/USB/U S B/; | 122 | $string =~ s/USB/U S B/; |
121 | 123 | ||
122 | case ("deutsch") { | 124 | case ("deutsch") { |
123 | switch($tts_engine) { | 125 | switch($$tts_object{"name"}) { |
124 | $string =~ s/alphabet/alfabet/; | 126 | $string =~ s/alphabet/alfabet/; |
125 | $string =~ s/alkaline/alkalein/; | 127 | $string =~ s/alkaline/alkalein/; |
126 | $string =~ s/ampere/amper/; | 128 | $string =~ s/ampere/amper/; |
@@ -146,10 +148,10 @@ sub correct_string { | |||
146 | # Produce a wav file of the text given | 148 | # Produce a wav file of the text given |
147 | sub voicestring { | 149 | sub voicestring { |
148 | our $verbose; | 150 | our $verbose; |
149 | my ($string, $output, $tts_engine, $tts_engine_opts, $tts_object) = @_; | 151 | my ($string, $output, $tts_engine_opts, $tts_object) = @_; |
150 | my $cmd; | 152 | my $cmd; |
151 | printf("Generate \"%s\" with %s in file %s\n", $string, $tts_engine, $output) if $verbose; | 153 | printf("Generate \"%s\" with %s in file %s\n", $string, $$tts_object{"name"}, $output) if $verbose; |
152 | switch($tts_engine) { | 154 | switch($$tts_object{"name"}) { |
153 | case "festival" { | 155 | case "festival" { |
154 | # festival_client lies to us, so we have to do awful soul-eating | 156 | # festival_client lies to us, so we have to do awful soul-eating |
155 | # work with IPC::open3() | 157 | # work with IPC::open3() |
@@ -180,15 +182,31 @@ sub voicestring { | |||
180 | close(ESPEAK); | 182 | close(ESPEAK); |
181 | } | 183 | } |
182 | case "sapi5" { | 184 | case "sapi5" { |
183 | print($tts_object sprintf("%s\r\n%s\r\n", $string, $output)); | 185 | print({$$tts_object{"stdin"}} sprintf("SPEAK\t%s\t%s\r\n", $output, $string)); |
184 | } | 186 | } |
185 | } | 187 | } |
186 | } | 188 | } |
187 | 189 | ||
190 | # trim leading / trailing silence from the clip | ||
191 | sub wavtrim { | ||
192 | our $verbose; | ||
193 | my ($file, $threshold, $tts_object) = @_; | ||
194 | printf("Trim \"%s\"\n", $file) if $verbose; | ||
195 | if ($$tts_object{"name"} eq "sapi5") { | ||
196 | my $cmd = $$tts_object{"toolspath"}."wavtrim $file $threshold"; | ||
197 | print({$$tts_object{"stdin"}} sprintf("EXEC\t%s\r\n", $cmd)); | ||
198 | } | ||
199 | else { | ||
200 | my $cmd = dirname($0) . "/wavtrim $file $threshold"; | ||
201 | print("> $cmd\n") if $verbose; | ||
202 | `$cmd`; | ||
203 | } | ||
204 | } | ||
205 | |||
188 | # Encode a wav file into the given destination file | 206 | # Encode a wav file into the given destination file |
189 | sub encodewav { | 207 | sub encodewav { |
190 | our $verbose; | 208 | our $verbose; |
191 | my ($input, $output, $encoder, $encoder_opts) = @_; | 209 | my ($input, $output, $encoder, $encoder_opts, $tts_object) = @_; |
192 | my $cmd = ''; | 210 | my $cmd = ''; |
193 | printf("Encode \"%s\" with %s in file %s\n", $input, $encoder, $output) if $verbose; | 211 | printf("Encode \"%s\" with %s in file %s\n", $input, $encoder, $output) if $verbose; |
194 | switch ($encoder) { | 212 | switch ($encoder) { |
@@ -202,16 +220,23 @@ sub encodewav { | |||
202 | $cmd = "speexenc $encoder_opts \"$input\" \"$output\""; | 220 | $cmd = "speexenc $encoder_opts \"$input\" \"$output\""; |
203 | } | 221 | } |
204 | } | 222 | } |
205 | print("> $cmd\n") if $verbose; | 223 | if ($$tts_object{"name"} eq "sapi5") { |
206 | `$cmd`; | 224 | print({$$tts_object{"stdin"}} sprintf("EXEC\t%s\r\n", $cmd)); |
225 | } | ||
226 | else { | ||
227 | print("> $cmd\n") if $verbose; | ||
228 | `$cmd`; | ||
229 | } | ||
207 | } | 230 | } |
208 | 231 | ||
209 | sub wavtrim { | 232 | # synchronize the clip generation / processing if it's running in another process |
210 | our $verbose; | 233 | sub synchronize { |
211 | my ($file) = @_; | 234 | my ($tts_object) = @_; |
212 | my $cmd = dirname($0) . "/wavtrim \"$file\""; | 235 | if ($$tts_object{"name"} eq "sapi5") { |
213 | print("> $cmd\n") if $verbose; | 236 | print({$$tts_object{"stdin"}} "SYNC\t42\r\n"); |
214 | `$cmd`; | 237 | my $wait = readline($$tts_object{"stdout"}); |
238 | #ignore what's actually returned | ||
239 | } | ||
215 | } | 240 | } |
216 | 241 | ||
217 | # Run genlang and create voice clips for each string | 242 | # Run genlang and create voice clips for each string |
@@ -267,11 +292,13 @@ sub generateclips { | |||
267 | copy(dirname($0)."/VOICE_PAUSE.wav", $wav); | 292 | copy(dirname($0)."/VOICE_PAUSE.wav", $wav); |
268 | } | 293 | } |
269 | else { | 294 | else { |
270 | voicestring($voice, $wav, $tts_engine, $tts_engine_opts, $tts_object); | 295 | voicestring($voice, $wav, $tts_engine_opts, $tts_object); |
271 | wavtrim($wav, 500); # 500 seems to be a reasonable default for now | 296 | wavtrim($wav, 500, $tts_object); |
297 | # 500 seems to be a reasonable default for now | ||
272 | } | 298 | } |
273 | 299 | ||
274 | encodewav($wav, $mp3, $encoder, $encoder_opts); | 300 | encodewav($wav, $mp3, $encoder, $encoder_opts, $tts_object); |
301 | synchronize($tts_object); | ||
275 | if (defined($ENV{'POOL'})) { | 302 | if (defined($ENV{'POOL'})) { |
276 | copy($mp3, $pool_file); | 303 | copy($mp3, $pool_file); |
277 | } | 304 | } |
@@ -284,7 +311,7 @@ sub generateclips { | |||
284 | } | 311 | } |
285 | print("\n"); | 312 | print("\n"); |
286 | close(VOICEFONTIDS); | 313 | close(VOICEFONTIDS); |
287 | shutdown_tts($tts_engine, $tts_object); | 314 | shutdown_tts($tts_object); |
288 | } | 315 | } |
289 | 316 | ||
290 | # Assemble the voicefile | 317 | # Assemble the voicefile |