9 Run a bunch of words through the speech synthesizer
10 and collect the results.
12 ./synth.pl [options] sport.vlist sport.vce sport.wav
15 -skip # skip a line from the .vlist file;
16 -one # take only the first word from each line
18 Note: -skip -one is useful if the .vlist file is actually in .vce format.
21 ./synth.pl -dump foo.wav # dump headers 'foo.wav'
22 ./synth.pl -help # print this message
24 where sport.vlist is a file containing a list of words, we use the
25 first word on each line and ignore anything else on that line
26 (which means a .vce file is acceptable as input, using -skip -one).
28 Note that "atis-lex.pl" and "list-airports.pl" are useful for
29 creating the .vlist file.
31 Note that you may also need to:
33 apt-get install festival mbrola sox festlex-oald
35 wget http://tcts.fpms.ac.be/synthesis/mbrola/dba/en1/en1-980910.zip
36 wget http://www.cstr.ed.ac.uk/downloads/festival/1.95/festvox_en1.tar.gz
37 cd /usr/share/festival/voices/english
40 unzip \$tars/en1-980910.zip
41 cd /usr/share/festival
46 tar -xpzvf \$tars/festvox_en1.tar.gz
48 You may also need to scrounge a non-buggy version of
49 /usr/local/share/perl/5.10.1/Audio/Wav/Read.pm
54 (voice_en1_mbrola) ;; best voice I know of
55 (setq voice_rab_diphone voice_en1_mbrola)
56 (setq voice_en1 voice_en1_mbrola)
57 (setq voice_el_diphone voice_en1_mbrola)
58 (setq voice_ked_diphone voice_en1_mbrola)
59 (utt.save.wave (utt.synth (Utterance Text "XXX")) "YYY" 'riff)
67 if (exists $list{lc $word}
68 && $list{lc $word} =~ m'^[A-Z][a-z]') {
69 ; # tasteful capitalization; leave as is
71 $list{lc $word} = $word;
76 ## This is an ugly way to fix festival's bad guess as to
77 ## pronunciation in special cases.
78 ## In the case of wind (windh versus wynd),
79 ## nominally there are ways of tagging parts-of-speech,
80 ## i.e. noun versus verb,
81 ## but they don't seem to be reliable.
84 'romeo' => 'Rome E O',
94 my $out_bits_sample = 8; ## this is what FGFS expects
97 my $arg = shift @ARGV;
99 if ($arg eq '-help' || $arg eq '-h') {
103 if ($arg eq '-dump') {
104 my $ifn = shift @ARGV || 'foo.wav';
105 my $wav = new Audio::Wav;
106 print "About to open '$ifn' ...\n";
107 my $waver = $wav -> read( $ifn );
108 print "xxxxxxxxxxxxxxxx\n";
109 for my $detail (keys %{$waver->details()}) {
110 printf("%-20s %s\n", $detail, ${$waver->details()}{$detail});
114 if ($arg eq '-skip') {
118 if ($arg eq '-gripe') {
122 if ($arg eq '-one' || $arg eq '-1') {
126 if ($arg eq '-nocheck') {
131 die "Unrecognized option '$arg'\n";
133 push @plain_args, $arg;
136 my $nargs = @plain_args;
138 die "Wrong number of arguments ($nargs); for help try:\n $0 -help\n";
141 my ($ifn, $indexfn, $out_wav) = @plain_args;
143 my $inch = Symbol::gensym;
144 open ($inch, '<', $ifn)
145 || die "Couldn't open input file '$ifn'\n";
147 # Skip some lines from the input list, as requested:
148 for (my $ii = 0; $ii < $skip; $ii++) {
149 my $ignore = <$inch>;
152 # Read the rest of the input file:
153 while (my $line = <$inch>) {
156 my @stuff = split(' ', $line, 2);
159 for my $word (split(' ', $line)) {
166 # Optionally print a list of things that the input file
167 # requested more than once.
169 foreach my $thing (sort keys %count) {
170 if ($count{$thing} > 1) {
171 printf("%4d %s\n", $count{$thing}, $list{$thing});
175 my $nsnip = (keys %list);
177 if (0 && $nsnip > 10) {
180 print STDERR "nsnip: $nsnip\n";
182 my $index = Symbol::gensym;
183 open ($index, '>', $indexfn)
184 || die "Couldn't write index file '$indexfn'\n";
186 print $index "$nsnip\n";
189 || die "Could not create directory 'snip' : $!\n";
192 my $wav = new Audio::Wav;
193 my $waver = $wav -> read("quiet0.500.wav");
194 my $sample_rate = -1;
196 my $bits_sample = -1;
197 $sample_rate = ${$waver->details()}{'sample_rate'};
198 $channels = ${$waver->details()}{'channels'};
199 $bits_sample = ${$waver->details()}{'bits_sample'};
201 ############## system "/bin/cp nothing.wav t1.wav";
205 snipper: for my $thing (sort keys %list) {
207 my $iix = sprintf('%05d', $ii);
208 my $xfn = "./snip/x$iix";
211 my $fraise = lc($thing);
212 if (exists $fixup{$fraise}) {
213 #xxxx print "fixing $fraise\n";
214 $fraise = $fixup{$fraise};
217 ## This turns dashes and other funny stuff into spaces
218 ## in the phrase to be processed:
219 $fraise =~ s%[^a-z']+% %gi;
220 if ($thing eq '/' || $thing eq '/_') {
221 system("/bin/cp quiet0.500.wav $xfn.wav");
224 $script =~ s/XXX/$fraise/;
225 $script =~ s|YYY|$xfn.wav|;
226 #xxxx print "$fraise ... $script\n";
228 my $cmd = '/usr/bin/festival';
229 my $pipe = Symbol::gensym;
230 open ($pipe, '|-', $cmd)
231 || die "Couldn't open pipe to '$cmd'\n";
235 print STDERR "Error in festival script: '$script'\n";
242 snipper: for my $thing (sort keys %list) {
244 my $iix = sprintf('%05d', $ii);
245 my $xfn = "./snip/x$iix";
247 if ($fmtcheck == 1) {
248 my $wav = new Audio::Wav;
249 my $waver = $wav -> read("$xfn.wav");
250 if ($sample_rate < 0) {
251 $sample_rate = ${$waver->details()}{'sample_rate'};
252 $channels = ${$waver->details()}{'channels'};
253 $bits_sample = ${$waver->details()}{'bits_sample'};
255 $sample_rate == ${$waver->details()}{'sample_rate'}
256 && $channels == ${$waver->details()}{'channels'}
257 && $bits_sample == ${$waver->details()}{'bits_sample'}
258 || die "audio format not the same: $xfn.wav";
262 my $statcmd = "2>&1 sox $xfn.wav -n stat";
263 my $stat = Symbol::gensym;
264 open ($stat, '-|', $statcmd)
265 || die "Couldn't open pipe from '$statcmd'\n";
270 while (my $line = <$stat>) {
273 my @stuff = split ':', $line;
275 #### print STDERR "$nw +++ $line\n";
277 if ($stuff[0] eq 'Volume adjustment') {
280 elsif ($stuff[0] eq 'Samples read') {
285 my $status = close $stat;
287 print STDERR "Stat command failed: $statcmd\n" . ": $lastline \n";
291 print STDERR "?Warning! Zero-size audio file for $iix '$thing'\n";
295 ## unreasonable volume, happens with 'silent' files
298 printf("%s %6.3f %6d '%s'\n", $iix, $vol, $size, $thing);
299 my $subsize = int($size/2);
300 printf $index ("%-45s %10d %10d\n", $thing, $where, $subsize);
303 my $volume_cmd = sprintf("sox -v %6.3f %s.wav %s.raw",
304 $vol*0.9, $xfn, $xfn);
305 ########## print "+ $volume_cmd\n";
307 my $vol_handle = Symbol::gensym;
308 open ($vol_handle, '|-', $volume_cmd)
309 || die "Couldn't open pipe to command '$volume_cmd'\n";
313 die "Volume command failed: $statcmd\n" . ": $lastline";
316 push @todo, "$xfn.raw";
320 my $cat_cmd = "cat " . join(' ', @todo) . " > ./snip/everything.raw";
321 my $cat_handle = Symbol::gensym;
322 open ($cat_handle, '|-', $cat_cmd)
323 || die "Couldn't open pipe to command '$cat_cmd'\n";
326 die "Cat command failed: $cat_cmd";
329 ## Convert RAW to WAVE format
330 my $wav_cmd = "sox --rate $sample_rate --bits $bits_sample"
331 . " --encoding signed-integer"
332 . " ./snip/everything.raw --rate 8000 --bits $out_bits_sample $out_wav";
334 my $wav_handle = Symbol::gensym;
335 open ($wav_handle, '|-', $wav_cmd)
336 || die "Couldn't open pipe to command '$wav_cmd'\n";
339 die ".wav command failed: $wav_cmd";
342 ## Compress WAVE file
343 my $gz_cmd = "gzip -f $out_wav";
344 my $gz_handle = Symbol::gensym;
345 open ($gz_handle, '|-', $gz_cmd)
346 || die "Couldn't open pipe to command '$gz_cmd'\n";
348 system("rm snip/*; rmdir snip");