diff options
-rwxr-xr-x | tools/voice.pl | 88 |
1 files changed, 57 insertions, 31 deletions
diff --git a/tools/voice.pl b/tools/voice.pl index 8e603308f3..216d514065 100755 --- a/tools/voice.pl +++ b/tools/voice.pl | |||
@@ -19,7 +19,7 @@ use strict; | |||
19 | use warnings; | 19 | use warnings; |
20 | use File::Basename; | 20 | use File::Basename; |
21 | use File::Copy; | 21 | use File::Copy; |
22 | use vars qw($V $C $t $l $e $E $s $S $i $v); | 22 | use vars qw($V $C $t $l $e $E $s $S $i $v $f); |
23 | use IPC::Open2; | 23 | use IPC::Open2; |
24 | use IPC::Open3; | 24 | use IPC::Open3; |
25 | use Digest::MD5 qw(md5_hex); | 25 | use Digest::MD5 qw(md5_hex); |
@@ -33,7 +33,7 @@ sub printusage { | |||
33 | 33 | ||
34 | Usage: voice.pl [options] [path to dir] | 34 | Usage: voice.pl [options] [path to dir] |
35 | -V | 35 | -V |
36 | Create voice file. You must also specify -t and -l. | 36 | Create voice file. You must also specify -l, -i, and -t or -f |
37 | 37 | ||
38 | -C | 38 | -C |
39 | Create .talk clips. | 39 | Create .talk clips. |
@@ -42,6 +42,8 @@ Usage: voice.pl [options] [path to dir] | |||
42 | Specify which target you want to build voicefile for. Must include | 42 | Specify which target you want to build voicefile for. Must include |
43 | any features that target supports. | 43 | any features that target supports. |
44 | 44 | ||
45 | -f=<file> Use existing voiceids file | ||
46 | |||
45 | -i=<target_id> | 47 | -i=<target_id> |
46 | Numeric target id. Needed for voice building. | 48 | Numeric target id. Needed for voice building. |
47 | 49 | ||
@@ -328,21 +330,32 @@ sub synchronize { | |||
328 | # Run genlang and create voice clips for each string | 330 | # Run genlang and create voice clips for each string |
329 | sub generateclips { | 331 | sub generateclips { |
330 | our $verbose; | 332 | our $verbose; |
331 | my ($language, $target, $encoder, $encoder_opts, $tts_engine, $tts_engine_opts) = @_; | 333 | my ($language, $target, $encoder, $encoder_opts, $tts_engine, $tts_engine_opts, $existingids) = @_; |
332 | my $english = dirname($0) . '/../apps/lang/english.lang'; | 334 | my $english = dirname($0) . '/../apps/lang/english.lang'; |
333 | my $langfile = dirname($0) . '/../apps/lang/' . $language . '.lang'; | 335 | my $langfile = dirname($0) . '/../apps/lang/' . $language . '.lang'; |
334 | my $correctionsfile = dirname($0) . '/voice-corrections.txt'; | 336 | my $correctionsfile = dirname($0) . '/voice-corrections.txt'; |
337 | my $idfile = "$language.vid"; | ||
338 | my $updfile = "$language-update.lang"; | ||
335 | my $id = ''; | 339 | my $id = ''; |
336 | my $voice = ''; | 340 | my $voice = ''; |
337 | my $cmd = "genlang -o -t=$target -e=$english $language-update.lang 2>/dev/null"; | 341 | my $cmd; |
338 | my $pool_file; | 342 | my $pool_file; |
339 | open(VOICEFONTIDS, "> voicefontids"); | ||
340 | my $i = 0; | 343 | my $i = 0; |
341 | local $| = 1; # make progress indicator work reliably | 344 | local $| = 1; # make progress indicator work reliably |
342 | 345 | ||
343 | # First run the language through an update pass so any missing strings | 346 | # First run the language through an update pass so any missing strings |
344 | # are backfilled from English. Without this, BADNESS. | 347 | # are backfilled from English. Without this, BADNESS. |
345 | system("genlang -u -e=$english $langfile > $language-update.lang"); | 348 | if ($existingids) { |
349 | $idfile = $existingids; | ||
350 | } else { | ||
351 | $cmd = "genlang -u -e=$english $langfile > $updfile"; | ||
352 | print("> $cmd\n") if $verbose; | ||
353 | system($cmd); | ||
354 | $cmd = "genlang -o -t=$target -e=$english $updfile 2>/dev/null > $idfile"; | ||
355 | print("> $cmd\n") if $verbose; | ||
356 | system($cmd); | ||
357 | } | ||
358 | open(VOICEFONTIDS, " < $idfile"); | ||
346 | 359 | ||
347 | my $tts_object = init_tts($tts_engine, $tts_engine_opts, $language); | 360 | my $tts_object = init_tts($tts_engine, $tts_engine_opts, $language); |
348 | # add string corrections to tts_object. | 361 | # add string corrections to tts_object. |
@@ -378,9 +391,8 @@ sub generateclips { | |||
378 | 391 | ||
379 | print("Generating voice clips"); | 392 | print("Generating voice clips"); |
380 | print("\n") if $verbose; | 393 | print("\n") if $verbose; |
381 | for (`$cmd`) { | 394 | for (<VOICEFONTIDS>) { |
382 | my $line = $_; | 395 | my $line = $_; |
383 | print(VOICEFONTIDS $line); | ||
384 | if ($line =~ /^id: (.*)$/) { | 396 | if ($line =~ /^id: (.*)$/) { |
385 | $id = $1; | 397 | $id = $1; |
386 | } | 398 | } |
@@ -388,7 +400,7 @@ sub generateclips { | |||
388 | $voice = $1; | 400 | $voice = $1; |
389 | if ($id !~ /^NOT_USED_.*$/ && $voice ne "") { | 401 | if ($id !~ /^NOT_USED_.*$/ && $voice ne "") { |
390 | my $wav = $id . '.wav'; | 402 | my $wav = $id . '.wav'; |
391 | my $mp3 = $id . '.mp3'; | 403 | my $enc = $id . '.mp3'; |
392 | 404 | ||
393 | # Print some progress information | 405 | # Print some progress information |
394 | if (++$i % 10 == 0 and !$verbose) { | 406 | if (++$i % 10 == 0 and !$verbose) { |
@@ -405,12 +417,12 @@ sub generateclips { | |||
405 | $language); | 417 | $language); |
406 | if (-f $pool_file) { | 418 | if (-f $pool_file) { |
407 | printf("Re-using %s (%s) from pool\n", $id, $voice) if $verbose; | 419 | printf("Re-using %s (%s) from pool\n", $id, $voice) if $verbose; |
408 | copy($pool_file, $mp3); | 420 | copy($pool_file, $enc); |
409 | } | 421 | } |
410 | } | 422 | } |
411 | 423 | ||
412 | # Don't generate MP3 if it already exists (probably from the POOL) | 424 | # Don't generate encoded file if it already exists (probably from the POOL) |
413 | if (! -f $mp3) { | 425 | if (! -f $enc) { |
414 | if ($id eq "VOICE_PAUSE") { | 426 | if ($id eq "VOICE_PAUSE") { |
415 | print("Use distributed $wav\n") if $verbose; | 427 | print("Use distributed $wav\n") if $verbose; |
416 | copy(dirname($0)."/VOICE_PAUSE.wav", $wav); | 428 | copy(dirname($0)."/VOICE_PAUSE.wav", $wav); |
@@ -422,14 +434,14 @@ sub generateclips { | |||
422 | } | 434 | } |
423 | } | 435 | } |
424 | if ($tts_object->{'format'} eq "wav" || $id eq "VOICE_PAUSE") { | 436 | if ($tts_object->{'format'} eq "wav" || $id eq "VOICE_PAUSE") { |
425 | encodewav($wav, $mp3, $encoder, $encoder_opts, $tts_object); | 437 | encodewav($wav, $enc, $encoder, $encoder_opts, $tts_object); |
426 | } else { | 438 | } else { |
427 | copy($wav, $mp3); | 439 | copy($wav, $enc); |
428 | } | 440 | } |
429 | 441 | ||
430 | synchronize($tts_object); | 442 | synchronize($tts_object); |
431 | if (defined($ENV{'POOL'})) { | 443 | if (defined($ENV{'POOL'})) { |
432 | copy($mp3, $pool_file); | 444 | copy($enc, $pool_file); |
433 | } | 445 | } |
434 | unlink($wav); | 446 | unlink($wav); |
435 | } | 447 | } |
@@ -438,25 +450,36 @@ sub generateclips { | |||
438 | } | 450 | } |
439 | } | 451 | } |
440 | } | 452 | } |
441 | print("\n"); | ||
442 | close(VOICEFONTIDS); | 453 | close(VOICEFONTIDS); |
454 | |||
455 | print("\n"); | ||
456 | |||
457 | unlink($updfile) if (-f $updfile); | ||
443 | shutdown_tts($tts_object); | 458 | shutdown_tts($tts_object); |
444 | } | 459 | } |
445 | 460 | ||
446 | # Assemble the voicefile | 461 | # Assemble the voicefile |
447 | sub createvoice { | 462 | sub createvoice { |
448 | our $verbose; | 463 | our $verbose; |
449 | my ($language, $target_id) = @_; | 464 | my ($language, $target_id, $existingids) = @_; |
450 | my $outfile = ""; | 465 | my $outfile = ""; |
466 | my $vfile = "$language.vid"; | ||
467 | |||
468 | if ($existingids) { | ||
469 | $vfile = $existingids; | ||
470 | } | ||
451 | $outfile = sprintf("%s.voice", $language); | 471 | $outfile = sprintf("%s.voice", $language); |
452 | printf("Saving voice file to %s\n", $outfile) if $verbose; | 472 | printf("Saving voice file to %s\n", $outfile) if $verbose; |
453 | my $cmd = "voicefont 'voicefontids' $target_id ./ $outfile"; | 473 | my $cmd = "voicefont '$vfile' $target_id ./ $outfile"; |
454 | print("> $cmd\n") if $verbose; | 474 | print("> $cmd\n") if $verbose; |
455 | my $output = `$cmd`; | 475 | my $output = `$cmd`; |
456 | print($output) if $verbose; | 476 | print($output) if $verbose; |
477 | if (!$existingids) { | ||
478 | unlink("$vfile"); | ||
479 | } | ||
457 | } | 480 | } |
458 | 481 | ||
459 | sub deletemp3s() { | 482 | sub deleteencs() { |
460 | for (glob('*.mp3')) { | 483 | for (glob('*.mp3')) { |
461 | unlink($_); | 484 | unlink($_); |
462 | } | 485 | } |
@@ -466,7 +489,7 @@ sub deletemp3s() { | |||
466 | } | 489 | } |
467 | 490 | ||
468 | sub panic_cleanup { | 491 | sub panic_cleanup { |
469 | deletemp3s(); | 492 | deletencs(); |
470 | die "moo"; | 493 | die "moo"; |
471 | } | 494 | } |
472 | 495 | ||
@@ -476,7 +499,7 @@ sub gentalkclips { | |||
476 | my ($dir, $tts_object, $encoder, $encoder_opts, $tts_engine_opts, $i) = @_; | 499 | my ($dir, $tts_object, $encoder, $encoder_opts, $tts_engine_opts, $i) = @_; |
477 | my $d = new DirHandle $dir; | 500 | my $d = new DirHandle $dir; |
478 | while (my $file = $d->read) { | 501 | while (my $file = $d->read) { |
479 | my ($voice, $wav, $mp3); | 502 | my ($voice, $wav, $enc); |
480 | # Print some progress information | 503 | # Print some progress information |
481 | if (++$i % 10 == 0 and !$verbose) { | 504 | if (++$i % 10 == 0 and !$verbose) { |
482 | print("."); | 505 | print("."); |
@@ -495,20 +518,20 @@ sub gentalkclips { | |||
495 | # Element is a dir | 518 | # Element is a dir |
496 | if ( -d $path) { | 519 | if ( -d $path) { |
497 | gentalkclips($path, $tts_object, $encoder, $encoder_opts, $tts_engine_opts, $i); | 520 | gentalkclips($path, $tts_object, $encoder, $encoder_opts, $tts_engine_opts, $i); |
498 | $mp3 = sprintf("%s/_dirname.talk", $path); | 521 | $enc = sprintf("%s/_dirname.talk", $path); |
499 | } | 522 | } |
500 | # Element is a file | 523 | # Element is a file |
501 | else { | 524 | else { |
502 | $mp3 = sprintf("%s.talk", $path); | 525 | $enc = sprintf("%s.talk", $path); |
503 | $voice =~ s/\.[^\.]*$//; # Trim extension | 526 | $voice =~ s/\.[^\.]*$//; # Trim extension |
504 | } | 527 | } |
505 | 528 | ||
506 | printf("Talkclip %s: %s", $mp3, $voice) if $verbose; | 529 | printf("Talkclip %s: %s", $enc, $voice) if $verbose; |
507 | 530 | ||
508 | voicestring($voice, $wav, $tts_engine_opts, $tts_object); | 531 | voicestring($voice, $wav, $tts_engine_opts, $tts_object); |
509 | wavtrim($wav, 500, $tts_object); | 532 | wavtrim($wav, 500, $tts_object); |
510 | # 500 seems to be a reasonable default for now | 533 | # 500 seems to be a reasonable default for now |
511 | encodewav($wav, $mp3, $encoder, $encoder_opts, $tts_object); | 534 | encodewav($wav, $enc, $encoder, $encoder_opts, $tts_object); |
512 | synchronize($tts_object); | 535 | synchronize($tts_object); |
513 | unlink($wav); | 536 | unlink($wav); |
514 | } | 537 | } |
@@ -519,9 +542,12 @@ sub gentalkclips { | |||
519 | my $printusage = 0; | 542 | my $printusage = 0; |
520 | unless (defined($V) or defined($C)) { print("Missing either -V or -C\n"); $printusage = 1; } | 543 | unless (defined($V) or defined($C)) { print("Missing either -V or -C\n"); $printusage = 1; } |
521 | if (defined($V)) { | 544 | if (defined($V)) { |
522 | unless (defined($t)) { print("Missing -t argument\n"); $printusage = 1; } | ||
523 | unless (defined($l)) { print("Missing -l argument\n"); $printusage = 1; } | 545 | unless (defined($l)) { print("Missing -l argument\n"); $printusage = 1; } |
524 | unless (defined($i)) { print("Missing -i argument\n"); $printusage = 1; } | 546 | unless (defined($i)) { print("Missing -i argument\n"); $printusage = 1; } |
547 | if (defined($t) && defined($f) || | ||
548 | !defined($t) && !defined($f)) { | ||
549 | print("Missing either -t or -f argument\n"); $printusage = 1; | ||
550 | } | ||
525 | } | 551 | } |
526 | elsif (defined($C)) { | 552 | elsif (defined($C)) { |
527 | unless (defined($ARGV[0])) { print "Missing path argument\n"; $printusage = 1; } | 553 | unless (defined($ARGV[0])) { print "Missing path argument\n"; $printusage = 1; } |
@@ -539,7 +565,6 @@ if (defined($v) or defined($ENV{'V'})) { | |||
539 | # add the tools dir to the path temporarily, for calling various tools | 565 | # add the tools dir to the path temporarily, for calling various tools |
540 | $ENV{'PATH'} = dirname($0) . ':' . $ENV{'PATH'}; | 566 | $ENV{'PATH'} = dirname($0) . ':' . $ENV{'PATH'}; |
541 | 567 | ||
542 | |||
543 | # Do what we're told | 568 | # Do what we're told |
544 | if ($V == 1) { | 569 | if ($V == 1) { |
545 | # Only do the panic cleanup for voicefiles | 570 | # Only do the panic cleanup for voicefiles |
@@ -547,10 +572,11 @@ if ($V == 1) { | |||
547 | $SIG{KILL} = \&panic_cleanup; | 572 | $SIG{KILL} = \&panic_cleanup; |
548 | 573 | ||
549 | printf("Generating voice\n Target: %s\n Language: %s\n Encoder (options): %s (%s)\n TTS Engine (options): %s (%s)\n", | 574 | printf("Generating voice\n Target: %s\n Language: %s\n Encoder (options): %s (%s)\n TTS Engine (options): %s (%s)\n", |
550 | $t, $l, $e, $E, $s, $S); | 575 | defined($t) ? $t : "unknown", |
551 | generateclips($l, $t, $e, $E, $s, $S); | 576 | $l, $e, $E, $s, $S); |
552 | createvoice($l, $i); | 577 | generateclips($l, $t, $e, $E, $s, $S, $f); |
553 | deletemp3s(); | 578 | createvoice($l, $i, $f); |
579 | deleteencs(); | ||
554 | } | 580 | } |
555 | elsif ($C) { | 581 | elsif ($C) { |
556 | printf("Generating .talk clips\n Path: %s\n Language: %s\n Encoder (options): %s (%s)\n TTS Engine (options): %s (%s)\n", $ARGV[0], $l, $e, $E, $s, $S); | 582 | printf("Generating .talk clips\n Path: %s\n Language: %s\n Encoder (options): %s (%s)\n TTS Engine (options): %s (%s)\n", $ARGV[0], $l, $e, $E, $s, $S); |