/[mbrola]/trunk/mbrola.pl
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Annotation of /trunk/mbrola.pl

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2 - (hide annotations)
Sat Aug 5 12:13:01 2006 UTC (13 years, 9 months ago) by dpavlin
File MIME type: text/plain
File size: 9411 byte(s)
double characters now extend duraton of than phoneme, moved recovery to inside loop
1 dpavlin 1 #!/usr/bin/perl -w
2    
3     # mbrola.pl
4     #
5     # 08/04/2006 07:54:54 PM CEST Dobrica Pavlinusic <dpavlin@rot13.org>
6    
7     use strict;
8     use Data::Dump qw/dump/;
9     use Term::ReadLine;
10    
11     my $term = new Term::ReadLine 'Simple Perl calc';
12     my $prompt = "Prièaj: ";
13     my $OUT = $term->OUT || \*STDOUT;
14    
15    
16     my $letters = {
17     'a' => [['a'],
18     [[170,]]],
19     'A' => [['a'],
20     [[170,]]],
21     'b' => [['b', 'e', 'b', 'e',],
22     [[90,], [15,], [1,], [150,]]],
23     'B' => [['b', 'e', 'b', 'e',],
24     [[90,], [15,], [1,], [150,]]],
25     'c' => [['ts', 'a'],
26     [[30,], [100,]]],
27     'C' => [['ts', 'a'],
28     [[30,], [100,]]],
29     'è' => [['tS', 'a'],
30     [[30,], [100,]]],
31     'È' => [['tS', 'a'],
32     [[30,], [100,]]],
33     'æ' => [['tS\'', 'a'],
34     [[30,], [100,]]],
35     'Æ' => [['tS\'', 'a'],
36     [[30,], [100,]]],
37     'd' => [['d', 'e', 'd', 'e'],
38     [[70,], [15,], [60,], [1, 90, 100]]],
39     'D' => [['d', 'e', 'd', 'e'],
40     [[70,], [15,], [60,], [1, 90, 100]]],
41     'ð'=> [['dZ\'', 'a'],
42     [[100,], [110,]]],
43     'Ð'=> [['dZ\'', 'a'],
44     [[100,], [110,]]],
45     'e' => [['e', 'x', 'e'],
46     [[150, 10, 100], [1,], [50, 50, 90]]],
47     'E' => [['e', 'x', 'e'],
48     [[150, 10, 100], [1,], [50, 50, 90]]],
49     'f' => [['f', 'e'],
50     [[100, 10, 100], [100,]]],
51     'F' => [['f', 'e'],
52     [[100, 10, 100], [100,]]],
53     'g' => [['g', 'a'],
54     [[70,], [150,]]],
55     'G' => [['g', 'a'],
56     [[70,], [150,]]],
57     #'h' => [['x', 'r', 'a'],
58     # [[120,], [1,], [170,]]],
59     'h' => [['x', 'a', 'x', 'a'],
60     [[150,], [170,], [1,], [170,]]],
61     'H' => [['x', 'a', 'x', 'a'],
62     [[150,], [170,], [1,], [170,]]],
63     'i' => [['i'],
64     [[150,]]],
65     'I' => [['i'],
66     [[150,]]],
67     'j' => [['j', 'a', 'r'],
68     [[60,], [10,], [50,]]],
69     'J' => [['j', 'a', 'r'],
70     [[60,], [10,], [50,]]],
71     'k' => [['k', 'a'],
72     [[70,], [150,]]],
73     'K' => [['k', 'a'],
74     [[70,], [150,]]],
75     #'l' => [['l', 'e'],
76     # [[100, 10, 100], [100,]]],
77     'l' => [['e', 'l'],
78     [[100,], [100,10, 100]]],
79     'L' => [['e', 'l'],
80     [[100,], [100,10, 100]]],
81     #'m' => [['m', 'e'],
82     # [[90,], [50,]]],
83     'm' => [['e', 'm'],
84     [[50,], [90,]]],
85     'M' => [['e', 'm'],
86     [[50,], [90,]]],
87     #'n' => [['n', 'e'],
88     # [[90,], [50,]]],
89     'n' => [['e', 'n'],
90     [[50,], [90,]]],
91     'N' => [['e', 'n'],
92     [[50,], [90,]]],
93     'o' => [['o', 'x'],
94     [[71,], [70,]]],
95     'O' => [['o', 'x'],
96     [[71,], [70,]]],
97     #'p' => [['p', 'a'],
98     # [[70,], [150,]]],
99     'p' => [['p', 'e'],
100     [[90,], [120,]]],
101     'P' => [['p', 'e'],
102     [[90,], [120,]]],
103     #'r' => [['r', 'x'],
104     # [[90,], [110,]]],
105     'r' => [['e', 'r', 'x'],
106     [[150,10,100], [90,], [110,]]],
107     'R' => [['e', 'r', 'x'],
108     [[150,10,100], [90,], [110,]]],
109     's' => [['s', 'a'],
110     [[90,], [110,]]],
111     'S' => [['s', 'a'],
112     [[90,], [110,]]],
113     '¹' => [['S', 'a'],
114     [[70,], [110,]]],
115     '©' => [['S', 'a'],
116     [[70,], [110,]]],
117     #'t' => [['t', 'a'],
118     # [[90,], [120,]]],
119     't' => [['t', 'e'],
120     [[90,], [150,]]],
121     'T' => [['t', 'e'],
122     [[90,], [150,]]],
123     'u' => [['u'],
124     [[170,]]],
125     'U' => [['u'],
126     [[170,]]],
127     'v' => [['v', 'a'],
128     [[100,], [120,]]],
129     'V' => [['v', 'a'],
130     [[100,], [120,]]],
131     'z' => [['z', 'a'],
132     [[100,], [110,]]],
133     'Z' => [['z', 'a'],
134     [[100,], [110,]]],
135     '¾' => [['Z', 'a'],
136     [[100,], [110,]]],
137     '®' => [['Z', 'a'],
138     [[100,], [110,]]],
139     'x' => [['i', 'k', 's'],
140     [[60,], [60,], [50,]]],
141     'X' => [['i', 'k', 's'],
142     [[60,], [60,], [50,]]],
143     'y' => [['i', 'p', 's', 'i', 'l', 'o', 'n', 'e'],
144     [[60, 10, 90, 70, 110],
145     [70, 10, 100],
146     [50,], [40,], [60,], [70,], [70,], [10,]]],
147     'Y' => [['i', 'p', 's', 'i', 'l', 'o', 'n', 'e'],
148     [[60, 10, 90, 70, 110],
149     [70, 10, 100],
150     [50,], [40,], [60,], [70,], [70,], [10,]]],
151     'q' => [['k', 'u', 'x'],
152     [[70,], [110,], [1,]]],
153     'Q' => [['k', 'u', 'x'],
154     [[70,], [110,], [1,]]],
155     'w' => [['d', 'u', 'p', 'l', 'o', '_', 'v', 'e'],
156     [[54, 50, 100], [50, 50, 110], [85, 10, 100], [35,],
157     [54, 90, 95], [10,], [100, 90, 110], [120, 20, 95]]],
158     'W' => [['d', 'u', 'p', 'l', 'o', '_', 'v', 'e'],
159     [[54, 50, 100], [50, 50, 110], [85, 10, 100], [35,],
160     [54, 90, 95], [10,], [100, 90, 110], [120, 20, 95]]]}
161     ;
162    
163     my $phonemes = {
164     'a' => 'a', 'b' => 'b', 'c' => 'ts', 'è' => 'tS', 'æ' => 'tS\'', 'd' => 'd',
165     'd¾' => 'dZ', 'ð'=> 'dZ\'', 'e' => 'e', 'f' => 'f', 'g' => 'g', 'h' => 'x',
166     'i' => 'i', 'j' => 'j', 'k' => 'k', 'l' => 'l', 'lj'=> 'L', 'm' => 'm',
167     'n' => 'n', 'nj'=> 'J', 'o' => 'o', 'p' => 'p', 'r' => 'r', 's' => 's',
168     '¹' => 'S', 't' => 't', 'u' => 'u', 'v' => 'v', 'z' => 'z', '¾' => 'Z'
169     };
170    
171     my $durations = {
172     'a' => 61, 'b' => 65, 'ts' => 113, 'tS' => 90, 'tS\''=> 98, 'd' => 54,
173     'dZ' => 56, 'dZ\''=> 61, 'e' => 53, 'f' => 86, 'g' => 56, 'x' => 68,
174     'i' => 49, 'j' => 53, 'k' => 81, 'l' => 35, 'L' => 59, 'm' => 56,
175     'n' => 45, 'J' => 60, 'o' => 54, 'p' => 85, 'r' => 25, 's' => 91,
176     'S' => 99, 't' => 76, 'u' => 50, 'v' => 40, 'z' => 68, 'Z' => 74,
177     # pause
178     '_' => 150 }
179     ;
180    
181     my $token_to_grapheme = {
182     'x' => 'ks',
183     'q' => 'k',
184     'w' => 'v',
185     'y' => 'j',
186     };
187    
188     my $silence = {
189     'word' => 40,
190     'sent' => 100,
191     'comma' => 180,
192     'hard' => 100,
193     'spell' => 150
194     };
195    
196     my $recovery;
197     foreach my $df (qw/ bp oo uks/) {
198     $recovery->{$df}++;
199     }
200    
201     sub speak_hr {
202    
203     my $text = shift @_ || 'ovo je na¹a difonska sinteza govora. kako vam se sviða? mo¾da èak i radi!';
204    
205     my $first = 1;
206     my $zarez = 0;
207    
208     my $speed = 1.2;
209    
210     # FIXME: lj, nj, d¾
211     my @chars = split(//, $text);
212    
213     my @pho = ({ char => '_', dur => [50] });
214    
215     my ($g,$f) = ('','');
216    
217 dpavlin 2 my $last_c = '';
218    
219 dpavlin 1 foreach my $i ( 0 .. $#chars ) {
220     my $c = $chars[$i];
221    
222     $g .= $c;
223    
224     if (defined( $phonemes->{$c} )) {
225     $c = $phonemes->{$c};
226     } elsif (defined( $token_to_grapheme->{$c} )) {
227     $c = $token_to_grapheme->{$c};
228     } else {
229     $pho[ $#pho ]->{dur}->[0] = $silence->{word};
230     next;
231     }
232    
233     my $d = $durations->{$c} || $silence->{word};
234    
235     my @dur = ( $d );
236    
237     if ($first) {
238     $first = 0;
239    
240     push @dur, ( 10, 120 );
241     }
242    
243     if ($zarez) {
244     $zarez = 0;
245     push @dur, ( 10, 100 );
246     }
247    
248    
249     if ($c =~ m/[,\.!\? _]/) {
250    
251     my $from = $#{ $pho[ $i - 1 ]->{dur} };
252     $from = 3 if ($from > 3);
253    
254     my $tmpr = 80;
255    
256     foreach my $j ( -($from) .. 0 ) {
257     $pho[ $i - 1 ]->{dur}->[$j] = $tmpr;
258     $tmpr += 30;
259     }
260    
261     if ($c =~ m/,/) {
262     $zarez = 1;
263     @dur = ( $silence->{comma} );
264     } else {
265     @dur = ( $silence->{sentence} );
266     $first = 1;
267     }
268    
269     }
270    
271 dpavlin 2 # same last chars? double duration
272     if ($last_c eq $c) {
273     $pho[ $#pho ]->{dur}->[0] *= 2;
274     next;
275     }
276    
277     # fixup sequences that need special handling
278     if (defined($recovery->{ $last_c . $c })) {
279     push @pho, {
280     char => '_',
281     dur => [ $silence->{word} * $speed ],
282     };
283     }
284    
285     $last_c = $c;
286 dpavlin 1 push @pho, {
287     char => $c,
288     dur => \@dur,
289     };
290    
291     $f .= $c;
292     }
293    
294     push @pho, { char => '_', dur => [50] };
295    
296     warn "# pho = ",dump(@pho),$/;
297    
298     my $out;
299    
300     foreach my $p (@pho) {
301     $out .= $p->{char} . ' ' . join(' ', map { $_ * $speed } @{ $p->{dur} }) . "\n";
302     }
303    
304     return ($out, $g, $f);
305     }
306    
307     my $mbrola = './bin/mbrola-linux-i386 ./cr1/cr1';
308    
309     while ( defined ($_ = $term->readline($prompt)) ) {
310    
311     $term->addhistory($_);
312    
313     my ($out,$g,$f) = speak_hr( $_ );
314    
315     open(my $fh, "| $mbrola - tmp.wav") || die "can't open $mbrola: $!";
316    
317     print $OUT ">>> $g\n>>> $f\n$out\n";
318    
319     print $fh $out || die "can't pipe to $mbrola";
320     close($fh) || die "error closing pipe to $mbrola";
321    
322     system 'play tmp.wav';
323    
324     }
325    
326    
327     __END__
328    
329     foreach my $c (split(//, $text)) {
330     warn "# c = $c\n";
331    
332     if (! defined($letters->{$c})) {
333     print "_ 50\n";
334     next;
335     }
336    
337     my $slova = $letters->{$c}->[0];
338     my $param = $letters->{$c}->[1];
339     warn "# ",dump($slova, $param), $/;
340     foreach my $i ( 0 ... $#$slova ) {
341     print $slova->[$i], " ", join(" ",@{ $param->[$i] }), "\n";
342     }
343    
344     }

Properties

Name Value
svn:executable

  ViewVC Help
Powered by ViewVC 1.1.26