After a multimeasure-rest correctly set mopen
[pae2xml.git] / pae2xml.pl
blob212fa61f10365ad09ce46dd3a55787992adb4bb4
1 #!/usr/bin/perl
4 # Copyright (C) 2003 Rainer Typke
5 #pae2xml is licensed under the terms of the GNU General Public License Version
6 #2 as published by the <a href="http://www.fsf.org/" target="_top">Free Software Foundation</a>.
7 #This gives you legal permission to copy, distribute and/or modify <em>pae2xml</em> under
8 #certain conditions. Read
9 #the <a href="http://www.gnu.org/copyleft/gpl.html" target="_top">online version of the license</a>
10 #for more details. pae2xml is provided AS IS with NO WARRANTY OF ANY KIND,
11 #INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE.
15 $divisions = 960;
16 $old_duration = $divisions;
17 $old_octave = 4;
19 ($mday, $mon, $year) = (localtime()) [3..5];
20 $encoding_date = sprintf("%4d-%02d-%02d", $year + 1900, $mon+1, $mday);
22 $TIE = 0;
24 foreach $a (@ARGV) {
25 $p = read_file($a);
26 $toprint = "";
27 $p =~ s/\s*\=\=+\s*(.*?)\s*\=\=+\s*/$1/sg;
28 $p =~ s/\s*included.*?-------------*\s*(.*?)\s*/$1/s;
30 ($q, $r) = ($p, $p);
31 if ($q !~ /^.*1\.1\.1.*$/gsx && $r =~ /^.*plain.*$/gsx) {
32 print_error("$a contains 'plain', but not 1.1.1!\n");
33 } else {
34 if ($p =~ /^\s*([^\n]+)\n(.*?)\n((\d+\.\d+\.\d.*?plain.*?\n)+)(.*?)\n?([^\n]+)\n([^\n]+)\s*$/gs) {
35 my ($comp, $title, $incipits, $sonst, $libsig, $rismsig) = ($1, $2, $3, $5, $6, $7);
37 $toprint .= "
38 COMPOSER: $comp
39 TITLE: $title
40 INCIPIT(S): $incipits
41 OTHER INFO: $sonst
42 LIB. SIGN.: $libsig
43 RISM SIGN.: $rismsig\n\n";
44 parse_incipits($incipits, $comp, $title, $sonst, $libsig, $rismsig);
46 else {
47 if (index($p,"plain&easy") > -1) {
48 print_error("Ignoring the following text:\n\n\n$p\n\n\n");
56 sub parse_incipits {
57 my ($incipits, $comp, $title, $sonst, $libsig, $rismsig) = @_;
59 $toprint .= "parsing: $incipits\n";
61 while ($incipits =~ /^(\d+\.\d+\..+?)(\d+\.\d+\..*)$/gs) {
62 my ($inc1) = $1;
63 $incipits = $2;
64 parse_pe($inc1, $comp, $title, $sonst, $libsig, $rismsig);
66 parse_pe($incipits, $comp, $title, $sonst, $libsig, $rismsig);
69 sub parse_pe {
70 my ($pe, $comp, $title, $sonst, $libsig, $rismsig) = @_;
72 $pe =~ s/@ü/@0ü/gs; # make missing time signature explicit
73 while ($pe =~ s/([^\-])(\d+)(\'|\,)(A|B|C|D|E|F|G)/$1$3$2$4/gs) {}; # octave first, then duration. Truly global.
75 if ($pe =~ /^\s*(\d+\.\d+\.\d)(\.|:)\s*(.*?)\nplain&easy:\s*(%([\w\-\d]+))?(@([\d\w\/]+))?\s*&?\s*(\$([^ü]+))(.*)$/gs) {
76 my ($inr, $instr, $clef, $timesig, $keysig, $rest) = ($1, $3, $5, $7, $9, $10);
78 my $filename="$rismsig-$inr.xml";
79 $filename =~ s/RISM\s*A\/II\s*:?\s*//gs;
80 print "Writing $filename...\n";
82 open(OUT, ">$filename");
84 if ($clef =~ /^(\w)\-(\d)$/) {
85 ($clefsign, $clefline) = ($1, $2);
86 } else {
87 ($clefsign, $clefline) = ("G", 2);
90 $timesig = timesignature($timesig);
92 my %fif=("", 0, "xF", 1, "xFC", 2, "xFCG",3, "xFCGD",4, "xFCGDA",5, "xFCGDAE",6, "xFCGDAEB",7, "bB",-1, "bBE",-2, "bBEA",-3, "bBEAD",-4, "bBEADG",-5, "bBEADGC",-6, "bBEADGCF",-7);
93 $keysig =~ s/(\s+)|&//gs; # it is unclear what the & means, so we'll ignore it for now.
94 $keysig =~ s/\[|\]//gs; # IGNORING brackets around a key sig.
95 $fifths = $fif{$keysig};
96 if ($fifths eq "") { $fifths = "0";
97 print_error("Strange key signature '$keysig'.\n");}
99 foreach $_ ($rismsig,$title,$inr,$instr,$comp,$encoding_date,$libsig,$sonst)
104 print OUT '<?xml version="1.0" encoding="iso-8859-1" standalone="no"?>
105 <!DOCTYPE score-partwise PUBLIC "-//Recordare//DTD MusicXML 0.6 Partwise//EN" "file:/c:/Program Files/MusicXML/partwise.dtd">
106 <score-partwise>
107 <work>
108 <work-number>'.$rismsig.'</work-number>
109 <work-title>'.$title.'</work-title>
110 </work>
111 <movement-number>'.$inr.'</movement-number>
112 <movement-title>'.$instr.'</movement-title>
113 <identification>
114 <creator type="composer">'.$comp.'</creator>
115 <encoding>
116 <software>pae2xml by R. Typke</software>
117 <encoding-date>'.$encoding_date.'</encoding-date>
118 </encoding>
119 <source>'.$libsig.'</source>
120 </identification>
121 <part-list>
122 <score-part id="P1">
123 <part-name>'.$sonst.'</part-name>
124 </score-part>
125 </part-list>
126 <part id="P1">
127 <measure number="1">
128 <attributes>
129 <divisions>'.$divisions.'</divisions>
130 <key>
131 <fifths>'.$fifths.'</fifths>
132 </key>
133 '.$timesig.'
134 <clef>
135 <sign>'.$clefsign.'</sign>
136 <line>'.$clefline.'</line>
137 </clef>
138 </attributes>
142 $toprint .= "
143 INCIPIT NO.: $inr
144 INSTR.: $instr
145 CLEF: $clef
146 KEY SIG.: $keysig
147 TIME SIG.: $timesig
148 REST: $rest\n";
149 parse_notes($rest, $keysig);
151 else { print_error("could not parse $pe\n"); }
152 print OUT " </part>
153 </score-partwise>\n";
154 close OUT;
158 sub parse_notes {
159 my ($notes, $keysig) = @_;
160 my $qq = 0; # in group of cue notes
162 my $meas = 2; # measure number
163 my $mopen = 1; # measure tag still open
165 if ($notes =~ /^\s*(.*?)\s*$/) {
166 $notes = $1;
169 $notes =~ s/!([^!]*)!/$1$1/gs; # write out repetitions
170 $notes =~ s/\{([^\}]*)\}/$1/gs; # ignore beamings
171 while ( $notes =~ s/(:?\/+:?|^)([^\/:]*)(:?\/+:?)i(:?\/+:?)/$1$2$3$2$4/gs) {}; # replace whole-measure repeats (i notation)
173 $notes =~ s/(\d+)\(([^;]+\))/\($1$2/gs; # pull note lengths into fermatas or triplets
174 $notes =~ s/(xx|x|bb|b|n)\(/\($1/gs; # pull accidentals into tuplets or fermatas:
175 $notes =~ s/(\d+)(xx|x|bb|b|n)(A|B|C|D|E|F|G)/$2$1$3/gs; # accidentals first, then duration
177 # $notes =~ s/x\(/\(x/gs; # pull accidentals into tuplets or fermatas
178 # $notes =~ s/bb\(/\(bb/gs; # pull accidentals into tuplets or fermatas
179 # $notes =~ s/b\(/\(b/gs; # pull accidentals into tuplets or fermatas
180 # $notes =~ s/n\(/\(n/gs; # pull accidentals into tuplets or fermatas
181 # $notes =~ s/(\'+|\,+)\(/\($1/g; # pull octave marks into tuplets or fermatas
183 $notes =~ s/(\.|\d|\,|\')qq/qq$1/gs; # pull beginning mark of group of grace notes in front of corresponding notes
184 $notes =~ s/(xx|x|bb|b|n)qq/qq$1/gs; # qq first, then parts of notes
186 $notes =~ s/\=(\d)/$1/gs; # replace multibar rests #n with just n
188 while ($notes ne "") {
189 if ($notes =~ /^(\'+|\,+)(.*)$/) { # Octave marks
190 ($oct, $notes) = ($1, $2);
191 octave($oct);
192 } elsif ($notes =~ /^qq(.*)$/) { # Begin grace
193 $notes = $1;
194 $qq = 1;
195 } elsif ($notes =~ /^r(.*)$/) { # End grace
196 $notes = $1;
197 $qq = 0;
198 } elsif ($notes =~ /^(\d+|\=)(\/.*)$/) { # normal and multi-measure rests
199 $measrest = $1;
200 $notes = $2;
201 if ($measrest eq '=') {
202 $measrest = 1;
204 $toprint .= "$measrest measures of rest.\n";
205 for $n (1..$measrest) {
206 print OUT ' <note>
207 <rest />
208 <duration>'.($beats*$divisions*4/$beattype).'</duration>
209 '.# <type>quarter</type>
211 </note>
213 if ($n < $measrest) {
214 print OUT " </measure>\n";
215 if ($notes ne "") {
216 print OUT ' <measure number="'.$meas.'">
218 $meas++;
219 } else {
220 $mopen = 0;
224 } elsif ($notes =~ /^((\,|\')*(x|xx|b|bb|n)?\d*\.*(g|q)?(\-|A|B|C|D|E|F|G)t?\+?)(.*)$/) { # a note
225 ($note, $notes) = ($1,$6);
226 parse_note($note, $keysig, "", "", $qq);
227 } elsif ($notes =~ /^(\((\,|\')*(x|xx|b|bb|n)?\d*\.*(g|q)?(\-|A|B|C|D|E|F|G)t?\+?\))(.*)$/) { # one note with a fermata
228 ($note, $notes) = ($1,$6);
229 parse_note($note, $keysig, "", "", $qq);
230 } elsif ($notes =~ /^(\(((\,|\')*(x|xx|b|bb|n)?\d*\.*(g|q)?(\-|A|B|C|D|E|F|G)t?\+?){3}\))(.*)$/) { # a triplet
231 ($triplet, $notes) = ($1,$7);
232 # print "TRIPLET: ".$triplet." -> ";
233 $triplet =~ /^\(((\,|\')*(x|xx|b|bb|n)?\d*\.*(g|q)?(\-|A|B|C|D|E|F|G)t?\+?)(.*)\)$/gs;
234 ($note, $triplet) = ($1,$6);
235 #print "$note $triplet\n";
236 parse_note($note, $keysig, '<tuplet type="start"/>', ' <time-modification>
237 <actual-notes>3</actual-notes>
238 <normal-notes>2</normal-notes>
239 </time-modification>', $qq);
240 $triplet =~ /^((\,|\')*(x|xx|b|bb|n)?\d*\.*(g|q)?(\-|A|B|C|D|E|F|G)t?\+?)(.*)$/gs;
241 ($note, $triplet) = ($1,$6);
242 #print "$note $triplet\n";
243 parse_note($note, $keysig, '', ' <time-modification>
244 <actual-notes>3</actual-notes>
245 <normal-notes>2</normal-notes>
246 </time-modification>', $qq);
247 parse_note($triplet, $keysig, '<tuplet type="stop"/>', ' <time-modification>
248 <actual-notes>3</actual-notes>
249 <normal-notes>2</normal-notes>
250 </time-modification>', $qq);
251 } elsif ($notes =~ /^((\d+)\(((\,|\')*(x|xx|b|bb|n)?\d*\.*(g|q)?(\-|A|B|C|D|E|F|G)t?\+?)+\;(\d+)\))(.*)$/) { # an n-tuplet
252 ($tuplet, $notes) = ($1,$9);
253 # print "N-TUPLET: ".$tuplet." -> ";
254 $tuplet =~ /^(\d+)\(((\,|\')*(x|xx|b|bb|n)?\d*\.*(g|q)?(\-|A|B|C|D|E|F|G)t?\+?)(.*);(\d)\)$/gs;
255 ($combdur, $note, $tuplet, $numval) = ($1,$2,$7,$8);
256 #print "i=$combdur, n=$numval; $note / $tuplet\n";
257 my $ind_dur = duration($combdur)/$numval;
258 # my $norm_notes =
259 my $act_notes = $numval;
260 parse_note($note, $keysig, '<tuplet type="start"/>', ' <time-modification>
261 <actual-notes>'.$act_notes.'</actual-notes>
262 <normal-notes>1</normal-notes>
263 </time-modification>', $qq);
264 while ($tuplet =~ /^((\,|\')*(x|xx|b|bb|n)?\d*\.*(g|q)?(\-|A|B|C|D|E|F|G)t?\+?)(.+)$/gs) {
265 ($note, $tuplet) = ($1,$6);
266 #print "$note / $tuplet\n";
267 parse_note($note, $keysig, '', ' <time-modification>
268 <actual-notes>'.$act_notes.'</actual-notes>
269 <normal-notes>1</normal-notes>
270 </time-modification>', $qq);
272 parse_note($tuplet, $keysig, '<tuplet type="stop"/>', ' <time-modification>
273 <actual-notes>'.$act_notes.'</actual-notes>
274 <normal-notes>1</normal-notes>
275 </time-modification>', $qq);
276 } elsif ($notes =~ /^(%\w-\d)(.*)$/) { # Clef change
277 ($clef,$notes) = ($1,$2);
278 $clef =~ /^%(\w)\-(\d)$/;
279 ($clefsign, $clefline) = ($1, $2);
280 print OUT ' <attributes>
281 <clef>
282 <sign>'.$clefsign.'</sign>
283 <line>'.$clefline.'</line>
284 </clef>
285 </attributes>
287 } elsif ($notes =~ /^@(\d\/\d|c\/?)\s*(.*)$/) { # time signatue change
288 # print "$notes\n";
289 ($timesig,$notes) = ($1,$2);
290 #print "-> $timesig / $notes\n"; exit;
291 $timesig = timesignature($timesig);
292 print OUT " <attributes>\n$timesig
293 </attributes>\n";
294 } elsif ($notes =~ /^(:?\/+:?)(.*)$/) { # Barline (and repeats)
295 $barline = $1;
296 $notes = $2;
297 if ($barline =~ /^:\/\/:/) {
298 print OUT ' <barline location="right">
299 <bar-style>light-light</bar-style>
300 <repeat direction="backward"/>
301 </barline>
303 } elsif ($barline =~ /^:\/\/$/ ) {
304 print OUT ' <barline location="right">
305 <bar-style>light-heavy</bar-style>
306 <repeat direction="backward"/>
307 </barline>
309 } elsif ($barline =~ /^\/\/$/) {
310 print OUT ' <barline location="right">
311 <bar-style>light-light</bar-style>
312 <repeat direction="backward"/>
313 </barline>
316 print OUT " </measure>\n";
317 if ($notes ne "") {
318 print OUT ' <measure number="'.$meas.'">
320 if ($barline =~ /^\/\/:$/) {
321 print OUT ' <barline location="left">
322 <bar-style>heavy-light</bar-style>
323 <repeat direction="forward"/>
324 </barline>
326 } elsif ($barline =~ /^:\/\/:$/) {
327 print OUT ' <barline location="left">
328 <repeat direction="forward"/>
329 </barline>
332 print OUT $clefattr;
333 $meas++;
334 } else {
335 $mopen = 0;
337 $toprint .= "bar line\n";
338 } #elsif ($notes =~ /^(\d*\.*\-)(.*)$/) {
339 #($rst, $notes) = ($1, $2);
340 #$toprint .= "rest: $rst\n";
341 #$rst =~ /^(\d*)(\.*)\-$/;
342 #($rst, $dots) =($1,$2);
343 #print OUT ' <note>
344 # <rest />
345 # <duration>'.duration($rst, $dots).'</duration>
346 #'.# <type>quarter</type>
348 # </note>
350 elsif ($notes =~ /^\((\=)\)(.*)$/) { # a bar of rest with a fermata
351 ($rst, $notes) = ($1, $2);
352 $toprint .= "rest: $rst\n";
353 print OUT ' <note>
354 <rest />
355 <duration>'.($beats*$divisions*4/$beattype).'</duration>
356 '.# <type>quarter</type>
357 ' <notations>
358 <fermata type="upright"/>
359 </notations>
360 </note>
363 elsif ($notes =~ s/(\d+\.*)\(((\,|\')*(x|xx|b|bb|n)?\d*\.*(g|q)?(\-|A|B|C|D|E|F|G)t?\+?)\)/\($1$2\)/gs) { # pull duration into fermata parentheses
364 # print "after replacement: $notes\n"; exit;
366 else {
367 print_error("got stuck with $notes\n");
368 $notes = "";
371 if ($mopen) {
372 print OUT " </measure>\n";
377 sub parse_note {
378 my($note, $keysig, $notation, $addition, $in_qq_group) = @_;
380 my ($fermata) = (0);
381 my ($actualnotes, $normalnotes) = (1,1);
383 if ($addition =~ /^\s*<time-modification>\s*<actual-notes>\s*(\d+)\s*<\/actual-notes>\s*<normal-notes>\s*(\d+)\s*<\/normal-notes>\s*<\/time-modification>\s*$/) {
384 ($actualnotes, $normalnotes) = ($1, $2);
387 if ($note =~ /^\((.*)\)$/) {
388 $note = $1;
389 $fermata = 1;
392 $note =~ /^((\,|\')*)(x|xx|b|bb|n)?(\d*)(\.*)(g|q)?(\-|A|B|C|D|E|F|G)(t?)(\+?)$/;
393 my ($oct, $acc, $dur, $dot, $gracecue, $pitch, $trill, $tie) = ($1, $3, $4, $5, $6, $7, $8, $9);
395 print OUT ' <note>
397 if ($gracecue eq "g") {
398 print OUT ' <grace steal-time-following="33"/>
401 if ($gracecue eq "q" || $in_qq_group) {
402 print OUT ' <cue/>
405 if ($pitch eq "-") {
406 print OUT " <rest />\n";
407 } else {
408 print OUT ' <pitch>
409 <step>'.$pitch.'</step>
411 alter($pitch, $acc, $keysig).'
412 <octave>'.octave($oct).'</octave>
413 </pitch>
416 if ($gracecue ne "g") {
417 print OUT ' <duration>'.(duration($dur, $dot)*$normalnotes/$actualnotes).'</duration>
420 # <type>quarter</type>
422 if ($tie eq "+") {
423 if (!$TIE) {
424 $TIE = 1;
425 print OUT '<tie type="start"/>
428 } else {
429 if ($TIE) {
430 print OUT '<tie type="stop"/>
432 $TIE = 0;
436 print OUT $addition;
438 my $notationbracket = $fermata || ($trill eq "t") || ($notation ne "");
439 if ($notationbracket) {
440 print OUT " <notations>\n";
442 if ($fermata) { print OUT '
443 <fermata type="upright"/>'."\n"; }
444 if ($trill eq "t") { print OUT ' <ornaments>
445 <trill-mark/>
446 </ornaments>
449 if ($notation ne "") {
450 print OUT " $notation\n";
452 if ($notationbracket) {
453 print OUT " </notations>\n";
456 print OUT ' </note>
459 $toprint .= "note: oct. $oct/acc. $acc/dur. $dur/dots $dot/grace,cue $gracecue/pitch $pitch\n";
462 sub alter {
463 my ($pitch, $acc, $keysig) = @_;
465 my $alt = 0;
467 if (index ($keysig,$pitch) > -1) {
468 $keysig =~ /^(.).*$/gs;
469 if ($1 eq 'x') {
470 $alt = 1;
471 } else {$alt = -1;}
474 my %acc_alt = ("n", 0, "b", -1, "bb", -2, "x", 1, "xx", 2);
475 if ($acc_alt{$acc} ne "") {
476 $alt = $acc_alt{$acc};
479 if ($alt != 0) {
480 return "<alter>$alt</alter>\n";
482 return "";
485 sub duration {
486 my ($duration, $dots) = @_;
488 if ($duration.$dots ne "") {
489 my %du=("1",4*$divisions,"2",2*$divisions,"4",$divisions,
490 "8",$divisions/2,"6",$divisions/4,"3",$divisions/8,
491 "5",$divisions/16,"7",$divisions/32,
492 "9",$divisions*8,"0",$divisions*16); # breve/long
493 $old_duration = $du{$duration};
494 if ($old_duration eq "") {
495 print_error("strange duration '$duration'\n");
497 my $add = $old_duration;
498 while ($dots ne "") {
499 $add /= 2;
500 $old_duration += $add;
501 $dots =~ /^.(.*)$/gs;
502 $dots = $1;
505 return $old_duration;
508 sub octave {
509 my ($octave) = @_;
511 if ($octave ne "") {
512 $octave =~ /^(.)(.*)$/gs;
513 if ($1 eq ",") {
514 $old_octave = 4 - length $octave;
515 } else {
516 $old_octave = 3 + length $octave;
519 return $old_octave;
522 sub timesignature {
523 my ($timesig) = @_;
525 if ($timesig eq "c3") {
526 $timesig = "3/2"; # it would be better to display it as "C". Example: 451.023.814
528 if ($timesig =~ /^c(\d+)\/(\d+)$/gs) {
529 $timesig = "$1/$2"; # it would be better to show the "C"
532 if ($timesig eq "0" || $timesig eq "") { # unclear how to handle absence of time signature.
533 $timesig ='<time symbol="common">
534 <beats>4</beats>
535 <beat-type>4</beat-type>
536 </time>
537 '; # using 4/4 for now.
538 ($beats, $beattype) = (4,4);
539 } elsif ($timesig =~ /^c(\/?)$/gi) {
540 if ($1 eq "/") {
541 $timesig = '<time symbol="cut">
542 <beats>2</beats>
543 <beat-type>2</beat-type>
544 </time>
546 ($beats, $beattype) = (2,2);
547 } else {
548 $timesig = '<time symbol="common">
549 <beats>4</beats>
550 <beat-type>4</beat-type>
551 </time>
553 ($beats, $beattype) = (4,4);
555 } elsif ($timesig =~ /^(\d+)\/(\d+)$/gs) {
556 ($beats, $beattype) = ($1, $2);
557 $timesig = '<time>
558 <beats>'.$beats.'</beats>
559 <beat-type>'.$beattype.'</beat-type>
560 </time>
562 } else {
563 print_error("Time signature '$timesig' looks strange.\n");
564 # $timesig = ""; we assume 4/4 just to get something legible:
565 ($beats, $beattype) = (4,4);
566 $timesig = '<time>
567 <beats>'.$beats.'</beats>
568 <beat-type>'.$beattype.'</beat-type>
569 </time>
572 return $timesig;
575 sub print_error {
576 my ($msg) = @_;
578 print "\nAn error occurred; context:\n\n$toprint\n
579 Error: $msg\n";
582 sub read_file {
583 my ($fn) = @_;
584 my $res = "";
585 if ($fn eq "-") {
586 while (<STDIN>) { $res .= $_; } # read all lines
587 } else {
588 if (!(open FH, $fn)) {
589 return "";
591 while (<FH>) { $res .= $_; } # read all lines
592 close (FH);
594 return $res;