Index: tools/voice.pl =================================================================== --- tools/voice.pl (revision 15234) +++ tools/voice.pl (working copy) @@ -20,7 +20,7 @@ use File::Basename; use File::Copy; use Switch; -use vars qw($V $C $t $l $e $E $s $S $i $v); +use vars qw($V $C $D $t $l $e $E $s $S $i $v); use IPC::Open2; use IPC::Open3; use Digest::MD5 qw(md5_hex); @@ -34,6 +34,9 @@ -C Create .talk clips. + + -D + Create .talk clips for Database. -t= Specify which target you want to build voicefile for. Must include @@ -226,11 +229,11 @@ my ($file, $threshold, $tts_object) = @_; printf("Trim \"%s\"\n", $file) if $verbose; if ($$tts_object{"name"} eq "sapi") { - my $cmd = $$tts_object{"toolspath"}."wavtrim $file $threshold"; + my $cmd = $$tts_object{"toolspath"}."wavtrim \"$file\" $threshold"; print({$$tts_object{"stdin"}} "EXEC\t$cmd\r\n"); } else { - my $cmd = dirname($0) . "/wavtrim $file $threshold"; + my $cmd = dirname($0) . "/wavtrim \"$file\" $threshold"; print("> $cmd\n") if $verbose; `$cmd`; } @@ -244,7 +247,7 @@ printf("Encode \"%s\" with %s in file %s\n", $input, $encoder, $output) if $verbose; switch ($encoder) { case 'lame' { - $cmd = "lame $encoder_opts \"$input\" \"$output\""; + $cmd = "lame -S $encoder_opts \"$input\" \"$output\""; } case 'vorbis' { $cmd = "oggenc $encoder_opts \"$input\" -o \"$output\""; @@ -367,7 +370,116 @@ print($output) if $verbose; } -sub deletemp3s() { +# Read a long or a short from a database file +sub ecread { + my ($fh, $length) = @_; + my $string; + read($fh, $string, $length); + if ($length == 4) { + $string = unpack("V", $string); + } + elsif ($length == 2) { + $string = unpack("v", $string); + } + return $string; +} + +# This function is somwhat stricter than it needs to be +sub fatsafe { + my ($string) = @_; + return $string; +} + +# Must be kept in sync with the implementation in tagtree.c +sub dbtalkname { + my ($string) = @_; + # The 235 is MAX_PATH - strlen(ROCKBOX_DIR "/talkclips/.talk") + $string = sprintf("%.235s", $string); + $string =~ tr/["*\/:<>?\\|]/_/; + $string = $string; +} + +# Generate .talk clips for database +sub databaseclips { + our $verbose; + my ($dir, $language, $encoder, $encoder_opts, $tts_engine, $tts_engine_opts) = @_; + my $i = 0; + + my $tts_object = init_tts($tts_engine, $tts_engine_opts, $language); + + if (! -d sprintf("%s/.rockbox/talkclips", $dir)) { + mkdir(sprintf("%s/.rockbox/talkclips", $dir)); + } + + print("Generating talk clips"); + print("\n") if $verbose; + + # Create clips for all labels in tagnavi.config and tagnavi_custom.config + for my $file (glob(sprintf("%s/.rockbox/tagnavi*.config", $dir))) { + open(TAGNAVI, $file); + while () { + my $line = $_; + if ($line =~ /^"([^"]+)"/) { + my $voice = $1; + $voice = correct_string($voice, $language, $tts_object); + my $mp3 = sprintf("%s/.rockbox/talkclips/%s.talk", $dir, dbtalkname($voice)); + if ( ! -f $mp3) { + my $wav = sprintf("/tmp/%s.wav", dbtalkname($voice)); + voicestring($voice, $wav, $tts_engine_opts, $tts_object); + wavtrim($wav, 500, $tts_object); + encodewav($wav, $mp3, $encoder, $encoder_opts, $tts_object); + unlink($wav); + # Print some progress information + if (++$i % 10 == 0 and !$verbose) { + print("."); + } + } + } + } + close(TAGNAVI); + } + + for my $file (glob(sprintf("%s/.rockbox/database_[0-9]*.tcd", $dir))) { + # Ignore the db with filenames + if ($file eq sprintf("%s/.rockbox/database_4.tcd", $dir)) { + next; + } + open(TCD, $file); + read(TCD, my $magic, 4); + # Add an elsif if the format changes + if ($magic eq pack("V", 0x5443480b)) { + my $datasize = ecread(*TCD, 4); + my $entrycount = ecread(*TCD, 4); + for (my $i = 1; $i <= $entrycount; $i++) { + my $fieldsize = ecread(*TCD, 2); + my $discard = ecread(*TCD, 2); + + read(TCD, my $voice, $fieldsize); + $voice =~ s/\0.*//; + $voice = correct_string($voice, $language, $tts_object); + my $mp3 = sprintf("%s/.rockbox/talkclips/%s.talk", $dir, dbtalkname($voice)); + if ( ! -f $mp3) { + my $wav = sprintf("/tmp/%s.wav", dbtalkname($voice)); + voicestring($voice, $wav, $tts_engine_opts, $tts_object); + wavtrim($wav, 500, $tts_object); + encodewav($wav, $mp3, $encoder, $encoder_opts, $tts_object); + unlink($wav); + # Print some progress information + if (++$i % 10 == 0 and !$verbose) { + print("."); + } + } + } + } + else { + die("Unknown file: $file"); + } + close(TCD); + } + print("\nDone\n"); +} + +sub deletemp3s { for (glob('*.mp3')) { unlink($_); } @@ -383,15 +495,20 @@ # Check parameters my $printusage = 0; -unless (defined($V) or defined($C)) { print("Missing either -V or -C\n"); $printusage = 1; } +unless (defined($V) or defined($C) or defined($D)) { print("Missing either -V, -C or -D\n"); $printusage = 1; } if (defined($V)) { unless (defined($t)) { print("Missing -t argument\n"); $printusage = 1; } unless (defined($l)) { print("Missing -l argument\n"); $printusage = 1; } unless (defined($i)) { print("Missing -i argument\n"); $printusage = 1; } } elsif (defined($C)) { + unless (defined($l)) { print("Missing -l argument\n"); $printusage = 1; } unless (defined($ARGV[0])) { print "Missing path argument\n"; $printusage = 1; } } +elsif (defined($D)) { + unless (defined($l)) { print("Missing -l argument\n"); $printusage = 1; } + unless (defined($ARGV[0])) { print "Missing path argument\n"; $printusage = 1; } +} unless (defined($e)) { print("Missing -e argument\n"); $printusage = 1; } unless (defined($E)) { print("Missing -E argument\n"); $printusage = 1; } unless (defined($s)) { print("Missing -s argument\n"); $printusage = 1; } @@ -407,16 +524,19 @@ # Do what we're told -if ($V == 1) { +if (defined($V)) { printf("Generating voice\n Target: %s\n Language: %s\n Encoder (options): %s (%s)\n TTS Engine (options): %s (%s)\n", $t, $l, $e, $E, $s, $S); generateclips($l, $t, $e, $E, $s, $S); createvoice($l, $i); deletemp3s(); } -elsif ($C) { +elsif (defined($C)) { # xxx: Implement .talk clip generation } +elsif (defined($D)) { + databaseclips($ARGV[0], $l, $e, $E, $s, $S); +} else { printusage(); exit 1; Index: apps/tree.c =================================================================== --- apps/tree.c (revision 15234) +++ apps/tree.c (working copy) @@ -223,11 +223,29 @@ int attr=0; #ifdef HAVE_TAGCACHE bool id3db = *(local_tc->dirfilter) == SHOW_ID3DB; + char clippath[MAX_PATH - 5]; + static const char invalid_chars[] = "\"*/:<>?\\|"; + int i, len; if (id3db) { attr = tagtree_get_attr(local_tc); - name = tagtree_get_entry(local_tc, selected_item)->name; + /* + * Pretend everything in the DB is a file and has a talk clip + * because it makes things easier + */ + attr &= ~ATTR_DIRECTORY; + attr |= FILE_ATTR_THUMBNAIL; + snprintf(clippath, sizeof clippath, ROCKBOX_DIR "/talkclips/%s", + tagtree_get_entry(local_tc, selected_item)->name); + /* Make sure the path doesn't include illegal characters */ + len = strlen(clippath); + for (i=strlen(ROCKBOX_DIR "/talkclips/"); i < len; i++) { + if (strchr(invalid_chars, clippath[i]) != NULL) { + clippath[i] = '_'; + } + } + name = clippath; } else #endif @@ -244,7 +262,7 @@ { if(global_settings.talk_dir_clip) { - DEBUGF("Playing directory thumbnail: %s", local_tc->currdir); + DEBUGF("Playing directory thumbnail: %s\n", local_tc->currdir); did_clip = true; if(ft_play_dirname(name) <0) /* failed, not existing */