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__