File Coverage

blib/lib/PerlSpeak.pm
Criterion Covered Total %
statement 18 510 3.5
branch 0 328 0.0
condition 0 102 0.0
subroutine 6 30 20.0
pod 0 24 0.0
total 24 994 2.4


line stmt bran cond sub pod time code
1             package PerlSpeak;
2 1     1   25020 use 5.006;
  1         3  
  1         38  
3 1     1   5 use strict;
  1         2  
  1         33  
4 1     1   4 use warnings;
  1         7  
  1         38  
5 1     1   848 use POSIX qw(:termios_h);
  1         7689  
  1         7  
6 1     1   2749 use IO::Socket;
  1         41372  
  1         5  
7 1     1   867 use vars qw($VERSION);
  1         4  
  1         9246  
8             $VERSION = '2.01';
9              
10              
11              
12             sub new {
13 0     0 0   my $pkg = shift;
14 0           my $self = {
15             "tts_engine" => "festival_pipe",
16             "tts_command" => "",
17             "tts_file_command" => "",
18             "file2wave_command" => "",
19             "make_readable" => "[_\/]",
20             "no_dot_files" => 1,
21             "hide_extentions" => 0,
22             "browsable" => 1,
23             "dir_return" => 1,
24             "file_prefix" => "File",
25             "dir_prefix" => "Folder",
26             "echo_off" => 0,
27             "voice" => "kal_diphone",
28             "rate" => 1,
29             "volume" => 1,
30             "pitch" => 50,
31             "lang" => "en",
32             @_};
33 0           return bless $self, $pkg;
34             }
35              
36             sub say {
37 0     0 0   my $self = shift;
38 0           my $arg = shift;
39 0   0       my $rep = shift || " ";
40 0           chomp $arg;
41 0 0         print "\n$arg\n" unless $self->{echo_off};
42 0 0         if ($self->{tts_command}){
    0          
    0          
    0          
    0          
    0          
43 0           my $command = $self->{tts_command};
44 0           $command =~s/text_arg/\"$arg\"/ ;
45 0 0         system $command or die "Error with tts_command";
46             }elsif ($self->{tts_engine} eq "festival"){
47 0           system "echo \"$arg\" | festival --tts";
48             }elsif ($self->{tts_engine} eq "cepstral"){
49 0           system "swift \"$arg\"";
50             }elsif ($self->{tts_engine} eq "espeak"){
51 0           $arg =~s/!/\./g;
52 0           system "echo \"$arg\" | espeak -v $self->{voice} -s $self->{rate} -a $self->{volume} -p $self->{pitch}";
53             }elsif ($self->{tts_engine} eq "festival_server") {
54 0           $arg =~ s/[\n\r"]/$rep/g;
55 0           $self->festival("(let ((utt (Utterance Text \"$arg\"))) (begin ($self->{voice}) (Parameter.set 'Duration_Stretch $self->{rate}) (utt.synth utt) (utt.wave.resample utt 8000) (utt.wave.rescale utt $self->{volume}) (utt.play utt)))\n");
56             }elsif ($self->{tts_engine} eq "festival_pipe") {
57 0 0         return unless $arg;
58 0           system("echo \"(let ((utt (Utterance Text \\\"$arg\\\"))) (begin ($self->{voice}) (Parameter.set 'Duration_Stretch $self->{rate}) (utt.synth utt) (utt.wave.resample utt 8000) (utt.wave.rescale utt $self->{volume}) (utt.play utt)))\" | festival --pipe");
59             }
60             }
61              
62             sub festival {
63 0     0 0   my $self = shift;
64 0           my $arg = shift;
65 0 0         return unless $self->{tts_engine} eq "festival_server";
66 0           $self->{'handle'}->print("$arg\n");
67             }
68              
69             sub config_festival { # voice, rate, volume
70 0     0 0   my $self = shift;
71 0           my $voice = shift;
72 0           my $rate = shift;
73 0           my $vol = shift;
74 0           return $self->config_voice($voice, $rate, $vol);
75             }
76              
77             sub config_voice { # voice, rate, volume, pitch
78 0     0 0   my $self = shift;
79 0           my $voice = shift;
80 0           my $rate = shift;
81 0           my $vol = shift;
82 0   0       my $pitch = shift || 50;
83 0 0         return 0 unless $self->voice($voice);
84 0 0         return 0 unless $self->rate($rate);
85 0 0         return 0 unless $self->volume($vol);
86 0 0         return 0 unless $self->pitch($pitch);
87 0           return 1;
88             }
89              
90              
91             sub voice {
92 0     0 0   my $self = shift;
93 0           my $voice = shift;
94 0 0         $self->{voice} = $voice if $voice;
95 0           return $self->{voice};
96             }
97              
98             sub pitch {
99 0     0 0   my $self = shift;
100 0   0       my $pitch = shift || "50";
101 0 0         $self->{pitch} = $pitch if $pitch;
102 0           return $self->{pitch};
103             }
104              
105             sub rate {
106 0     0 0   my $self = shift;
107 0           my $rate = shift;
108 0 0         $self->{rate} = $rate if $rate;
109 0           return $self->{rate};
110             }
111              
112             sub volume {
113 0     0 0   my $self = shift;
114 0           my $vol = shift;
115 0 0         $self->{volume} = $vol if $vol;
116 0           return $self->{volume};
117             }
118              
119             sub get_voices {
120 0     0 0   my $self = shift;
121 0           my $line = "";
122 0           my @lst = ();
123 0           my @voice_lst = ();
124 0 0 0       if (($self->{tts_engine} eq "festival_server") || ($self->{tts_engine} eq "festival_pipe")) {
    0          
125 0 0         return unless $self->{handle}->connected();
126            
127 0 0         die "can't fork: $!" unless defined(my $kidpid = fork());
128              
129 0 0         if ($kidpid) {
130             # parent copies the socket to standard output
131 0           while ($line !~ /voices/) {
132 0           $line = $self->{handle}->getline;
133             }
134 0           $line =~ s/[()\n\r]//g;
135 0           @lst = split " ", $line;
136 0           foreach (@lst) {
137 0 0         next if /\.|1|voices/;
138 0 0         push @voice_lst, $_ if /\w_\w/;
139             }
140              
141 0           kill("TERM" => $kidpid); # send SIGTERM to child
142 0           return \@voice_lst;
143             }
144             else {
145 0           $self->{handle}->print("voice-locations\n");
146             }
147             } elsif ($self->{tts_engine} eq "espeak") {
148 0           my @tmp = `espeak --voices=$self->{lang}`;
149 0           foreach my $line (@tmp) {
150 0 0         next if $line =~ /ender/;
151 0           $line =~ s/^ //;
152 0           my @word = split /\s/, $line;
153 0           foreach (@word) {
154 0 0         if (/\//) {
155 0           push @voice_lst, $_;
156 0           last;
157             }
158             }
159             }
160 0           return \@voice_lst;
161             }
162             }
163              
164             sub festival_connect {
165 0     0 0   my $self = shift;
166 0 0         if ($self->{handle}) {
167 0 0         return 1 if $self->{handle}->connected();
168             }
169 0   0       $self->{host} = shift || "127.0.0.1";
170 0   0       $self->{port} = shift || 1314;
171 0 0         $self->{handle} = IO::Socket::INET->new(Proto => "tcp",
172             PeerAddr => $self->{host},
173             PeerPort => $self->{port})
174             or die "
175             Can't connect to port $self->{port} on $self->{host}: $!
176             (Are you sure the server is running and accepting connections?)
177              
178             ";
179 0           return $self->{handle};
180             }
181              
182             sub tts_engine {
183 0     0 0   my $self = shift;
184 0 0         if (my $tts = shift) {
185 0           $self->{tts_engine} = $tts;
186             }
187 0           return $self->{tts_engine};
188             }
189              
190             sub readfile {
191 0     0 0   my $self = shift;
192 0           my $arg = shift;
193 0 0         if (-e $arg){
194 0 0 0       if ($self->{tts_file_command}){
    0          
    0          
    0          
    0          
195 0           my $command = $self->{tts_file_command};
196 0           $command =~s/file_arg/$arg/;
197 0           system $command;
198             }elsif ($self->{tts_engine} eq "festival"){
199 0           system "festival --tts $arg";
200             }elsif ($self->{tts_engine} eq "cepstral"){
201 0           system "$self->{path_to_tts}swift -f $arg";
202             }elsif ($self->{tts_engine} eq "espeak"){
203 0           system "espeak -f $arg";
204             }elsif (($self->{tts_engine} eq "festival_server") or ($self->{tts_engine} eq "festival_pipe")) {
205 0 0         open FH, "$arg" or die "ERROR! Could not open $arg: $!\n";
206 0           my $txt = "";
207 0           while () {
208 0           $txt .= $_;
209             }
210 0           $txt =~ s/[\n\r"`]/ /g;
211 0           close FH;
212 0           $self->say($txt);
213             }else {
214 0           $self->say("ERROR! with tts engine or tts file command.") & die "ERROR! with tts_engine or tts_file_command.";
215             }
216             } else {
217 0           $self->say("ERROR! $arg is not a file.") & die "ERROR! $arg is not a file.";
218             }
219             }
220              
221             sub file2wave {
222 0     0 0   my $self = shift;
223 0           my $in = shift;
224 0           my $out = shift;
225 0 0         my $play = shift or 1;
226 0 0         if (-e $in){
227 0 0         if ($self->{file2wave_command}){
    0          
    0          
    0          
    0          
    0          
228 0           my $command = $self->{file2wave_command};
229 0           $command =~s/IN/$in/;
230 0           $command =~s/OUT/$out/;
231 0           system "$command";
232             } elsif ($self->{tts_engine} eq "festival") {
233 0           system "text2wave -otype riff -o $out $in";
234             } elsif ($self->{tts_engine} eq "cepstral") {
235 0           system "swift -m text -f $in -o $out";
236             } elsif ($self->{tts_engine} eq "espeak") {
237 0           print "espeak -f $in -w $out\n";
238 0           system "espeak -f $in -w $out";
239             } elsif ($self->{tts_engine} eq "festival_server") {
240 0           $self->file2wave_festival($in, $out, " ", $play);
241             } elsif ($self->{tts_engine} eq "festival_pipe") {
242 0           $self->say("ERROR! TTS engine festival_pipe cannot convert text to wave files. Use TTS engine festival_server instead.");
243             }
244             } else {
245 0           $self->say("ERROR! $in is not a file.") & die "ERROR! $in is not a file.";
246             }
247             }
248              
249             sub file2wave_festival {
250 0     0 0   my $self = shift;
251 0           my $in = shift;
252 0           my $out = shift;
253 0           my $rep = shift;
254 0           my $play = shift;
255 0 0         $rep = " " unless $rep;
256              
257 0           my ($host, $port, $kidpid, $handle, $line, $remains, $result);
258              
259 0           my $wave_type = "riff"; # the type of the audio files
260 0           my $file_stuff_key = "ft_StUfF_key"; # defined in speech tools
261              
262             # tell the server to send us back a 'file' of the right type
263 0           $self->festival("(Parameter.set 'Wavefiletype '$wave_type)");
264              
265             # split the program into two processes, identical twins
266 0 0         die "can't fork: $!" unless defined($kidpid = fork());
267              
268             # the if{} block runs only in the parent process
269 0 0         if ($kidpid) {
270             # the parent handles the input so it can exit on quit
271 0           undef $line;
272 0   0       while (($line = $remains) || defined ($line = $self->{handle}->getline())) {
273 0           undef $remains;
274 0 0         if ($line eq "WV\n") { # we have a waveform coming
275 0           undef $result;
276 0 0         if ($out) {
277 0           open(AUDIO, ">$out");
278             } else {
279 0           die "ERROR! No output file argument";
280             }
281 0           while ($line = $self->{handle}->getline()) {
282 0 0         if ($line =~ s/$file_stuff_key(.*)$//s) {
283 0           $remains = $1;
284 0           print AUDIO $line;
285 0           last;
286             }
287 0           print AUDIO $line;
288             }
289 0           close AUDIO;
290 0           last;
291             }
292             }
293 0           kill("TERM" => $kidpid); # send SIGTERM to child
294 0 0         system("mplayer $out") if $play;
295             } else {
296 0           my $txt = "";
297 0 0         open FH, "$in" or die "ERROR! Could not open $in: $!\n";
298 0           while () {
299 0           $txt .= $_;
300             }
301 0           $txt =~ s/[\n\r"]/$rep/g;
302 0           close FH;
303 0           $self->festival("(let ((utt (Utterance Text \"$txt\"))) (begin ($self->{voice}) (Parameter.set 'Duration_Stretch $self->{rate}) (utt.synth utt) (utt.wave.resample utt 8000) (utt.wave.rescale utt $self->{volume}) (utt.send.wave.client utt)))");
304             }
305             }
306              
307              
308              
309              
310             sub menu {
311 0     0 0   my $self = shift;
312 0           my $count = shift;
313 0           my @var = @_;
314 0 0         if ($#var % 2 == 0) {
315 0           unshift @var, $count;
316 0           $count = 0;
317             }
318 0           my %var_hash = @var;
319 0           my @keys = sort(keys %var_hash);
320 0           my $str = "";
321 0           my $command = "";
322 0           while (not $command){
323 0           $self->say($keys[$count]);
324 0           my $answ = $self->getch();
325 0 0 0       if (ord($answ)==27){
    0 0        
    0 0        
    0          
326 0           $str = "";
327 0           $answ = $self->getch();
328 0 0         if (ord($answ)==91){
329 0           $answ = $self->getch();
330 0 0         $count++ if $answ =~/B/;
331 0 0         $count-- if $answ =~/A/;
332 0 0         $count = 0 if $count == scalar(@keys);
333 0 0         $count = scalar(@keys) - 1 if $count < 0;
334             }
335              
336             } elsif ((ord($answ)==10) or (ord($answ)==13) or ($answ =~ /[yY]/)){
337 0           $command = 1;
338 0           &{$var_hash{$keys[$count]}};
  0            
339             } elsif (($answ =~ /\d/) and ($str eq "")) {
340 0           $count = $answ -1;
341 0           $command = 1;
342             #&{$var_hash{$keys[$count]}};
343             } elsif ($answ =~ /\w/) {
344 0           $str .= uc $answ;
345 0           foreach my $i (0..$#keys) {
346 0           my $test = uc $keys[$i];
347 0 0 0       $count = $i and last if ($test =~ /^\d\. $str/);
348             }
349             }
350             }
351 0           return $count;
352             }
353              
354             sub menu_list {
355 0     0 0   my $self = shift;
356 0           my @lst = @_;
357 0           my $count = 0;
358 0           my $str = "";
359 0           while (1) {
360 0           $self->say($lst[$count]);
361 0           my $answ = $self->getch();
362 0 0 0       if (ord($answ)==27){
    0 0        
    0 0        
363 0           $str = "";
364 0           $answ = $self->getch();
365 0 0         if (ord($answ)==91){
366 0           $answ = $self->getch();
367 0 0         $count++ if $answ =~/B/;
368 0 0         $count-- if $answ =~/A/;
369 0 0         $count = 0 if $count > $#lst;
370 0 0         $count = $#lst if $count < 0;
371             }
372             } elsif ((ord($answ)==10) or (ord($answ)==13) or (ord($answ)==89) or (ord($answ)==121)){
373 0           last;
374             } elsif ($answ =~ /\w/) {
375 0           $str .= lc $answ;
376 0           $count = 0;
377 0           foreach (@lst) {
378 0           my $test = lc $_;
379 0 0         if ($test =~ /^$str/) {
380 0           last;
381             } else {
382 0           $count++;
383 0 0         $count = $#lst if $count > $#lst;
384             }
385             }
386             }
387             }
388 0           return $lst[$count];
389             }
390              
391             sub filepicker {
392 0     0 0   my $self = shift;
393 0           my $d = shift;
394 0           my $file = "";
395 0           my $flter = "";
396 0           my $answ = "";
397 0           my @tmp = ();
398 0           my @lst = ();
399 0           while (not $file) {
400 0           my $count = 0;
401 0 0         opendir DH, $d or die("Error opening directory: $d\n $!");
402 0 0         my @dirlst = (sort readdir DH) or die("Error reading directory: $d\n $!");
403 0           my $od = $d;
404 0   0       while ((not $file) and ($od eq $d)) {
405 0           my $f = $dirlst[$count];
406 0 0 0       if (($f eq ".") or ($f eq "..") or ($self->{no_dot_files} and $f =~/^\./)) {
      0        
      0        
407 0           $count++;
408 0           next;
409             }
410 0 0         if (-d"$d/$f"){
    0          
411 0           $flter = $f;
412 0           $flter =~ s/_/ /g;
413 0           $self->say("$self->{dir_prefix} $flter?");
414 0           $answ = $self->getch();
415 0 0         if ($answ =~ /[a-zA-Z0-9]/){
416 0           for( my $c = 0; $c < $#dirlst; $c++) {
417 0 0         if ($dirlst[$c] =~ /^$answ/) {
418 0           $count = $c;
419 0           last;
420             }
421             }
422 0           next;
423             }
424 0 0 0       if (ord($answ)==27){
    0 0        
    0 0        
      0        
425 0           $answ = $self->getch();
426 0 0         if (ord($answ)==91){
427            
428 0           $answ = $self->getch();
429 0 0         $count++ if $answ =~/B/;
430 0 0         $count-- if $answ =~/A/;
431 0 0         $count = 0 if $count == scalar(@dirlst);
432 0 0         $count = scalar(@dirlst) - 1 if $count < 0;
433 0 0 0       if (($answ =~/C/) && ($self->{browsable})) {
434 0           $d = "$d/$f";
435 0           last;
436             }
437 0 0 0       if (($answ =~/D/) && ($self->{browsable})) {
438 0           @lst = split '/', $d;
439 0           pop @lst;
440 0           $d = join '/', @lst;
441 0 0         $d = '/' if $d eq "";
442 0           next;
443             }
444             }
445             }elsif ((ord($answ)==10) or (ord($answ)==13) or (ord($answ)==89) or (ord($answ)==121)){
446 0           $file = "$d/$f";
447 0           return $file;
448             }elsif ((ord($answ)==85) or (ord($answ)==117)){
449 0           @lst = split '/', $d;
450 0           pop @lst;
451 0           $d = join '/', @lst;
452 0 0         $d = '/' if $d eq "";
453 0           next;
454             }
455 0           }elsif (-f "$d/$f"){
456 0           $flter = $f;
457 0 0         if ($self->{hide_extentions}){
458 0           $flter =~ s/\.[\w]*$//;
459             }
460 0 0         if ($self->{make_readable}) {
461 0           my $pattern = $self->{make_readable};
462 0           $flter =~ s/$pattern/ /g;
463             }
464 0           $self->say("$self->{file_prefix} $flter?");
465 0           $answ = $self->getch();
466 0 0         if ($answ =~ /[a-zA-Z0-9]/){
467 0           for( my $c = 0; $c < $#dirlst; $c++) {
468 0 0         if ($dirlst[$c] =~ /^$answ/) {
469 0           $count = $c;
470 0           last;
471             }
472             }
473 0           next;
474             }
475 0 0 0       if (ord($answ)==27){
    0 0        
476 0           $answ = $self->getch();
477 0 0         if (ord($answ)==91){
478 0           $answ = $self->getch();
479 0 0         $count++ if $answ =~/B/;
480 0 0         $count-- if $answ =~/A/;
481 0 0         $count = 0 if $count == scalar(@dirlst);
482 0 0         $count = scalar(@dirlst) - 1 if $count < 0;
483 0 0 0       if (($answ =~/C/) && ($self->{browsable})) {
484 0           $file = "$d/$f";
485 0           last;
486             }
487 0 0 0       if (($answ =~/D/) && ($self->{browsable})) {
488 0           @lst = split '/', $d;
489 0           pop @lst;
490 0           $d = join '/', @lst;
491 0 0         $d = '/' if $d eq "";
492 0           next;
493             }
494             }
495             }elsif ((ord($answ)==10) or (ord($answ)==89) or (ord($answ)==121)){
496 0           $file = "$d/$f";
497 0           return $file;
498 0           last;
499             }
500             }else{print "Error $d/$f";}
501             }
502 0           closedir DH;
503             }
504 0           return $file;
505             }
506              
507              
508             sub dirpicker {
509 0     0 0   my $self = shift;
510 0           my $d = shift;
511 0           my $folder = "";
512 0           my $answ = "";
513 0           my @lst = ();
514 0           while ($folder eq "") {
515 0           my $count = 0;
516 0 0         opendir DH, $d or die("Error opening directory: $d\n $!");
517 0 0         my @dirlst = (sort readdir DH) or die("Error reading directory: $d\n $!");
518 0           closedir DH;
519 0           while ($folder eq "") {
520 0           my $f = $dirlst[$count];
521 0 0 0       if (($f eq ".") or ($f eq "..") or ($self->{no_dot_files} and $f =~/^\./)) {
      0        
      0        
522 0           $count++;
523 0           next;
524             }
525 0 0         if (-d"$d/$f"){
526 0           $self->say($f);
527 0           $answ = $self->getch();
528 0 0 0       if (ord($answ)==27){
    0 0        
    0 0        
529 0           $answ = $self->getch();
530 0 0         if (ord($answ)==91){
531 0           $answ = $self->getch();
532 0 0         $count++ if $answ =~/B/;
533 0 0         $count-- if $answ =~/A/;
534 0 0         $count = 0 if $count == scalar(@dirlst);
535 0 0         $count = scalar(@dirlst) - 1 if $count < 0;
536 0 0         if ($answ =~/C/){
537 0           $folder = $self->dirpicker("$d/$f");
538             }
539 0 0         if ($answ =~/D/){
540 0           @lst = split '/', $d;
541 0           pop @lst;
542 0           $d = join '/', @lst;
543 0 0         $d = '/' if $d eq "";
544 0           last;
545             }
546              
547             }
548             }elsif ((ord($answ)==10) or (ord($answ)==89) or (ord($answ)==121)){
549 0           $folder = "$d/$f";
550             }elsif ((ord($answ)==85) or (ord($answ)==117)){
551 0           @lst = split '/', $d;
552 0           pop @lst;
553 0           $d = join '/', @lst;
554 0 0         $d = '/' if $d eq "";
555 0           last;
556             }
557              
558             }else{
559 0           $count++;
560 0 0         if ($count > $#dirlst) {
561 0           $self->say("There are no folders in this directory. Moving up one level.");
562 0           @lst = split '/', $d;
563 0           pop @lst;
564 0           $d = join '/', @lst;
565 0 0         $d = '/' if $d eq "";
566 0           last;
567             }
568 0           next;
569             }
570             }
571             }
572 0           return $folder;
573             }
574              
575             sub fileselect {
576 0     0 0   my $self = shift;
577 0           my $dir = shift;
578 0           my @prompt = @_;
579 0 0         $prompt[0] = "Enter a file filter" unless $prompt[0];
580 0 0         $prompt[1] = "Press F1 for help" unless $prompt[1];
581 0 0         $prompt[2] = "Spacebar Selects or Deselects a file... Press Control-A to select all... Press enter key when done... Press F1 for help" unless $prompt[2];
582 0           chdir $dir;
583 0           my $filter = $self->getString($prompt[0], 1);
584 0           my @lst = `ls $filter`;
585 0 0         unless ($lst[0]) {
586 0           $self->say("No Files Found.");
587 0           return 0;
588             }
589 0           my @counts;
590 0           my $count = 0;
591 0           my $str = "";
592 0           my $speech_flag = 1;
593 0 0         $self->say($prompt[1]) if $prompt[1];
594 0           while (1) {
595 0           my $fname = "";
596 0           chomp $lst[$count];
597 0           print "$dir/$lst[$count]\n";
598 0 0         if (-d "$dir/$lst[$count]") {
    0          
599 0           print "DIR\n";
600 0           $fname = "$self->{dir_prefix} $lst[$count]";
601             } elsif (-f "$dir/$lst[$count]") {
602 0           print "FILE\n";
603 0           $fname = "$self->{file_prefix} $lst[$count]";
604             }
605 0 0         $self->say($fname) if $speech_flag;
606 0           my $answ = $self->getch();
607 0 0 0       if ($answ eq " ") { # Select or Deselect a file
    0          
    0          
    0          
    0          
608 0           push @counts, $count;
609 0           $speech_flag = 0;
610             } elsif (ord($answ)==1) { # Control-A
611 0           @counts = ();
612 0           my $i;
613 0           foreach (@lst) {
614 0           push @counts, $i++;
615             }
616 0           $speech_flag = 0;
617             } elsif (ord($answ)==27){
618 0           $str = "";
619 0           $answ = $self->getch();
620 0 0         if (ord($answ)==91){
621 0           $answ = $self->getch();
622 0 0         $count++ if $answ =~/B/;
623 0 0         $count-- if $answ =~/A/;
624 0 0         $count = 0 if $count > $#lst;
625 0 0         $count = $#lst if $count < 0;
626 0           $speech_flag = 1;
627 0 0         if (ord($answ)==49){
628 0           $a = $self->getch();
629 0 0         $b = $self->getch() if ord($a) == 49;
630 0 0         if (ord($b) == 126) { # F1 pressed
631 0 0         if ($prompt[2] ne 'F1') {
632 0           $self->say($prompt[2]);
633             } else {
634 0           return '^F1 Pressed^';
635             }
636             }
637             }
638             }
639             } elsif ((ord($answ)==10) or (ord($answ)==13)) {
640 0           last;
641             } elsif ($answ =~ /\w/) {
642 0           $str .= lc $answ;
643 0           $count = 0;
644 0           foreach (@lst) {
645 0           my $test = lc $_;
646 0 0         if ($test =~ /^$str/) {
647 0           last;
648             } else {
649 0           $count++;
650 0 0         $count = $#lst if $count > $#lst;
651             }
652             }
653 0           $speech_flag = 1;
654             }
655             }
656 0           my %hash;
657             my @file_list;
658 0           foreach (@counts) {
659 0           $hash{$_}++;
660             }
661 0           foreach (keys %hash) {
662 0 0         if ($hash{$_} % 2 == 1) { # File is selected
663 0           push @file_list, $lst[$_];
664             }
665             }
666 0           return @file_list;
667             }
668              
669              
670             sub getch {
671 0     0 0   my $self = shift;
672 0           my $fd_stdin = fileno(STDIN);
673 0           my $term = POSIX::Termios->new();
674 0           $term->getattr($fd_stdin);
675 0           my $oterm = $term->getlflag();
676 0           my $echo = ECHO | ECHOK | ICANON;
677 0           my $noecho = $oterm & ~$echo;
678 0           my $key = '';
679 0           $term->setlflag($noecho);
680 0           $term->setcc(VTIME, 1);
681 0           $term->setattr($fd_stdin, TCSANOW);
682 0           sysread(STDIN, $key, 1);
683 0           $term->setlflag($oterm);
684 0           $term->setcc( VTIME, 0);
685 0           $term->setattr($fd_stdin, TCSANOW);
686 0           return $key;
687             }
688              
689             sub getString {
690 0     0 0   my $self = shift;
691 0           my $prompt = shift;
692 0           my $no_confirm = shift;
693 0 0         $self->say($prompt) if $prompt;
694 0           my $ord = 0;
695 0           my $string;
696             my @chrlst;
697 0           while (1){
698 0           my $chr = $self->getch();
699 0           $ord = ord($chr);
700 0 0         if ($ord == 127) {
    0          
    0          
    0          
    0          
    0          
701 0           pop @chrlst;
702 0           $self->say("Backspace");
703             } elsif ($ord == 32) {
704 0           $self->say("Space");
705 0           push @chrlst, $chr;
706             } elsif ($ord == 46) {
707 0           $self->say("dot");
708 0           push @chrlst, $chr;
709             } elsif ($ord == 45) {
710 0           $self->say("dash");
711 0           push @chrlst, $chr;
712             } elsif ($ord == 10){
713 0           last;
714             } elsif ($ord < 28) {
715 0           return $ord;
716             } else {
717 0           $self->say($chr);
718 0           push @chrlst, $chr;
719             }
720             }
721            
722 0           $string = join '', @chrlst;
723 0           chomp $string;
724 0 0         if ($no_confirm){
725 0           return $string;
726             } else {
727 0           $self->say("You have entered $string. Is this correct?");
728 0 0         $self->confirm() ? return $string : return $self->getString($prompt);
729             }
730             }
731              
732             sub confirm {
733 0     0 0   my $self = shift;
734 0           my $txt = shift;
735 0 0         $self->say($txt) if $txt;
736 0           my $answ = $self->getch();
737 0 0         return 1 if $answ =~/[yY\n]/;
738 0 0         return 0 if $answ =~/[nN]/;
739 0           $self->say("Please answer Y for yes or N for no.");
740 0           return $self->confirm();
741             }
742              
743             sub getType {
744 0     0 0   my $self = shift;
745 0           my $fname=shift;
746 0           my %Type = (
747             'HTML', "text/html",
748             'HTM', "text/html",
749             'STM', "text/html",
750             'SHTML', "text/html",
751             'TXT', "text/plain",
752             'PREF', "text/plain",
753             'AIS', "text/plain",
754             'RTX', "text/richtext",
755             'TSV', "text/tab-separated-values",
756             'NFO', "text/warez-info",
757             'ETX', "text/x-setext",
758             'SGML', "text/x-sgml",
759             'SGM', "text/x-sgml",
760             'TALK', "text/x-speech",
761             'CGI', "text/plain", # we want these two as text files
762             'PL', "text/plain", # and not application/x-httpd-cgi
763             'PHP', "text/plain",
764             #-----------------------------------------
765             'COD', "image/cis-cod",
766             'FID', "image/fif",
767             'GIF', "image/gif",
768             'ICO', "image/ico",
769             'IEF', "image/ief",
770             'JPEG', "image/jpeg",
771             'JPG', "image/jpeg",
772             'JPE', "image/jpeg",
773             'PNG', "image/png",
774             'TIF', "image/tiff",
775             'TIFF', "image/tiff",
776             'MCF', "image/vasa",
777             'RAS', "image/x-cmu-raster",
778             'CMX', "image/x-cmx",
779             'PCD', "image/x-photo-cd",
780             'PNM', "image/x-portable-anymap",
781             'PBM', "image/x-portable-bitmap",
782             'PGM', "image/x-portable-graymap",
783             'PPM', "image/x-portable-pixmap",
784             'RGB', "image/x-rgb",
785             'XBM', "image/x-xbitmap",
786             'XPM', "image/x-xpixmap",
787             'XWD', "image/x-xwindowdump",
788             #------------------------------------------
789             'BZ2', "application/x-bzip2",
790             'EXE', "application/octet-stream",
791             'BIN', "application/octet-stream",
792             'DMS', "application/octet-stream",
793             'LHA', "application/octet-stream",
794             'CLASS', "application/octet-stream",
795             'DLL', "application/octet-stream",
796             'AAM', "application/x-authorware-map",
797             'AAS', "application/x-authorware-seg",
798             'AAB', "application/x-authorware-bin",
799             'VMD', "application/vocaltec-media-desc",
800             'VMF', "application/vocaltec-media-file",
801             'ASD', "application/astound",
802             'ASN', "application/astound",
803             'DWG', "application/autocad",
804             'DSP', "application/dsptype",
805             'DFX', "application/dsptype",
806             'EVY', "application/envoy",
807             'SPL', "application/futuresplash",
808             'IMD', "application/immedia",
809             'HQX', "application/mac-binhex40",
810             'CPT', "application/mac-compactpro",
811             'DOC', "application/msword",
812             'ODA', "application/oda",
813             'PDF', "application/pdf",
814             'AI', "application/postscript",
815             'EPS', "application/postscript",
816             'PS', "application/postscript",
817             'PPT', "application/powerpoint",
818             'RTF', "application/rtf",
819             'APM', "application/studiom",
820             'XAR', "application/vnd.xara",
821             'ANO', "application/x-annotator",
822             'ASP', "application/x-asap",
823             'CHAT', "application/x-chat",
824             'BCPIO', "application/x-bcpio",
825             'VCD', "application/x-cdlink",
826             'TGZ', "application/x-compressed",
827             'Z', "application/x-compress",
828             'CPIO', "application/x-cpio",
829             'PUZ', "application/x-crossword",
830             'CSH', "application/x-csh",
831             'DCR', "application/x-director",
832             'DIR', "application/x-director",
833             'DXR', "application/x-director",
834             'FGD', "application/x-director",
835             'DVI', "application/x-dvi",
836             'LIC', "application/x-enterlicense",
837             'EPB', "application/x-epublisher",
838             'FAXMGR', "application/x-fax-manager",
839             'FAXMGRJOB', "application/x-fax-manager-job",
840             'FM', "application/x-framemaker",
841             'FRAME', "application/x-framemaker",
842             'FRM', "application/x-framemaker",
843             'MAKER', "application/x-framemaker",
844             'GTAR', "application/x-gtar",
845             'GZ', "application/x-gzip",
846             'HDF', "application/x-hdf",
847             'INS', "application/x-insight",
848             'INSIGHT', "application/x-insight",
849             'INST', "application/x-install",
850             'IV', "application/x-inventor",
851             'JS', "application/x-javascript",
852             'SKP', "application/x-koan",
853             'SKD', "application/x-koan",
854             'SKT', "application/x-koan",
855             'SKM', "application/x-koan",
856             'LATEX', "application/x-latex",
857             'LICMGR', "application/x-licensemgr",
858             'MAIL', "application/x-mailfolder",
859             'MIF', "application/x-mailfolder",
860             'NC', "application/x-netcdf",
861             'CDF', "application/x-netcdf",
862             'SDS', "application/x-onlive",
863             'SGI-LPR', "application/x-sgi-lpr",
864             'SH', "application/x-sh",
865             'SHAR', "application/x-shar",
866             'SWF', "application/x-shockwave-flash",
867             'SPRITE', "application/x-sprite",
868             'SPR', "application/x-sprite",
869             'SIT', "application/x-stuffit",
870             'SV4CPIO', "application/x-sv4cpio",
871             'SV4CRC', "application/x-sv4crc",
872             'TAR', "application/x-tar",
873             'TARDIST', "application/x-tardist",
874             'TCL', "application/x-tcl",
875             'TEX', "application/x-tex",
876             'TEXINFO', "application/x-texinfo",
877             'TEXI', "application/x-texinfo",
878             'T', "application/x-troff",
879             'TR', "application/x-troff",
880             'TROFF', "application/x-troff",
881             'MAN', "application/x-troff-man",
882             'ME', "application/x-troff-me",
883             'MS', "application/x-troff-ms",
884             'TVM', "application/x-tvml",
885             'TVM', "application/x-tvml",
886             'USTAR', "application/x-ustar",
887             'SRC', "application/x-wais-source",
888             'WKZ', "application/x-wingz",
889             'ZIP', "application/x-zip-compressed",
890             'ZTARDIST', "application/x-ztardist",
891             #-------------------------------------
892             'AU', "audio/basic",
893             'SND', "audio/basic",
894             'ES', "audio/echospeech",
895             'MID', "audio/midi",
896             'KAR', "audio/midi",
897             'MPGA', "audio/mpeg",
898             'MP2', "audio/mpeg",
899             'TSI', "audio/tsplayer",
900             'VOX', "audio/voxware",
901             'AIF', "audio/x-aiff",
902             'AIFC', "audio/x-aiff",
903             'AIFF', "audio/x-aiff",
904             'MID', "audio/x-midi",
905             'MP3', "audio/x-mpeg",
906             'MP2A', "audio/x-mpeg2",
907             'MPA2', "audio/x-mpeg2",
908             'M3U', "audio/x-mpegurl",
909             'MP3URL', "audio/x-mpegurl",
910             'PAT', "audio/x-pat",
911             'RAM', "audio/x-pn-realaudio",
912             'RPM', "audio/x-pn-realaudio-plugin",
913             'RA', "audio/x-realaudio",
914             'SBK', "audio/x-sbk",
915             'STR', "audio/x-str",
916             'WAV', "audio/x-wav",
917             #-------------------------------------
918             'MPEG', "video/mpeg",
919             'MPG', "video/mpeg",
920             'MPE', "video/mpeg",
921             'QT', "video/quicktime",
922             'MOV', "video/quicktime",
923             'VIV', "video/vivo",
924             'VIVO', "video/vivo",
925             'MPS', "video/x-mpeg-system",
926             'SYS', "video/x-mpeg-system",
927             'MP2V', "video/x-mpeg2",
928             'MPV2', "video/x-mpeg2",
929             'AVI', "video/x-msvideo",
930             'MV', "video/x-sgi-movie",
931             'MOVIE', "video/x-sgi-movie",
932             #-----------------------------------------
933             'PDB', "chemical/x-pdb",
934             'XYZ', "chemical/x-pdb",
935             'CHM', "chemical/x-cs-chemdraw",
936             'SMI', "chemical/x-daylight-smiles",
937             'SKC', "chemical/x-mdl-isis",
938             'MOL', "chemical/x-mdl-molfile",
939             'RXN', "chemical/x-mdl-rxn",
940             'SMD', "chemical/x-smd",
941             'ACC', "chemical/x-synopsys-accord",
942             'ICE', "x-conference/x-cooltalk",
943             'SVR', "x-world/x-svr",
944             'WRL', "x-world/x-vrml",
945             'VRML', "x-world/x-vrml",
946             'VRJ', "x-world/x-vrt",
947             'VRJT', "x-world/x-vrt",
948             #-----------------------------------
949             'ASX', "video/x-ms-asf",
950             'WMA', "audio/x-ms-wma",
951             'WAX', "audio/x-ms-wax",
952             'WMV', "audio/x-ms-wmv",
953             'WVX', "video/x-ms-wvx",
954             'WM', "video/x-ms-wm",
955             'WMX', "video/x-ms-wmx",
956             'WMZ', "application/x-ms-wmz",
957             'WMD', "application/x-ms-wmd",
958             #------------------------------------
959             'ODT', "application/vnd.oasis.opendocument.text",
960             'OTT', "application/vnd.oasis.opendocument.text-template",
961             'OTH', "application/vnd.oasis.opendocument.text-web",
962             'ODM', "application/vnd.oasis.opendocument.text-master",
963             'ODG', "application/vnd.oasis.opendocument.graphics",
964             'OTG', "application/vnd.oasis.opendocument.graphics-template",
965             'ODP', "application/vnd.oasis.opendocument.presentation",
966             'OTP', "application/vnd.oasis.opendocument.presentation-template",
967             'ODS', "application/vnd.oasis.opendocument.spreadsheet",
968             'OTS', "application/vnd.oasis.opendocument.spreadsheet-template",
969             'ODC', "application/vnd.oasis.opendocument.chart",
970             'ODF', "application/vnd.oasis.opendocument.formula",
971             'ODB', "application/vnd.oasis.opendocument.database",
972             'ODI', "application/vnd.oasis.opendocument.image",
973             );
974 0           my @tmp = split(/\./, $fname);
975 0           my $ext = pop @tmp;
976 0           $ext = uc $ext;
977 0 0         $Type{$ext}?return $Type{$ext}:return "unknown/unknown";
978             }
979              
980              
981             1;
982              
983             __END__