/[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

Contents of /trunk/mbrola.pl

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3 - (show annotations)
Sat Aug 5 12:44:39 2006 UTC (13 years, 9 months ago) by dpavlin
File MIME type: text/plain
File size: 9528 byte(s)
cleanup and re-structure code (fix $speed nonsenese)
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 => [ $silence->{word} ] });
214
215 my ($g,$f) = ('','');
216
217 my $last_c = '';
218
219 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 }
229
230 my $d = $durations->{$c} || $silence->{word};
231
232 my @dur = ( $d );
233
234 if ($last_c =~ m/[,\.!\? _]/) {
235
236 my $from = $#{ $pho[ $i - 1 ]->{dur} };
237 $from = 3 if ($from > 3);
238
239 my $tmpr = 80;
240
241 foreach my $j ( -($from) .. 0 ) {
242 $pho[ $i - 1 ]->{dur}->[$j] = $tmpr;
243 $tmpr += 30;
244 }
245
246 # begining of sentence
247 push @dur, ( 10, 120 );
248 }
249
250 if ($c =~ m/\s/) {
251 $pho[ $#pho ]->{dur}->[0] += $silence->{word};
252 $last_c = $c;
253 next;
254 } elsif ($c =~ m/[\.!\?]/) {
255 $pho[ $#pho ]->{dur}->[0] += $silence->{word};
256 push @pho, {
257 char => '_',
258 dur => [ $silence->{word} ],
259 };
260 $last_c = $c;
261 next;
262 } elsif ($c =~ m/,/) {
263 push @dur, ( 10, 100 );
264 }
265
266 # same last chars? double duration
267 if ($last_c eq $c) {
268 $pho[ $#pho ]->{dur}->[0] *= 2;
269 next;
270 }
271
272 # fixup sequences that need special handling
273 if (defined($recovery->{ $last_c . $c })) {
274 push @pho, {
275 char => '_',
276 dur => [ $silence->{word} ],
277 };
278 }
279
280 $last_c = $c;
281 push @pho, {
282 char => $c,
283 dur => \@dur,
284 };
285
286 $f .= $c;
287 }
288
289 push @pho, { char => '_', dur => [ $silence->{sent} ] };
290
291 # warn "# pho = ",dump(@pho),$/;
292
293 my $out;
294
295 foreach my $p (@pho) {
296 $out .= $p->{char} . ' ' . join(' ', map { $_ * $speed } @{ $p->{dur} }) . "\n";
297 }
298
299 return ($out, $g, $f);
300 }
301
302 my $mbrola = './bin/mbrola-linux-i386 ./cr1/cr1';
303
304 while ( defined ($_ = $term->readline($prompt)) ) {
305
306 $term->addhistory($_);
307
308 my ($out,$g,$f) = speak_hr( $_ );
309
310 open(my $fh, "| $mbrola - tmp.wav") || die "can't open $mbrola: $!";
311
312 print $OUT ">>> $g\n<<< $f\n";
313
314 print $fh $out || die "can't pipe to $mbrola";
315 close($fh) || die "error closing pipe to $mbrola";
316
317 $out =~ s/\n/ | /gs;
318 print $OUT "# $out\n";
319
320 system 'play tmp.wav';
321
322 }
323
324
325 __END__
326
327 foreach my $c (split(//, $text)) {
328 warn "# c = $c\n";
329
330 if (! defined($letters->{$c})) {
331 print "_ 50\n";
332 next;
333 }
334
335 my $slova = $letters->{$c}->[0];
336 my $param = $letters->{$c}->[1];
337 warn "# ",dump($slova, $param), $/;
338 foreach my $i ( 0 ... $#$slova ) {
339 print $slova->[$i], " ", join(" ",@{ $param->[$i] }), "\n";
340 }
341
342 }

Properties

Name Value
svn:executable

  ViewVC Help
Powered by ViewVC 1.1.26