Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 352 lines (311 sloc) 12.887 kB
29745ec @colomon Crude first script to translate ABC to Lilypond.
authored
1 use v6;
2 use ABC::Header;
3 use ABC::Tune;
4 use ABC::Grammar;
5 use ABC::Actions;
0d17ae0 @colomon Rename Duration.Str to Duration.duration-to-str (so it can be easily …
authored
6 use ABC::Duration;
7 use ABC::Note;
29745ec @colomon Crude first script to translate ABC to Lilypond.
authored
8
6aeb675 @colomon Add crude paper size control.
authored
9 my $paper-size = "letter"; # or switch to "a4" for European paper
10
945131a @colomon Get the key signature correct (at least for simple key signatures) bu…
authored
11 my %accidental-map = ( '' => "",
12 '=' => "",
13 '^' => "is",
14 '^^' => "isis",
15 '_' => "es",
16 '__' => "eses" );
17
6c5a781 @colomon Add support for 3/4 time, octave modifiers.
authored
18 my %octave-map = ( -1 => "",
19 0 => "'",
20 1 => "''",
21 2 => "'''" );
b73499a @colomon Get the octave right again, delete dead %note-map hash.
authored
22
29745ec @colomon Crude first script to translate ABC to Lilypond.
authored
23 class Context {
c68369e @colomon Tweaks to get inline fields working better, plus more tests for them.
authored
24 has $.key-name;
945131a @colomon Get the key signature correct (at least for simple key signatures) bu…
authored
25 has %.key;
e57f6a8 @colomon Add support for meters other than 4/4.
authored
26 has $.meter;
56c09ce @colomon Support length field and proper final bar line.
authored
27 has $.length;
28 has %.cheat-length-map;
945131a @colomon Get the key signature correct (at least for simple key signatures) bu…
authored
29
56c09ce @colomon Support length field and proper final bar line.
authored
30 method new($key-name, $meter, $length) {
31 my %cheat-length-map;
32 given $length {
33 when "1/8" { %cheat-length-map = ( '/' => "16",
34 "" => "8",
35 "1" => "8",
36 "3/2" => "8.",
37 "2" => "4",
38 "3" => "4.",
39 "4" => "2",
40 "6" => "2.",
41 "8" => "1");
42 }
43 when "1/4" { %cheat-length-map = ( '/' => "8",
44 "" => "4",
45 "1" => "4",
46 "3/2" => "4.",
47 "2" => "2",
48 "3" => "2.",
49 "4" => "1",
50 "6" => "1.");
51 }
52 die "Don't know how to handle note length $length";
53 }
54 self.bless(*, :$key-name,
55 :key(key_signature($key-name)),
56 :$meter,
57 :$length,
58 :%cheat-length-map);
945131a @colomon Get the key signature correct (at least for simple key signatures) bu…
authored
59 }
60
61 method get-real-pitch($nominal-pitch) {
62 my $match = ABC::Grammar.parse($nominal-pitch, :rule<pitch>);
63 if $match<accidental> {
64 $nominal-pitch;
65 } else {
66 ($.key{$match<basenote>.uc} // "") ~ $match<basenote> ~ $match<octave>;
67 }
68 }
29745ec @colomon Crude first script to translate ABC to Lilypond.
authored
69
945131a @colomon Get the key signature correct (at least for simple key signatures) bu…
authored
70 method get-Lilypond-pitch($abc-pitch) {
71 # say :$abc-pitch.perl;
72 my $real-pitch = self.get-real-pitch($abc-pitch);
73 # say :$real-pitch.perl;
74 my $match = ABC::Grammar.parse($real-pitch, :rule<pitch>);
75
b73499a @colomon Get the octave right again, delete dead %note-map hash.
authored
76 my $octave = +((~$match<basenote>) ~~ 'a'..'z');
6c5a781 @colomon Add support for 3/4 time, octave modifiers.
authored
77 given $match<octave> {
78 when /\,/ { $octave -= (~$match<octave>).chars }
79 when /\'/ { $octave += (~$match<octave>).chars }
80 }
945131a @colomon Get the key signature correct (at least for simple key signatures) bu…
authored
81
b73499a @colomon Get the octave right again, delete dead %note-map hash.
authored
82 $match<basenote>.lc ~ %accidental-map{~$match<accidental>} ~ %octave-map{$octave};
945131a @colomon Get the key signature correct (at least for simple key signatures) bu…
authored
83 }
f9cdf54 @colomon Refactor in preparation for handling base note lengths other than 1/8.
authored
84
85 method get-Lilypond-duration(ABC::Duration $abc-duration) {
56c09ce @colomon Support length field and proper final bar line.
authored
86 %.cheat-length-map{$abc-duration.duration-to-str};
f9cdf54 @colomon Refactor in preparation for handling base note lengths other than 1/8.
authored
87 }
e57f6a8 @colomon Add support for meters other than 4/4.
authored
88
138f1a9 @colomon Replace write-meter and write-key with meter-to-string and key-to-str…
authored
89 method meter-to-string() {
90 "\\time $.meter ";
e57f6a8 @colomon Add support for meters other than 4/4.
authored
91 }
2261a59 @colomon Refactor a bit, handle measures with other numbers of eighth notes.
authored
92
56c09ce @colomon Support length field and proper final bar line.
authored
93 method ticks-in-measure() {
2261a59 @colomon Refactor a bit, handle measures with other numbers of eighth notes.
authored
94 given $.meter {
56c09ce @colomon Support length field and proper final bar line.
authored
95 when "C" { 1 / $.length.eval; }
96 $.meter.eval / $.length.eval;
2261a59 @colomon Refactor a bit, handle measures with other numbers of eighth notes.
authored
97 }
98 }
b561dfc @colomon Refactor key signature handling a bit.
authored
99
138f1a9 @colomon Replace write-meter and write-key with meter-to-string and key-to-str…
authored
100 method key-to-string() {
b561dfc @colomon Refactor key signature handling a bit.
authored
101 my $sf = %.key.map({ "{.key}{.value}" }).sort.Str.lc;
102 my $major-key-name;
103 given $sf {
104 when "" { $major-key-name = "c"; }
105 when "f^" { $major-key-name = "g"; }
106 when "c^ f^" { $major-key-name = "d"; }
107 when "c^ f^ g^" { $major-key-name = "a"; }
108 when "c^ d^ f^ g^" { $major-key-name = "e"; }
109 when "a^ c^ d^ f^ g^" { $major-key-name = "b"; }
110 when "a^ c^ d^ e^ f^ g^" { $major-key-name = "fis"; }
111 when "a^ b^ c^ d^ e^ f^ g^" { $major-key-name = "cis"; }
112 when "b_" { $major-key-name = "f"; }
113 when "b_ e_" { $major-key-name = "bes"; }
114 when "a_ b_ e_" { $major-key-name = "ees"; }
115 when "a_ b_ d_ e_" { $major-key-name = "aes"; }
116 when "a_ b_ d_ e_ g_" { $major-key-name = "des"; }
117 when "a_ b_ c_ d_ e_ g_" { $major-key-name = "ges"; }
118 when "a_ b_ c_ d_ e_ f_ g_" { $major-key-name = "ces"; }
119 }
138f1a9 @colomon Replace write-meter and write-key with meter-to-string and key-to-str…
authored
120 "\\key $major-key-name \\major\n";
b561dfc @colomon Refactor key signature handling a bit.
authored
121 }
29745ec @colomon Crude first script to translate ABC to Lilypond.
authored
122 }
123
124 sub HeaderToLilypond(ABC::Header $header) {
125 say "\\header \{";
126
127 my @titles = $header.get("T")>>.value;
70e5a12 @colomon Add "Tali Foster's" to r-star.abc file so we can easily test multiple…
authored
128 say " piece = \"{ @titles[0] }\"";
129 my @composers = $header.get("C")>>.value;
130 say " composer = \"{ @composers[0] }\"" if ?@composers;
29745ec @colomon Crude first script to translate ABC to Lilypond.
authored
131
132 say "}";
133 }
134
102ec2f @colomon Refactor a bunch of the functions into a TuneConvertor class, so it i…
authored
135 class TuneConvertor {
136 has $.context;
7f72e30 @colomon Very weak implementation of tuple that works for the common triplet c…
authored
137
56c09ce @colomon Support length field and proper final bar line.
authored
138 method new($key, $meter, $length) {
139 self.bless(*, :context(Context.new($key, $meter, $length)));
0d17ae0 @colomon Rename Duration.Str to Duration.duration-to-str (so it can be easily …
authored
140 }
102ec2f @colomon Refactor a bunch of the functions into a TuneConvertor class, so it i…
authored
141
142 # MUST: this is context dependent too
143 method Duration($element) {
144 $element.value ~~ ABC::Duration ?? $element.value.ticks !! 0;
145 }
146
147 method StemToLilypond($stem, $suffix = "") {
2f95a2d @colomon Rewrite StemToLilypond to return the string for the note, rather than…
authored
148 given $stem {
149 when ABC::Note {
150 " "
151 ~ $.context.get-Lilypond-pitch($stem.pitch)
152 ~ $.context.get-Lilypond-duration($stem)
f1f1c11 @colomon Add support for ties.
authored
153 ~ ($stem.is-tie ?? '~' !! '')
2f95a2d @colomon Rewrite StemToLilypond to return the string for the note, rather than…
authored
154 ~ $suffix
155 ~ " ";
156 }
157 "";
102ec2f @colomon Refactor a bunch of the functions into a TuneConvertor class, so it i…
authored
158 }
159 }
160
f133cf1 @colomon Refactor so that notes are stored up rather than output bar-by-bar.
authored
161 method WrapBar($lilypond-bar, $duration) {
56c09ce @colomon Support length field and proper final bar line.
authored
162 my $ticks-in-measure = $.context.ticks-in-measure;
f133cf1 @colomon Refactor so that notes are stored up rather than output bar-by-bar.
authored
163 my $result = "";
56c09ce @colomon Support length field and proper final bar line.
authored
164 if $duration % $ticks-in-measure != 0 {
f133cf1 @colomon Refactor so that notes are stored up rather than output bar-by-bar.
authored
165 $result = "\\partial { 1 / $.context.length.eval }*{ $duration % $ticks-in-measure } ";
2261a59 @colomon Refactor a bit, handle measures with other numbers of eighth notes.
authored
166 }
167
f133cf1 @colomon Refactor so that notes are stored up rather than output bar-by-bar.
authored
168 $result ~ $lilypond-bar;
2261a59 @colomon Refactor a bit, handle measures with other numbers of eighth notes.
authored
169 }
170
102ec2f @colomon Refactor a bunch of the functions into a TuneConvertor class, so it i…
authored
171 method SectionToLilypond(@elements) {
f133cf1 @colomon Refactor so that notes are stored up rather than output bar-by-bar.
authored
172 my $chords = "";
173 my $notes = "";
2f95a2d @colomon Rewrite StemToLilypond to return the string for the note, rather than…
authored
174 my $lilypond = "";
2261a59 @colomon Refactor a bit, handle measures with other numbers of eighth notes.
authored
175 my $duration = 0;
f133cf1 @colomon Refactor so that notes are stored up rather than output bar-by-bar.
authored
176 my $chord-duration = 0;
102ec2f @colomon Refactor a bunch of the functions into a TuneConvertor class, so it i…
authored
177 my $suffix = "";
178 for @elements -> $element {
2261a59 @colomon Refactor a bit, handle measures with other numbers of eighth notes.
authored
179 $duration += self.Duration($element);
f133cf1 @colomon Refactor so that notes are stored up rather than output bar-by-bar.
authored
180 $chord-duration += self.Duration($element);
102ec2f @colomon Refactor a bunch of the functions into a TuneConvertor class, so it i…
authored
181 given $element.key {
2ea4347 @colomon Add dynamics support (and a few dynamics to "Tali Foster's" so we can…
authored
182 when "stem" {
183 $lilypond ~= self.StemToLilypond($element.value, $suffix);
184 $suffix = "";
185 }
186 when "rest" {
187 $lilypond ~= " r{ $.context.get-Lilypond-duration($element.value) } ";
188 $suffix = "";
189 }
102ec2f @colomon Refactor a bunch of the functions into a TuneConvertor class, so it i…
authored
190 when "tuplet" {
7d82741 @colomon Special hack to try to get eighth note triplets to have a beam of the…
authored
191 $lilypond ~= " \\times 2/3 \{";
192 if +$element.value.notes == 3 && $element.value.ticks == 2 {
193 $lilypond ~= self.StemToLilypond($element.value.notes[0], "[");
194 $lilypond ~= self.StemToLilypond($element.value.notes[1]);
195 $lilypond ~= self.StemToLilypond($element.value.notes[2], "]");
196 } else {
197 for $element.value.notes -> $stem {
198 $lilypond ~= self.StemToLilypond($stem);
199 }
102ec2f @colomon Refactor a bunch of the functions into a TuneConvertor class, so it i…
authored
200 }
2f95a2d @colomon Rewrite StemToLilypond to return the string for the note, rather than…
authored
201 $lilypond ~= " } ";
2ea4347 @colomon Add dynamics support (and a few dynamics to "Tali Foster's" so we can…
authored
202 $suffix = "";
1221bde @colomon Get triplets working.
authored
203 }
102ec2f @colomon Refactor a bunch of the functions into a TuneConvertor class, so it i…
authored
204 when "broken_rhythm" {
2f95a2d @colomon Rewrite StemToLilypond to return the string for the note, rather than…
authored
205 $lilypond ~= self.StemToLilypond($element.value.effective-stem1, $suffix);
102ec2f @colomon Refactor a bunch of the functions into a TuneConvertor class, so it i…
authored
206 # MUST: handle interior graciings
2f95a2d @colomon Rewrite StemToLilypond to return the string for the note, rather than…
authored
207 $lilypond ~= self.StemToLilypond($element.value.effective-stem2);
2ea4347 @colomon Add dynamics support (and a few dynamics to "Tali Foster's" so we can…
authored
208 $suffix = "";
102ec2f @colomon Refactor a bunch of the functions into a TuneConvertor class, so it i…
authored
209 }
210 when "gracing" {
211 given $element.value {
2ea4347 @colomon Add dynamics support (and a few dynamics to "Tali Foster's" so we can…
authored
212 when "~" { $suffix ~= "\\turn"; }
213 when "." { $suffix ~= "\\staccato"; }
db5ceec @colomon Substitute regular expressions to detect pianos and fortes, so that a…
authored
214 when /^p+$/ | "mp" | "mf" | /^f+$/
2ea4347 @colomon Add dynamics support (and a few dynamics to "Tali Foster's" so we can…
authored
215 { $suffix ~= "\\" ~ $element.value; }
102ec2f @colomon Refactor a bunch of the functions into a TuneConvertor class, so it i…
authored
216 }
06b7722 @colomon Get basic rolls and staccato notes working.
authored
217 }
2261a59 @colomon Refactor a bit, handle measures with other numbers of eighth notes.
authored
218 when "barline" {
f133cf1 @colomon Refactor so that notes are stored up rather than output bar-by-bar.
authored
219 $notes ~= self.WrapBar($lilypond, $duration);
220 $notes ~= " |\n";
2261a59 @colomon Refactor a bit, handle measures with other numbers of eighth notes.
authored
221 $lilypond = "";
222 $duration = 0;
2f95a2d @colomon Rewrite StemToLilypond to return the string for the note, rather than…
authored
223 }
8d09a3e @colomon Get in-line key changes working.
authored
224 when "inline_field" {
225 given $element.value.key {
226 when "K" {
56c09ce @colomon Support length field and proper final bar line.
authored
227 $!context = Context.new($element.value.value,
228 $!context.meter,
229 $!context.length);
138f1a9 @colomon Replace write-meter and write-key with meter-to-string and key-to-str…
authored
230 $lilypond ~= $!context.key-to-string;
8d09a3e @colomon Get in-line key changes working.
authored
231 }
ee29618 @colomon Allow changing meters, add crooked Newfoundland double to the ABC tun…
authored
232 when "M" {
56c09ce @colomon Support length field and proper final bar line.
authored
233 $!context = Context.new($!context.key-name,
234 $element.value.value,
235 $!context.length);
138f1a9 @colomon Replace write-meter and write-key with meter-to-string and key-to-str…
authored
236 $lilypond ~= $!context.meter-to-string;
ee29618 @colomon Allow changing meters, add crooked Newfoundland double to the ABC tun…
authored
237 }
56c09ce @colomon Support length field and proper final bar line.
authored
238 when "L" {
239 $!context = Context.new($!context.key-name,
240 $!context.meter,
241 $element.value.value);
242 }
8d09a3e @colomon Get in-line key changes working.
authored
243 }
244 }
a63e6e6 @colomon Support slurs, too.
authored
245 when "slur_begin" {
246 $suffix ~= "(";
247 }
248 when "slur_end" {
249 $lilypond .= subst(/(\s+)$/, { ")$0" });
250 }
8d09a3e @colomon Get in-line key changes working.
authored
251 # .say;
06b7722 @colomon Get basic rolls and staccato notes working.
authored
252 }
29745ec @colomon Crude first script to translate ABC to Lilypond.
authored
253 }
102ec2f @colomon Refactor a bunch of the functions into a TuneConvertor class, so it i…
authored
254
f133cf1 @colomon Refactor so that notes are stored up rather than output bar-by-bar.
authored
255 say "\{";
256 $notes ~= self.WrapBar($lilypond, $duration);
257 say $notes;
2261a59 @colomon Refactor a bit, handle measures with other numbers of eighth notes.
authored
258 say " \}";
29745ec @colomon Crude first script to translate ABC to Lilypond.
authored
259 }
260
b561dfc @colomon Refactor key signature handling a bit.
authored
261 method BodyToLilypond(@elements) {
102ec2f @colomon Refactor a bunch of the functions into a TuneConvertor class, so it i…
authored
262 say "\{";
138f1a9 @colomon Replace write-meter and write-key with meter-to-string and key-to-str…
authored
263 print $.context.key-to-string;
264 printf $.context.meter-to-string;
102ec2f @colomon Refactor a bunch of the functions into a TuneConvertor class, so it i…
authored
265
266 my $start-of-section = 0;
c4d049a @colomon Repeats are up and running, though still a bit hacky.
authored
267 loop (my $i = 0; $i < +@elements; $i++) {
4385d53 @colomon Make $length a bit smarter.
authored
268 # say @elements[$i].WHAT;
c4d049a @colomon Repeats are up and running, though still a bit hacky.
authored
269 if @elements[$i].key eq "nth_repeat"
270 || ($i > $start-of-section
271 && @elements[$i].key eq "barline"
272 && @elements[$i].value ne "|") {
273 if @elements[$i].key eq "nth_repeat"
274 || @elements[$i].value eq ':|:' | ':|' | '::' {
102ec2f @colomon Refactor a bunch of the functions into a TuneConvertor class, so it i…
authored
275 print "\\repeat volta 2 "; # 2 is abitrarily chosen here!
276 }
277 self.SectionToLilypond(@elements[$start-of-section ..^ $i]);
278 $start-of-section = $i + 1;
7e906b7 @colomon Turn off the double bar code, as it currently conflicts with more imp…
authored
279 # if @elements[$i].value eq '||' {
280 # say '\\bar "||"';
281 # }
56c09ce @colomon Support length field and proper final bar line.
authored
282 if @elements[$i].value eq '|]' {
283 say '\\bar "|."';
284 }
102ec2f @colomon Refactor a bunch of the functions into a TuneConvertor class, so it i…
authored
285 }
c4d049a @colomon Repeats are up and running, though still a bit hacky.
authored
286
287 if @elements[$i].key eq "nth_repeat" {
56c09ce @colomon Support length field and proper final bar line.
authored
288 my $final-bar = False;
c4d049a @colomon Repeats are up and running, though still a bit hacky.
authored
289 say "\\alternative \{";
290 my $endings = 0;
291 loop (; $i < +@elements; $i++) {
4385d53 @colomon Make $length a bit smarter.
authored
292 # say @elements[$i].WHAT;
c4d049a @colomon Repeats are up and running, though still a bit hacky.
authored
293 if @elements[$i].key eq "barline"
294 && @elements[$i].value ne "|" {
295 self.SectionToLilypond(@elements[$start-of-section ..^ $i]);
296 $start-of-section = $i + 1;
56c09ce @colomon Support length field and proper final bar line.
authored
297 $final-bar = True if @elements[$i].value eq '|]';
c4d049a @colomon Repeats are up and running, though still a bit hacky.
authored
298 last if ++$endings == 2;
299 }
300 }
301 if $endings == 1 {
4385d53 @colomon Make $length a bit smarter.
authored
302 # say @elements[$i].WHAT;
c4d049a @colomon Repeats are up and running, though still a bit hacky.
authored
303 self.SectionToLilypond(@elements[$start-of-section ..^ $i]);
304 $start-of-section = $i + 1;
56c09ce @colomon Support length field and proper final bar line.
authored
305 $final-bar = True if @elements[$i].value eq '|]';
c4d049a @colomon Repeats are up and running, though still a bit hacky.
authored
306 }
307 say "\}";
56c09ce @colomon Support length field and proper final bar line.
authored
308
309 if $final-bar {
310 say '\\bar "|."';
311 }
312
c4d049a @colomon Repeats are up and running, though still a bit hacky.
authored
313 }
102ec2f @colomon Refactor a bunch of the functions into a TuneConvertor class, so it i…
authored
314 }
315
316 if $start-of-section + 1 < @elements.elems {
317 if @elements[*-1].value eq ':|:' | ':|' | '::' {
549756a @colomon Make abs2ly.pl somewhat smarter about repeats and partial measures.
authored
318 print "\\repeat volta 2 "; # 2 is abitrarily chosen here!
319 }
102ec2f @colomon Refactor a bunch of the functions into a TuneConvertor class, so it i…
authored
320 self.SectionToLilypond(@elements[$start-of-section ..^ +@elements]);
56c09ce @colomon Support length field and proper final bar line.
authored
321 if @elements[*-1].value eq '|]' {
322 say '\\bar "|."';
323 }
549756a @colomon Make abs2ly.pl somewhat smarter about repeats and partial measures.
authored
324 }
325
102ec2f @colomon Refactor a bunch of the functions into a TuneConvertor class, so it i…
authored
326 say "\}";
549756a @colomon Make abs2ly.pl somewhat smarter about repeats and partial measures.
authored
327 }
102ec2f @colomon Refactor a bunch of the functions into a TuneConvertor class, so it i…
authored
328
549756a @colomon Make abs2ly.pl somewhat smarter about repeats and partial measures.
authored
329 }
330
29745ec @colomon Crude first script to translate ABC to Lilypond.
authored
331 my $match = ABC::Grammar.parse($*IN.slurp, :rule<tune_file>, :actions(ABC::Actions.new));
332
333 say '\\version "2.12.3"';
6aeb675 @colomon Add crude paper size control.
authored
334 say "#(set-default-paper-size \"{$paper-size}\")";
945131a @colomon Get the key signature correct (at least for simple key signatures) bu…
authored
335
70e5a12 @colomon Add "Tali Foster's" to r-star.abc file so we can easily test multiple…
authored
336 for @( $match.ast ) -> $tune {
337 say "\\score \{";
e6612a5 @colomon Add double bar line to the grammar, and some durations longer than a …
authored
338
339 # say ~$tune.music;
70e5a12 @colomon Add "Tali Foster's" to r-star.abc file so we can easily test multiple…
authored
340
341 my $key = $tune.header.get("K")[0].value;
342 my $meter = $tune.header.get("M")[0].value;
4385d53 @colomon Make $length a bit smarter.
authored
343 my $length = $tune.header.get("L") ?? $tune.header.get("L")[0].value !! "1/8";
70e5a12 @colomon Add "Tali Foster's" to r-star.abc file so we can easily test multiple…
authored
344
56c09ce @colomon Support length field and proper final bar line.
authored
345 my $convertor = TuneConvertor.new($key, $meter, $length);
b561dfc @colomon Refactor key signature handling a bit.
authored
346 $convertor.BodyToLilypond($tune.music);
70e5a12 @colomon Add "Tali Foster's" to r-star.abc file so we can easily test multiple…
authored
347 HeaderToLilypond($tune.header);
348
f92e603 @colomon Add tests for inline meter changes.
authored
349 say "}\n\n";
70e5a12 @colomon Add "Tali Foster's" to r-star.abc file so we can easily test multiple…
authored
350 }
351
Something went wrong with that request. Please try again.