use v6; use Test; use ABC::Grammar; use ABC::Header; use ABC::Tune; use ABC::Duration; use ABC::Note; use ABC::Rest; use ABC::Tuplet; use ABC::BrokenRhythm; use ABC::Chord; use ABC::LongRest; use ABC::GraceNotes; use ABC::Actions; use ABC::Utils; use ABC::Pitched; use ABC::KeyInfo; sub transpose(Str $test, $pitch-changer) { my $match = ABC::Grammar.parse($test, :rule, :actions(ABC::Actions.new)); if $match { given $match.ast.value { when Positional { $_>>.transpose($pitch-changer); } when ABC::Pitched { $_.transpose($pitch-changer); } die "Don't know how to transpose { $_.WHAT }"; } } } sub up-octave($accidental, $basenote, $octave) { if $octave ~~ /","/ { return ($accidental, $basenote, $/.postmatch); } elsif $octave ~~ /"'"/ || $basenote ~~ // { return ($accidental, $basenote, $octave ~ "'"); } else { return ($accidental, $basenote.lc, $octave); } } is transpose("A", &up-octave), "a", "Octave bump to A yields a"; is transpose("a", &up-octave), "a'", "Octave bump to a yields a'"; is transpose("a''2", &up-octave), "a'''2", "Octave bump to a'' yields a'''"; is transpose("A,-", &up-octave), "A-", "Octave bump to A, yields A"; is transpose("A,,", &up-octave), "A,", "Octave bump to A,, yields A,"; is transpose("[C,Eg]", &up-octave), "[Ceg']", "Octave bump to [C,Eg] yields [Ceg']"; is transpose("[C,Eg]", &up-octave), "[Ceg']", "Octave bump to [C,Eg] yields [Ceg']"; is transpose("(3C,Eg", &up-octave), "(3Ceg'", "Octave bump to (3C,Eg yields (3Ceg'"; is transpose("A, :actions(ABC::Actions.new)); if $match { pitch-to-ordinal(%key, $match.ast.accidental, $match.ast.basenote, $match.ast.octave); } } { my %key = ABC::KeyInfo.new("C").key; is pitch2ordinal(%key, "C"), 0, "C ==> 0"; is pitch2ordinal(%key, "D"), 2, "D ==> 2"; is pitch2ordinal(%key, "E"), 4, "E ==> 4"; is pitch2ordinal(%key, "F"), 5, "F ==> 5"; is pitch2ordinal(%key, "G"), 7, "G ==> 7"; is pitch2ordinal(%key, "A"), 9, "A ==> 9"; is pitch2ordinal(%key, "B"), 11, "B ==> 11"; is pitch2ordinal(%key, "c"), 12, "c ==> 12"; is pitch2ordinal(%key, "=A"), 9, "=A ==> 9"; is pitch2ordinal(%key, "^A"), 10, "^A ==> 10"; is pitch2ordinal(%key, "_A"), 8, "_A ==> 8"; is pitch2ordinal(%key, "^^A"), 11, "^^A ==> 11"; is pitch2ordinal(%key, "__A"), 7, "__A ==> 7"; is pitch2ordinal(%key, "^^G,,,"), -27, "^^G,,, ==> -27"; is pitch2ordinal(%key, "d'''"), 50, "d''' ==> 50"; %key = ABC::KeyInfo.new("Ab").key; is pitch2ordinal(%key, "C"), 0, "C ==> 0"; is pitch2ordinal(%key, "D"), 1, "D ==> 1"; is pitch2ordinal(%key, "E"), 3, "E ==> 3"; is pitch2ordinal(%key, "F"), 5, "F ==> 5"; is pitch2ordinal(%key, "G"), 7, "G ==> 7"; is pitch2ordinal(%key, "A"), 8, "A ==> 8"; is pitch2ordinal(%key, "B"), 10, "B ==> 10"; is pitch2ordinal(%key, "c"), 12, "c ==> 12"; is pitch2ordinal(%key, "=A"), 9, "=A ==> 9"; is pitch2ordinal(%key, "^A"), 10, "^A ==> 10"; is pitch2ordinal(%key, "_A"), 8, "_A ==> 8"; is pitch2ordinal(%key, "^^A"), 11, "^^A ==> 11"; is pitch2ordinal(%key, "__A"), 7, "__A ==> 7"; is pitch2ordinal(%key, "^^G,,,"), -27, "^^G,,, ==> -27"; is pitch2ordinal(%key, "d'''"), 49, "d''' ==> 49"; %key = ABC::KeyInfo.new("C").key; is ordinal-to-pitch(%key, "C", 0), " C ", "0/C => C"; is ordinal-to-pitch(%key, "D", 0), "__ D ", "0/D => __D"; is ordinal-to-pitch(%key, "B", 0), "^ B ,", "0/B => ^B,"; is ordinal-to-pitch(%key, "C", 1), "^ C ", "1/C => ^C"; is ordinal-to-pitch(%key, "D", 1), "_ D ", "1/D => _D"; is ordinal-to-pitch(%key, "B", 1), "^^ B ,", "1/B => ^^B,"; is ordinal-to-pitch(%key, "C", -1), "_ C ", "-1/C => _C"; is ordinal-to-pitch(%key, "B", -1), " B ,", "-1/B => B,"; is ordinal-to-pitch(%key, "C", -12), " C ,", "-12/C => C,"; is ordinal-to-pitch(%key, "D", -12), "__ D ,", "-12/D => __D,"; is ordinal-to-pitch(%key, "B", -12), "^ B ,,", "-12/B => ^B,,"; is ordinal-to-pitch(%key, "C", 11), "_ c ", "11/C => _c"; is ordinal-to-pitch(%key, "B", 11), " B ", "11/B => B"; is ordinal-to-pitch(%key, "C", 12), " c ", "12/C => c"; is ordinal-to-pitch(%key, "D", 12), "__ d ", "12/D => __d"; is ordinal-to-pitch(%key, "B", 12), "^ B ", "12/B => ^B"; is ordinal-to-pitch(%key, "C", 13), "^ c ", "1/C => ^c"; is ordinal-to-pitch(%key, "D", 13), "_ d ", "1/D => _d"; is ordinal-to-pitch(%key, "B", 13), "^^ B ", "1/B => ^^B"; is ordinal-to-pitch(%key, "C", 23), "_ c '", "23/C => _c'"; is ordinal-to-pitch(%key, "B", 23), " b ", "23/B => b"; is ordinal-to-pitch(%key, "C", 24), " c '", "24/C => c'"; is ordinal-to-pitch(%key, "D", 24), "__ d '", "24/D => __d'"; is ordinal-to-pitch(%key, "B", 24), "^ b ", "24/B => ^b"; is ordinal-to-pitch(%key, "C", 25), "^ c '", "25/C => ^c'"; is ordinal-to-pitch(%key, "D", 25), "_ d '", "25/D => _d'"; is ordinal-to-pitch(%key, "B", 25), "^^ b ", "25/B => ^^b"; } sub e-flat-to-d($accidental, $basenote, $octave) { my %e-flat = ABC::KeyInfo.new("Eb").key; my %d = ABC::KeyInfo.new("D").key; my $ordinal = pitch-to-ordinal(%e-flat, $accidental, $basenote, $octave); my $basenote-in-d = $basenote.uc eq "A" ?? "G" !! ($basenote.ord - 1).chr.uc; ordinal-to-pitch(%d, $basenote-in-d, $ordinal - 1); } is transpose("A", &e-flat-to-d), "G", "Eb to D on A yields G"; is transpose("a", &e-flat-to-d), "g", "Eb to D on a yields g"; is transpose("a''2", &e-flat-to-d), "g''2", "Eb to D on a'' yields g''"; is transpose("A,-", &e-flat-to-d), "G,-", "Eb to D on A,- yields G,-"; is transpose("[_EG_B]", &e-flat-to-d), "[DFA]", "Eb to D on [_EG_B] yields [DFA]"; is transpose("[EGB]", &e-flat-to-d), "[DFA]", "Eb to D on [EGB] yields [DFA]"; is transpose("(3C,Eg", &e-flat-to-d), "(3B,,Df", "Eb to D on (3C,Eg yields (3B,,Df"; is transpose("=A($accidental, $basenote, $octave) { my $ordinal = pitch-to-ordinal(%.current-from, $accidental, $basenote, $octave); my $basenote-in-new-key = "A".ord + ($basenote.uc.ord - "A".ord + $.pitch-name-shift) % 7; ordinal-to-pitch(%.current-to, $basenote-in-new-key, $ordinal + $.half-step-shift); } } done-testing;