| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | #! perl | 
| 2 |  |  |  |  |  |  |  | 
| 3 | 79 |  |  | 79 |  | 995 | use v5.26; | 
|  | 79 |  |  |  |  | 303 |  | 
| 4 | 79 |  |  | 79 |  | 445 | use utf8; | 
|  | 79 |  |  |  |  | 196 |  | 
|  | 79 |  |  |  |  | 437 |  | 
| 5 | 79 |  |  | 79 |  | 1999 | use Carp; | 
|  | 79 |  |  |  |  | 194 |  | 
|  | 79 |  |  |  |  | 4524 |  | 
| 6 | 79 |  |  | 79 |  | 530 | use feature qw( signatures ); | 
|  | 79 |  |  |  |  | 179 |  | 
|  | 79 |  |  |  |  | 6868 |  | 
| 7 | 79 |  |  | 79 |  | 590 | no warnings "experimental::signatures"; | 
|  | 79 |  |  |  |  | 219 |  | 
|  | 79 |  |  |  |  | 3686 |  | 
| 8 |  |  |  |  |  |  |  | 
| 9 |  |  |  |  |  |  | # package ParserWatch; | 
| 10 |  |  |  |  |  |  | # | 
| 11 |  |  |  |  |  |  | # require Tie::Hash; | 
| 12 |  |  |  |  |  |  | # our @ISA = qw( Tie::StdHash ); | 
| 13 |  |  |  |  |  |  | # | 
| 14 |  |  |  |  |  |  | # sub STORE { | 
| 15 |  |  |  |  |  |  | #     if ( $_[1] !~ /^[[:alpha:]]+$/ ) { | 
| 16 |  |  |  |  |  |  | # 	Carp::cluck("STORE $_[1] " . $_[2]); | 
| 17 |  |  |  |  |  |  | # 	::dump($_[2]); | 
| 18 |  |  |  |  |  |  | #     } | 
| 19 |  |  |  |  |  |  | #     $_[0]->{$_[1]} = $_[2]; | 
| 20 |  |  |  |  |  |  | # } | 
| 21 |  |  |  |  |  |  |  | 
| 22 | 79 |  |  | 79 |  | 565 | use ChordPro; | 
|  | 79 |  |  |  |  | 232 |  | 
|  | 79 |  |  |  |  | 87027 |  | 
| 23 |  |  |  |  |  |  |  | 
| 24 |  |  |  |  |  |  | my %parsers; | 
| 25 |  |  |  |  |  |  |  | 
| 26 |  |  |  |  |  |  | # tie %parsers => 'ParserWatch'; | 
| 27 |  |  |  |  |  |  |  | 
| 28 |  |  |  |  |  |  | package ChordPro::Chords::Parser; | 
| 29 |  |  |  |  |  |  |  | 
| 30 |  |  |  |  |  |  | # The parser analyses a chord and returns an object with the following | 
| 31 |  |  |  |  |  |  | # attributes: | 
| 32 |  |  |  |  |  |  | # | 
| 33 |  |  |  |  |  |  | #  name		name as passed to the parser (e.g. Cism7) | 
| 34 |  |  |  |  |  |  | # | 
| 35 |  |  |  |  |  |  | #  root		textual decomposition: root part (e.g. Cis) | 
| 36 |  |  |  |  |  |  | #  qual		textual decomposition: quality part (e.g. m) | 
| 37 |  |  |  |  |  |  | #  ext		textual decomposition: extension part (e.g. 7) | 
| 38 |  |  |  |  |  |  | #  bass		textual decomposition: bass part (a root) | 
| 39 |  |  |  |  |  |  | # | 
| 40 |  |  |  |  |  |  | #  system	notation system (common, nashville, user defined) | 
| 41 |  |  |  |  |  |  | #  root_canon	canonical root (e.g. Cis => C#) | 
| 42 |  |  |  |  |  |  | #  root_ord	root ordinal (e.g. Cis => 1) | 
| 43 |  |  |  |  |  |  | #  root_mod	root modifier (e.g. is => # => +1) | 
| 44 |  |  |  |  |  |  | #  qual_canon	canonical qualifier (e.g. m => -) | 
| 45 |  |  |  |  |  |  | #  ext_canon	canonical extension (e.g. sus => sus4) | 
| 46 |  |  |  |  |  |  | #  bass_canon	like root, for bass note | 
| 47 |  |  |  |  |  |  | #  bass_ord	like root, for bass note | 
| 48 |  |  |  |  |  |  | #  bass_mod	like root, for bass note | 
| 49 |  |  |  |  |  |  | # | 
| 50 |  |  |  |  |  |  | # The parsers are one of | 
| 51 |  |  |  |  |  |  | #  ChordPro::Chords::Parser::Common | 
| 52 |  |  |  |  |  |  | #  ChordPro::Chords::Parser::Nashville | 
| 53 |  |  |  |  |  |  | #  ChordPro::Chords::Parser::Roman | 
| 54 |  |  |  |  |  |  | # | 
| 55 |  |  |  |  |  |  | # The objects are one of | 
| 56 |  |  |  |  |  |  | #  ChordPro::Chord::Common | 
| 57 |  |  |  |  |  |  | #  ChordPro::Chord::Nashville | 
| 58 |  |  |  |  |  |  | #  ChordPro::Chord::Roman | 
| 59 |  |  |  |  |  |  |  | 
| 60 |  |  |  |  |  |  | # Creates a parser based on the current (optionally augmented) | 
| 61 |  |  |  |  |  |  | # context. | 
| 62 |  |  |  |  |  |  | # Note that the appropriate way is to call | 
| 63 |  |  |  |  |  |  | # ChordPro::Chords::Parser->get_parser. | 
| 64 |  |  |  |  |  |  |  | 
| 65 | 2 |  |  | 2 | 0 | 5 | sub new ( $pkg, $init ) { | 
|  | 2 |  |  |  |  | 4 |  | 
|  | 2 |  |  |  |  | 5 |  | 
|  | 2 |  |  |  |  | 4 |  | 
| 66 |  |  |  |  |  |  |  | 
| 67 | 2 | 50 |  |  |  | 11 | Carp::confess("Missing config?") unless $::config; | 
| 68 |  |  |  |  |  |  | # Use current config, optionally augmented by $init. | 
| 69 | 2 |  | 50 |  |  | 5 | my $cfg = { %{$::config//{}}, %{$init//{}} }; | 
|  | 2 |  | 50 |  |  | 15 |  | 
|  | 2 |  |  |  |  | 30 |  | 
| 70 |  |  |  |  |  |  |  | 
| 71 |  |  |  |  |  |  | Carp::croak("Missing notes in parser creation") | 
| 72 | 2 | 50 |  |  |  | 12 | unless $cfg->{notes}; | 
| 73 | 2 |  |  |  |  | 5 | my $system = $cfg->{notes}->{system}; | 
| 74 | 2 | 50 |  |  |  | 7 | Carp::croak("Missing notes system in parser creation") | 
| 75 |  |  |  |  |  |  | unless $system; | 
| 76 |  |  |  |  |  |  |  | 
| 77 | 2 | 50 |  |  |  | 7 | if ( $system eq "nashville" ) { | 
| 78 | 0 |  |  |  |  | 0 | return ChordPro::Chords::Parser::Nashville->new($cfg); | 
| 79 |  |  |  |  |  |  | } | 
| 80 | 2 | 50 |  |  |  | 8 | if ( $system eq "roman" ) { | 
| 81 | 0 |  |  |  |  | 0 | return ChordPro::Chords::Parser::Roman->new($cfg); | 
| 82 |  |  |  |  |  |  | } | 
| 83 | 2 |  |  |  |  | 13 | return ChordPro::Chords::Parser::Common->new($cfg); | 
| 84 |  |  |  |  |  |  | } | 
| 85 |  |  |  |  |  |  |  | 
| 86 |  |  |  |  |  |  | # The default parser has built-in support for common (dutch) note | 
| 87 |  |  |  |  |  |  | # names. | 
| 88 |  |  |  |  |  |  |  | 
| 89 | 11504 |  |  | 11504 | 0 | 18543 | sub default ( $pkg ) { | 
|  | 11504 |  |  |  |  | 18955 |  | 
|  | 11504 |  |  |  |  | 16166 |  | 
| 90 |  |  |  |  |  |  |  | 
| 91 |  |  |  |  |  |  | return $parsers{common} //= | 
| 92 |  |  |  |  |  |  | ChordPro::Chords::Parser::Common->new | 
| 93 | 11504 |  | 33 |  |  | 39972 | ( { %{$::config}, | 
|  | 0 |  |  |  |  | 0 |  | 
| 94 |  |  |  |  |  |  | "notes" => | 
| 95 |  |  |  |  |  |  | { "system" => "common", | 
| 96 |  |  |  |  |  |  | "sharp" => [ "C", [ "C#", "Cis", "C♯" ], | 
| 97 |  |  |  |  |  |  | "D", [ "D#", "Dis", "D♯" ], | 
| 98 |  |  |  |  |  |  | "E", | 
| 99 |  |  |  |  |  |  | "F", [ "F#", "Fis", "F♯" ], | 
| 100 |  |  |  |  |  |  | "G", [ "G#", "Gis", "G♯" ], | 
| 101 |  |  |  |  |  |  | "A", [ "A#", "Ais", "A♯" ], | 
| 102 |  |  |  |  |  |  | "B", | 
| 103 |  |  |  |  |  |  | ], | 
| 104 |  |  |  |  |  |  | "flat"  => [                               "C", | 
| 105 |  |  |  |  |  |  | [ "Db", "Des",        "D♭" ], "D", | 
| 106 |  |  |  |  |  |  | [ "Eb", "Es",  "Ees", "E♭" ], "E", | 
| 107 |  |  |  |  |  |  | "F", | 
| 108 |  |  |  |  |  |  | [ "Gb", "Ges",        "G♭" ], "G", | 
| 109 |  |  |  |  |  |  | [ "Ab", "As",  "Aes", "A♭" ], "A", | 
| 110 |  |  |  |  |  |  | [ "Bb", "Bes",        "B♭" ], "B", | 
| 111 |  |  |  |  |  |  | ], | 
| 112 |  |  |  |  |  |  | }, | 
| 113 |  |  |  |  |  |  | }, | 
| 114 |  |  |  |  |  |  | ); | 
| 115 |  |  |  |  |  |  | } | 
| 116 |  |  |  |  |  |  |  | 
| 117 |  |  |  |  |  |  | # Cached version of the individual parser's parse_chord. | 
| 118 | 64811 |  |  | 64811 | 0 | 94203 | sub parse ( $self, $chord ) { | 
|  | 64811 |  |  |  |  | 95630 |  | 
|  | 64811 |  |  |  |  | 98347 |  | 
|  | 64811 |  |  |  |  | 89345 |  | 
| 119 |  |  |  |  |  |  | ####    $self->{chord_cache}->{$chord} //= | 
| 120 | 64811 |  |  |  |  | 141323 | $self->parse_chord($chord); | 
| 121 |  |  |  |  |  |  | } | 
| 122 |  |  |  |  |  |  |  | 
| 123 |  |  |  |  |  |  | # Virtual. | 
| 124 | 0 |  |  | 0 | 0 | 0 | sub parse_chord ( $self, $chord ) { | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 125 | 0 |  |  |  |  | 0 | Carp::confess("Virtual method 'parse_chord' not defined"); | 
| 126 |  |  |  |  |  |  | } | 
| 127 |  |  |  |  |  |  |  | 
| 128 |  |  |  |  |  |  | # Fetch a parser for a known system, with fallback. | 
| 129 |  |  |  |  |  |  | # Default is a parser for the current config. | 
| 130 | 348 |  |  | 348 | 0 | 898 | sub get_parser ( $self, $system = undef, $nofallback = undef ) { | 
|  | 348 |  |  |  |  | 802 |  | 
|  | 348 |  |  |  |  | 914 |  | 
|  | 348 |  |  |  |  | 724 |  | 
|  | 348 |  |  |  |  | 637 |  | 
| 131 |  |  |  |  |  |  |  | 
| 132 | 348 |  | 66 |  |  | 1625 | $system //= $::config->{notes}->{system}; | 
| 133 | 348 | 100 |  |  |  | 1936 | return $parsers{$system} if $parsers{$system}; | 
| 134 |  |  |  |  |  |  |  | 
| 135 | 210 | 100 |  |  |  | 1967 | if ( $system eq "nashville" ) { | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 136 | 2 |  | 33 |  |  | 24 | return $parsers{$system} //= | 
| 137 |  |  |  |  |  |  | ChordPro::Chords::Parser::Nashville->new; | 
| 138 |  |  |  |  |  |  | } | 
| 139 |  |  |  |  |  |  | elsif ( $system eq "roman" ) { | 
| 140 | 2 |  | 33 |  |  | 29 | return $parsers{$system} //= | 
| 141 |  |  |  |  |  |  | ChordPro::Chords::Parser::Roman->new; | 
| 142 |  |  |  |  |  |  | } | 
| 143 |  |  |  |  |  |  | elsif ( $system ne $::config->{notes}->{system} ) { | 
| 144 | 0 |  |  |  |  | 0 | my $p = ChordPro::Chords::Parser::Common->new | 
| 145 |  |  |  |  |  |  | ( { notes => $system } ); | 
| 146 | 0 |  |  |  |  | 0 | return $parsers{$system} = $p; | 
| 147 |  |  |  |  |  |  | } | 
| 148 |  |  |  |  |  |  | elsif ( $system ) { | 
| 149 | 206 |  |  |  |  | 2192 | my $p = ChordPro::Chords::Parser::Common->new; | 
| 150 | 206 |  |  |  |  | 786 | $p->{system} = $system; | 
| 151 | 206 |  |  |  |  | 1109 | return $parsers{$system} = $p; | 
| 152 |  |  |  |  |  |  | } | 
| 153 |  |  |  |  |  |  | elsif ( $nofallback ) { | 
| 154 | 0 |  |  |  |  | 0 | return; | 
| 155 |  |  |  |  |  |  | }; | 
| 156 |  |  |  |  |  |  |  | 
| 157 | 0 |  |  |  |  | 0 | Carp::confess("No parser for $system, falling back to default\n"); | 
| 158 | 0 |  | 0 |  |  | 0 | return $parsers{common} //= $self->default; | 
| 159 |  |  |  |  |  |  | } | 
| 160 |  |  |  |  |  |  |  | 
| 161 | 4 |  |  | 4 | 0 | 11 | sub have_parser ( $self, $system ) { | 
|  | 4 |  |  |  |  | 8 |  | 
|  | 4 |  |  |  |  | 9 |  | 
|  | 4 |  |  |  |  | 6 |  | 
| 162 | 4 |  |  |  |  | 21 | exists $parsers{$system}; | 
| 163 |  |  |  |  |  |  | } | 
| 164 |  |  |  |  |  |  |  | 
| 165 |  |  |  |  |  |  | # The list of instantiated parsers. | 
| 166 | 0 |  |  | 0 | 0 | 0 | sub parsers ( $self ) { | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 167 | 0 |  |  |  |  | 0 | \%parsers; | 
| 168 |  |  |  |  |  |  | } | 
| 169 |  |  |  |  |  |  |  | 
| 170 | 208 |  |  | 208 | 0 | 515 | sub reset_parsers ( $self,  @which ) { | 
|  | 208 |  |  |  |  | 501 |  | 
|  | 208 |  |  |  |  | 495 |  | 
|  | 208 |  |  |  |  | 454 |  | 
| 171 | 208 | 50 |  |  |  | 1460 | @which = keys(%parsers) unless @which; | 
| 172 | 208 |  |  |  |  | 2331 | delete $parsers{$_} for @which; | 
| 173 |  |  |  |  |  |  | } | 
| 174 |  |  |  |  |  |  |  | 
| 175 |  |  |  |  |  |  | # The number of intervals for this note system. | 
| 176 | 371 |  |  | 371 | 0 | 650 | sub intervals ( $self ) { | 
|  | 371 |  |  |  |  | 618 |  | 
|  | 371 |  |  |  |  | 542 |  | 
| 177 | 371 |  |  |  |  | 1051 | $self->{intervals}; | 
| 178 |  |  |  |  |  |  | } | 
| 179 |  |  |  |  |  |  |  | 
| 180 | 0 |  |  | 0 | 0 | 0 | sub simplify ( $self ) { | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 181 | 0 |  |  |  |  | 0 | ref($self); | 
| 182 |  |  |  |  |  |  | } | 
| 183 |  |  |  |  |  |  |  | 
| 184 |  |  |  |  |  |  | ################ Parsing Common notated chords ################ | 
| 185 |  |  |  |  |  |  |  | 
| 186 |  |  |  |  |  |  | package ChordPro::Chords::Parser::Common; | 
| 187 |  |  |  |  |  |  |  | 
| 188 |  |  |  |  |  |  | our @ISA = qw( ChordPro::Chords::Parser ); | 
| 189 |  |  |  |  |  |  |  | 
| 190 | 79 |  |  | 79 |  | 51814 | use Storable qw(dclone); | 
|  | 79 |  |  |  |  | 322285 |  | 
|  | 79 |  |  |  |  | 29347 |  | 
| 191 |  |  |  |  |  |  |  | 
| 192 | 208 |  |  | 208 |  | 610 | sub new ( $pkg, $cfg = $::config ) { | 
|  | 208 |  |  |  |  | 528 |  | 
|  | 208 |  |  |  |  | 586 |  | 
|  | 208 |  |  |  |  | 495 |  | 
| 193 | 208 |  |  |  |  | 1113 | my $self = bless { chord_cache => {} } => $pkg; | 
| 194 | 208 |  |  |  |  | 731 | bless $self => 'ChordPro::Chords::Parser::Common'; | 
| 195 | 208 |  |  |  |  | 664 | my $notes = $cfg->{notes}; | 
| 196 | 208 |  |  |  |  | 1318 | $self->load_notes($cfg); | 
| 197 | 208 |  |  |  |  | 1025 | $self->{system} = $notes->{system}; | 
| 198 | 208 |  |  |  |  | 792 | $self->{target} = 'ChordPro::Chord::Common'; | 
| 199 | 208 |  |  |  |  | 851 | $self->{movable} = $notes->{movable}; | 
| 200 |  |  |  |  |  |  | warn("Chords: Created parser for ", $self->{system}, | 
| 201 |  |  |  |  |  |  | $cfg->{settings}->{chordnames} eq "relaxed" | 
| 202 |  |  |  |  |  |  | ? ", relaxed" : "", | 
| 203 | 208 | 0 |  |  |  | 1043 | "\n") if $::options->{verbose} > 1; | 
|  |  | 50 |  |  |  |  |  | 
| 204 | 208 |  |  |  |  | 1127 | return $parsers{$self->{system}} = $self; | 
| 205 |  |  |  |  |  |  | } | 
| 206 |  |  |  |  |  |  |  | 
| 207 | 63791 |  |  | 63791 |  | 86571 | sub parse_chord ( $self, $chord ) { | 
|  | 63791 |  |  |  |  | 89596 |  | 
|  | 63791 |  |  |  |  | 100293 |  | 
|  | 63791 |  |  |  |  | 85304 |  | 
| 208 |  |  |  |  |  |  |  | 
| 209 |  |  |  |  |  |  | my $info = { system => $self->{system}, | 
| 210 | 63791 |  |  |  |  | 203465 | parser => $self, | 
| 211 |  |  |  |  |  |  | name   => $chord }; | 
| 212 |  |  |  |  |  |  |  | 
| 213 | 63791 |  |  |  |  | 106651 | my $bass = ""; | 
| 214 | 63791 | 100 |  |  |  | 339581 | if ( $chord =~ m;^(.*)/($self->{n_pat})$; ) { | 
| 215 | 149 |  |  |  |  | 500 | $chord = $1; | 
| 216 | 149 |  |  |  |  | 328 | $bass = $2; | 
| 217 |  |  |  |  |  |  | } | 
| 218 |  |  |  |  |  |  |  | 
| 219 | 63791 |  |  |  |  | 107999 | my %plus; | 
| 220 |  |  |  |  |  |  |  | 
| 221 |  |  |  |  |  |  | # Match chord. | 
| 222 | 63791 | 50 | 33 |  |  | 744748 | if ( $chord eq "" && $bass ne "" ) { | 
|  |  | 100 | 66 |  |  |  |  | 
|  |  | 100 | 100 |  |  |  |  | 
|  |  | 100 | 66 |  |  |  |  | 
| 223 | 0 |  |  |  |  | 0 | $info->{rootless} = 1; | 
| 224 |  |  |  |  |  |  | } | 
| 225 |  |  |  |  |  |  | elsif ( $chord =~ /^$self->{c_pat}$/ ) { | 
| 226 | 79 |  |  | 79 |  | 41522 | %plus = %+; | 
|  | 79 |  |  |  |  | 29929 |  | 
|  | 79 |  |  |  |  | 182506 |  | 
|  | 39844 |  |  |  |  | 586679 |  | 
| 227 | 39844 |  |  |  |  | 149434 | $info->{root} = $plus{root}; | 
| 228 |  |  |  |  |  |  | } | 
| 229 |  |  |  |  |  |  | # Retry with relaxed pattern if requested. | 
| 230 |  |  |  |  |  |  | elsif ( $self->{c_rpat} | 
| 231 |  |  |  |  |  |  | && $::config->{settings}->{chordnames} eq "relaxed" | 
| 232 |  |  |  |  |  |  | && $chord =~ /^$self->{c_rpat}$/ ) { | 
| 233 | 22 |  |  |  |  | 408 | %plus = %+;		# keep it outer | 
| 234 | 22 | 50 |  |  |  | 134 | return unless $info->{root} = $plus{root}; | 
| 235 |  |  |  |  |  |  | } | 
| 236 |  |  |  |  |  |  | # Not a chord. Try note name. | 
| 237 |  |  |  |  |  |  | elsif ( $::config->{settings}->{notenames} | 
| 238 |  |  |  |  |  |  | && ucfirst($chord) =~ /^$self->{n_pat}$/ ) { | 
| 239 | 6 |  |  |  |  | 23 | $info->{root} = $chord; | 
| 240 | 6 |  |  |  |  | 17 | $info->{isnote} = 1; | 
| 241 |  |  |  |  |  |  | } | 
| 242 |  |  |  |  |  |  | # Nope. | 
| 243 |  |  |  |  |  |  | else { | 
| 244 | 23919 |  |  |  |  | 444119 | return; | 
| 245 |  |  |  |  |  |  | } | 
| 246 |  |  |  |  |  |  |  | 
| 247 | 39872 |  |  |  |  | 85687 | bless $info => $self->{target}; | 
| 248 |  |  |  |  |  |  |  | 
| 249 | 39872 |  | 100 |  |  | 96422 | my $q = $plus{qual} // ""; | 
| 250 | 39872 |  |  |  |  | 75908 | $info->{qual} = $q; | 
| 251 | 39872 | 100 | 100 |  |  | 136146 | $q = "-" if $q eq "m" || $q eq "min"; | 
| 252 | 39872 | 100 |  |  |  | 79411 | $q = "+" if $q eq "aug"; | 
| 253 | 39872 | 100 |  |  |  | 74143 | $q = "0" if $q eq "dim"; | 
| 254 | 39872 | 100 |  |  |  | 74004 | $q = "0" if $q eq "o"; | 
| 255 | 39872 |  |  |  |  | 70108 | $info->{qual_canon} = $q; | 
| 256 |  |  |  |  |  |  |  | 
| 257 | 39872 |  | 100 |  |  | 85463 | my $x = $plus{ext} // ""; | 
| 258 | 39872 | 100 |  |  |  | 84211 | if ( !$info->{qual} ) { | 
| 259 | 19749 | 100 |  |  |  | 40040 | if ( $x eq "maj" ) { | 
| 260 | 48 |  |  |  |  | 93 | $x = ""; | 
| 261 |  |  |  |  |  |  | } | 
| 262 |  |  |  |  |  |  | } | 
| 263 | 39872 |  |  |  |  | 75115 | $info->{ext} = $x; | 
| 264 | 39872 | 100 |  |  |  | 77619 | $x = "sus4" if $x eq "sus"; | 
| 265 | 39872 |  |  |  |  | 96812 | $info->{ext_canon} = $x; | 
| 266 |  |  |  |  |  |  |  | 
| 267 |  |  |  |  |  |  | my $ordmod = sub { | 
| 268 | 40021 |  |  | 40021 |  | 80206 | my ( $pfx ) = @_; | 
| 269 | 40021 |  |  |  |  | 77422 | my $r = $info->{$pfx}; | 
| 270 | 40021 | 100 |  |  |  | 80740 | $r = ucfirst($r) if $info->{isnote}; | 
| 271 | 40021 | 100 |  |  |  | 104109 | if ( defined $self->{ns_tbl}->{$r} ) { | 
|  |  | 50 |  |  |  |  |  | 
| 272 | 29204 |  |  |  |  | 81001 | $info->{"${pfx}_ord"} = $self->{ns_tbl}->{$r}; | 
| 273 | 29204 | 100 |  |  |  | 82762 | $info->{"${pfx}_mod"} = defined $self->{nf_tbl}->{$r} ? 0 : 1; | 
| 274 | 29204 |  |  |  |  | 95612 | $info->{"${pfx}_canon"} = $self->{ns_canon}->[$self->{ns_tbl}->{$r}]; | 
| 275 |  |  |  |  |  |  | } | 
| 276 |  |  |  |  |  |  | elsif ( defined $self->{nf_tbl}->{$r} ) { | 
| 277 | 10817 |  |  |  |  | 30615 | $info->{"${pfx}_ord"} = $self->{nf_tbl}->{$r}; | 
| 278 | 10817 |  |  |  |  | 23466 | $info->{"${pfx}_mod"} = -1; | 
| 279 | 10817 |  |  |  |  | 35296 | $info->{"${pfx}_canon"} = $self->{nf_canon}->[$self->{nf_tbl}->{$r}]; | 
| 280 |  |  |  |  |  |  | } | 
| 281 |  |  |  |  |  |  | else { | 
| 282 | 0 |  |  |  |  | 0 | Carp::croak("CANT HAPPEN ($r)"); | 
| 283 | 0 |  |  |  |  | 0 | return; | 
| 284 |  |  |  |  |  |  | } | 
| 285 |  |  |  |  |  |  | ####	$info->{isflat} = $info->{"${pfx}_mod"} < 0; | 
| 286 | 39872 |  |  |  |  | 184786 | }; | 
| 287 |  |  |  |  |  |  |  | 
| 288 | 39872 | 50 |  |  |  | 105778 | $ordmod->("root") unless $info->is_rootless; | 
| 289 |  |  |  |  |  |  |  | 
| 290 | 39872 | 50 |  |  |  | 134717 | cluck("BLESS info for $chord into ", $self->{target}, "\n") | 
| 291 |  |  |  |  |  |  | unless ref($info) =~ /ChordPro::Chord::/; | 
| 292 |  |  |  |  |  |  |  | 
| 293 | 39872 | 100 |  |  |  | 103302 | if ( $info->{bass} = $bass ) { | 
| 294 | 149 | 50 |  |  |  | 2679 | if ( $bass =~ /^$self->{n_pat}$/ ) { | 
| 295 | 149 |  |  |  |  | 446 | $ordmod->("bass"); | 
| 296 | 149 | 50 |  |  |  | 400 | if ( $info->is_rootless ) { | 
| 297 | 0 |  |  |  |  | 0 | for ( qw( ord mod canon ) ) { | 
| 298 | 0 |  |  |  |  | 0 | $info->{"root_$_"} = $info->{"bass_$_"}; | 
| 299 |  |  |  |  |  |  | } | 
| 300 |  |  |  |  |  |  | } | 
| 301 |  |  |  |  |  |  | } | 
| 302 |  |  |  |  |  |  | } | 
| 303 |  |  |  |  |  |  |  | 
| 304 | 39872 | 50 |  |  |  | 174824 | if ( $::config->{settings}->{'chords-canonical'} ) { | 
| 305 | 0 |  |  |  |  | 0 | my $t = $info->{name}; | 
| 306 | 0 |  |  |  |  | 0 | $info->{name_canon} = $info->canonical; | 
| 307 |  |  |  |  |  |  | warn("Parsing chord: \"$chord\" canon \"", $info->canonical, "\"\n" ) | 
| 308 | 0 | 0 | 0 |  |  | 0 | if $info->{name_canon} ne $t and $::config->{debug}->{chords}; | 
| 309 |  |  |  |  |  |  | } | 
| 310 |  |  |  |  |  |  |  | 
| 311 | 39872 |  |  |  |  | 591419 | return $info; | 
| 312 |  |  |  |  |  |  | } | 
| 313 |  |  |  |  |  |  |  | 
| 314 |  |  |  |  |  |  | ################ Chords ################ | 
| 315 |  |  |  |  |  |  |  | 
| 316 |  |  |  |  |  |  | # The following additions are recognized for major chords. | 
| 317 |  |  |  |  |  |  |  | 
| 318 |  |  |  |  |  |  | my $additions_maj = | 
| 319 |  |  |  |  |  |  | { | 
| 320 |  |  |  |  |  |  | map { $_ => $_ } | 
| 321 |  |  |  |  |  |  | "", | 
| 322 |  |  |  |  |  |  | "11", | 
| 323 |  |  |  |  |  |  | "13", | 
| 324 |  |  |  |  |  |  | "13#11", | 
| 325 |  |  |  |  |  |  | "13#9", | 
| 326 |  |  |  |  |  |  | "13b9", | 
| 327 |  |  |  |  |  |  | "2", | 
| 328 |  |  |  |  |  |  | "3", | 
| 329 |  |  |  |  |  |  | "4", | 
| 330 |  |  |  |  |  |  | "5", | 
| 331 |  |  |  |  |  |  | "6", | 
| 332 |  |  |  |  |  |  | "69", | 
| 333 |  |  |  |  |  |  | "6add9", | 
| 334 |  |  |  |  |  |  | "7", | 
| 335 |  |  |  |  |  |  | "711", | 
| 336 |  |  |  |  |  |  | "7add11", | 
| 337 |  |  |  |  |  |  | "7#11", | 
| 338 |  |  |  |  |  |  | "7#5", | 
| 339 |  |  |  |  |  |  | "7#9", | 
| 340 |  |  |  |  |  |  | "7#9#11", | 
| 341 |  |  |  |  |  |  | "7#9#5", | 
| 342 |  |  |  |  |  |  | "7#9b5", | 
| 343 |  |  |  |  |  |  | "7alt", | 
| 344 |  |  |  |  |  |  | "7b13", | 
| 345 |  |  |  |  |  |  | "7b13sus", | 
| 346 |  |  |  |  |  |  | "7b5", | 
| 347 |  |  |  |  |  |  | "7b9", | 
| 348 |  |  |  |  |  |  | "7b9#11", | 
| 349 |  |  |  |  |  |  | "7b9#5", | 
| 350 |  |  |  |  |  |  | "7b9#9", | 
| 351 |  |  |  |  |  |  | "7b9b13", | 
| 352 |  |  |  |  |  |  | "7b9b5", | 
| 353 |  |  |  |  |  |  | "7b9sus", | 
| 354 |  |  |  |  |  |  | "7-13", | 
| 355 |  |  |  |  |  |  | "7-13sus", | 
| 356 |  |  |  |  |  |  | "7-5", | 
| 357 |  |  |  |  |  |  | "7\\+5", | 
| 358 |  |  |  |  |  |  | "7-9", | 
| 359 |  |  |  |  |  |  | "7-9#11", | 
| 360 |  |  |  |  |  |  | "7-9#5", | 
| 361 |  |  |  |  |  |  | "7-9#9", | 
| 362 |  |  |  |  |  |  | "7-9-13", | 
| 363 |  |  |  |  |  |  | "7-9-5", | 
| 364 |  |  |  |  |  |  | "7-9sus", | 
| 365 |  |  |  |  |  |  | "7sus", | 
| 366 |  |  |  |  |  |  | "7susadd3", | 
| 367 |  |  |  |  |  |  | "7\\+",			# REGEXP!!! | 
| 368 |  |  |  |  |  |  | "9", | 
| 369 |  |  |  |  |  |  | "9\\+",			# REGEXP!!! | 
| 370 |  |  |  |  |  |  | "911", | 
| 371 |  |  |  |  |  |  | "9#11", | 
| 372 |  |  |  |  |  |  | "9#5", | 
| 373 |  |  |  |  |  |  | "9b5", | 
| 374 |  |  |  |  |  |  | "9-5", | 
| 375 |  |  |  |  |  |  | "9sus", | 
| 376 |  |  |  |  |  |  | "9add6", | 
| 377 |  |  |  |  |  |  | ( map { ( "maj$_", "^$_" ) } | 
| 378 |  |  |  |  |  |  | "", | 
| 379 |  |  |  |  |  |  | "13", | 
| 380 |  |  |  |  |  |  | "7", | 
| 381 |  |  |  |  |  |  | "711", | 
| 382 |  |  |  |  |  |  | "7#11", | 
| 383 |  |  |  |  |  |  | "7#5", | 
| 384 |  |  |  |  |  |  | ( map { "7sus$_" } "", "2", "4" ), | 
| 385 |  |  |  |  |  |  | "9", | 
| 386 |  |  |  |  |  |  | "911", | 
| 387 |  |  |  |  |  |  | "9#11", | 
| 388 |  |  |  |  |  |  | ), | 
| 389 |  |  |  |  |  |  | "alt", | 
| 390 |  |  |  |  |  |  | "h", | 
| 391 |  |  |  |  |  |  | "h7", | 
| 392 |  |  |  |  |  |  | "h9", | 
| 393 |  |  |  |  |  |  | ( map { "add$_"   }     "2", "4", "9", "11" ), | 
| 394 |  |  |  |  |  |  | ( map { "sus$_"   } "", "2", "4", "9" ), | 
| 395 |  |  |  |  |  |  | ( map { "6sus$_"  } "", "2", "4" ), | 
| 396 |  |  |  |  |  |  | ( map { "7sus$_"  } "", "2", "4" ), | 
| 397 |  |  |  |  |  |  | ( map { "13sus$_" } "", "2", "4" ), | 
| 398 |  |  |  |  |  |  | }; | 
| 399 |  |  |  |  |  |  |  | 
| 400 |  |  |  |  |  |  | # The following additions are recognized for minor chords. | 
| 401 |  |  |  |  |  |  |  | 
| 402 |  |  |  |  |  |  | my $additions_min = | 
| 403 |  |  |  |  |  |  | { | 
| 404 |  |  |  |  |  |  | map { $_ => $_ } | 
| 405 |  |  |  |  |  |  | "", | 
| 406 |  |  |  |  |  |  | "#5", | 
| 407 |  |  |  |  |  |  | "11", | 
| 408 |  |  |  |  |  |  | "13", | 
| 409 |  |  |  |  |  |  | "6", | 
| 410 |  |  |  |  |  |  | "69", | 
| 411 |  |  |  |  |  |  | "7b5", | 
| 412 |  |  |  |  |  |  | "7-5", | 
| 413 |  |  |  |  |  |  | ( map { ( "$_", "maj$_", "^$_" ) } | 
| 414 |  |  |  |  |  |  | "7", | 
| 415 |  |  |  |  |  |  | "9", | 
| 416 |  |  |  |  |  |  | ), | 
| 417 |  |  |  |  |  |  | "9maj7", "9^7", | 
| 418 |  |  |  |  |  |  | "add9", | 
| 419 |  |  |  |  |  |  | "b6", | 
| 420 |  |  |  |  |  |  | "#7", | 
| 421 |  |  |  |  |  |  | ( map { "sus$_" } "", "4", "9" ), | 
| 422 |  |  |  |  |  |  | ( map { "7sus$_" } "", "4" ), | 
| 423 |  |  |  |  |  |  | }; | 
| 424 |  |  |  |  |  |  |  | 
| 425 |  |  |  |  |  |  | # The following additions are recognized for augmented chords. | 
| 426 |  |  |  |  |  |  |  | 
| 427 |  |  |  |  |  |  | my $additions_aug = | 
| 428 |  |  |  |  |  |  | { | 
| 429 |  |  |  |  |  |  | map { $_ => $_ } | 
| 430 |  |  |  |  |  |  | "", | 
| 431 |  |  |  |  |  |  | "7", | 
| 432 |  |  |  |  |  |  | }; | 
| 433 |  |  |  |  |  |  |  | 
| 434 |  |  |  |  |  |  | # The following additions are recognized for diminished chords. | 
| 435 |  |  |  |  |  |  |  | 
| 436 |  |  |  |  |  |  | my $additions_dim = | 
| 437 |  |  |  |  |  |  | { | 
| 438 |  |  |  |  |  |  | map { $_ => $_ } | 
| 439 |  |  |  |  |  |  | "", | 
| 440 |  |  |  |  |  |  | "7", | 
| 441 |  |  |  |  |  |  | }; | 
| 442 |  |  |  |  |  |  |  | 
| 443 |  |  |  |  |  |  | # Build tables and patterns from the "notes" element from the | 
| 444 |  |  |  |  |  |  | # configuration. | 
| 445 |  |  |  |  |  |  |  | 
| 446 | 208 |  |  | 208 |  | 453 | sub load_notes ( $self, $init ) { | 
|  | 208 |  |  |  |  | 473 |  | 
|  | 208 |  |  |  |  | 499 |  | 
|  | 208 |  |  |  |  | 505 |  | 
| 447 | 208 |  | 50 |  |  | 576 | my $cfg = { %{$::config//{}}, %{$init//{}} }; | 
|  | 208 |  | 50 |  |  | 2143 |  | 
|  | 208 |  |  |  |  | 3743 |  | 
| 448 | 208 |  |  |  |  | 1225 | my $n = $cfg->{notes}; | 
| 449 | 208 | 50 |  |  |  | 956 | Carp::confess("No notes?") unless $n->{system}; | 
| 450 | 208 |  |  |  |  | 607 | my ( @ns_canon, %ns_tbl, @nf_canon, %nf_tbl ); | 
| 451 |  |  |  |  |  |  |  | 
| 452 | 208 |  |  |  |  | 549 | my $rix = 0; | 
| 453 | 208 |  |  |  |  | 447 | foreach my $root ( @{ $n->{sharp} } ) { | 
|  | 208 |  |  |  |  | 900 |  | 
| 454 | 2496 | 100 |  |  |  | 7599 | if ( UNIVERSAL::isa($root, 'ARRAY') ) { | 
| 455 | 1025 |  |  |  |  | 2759 | $ns_canon[$rix] = $root->[0]; | 
| 456 | 1025 |  |  |  |  | 6138 | $ns_tbl{$_} = $rix foreach @$root; | 
| 457 |  |  |  |  |  |  | } | 
| 458 |  |  |  |  |  |  | else { | 
| 459 | 1471 |  |  |  |  | 3151 | $ns_canon[$rix] = $root; | 
| 460 | 1471 |  |  |  |  | 3656 | $ns_tbl{$root} = $rix; | 
| 461 |  |  |  |  |  |  | } | 
| 462 | 2496 |  |  |  |  | 4062 | $rix++; | 
| 463 |  |  |  |  |  |  | } | 
| 464 | 208 |  |  |  |  | 889 | $rix = 0; | 
| 465 | 208 |  |  |  |  | 554 | foreach my $root ( @{ $n->{flat} } ) { | 
|  | 208 |  |  |  |  | 914 |  | 
| 466 | 2496 | 100 |  |  |  | 6046 | if ( UNIVERSAL::isa($root, 'ARRAY') ) { | 
| 467 | 1034 |  |  |  |  | 2765 | $nf_canon[$rix] = $root->[0]; | 
| 468 | 1034 |  |  |  |  | 6760 | $nf_tbl{$_} = $rix foreach @$root; | 
| 469 |  |  |  |  |  |  | } | 
| 470 |  |  |  |  |  |  | else { | 
| 471 | 1462 |  |  |  |  | 2847 | $nf_canon[$rix] = $root; | 
| 472 | 1462 |  |  |  |  | 2687 | $nf_tbl{$root} = $rix; | 
| 473 |  |  |  |  |  |  | } | 
| 474 | 2496 |  |  |  |  | 4053 | $rix++; | 
| 475 |  |  |  |  |  |  | } | 
| 476 |  |  |  |  |  |  |  | 
| 477 |  |  |  |  |  |  | # Pattern to match note names. | 
| 478 | 208 |  |  |  |  | 969 | my $n_pat = '(?:' ; | 
| 479 | 208 |  |  |  |  | 626 | my @n; | 
| 480 | 208 |  |  |  |  | 1668 | foreach ( keys %ns_tbl ) { | 
| 481 | 4521 |  |  |  |  | 7732 | push( @n, $_ ); | 
| 482 |  |  |  |  |  |  | } | 
| 483 | 208 |  |  |  |  | 4169 | foreach ( sort keys %nf_tbl ) { | 
| 484 | 4930 | 100 |  |  |  | 9560 | next if $ns_tbl{$_}; | 
| 485 | 3682 |  |  |  |  | 6450 | push( @n, $_ ); | 
| 486 |  |  |  |  |  |  | } | 
| 487 |  |  |  |  |  |  |  | 
| 488 | 208 |  |  |  |  | 2594 | $n_pat = '(?:' . join( '|', sort { length($b) <=> length($a) } @n ) . ')'; | 
|  | 32970 |  |  |  |  | 48163 |  | 
| 489 |  |  |  |  |  |  |  | 
| 490 |  |  |  |  |  |  | # Pattern to match chord names. | 
| 491 | 208 |  |  |  |  | 675 | my $c_pat; | 
| 492 |  |  |  |  |  |  | # Accept root, qual, and only known extensions. | 
| 493 | 208 |  |  |  |  | 928 | $c_pat = "(?" . $n_pat . ")"; | 
| 494 | 208 |  |  |  |  | 857 | $c_pat .= "(?:"; | 
| 495 | 208 |  |  |  |  | 3320 | $c_pat .= "(?-|min|m(?!aj))". | 
| 496 |  |  |  |  |  |  | "(?" . join("|", keys(%$additions_min)) . ")|"; | 
| 497 | 208 |  |  |  |  | 1921 | $c_pat .= "(?\\+|aug)". | 
| 498 |  |  |  |  |  |  | "(?" . join("|", keys(%$additions_aug)) . ")|"; | 
| 499 | 208 |  |  |  |  | 1241 | $c_pat .= "(?0|o|dim|h)". | 
| 500 |  |  |  |  |  |  | "(?" . join("|", keys(%$additions_dim)) . ")|"; | 
| 501 | 208 |  |  |  |  | 7737 | $c_pat .= "(?)". | 
| 502 |  |  |  |  |  |  | "(?" . join("|", keys(%$additions_maj)) . ")"; | 
| 503 | 208 |  |  |  |  | 1401 | $c_pat .= ")"; | 
| 504 | 208 |  |  |  |  | 84789 | $c_pat = qr/$c_pat/; | 
| 505 | 208 |  |  |  |  | 18757 | $n_pat = qr/$n_pat/; | 
| 506 |  |  |  |  |  |  |  | 
| 507 |  |  |  |  |  |  | # In relaxed form, we accept anything for extension. | 
| 508 | 208 |  |  |  |  | 1942 | my $c_rpat = "(?" . $n_pat . ")"; | 
| 509 | 208 |  |  |  |  | 883 | $c_rpat .= "(?:(?-|min|m(?!aj)|\\+|aug|0|o|dim|)(?.*))"; | 
| 510 | 208 |  |  |  |  | 22583 | $c_rpat = qr/$c_rpat/; | 
| 511 |  |  |  |  |  |  |  | 
| 512 |  |  |  |  |  |  | # Store in the object. | 
| 513 | 208 |  |  |  |  | 2459 | $self->{n_pat}    = $n_pat; | 
| 514 | 208 |  |  |  |  | 784 | $self->{c_pat}    = $c_pat; | 
| 515 | 208 |  |  |  |  | 632 | $self->{c_rpat}   = $c_rpat; | 
| 516 | 208 |  |  |  |  | 803 | $self->{ns_tbl}   = \%ns_tbl; | 
| 517 | 208 |  |  |  |  | 646 | $self->{nf_tbl}   = \%nf_tbl; | 
| 518 | 208 |  |  |  |  | 739 | $self->{ns_canon} = \@ns_canon; | 
| 519 | 208 |  |  |  |  | 828 | $self->{nf_canon} = \@nf_canon; | 
| 520 | 208 |  |  |  |  | 2384 | $self->{intervals} = @ns_canon; | 
| 521 |  |  |  |  |  |  | } | 
| 522 |  |  |  |  |  |  |  | 
| 523 | 882 |  |  | 882 |  | 1308 | sub root_canon ( $self, $root, $sharp = 0, $minor = 0 ) { | 
|  | 882 |  |  |  |  | 1232 |  | 
|  | 882 |  |  |  |  | 1243 |  | 
|  | 882 |  |  |  |  | 1402 |  | 
|  | 882 |  |  |  |  | 1314 |  | 
|  | 882 |  |  |  |  | 1213 |  | 
| 524 | 882 | 100 |  |  |  | 3369 | ( $sharp ? $self->{ns_canon} : $self->{nf_canon} )->[$root]; | 
| 525 |  |  |  |  |  |  | } | 
| 526 |  |  |  |  |  |  |  | 
| 527 |  |  |  |  |  |  | # Has chord diagrams. | 
| 528 | 181 |  |  | 181 |  | 348 | sub has_diagrams ( $self ) { !$self->{movable} } | 
|  | 181 |  |  |  |  | 339 |  | 
|  | 181 |  |  |  |  | 278 |  | 
|  | 181 |  |  |  |  | 676 |  | 
| 529 |  |  |  |  |  |  |  | 
| 530 |  |  |  |  |  |  | # Movable notes system. | 
| 531 | 8 |  |  | 8 |  | 14 | sub movable ( $self ) { $self->{movable} } | 
|  | 8 |  |  |  |  | 15 |  | 
|  | 8 |  |  |  |  | 9 |  | 
|  | 8 |  |  |  |  | 36 |  | 
| 532 |  |  |  |  |  |  |  | 
| 533 |  |  |  |  |  |  | ################ Parsing Nashville notated chords ################ | 
| 534 |  |  |  |  |  |  |  | 
| 535 |  |  |  |  |  |  | package ChordPro::Chords::Parser::Nashville; | 
| 536 |  |  |  |  |  |  |  | 
| 537 |  |  |  |  |  |  | our @ISA = qw(ChordPro::Chords::Parser::Common); | 
| 538 |  |  |  |  |  |  |  | 
| 539 | 79 |  |  | 79 |  | 760 | use Storable qw(dclone); | 
|  | 79 |  |  |  |  | 176 |  | 
|  | 79 |  |  |  |  | 27990 |  | 
| 540 |  |  |  |  |  |  |  | 
| 541 |  |  |  |  |  |  | sub new { | 
| 542 | 2 |  |  | 2 |  | 8 | my ( $pkg, $init ) = @_; | 
| 543 | 2 |  |  |  |  | 9 | my $self = bless { chord_cache => {} } => $pkg; | 
| 544 | 2 |  |  |  |  | 22 | $self->{system} = "nashville"; | 
| 545 | 2 |  |  |  |  | 5 | $self->{target} = 'ChordPro::Chord::Nashville'; | 
| 546 |  |  |  |  |  |  | warn("Chords: Created parser for ", $self->{system}, "\n") | 
| 547 | 2 | 50 | 33 |  |  | 11 | if $::options->{verbose} && $::options->{verbose} > 1; | 
| 548 | 2 |  |  |  |  | 17 | return $parsers{$self->{system}} = $self; | 
| 549 |  |  |  |  |  |  | } | 
| 550 |  |  |  |  |  |  |  | 
| 551 |  |  |  |  |  |  | my $n_pat = qr/(?[b#]?)(?[1-7])/; | 
| 552 |  |  |  |  |  |  |  | 
| 553 |  |  |  |  |  |  | my %nmap = ( 1 => 0, 2 => 2, 3 => 4, 4 => 5, 5 => 7, 6 => 9, 7 => 11 ); | 
| 554 |  |  |  |  |  |  | my @nmap = ( 1, 1, 2, 2, 3, 4, 4, 5, 5, 6, 6, 7, 1 ); | 
| 555 |  |  |  |  |  |  |  | 
| 556 |  |  |  |  |  |  | sub parse_chord { | 
| 557 | 408 | 50 |  | 408 |  | 1628 | Carp::confess("NMC") unless UNIVERSAL::isa($_[0],__PACKAGE__); | 
| 558 | 408 |  |  |  |  | 923 | my ( $self, $chord ) = @_; | 
| 559 |  |  |  |  |  |  |  | 
| 560 | 79 |  |  | 79 |  | 644 | $chord =~ tr/\x{266d}\x{266f}\x{0394}\x{f8}\x{b0}/b#^h0/; | 
|  | 79 |  |  |  |  | 186 |  | 
|  | 79 |  |  |  |  | 1430 |  | 
|  | 408 |  |  |  |  | 1286 |  | 
| 561 |  |  |  |  |  |  |  | 
| 562 | 408 |  |  |  |  | 698 | my $bass = ""; | 
| 563 | 408 | 50 |  |  |  | 1417 | if ( $chord =~ m;^(.*)/(.*); ) { | 
| 564 | 0 |  |  |  |  | 0 | $chord = $1; | 
| 565 | 0 |  |  |  |  | 0 | $bass = $2; | 
| 566 |  |  |  |  |  |  | } | 
| 567 |  |  |  |  |  |  |  | 
| 568 | 408 | 50 |  |  |  | 4458 | return unless $chord =~ /^$n_pat(?-|\+|0|o|aug|m(?!aj)|dim)?(?.*)$/; | 
| 569 |  |  |  |  |  |  |  | 
| 570 |  |  |  |  |  |  | my $info = { system => "nashville", | 
| 571 |  |  |  |  |  |  | parser => $self, | 
| 572 |  |  |  |  |  |  | name   => $_[1], | 
| 573 |  |  |  |  |  |  | root   => $+{root}, | 
| 574 | 408 |  |  |  |  | 4104 | }; | 
| 575 | 408 |  |  |  |  | 1385 | bless $info => $self->{target}; | 
| 576 |  |  |  |  |  |  |  | 
| 577 | 408 |  | 100 |  |  | 2013 | my $q = $+{qual} // ""; | 
| 578 | 408 |  |  |  |  | 1059 | $info->{qual} = $q; | 
| 579 | 408 | 50 |  |  |  | 1008 | $q = "-" if $q eq "m"; | 
| 580 | 408 | 50 |  |  |  | 797 | $q = "+" if $q eq "aug"; | 
| 581 | 408 | 50 |  |  |  | 734 | $q = "0" if $q eq "dim"; | 
| 582 | 408 | 50 |  |  |  | 713 | $q = "0" if $q eq "o"; | 
| 583 | 408 |  |  |  |  | 811 | $info->{qual_canon} = $q; | 
| 584 |  |  |  |  |  |  |  | 
| 585 | 408 |  | 50 |  |  | 1551 | my $x = $+{ext} // ""; | 
| 586 | 408 |  |  |  |  | 920 | $info->{ext} = $x; | 
| 587 | 408 | 50 |  |  |  | 781 | $x = "sus4" if $x eq "sus"; | 
| 588 | 408 |  |  |  |  | 1035 | $info->{ext_canon} = $x; | 
| 589 |  |  |  |  |  |  |  | 
| 590 |  |  |  |  |  |  | my $ordmod = sub { | 
| 591 | 408 |  |  | 408 |  | 824 | my ( $pfx ) = @_; | 
| 592 | 408 |  |  |  |  | 902 | my $r = 0 + $info->{$pfx}; | 
| 593 | 408 |  |  |  |  | 1343 | $info->{"${pfx}_ord"} = $nmap{$r}; | 
| 594 | 408 | 100 |  |  |  | 2612 | if ( $+{shift} eq "#" ) { | 
|  |  | 100 |  |  |  |  |  | 
| 595 | 120 |  |  |  |  | 333 | $info->{"${pfx}_mod"} = 1; | 
| 596 | 120 |  |  |  |  | 266 | $info->{"${pfx}_ord"}++; | 
| 597 |  |  |  |  |  |  | $info->{"${pfx}_ord"} = 0 | 
| 598 | 120 | 50 |  |  |  | 313 | if $info->{"${pfx}_ord"} >= 12; | 
| 599 |  |  |  |  |  |  | } | 
| 600 |  |  |  |  |  |  | elsif ( $+{shift} eq "b" ) { | 
| 601 | 120 |  |  |  |  | 313 | $info->{"${pfx}_mod"} = -1; | 
| 602 | 120 |  |  |  |  | 241 | $info->{"${pfx}_ord"}--; | 
| 603 |  |  |  |  |  |  | $info->{"${pfx}_ord"} += 12 | 
| 604 | 120 | 50 |  |  |  | 343 | if $info->{"${pfx}_ord"} < 0; | 
| 605 |  |  |  |  |  |  | } | 
| 606 |  |  |  |  |  |  | else { | 
| 607 | 168 |  |  |  |  | 465 | $info->{"${pfx}_mod"} = 0; | 
| 608 |  |  |  |  |  |  | } | 
| 609 | 408 |  |  |  |  | 1402 | $info->{"${pfx}_canon"} = $r; | 
| 610 | 408 |  |  |  |  | 2219 | }; | 
| 611 |  |  |  |  |  |  |  | 
| 612 | 408 |  |  |  |  | 1118 | $ordmod->("root"); | 
| 613 |  |  |  |  |  |  |  | 
| 614 | 408 |  |  |  |  | 868 | $info->{bass} = $bass; | 
| 615 | 408 | 50 |  |  |  | 4076 | return $info unless $bass; | 
| 616 | 0 | 0 |  |  |  | 0 | return unless $bass =~ /^$n_pat$/; | 
| 617 | 0 |  |  |  |  | 0 | $ordmod->("bass"); | 
| 618 |  |  |  |  |  |  |  | 
| 619 | 0 |  |  |  |  | 0 | return $info; | 
| 620 |  |  |  |  |  |  | } | 
| 621 |  |  |  |  |  |  |  | 
| 622 | 0 |  |  | 0 |  | 0 | sub load_notes { Carp::confess("OOPS") } | 
| 623 |  |  |  |  |  |  |  | 
| 624 |  |  |  |  |  |  | sub root_canon { | 
| 625 | 12 | 50 |  | 12 |  | 41 | Carp::confess("NMC") unless UNIVERSAL::isa($_[0],__PACKAGE__); | 
| 626 | 12 |  |  |  |  | 30 | my ( $self, $root, $sharp ) = @_; | 
| 627 | 79 |  |  | 79 |  | 480893 | no warnings 'qw'; | 
|  | 79 |  |  |  |  | 234 |  | 
|  | 79 |  |  |  |  | 17511 |  | 
| 628 | 12 | 50 |  |  |  | 45 | $sharp | 
| 629 |  |  |  |  |  |  | ? qw( 1 #1 2 #2 3 4 #4 5 #5 6 #6 7 )[$root] | 
| 630 |  |  |  |  |  |  | : qw( 1 b2 2 b3 3 4 b5 5 b6 6 b7 7 )[$root] | 
| 631 |  |  |  |  |  |  | } | 
| 632 |  |  |  |  |  |  |  | 
| 633 |  |  |  |  |  |  | # Has chord diagrams. | 
| 634 |  |  |  |  |  |  | sub has_diagrams { | 
| 635 | 2 | 50 |  | 2 |  | 11 | Carp::confess("NMC") unless UNIVERSAL::isa($_[0],__PACKAGE__); | 
| 636 | 2 |  |  |  |  | 7 | 0; | 
| 637 |  |  |  |  |  |  | } | 
| 638 |  |  |  |  |  |  |  | 
| 639 |  |  |  |  |  |  | # Movable notes system. | 
| 640 |  |  |  |  |  |  | sub movable { | 
| 641 | 2 | 50 |  | 2 |  | 11 | Carp::confess("NMC") unless UNIVERSAL::isa($_[0],__PACKAGE__); | 
| 642 | 2 |  |  |  |  | 10 | 1; | 
| 643 |  |  |  |  |  |  | } | 
| 644 |  |  |  |  |  |  |  | 
| 645 |  |  |  |  |  |  | ################ Parsing Roman notated chords ################ | 
| 646 |  |  |  |  |  |  |  | 
| 647 |  |  |  |  |  |  | package ChordPro::Chords::Parser::Roman; | 
| 648 |  |  |  |  |  |  |  | 
| 649 | 79 |  |  | 79 |  | 699 | use ChordPro; | 
|  | 79 |  |  |  |  | 213 |  | 
|  | 79 |  |  |  |  | 27313 |  | 
| 650 |  |  |  |  |  |  |  | 
| 651 |  |  |  |  |  |  | our @ISA = qw(ChordPro::Chords::Parser::Common); | 
| 652 |  |  |  |  |  |  |  | 
| 653 |  |  |  |  |  |  | sub new { | 
| 654 | 2 |  |  | 2 |  | 10 | my ( $pkg, $init ) = @_; | 
| 655 | 2 |  |  |  |  | 10 | my $self = bless { chord_cache => {} } => $pkg; | 
| 656 | 2 |  |  |  |  | 34 | $self->{system} = "roman"; | 
| 657 | 2 |  |  |  |  | 6 | $self->{target} = 'ChordPro::Chord::Roman'; | 
| 658 |  |  |  |  |  |  | warn("Chords: Created parser for ", $self->{system}, "\n") | 
| 659 | 2 | 50 | 33 |  |  | 23 | if $::options->{verbose} && $::options->{verbose} > 1; | 
| 660 | 2 |  |  |  |  | 17 | return $parsers{$self->{system}} = $self; | 
| 661 |  |  |  |  |  |  | } | 
| 662 |  |  |  |  |  |  |  | 
| 663 |  |  |  |  |  |  | my $r_pat = qr/(?[b#]?)(?(?i)iii|ii|iv|i|viii|vii|vi|v)/; | 
| 664 |  |  |  |  |  |  |  | 
| 665 |  |  |  |  |  |  | my %rmap = ( I => 0, II => 2, III => 4, IV => 5, V => 7, VI => 9, VII => 11 ); | 
| 666 |  |  |  |  |  |  |  | 
| 667 |  |  |  |  |  |  | sub parse_chord { | 
| 668 | 612 | 50 |  | 612 |  | 2397 | Carp::confess("NMC") unless UNIVERSAL::isa($_[0],__PACKAGE__); | 
| 669 | 612 |  |  |  |  | 1483 | my ( $self, $chord ) = @_; | 
| 670 |  |  |  |  |  |  |  | 
| 671 | 612 |  |  |  |  | 1874 | $chord =~ tr/\x{266d}\x{266f}\x{0394}\x{f8}\x{b0}/b#^h0/; | 
| 672 |  |  |  |  |  |  |  | 
| 673 | 612 |  |  |  |  | 1095 | my $bass = ""; | 
| 674 | 612 | 50 |  |  |  | 2028 | if ( $chord =~ m;^(.*)/(.*); ) { | 
| 675 | 0 |  |  |  |  | 0 | $chord = $1; | 
| 676 | 0 |  |  |  |  | 0 | $bass = $2; | 
| 677 |  |  |  |  |  |  | } | 
| 678 |  |  |  |  |  |  |  | 
| 679 | 612 | 50 |  |  |  | 6773 | return unless $chord =~ /^$r_pat(?\+|0|o|aug|dim|h)?(?.*)$/; | 
| 680 | 612 |  |  |  |  | 5573 | my $r = $+{shift}.$+{root}; | 
| 681 |  |  |  |  |  |  |  | 
| 682 | 612 |  |  |  |  | 3677 | my $info = { system => "roman", | 
| 683 |  |  |  |  |  |  | parser => $self, | 
| 684 |  |  |  |  |  |  | name   => $_[1], | 
| 685 |  |  |  |  |  |  | root   => $r }; | 
| 686 | 612 |  |  |  |  | 1543 | bless $info => $self->{target}; | 
| 687 |  |  |  |  |  |  |  | 
| 688 | 612 |  | 100 |  |  | 2838 | my $q = $+{qual} // ""; | 
| 689 | 612 |  |  |  |  | 1667 | $info->{qual} = $q; | 
| 690 | 612 | 100 |  |  |  | 1915 | $q = "-" if $r eq lc($r); | 
| 691 | 612 | 50 |  |  |  | 1397 | $q = "+" if $q eq "aug"; | 
| 692 | 612 | 50 |  |  |  | 1201 | $q = "0" if $q eq "dim"; | 
| 693 | 612 | 50 |  |  |  | 1187 | $q = "0" if $q eq "o"; | 
| 694 | 612 |  |  |  |  | 1111 | $info->{qual_canon} = $q; | 
| 695 |  |  |  |  |  |  |  | 
| 696 | 612 |  | 50 |  |  | 2309 | my $x = $+{ext} // ""; | 
| 697 | 612 |  |  |  |  | 1614 | $info->{ext} = $x; | 
| 698 | 612 | 50 |  |  |  | 1246 | $x = "sus4" if $x eq "sus"; | 
| 699 | 612 | 50 |  |  |  | 1065 | $x = "^7" if $x eq "7+"; | 
| 700 | 612 |  |  |  |  | 1736 | $info->{ext_canon} = $x; | 
| 701 |  |  |  |  |  |  |  | 
| 702 |  |  |  |  |  |  | my $ordmod = sub { | 
| 703 | 612 |  |  | 612 |  | 1259 | my ( $pfx ) = @_; | 
| 704 | 612 |  |  |  |  | 1198 | my $r = $info->{$pfx}; | 
| 705 | 612 |  |  |  |  | 2567 | $info->{"${pfx}_ord"} = $rmap{uc $r}; | 
| 706 | 612 | 100 |  |  |  | 3577 | if ( $+{shift} eq "#" ) { | 
|  |  | 100 |  |  |  |  |  | 
| 707 | 180 |  |  |  |  | 444 | $info->{"${pfx}_mod"} = 1; | 
| 708 | 180 |  |  |  |  | 391 | $info->{"${pfx}_ord"}++; | 
| 709 |  |  |  |  |  |  | $info->{"${pfx}_ord"} = 0 | 
| 710 | 180 | 50 |  |  |  | 497 | if $info->{"${pfx}_ord"} >= 12; | 
| 711 |  |  |  |  |  |  | } | 
| 712 |  |  |  |  |  |  | elsif ( $+{shift} eq "b" ) { | 
| 713 | 180 |  |  |  |  | 485 | $info->{"${pfx}_mod"} = -1; | 
| 714 | 180 |  |  |  |  | 418 | $info->{"${pfx}_ord"}--; | 
| 715 |  |  |  |  |  |  | $info->{"${pfx}_ord"} += 12 | 
| 716 | 180 | 50 |  |  |  | 660 | if $info->{"${pfx}_ord"} < 0; | 
| 717 |  |  |  |  |  |  | } | 
| 718 |  |  |  |  |  |  | else { | 
| 719 | 252 |  |  |  |  | 676 | $info->{"${pfx}_mod"} = 0; | 
| 720 |  |  |  |  |  |  | } | 
| 721 | 612 |  |  |  |  | 1985 | $info->{"${pfx}_canon"} = $r; | 
| 722 | 612 |  |  |  |  | 3397 | }; | 
| 723 |  |  |  |  |  |  |  | 
| 724 | 612 |  |  |  |  | 1703 | $ordmod->("root"); | 
| 725 |  |  |  |  |  |  |  | 
| 726 | 612 |  |  |  |  | 1361 | $info->{bass} = uc $bass; | 
| 727 | 612 | 50 |  |  |  | 6276 | return $info unless $bass; | 
| 728 | 0 | 0 |  |  |  | 0 | return unless $bass =~ /^$r_pat$/; | 
| 729 | 0 |  |  |  |  | 0 | $ordmod->("bass"); | 
| 730 |  |  |  |  |  |  |  | 
| 731 | 0 |  |  |  |  | 0 | return $info; | 
| 732 |  |  |  |  |  |  | } | 
| 733 |  |  |  |  |  |  |  | 
| 734 | 0 |  |  | 0 |  | 0 | sub load_notes { Carp::confess("OOPS") } | 
| 735 |  |  |  |  |  |  |  | 
| 736 |  |  |  |  |  |  | sub root_canon { | 
| 737 | 12 | 50 |  | 12 |  | 42 | Carp::confess("NMC") unless UNIVERSAL::isa($_[0],__PACKAGE__); | 
| 738 | 12 |  |  |  |  | 27 | my ( $self, $root, $sharp, $minor ) = @_; | 
| 739 | 12 | 50 |  |  |  | 24 | return lc( $self->root_canon( $root, $sharp ) ) if $minor; | 
| 740 | 79 |  |  | 79 |  | 73423 | no warnings 'qw'; | 
|  | 79 |  |  |  |  | 230 |  | 
|  | 79 |  |  |  |  | 17606 |  | 
| 741 | 12 | 50 |  |  |  | 49 | $sharp | 
| 742 |  |  |  |  |  |  | ? qw( I #I II #II III IV #IV V #V VI #VI VII )[$root] | 
| 743 |  |  |  |  |  |  | : qw( I bII II bIII III IV bV V bVI VI bVII VII )[$root] | 
| 744 |  |  |  |  |  |  | } | 
| 745 |  |  |  |  |  |  |  | 
| 746 |  |  |  |  |  |  | # Has chord diagrams. | 
| 747 |  |  |  |  |  |  | sub has_diagrams { | 
| 748 | 2 | 50 |  | 2 |  | 12 | Carp::confess("NMC") unless UNIVERSAL::isa($_[0],__PACKAGE__); | 
| 749 | 2 |  |  |  |  | 6 | 0; | 
| 750 |  |  |  |  |  |  | } | 
| 751 |  |  |  |  |  |  |  | 
| 752 |  |  |  |  |  |  | # Movable notes system. | 
| 753 |  |  |  |  |  |  | sub movable { | 
| 754 | 2 | 50 |  | 2 |  | 12 | Carp::confess("NMC") unless UNIVERSAL::isa($_[0],__PACKAGE__); | 
| 755 | 2 |  |  |  |  | 9 | 1; | 
| 756 |  |  |  |  |  |  | } | 
| 757 |  |  |  |  |  |  |  | 
| 758 |  |  |  |  |  |  | ################ Chord objects: Common ################ | 
| 759 |  |  |  |  |  |  |  | 
| 760 |  |  |  |  |  |  | package ChordPro::Chord::Base; | 
| 761 |  |  |  |  |  |  |  | 
| 762 | 79 |  |  | 79 |  | 667 | use Storable qw(dclone); | 
|  | 79 |  |  |  |  | 242 |  | 
|  | 79 |  |  |  |  | 121815 |  | 
| 763 |  |  |  |  |  |  |  | 
| 764 |  |  |  |  |  |  | sub new { | 
| 765 | 13508 |  |  | 13508 |  | 31573 | my ( $pkg, $data ) = @_; | 
| 766 | 13508 |  | 66 |  |  | 39761 | $pkg = ref($pkg) || $pkg; | 
| 767 | 13508 |  |  |  |  | 86913 | bless { %$data } => $pkg; | 
| 768 |  |  |  |  |  |  | } | 
| 769 |  |  |  |  |  |  |  | 
| 770 |  |  |  |  |  |  | sub clone { | 
| 771 | 376 | 50 |  | 376 |  | 1482 | Carp::confess("NMC") unless UNIVERSAL::isa($_[0],__PACKAGE__); | 
| 772 | 376 |  |  |  |  | 781 | my ( $self ) = shift; | 
| 773 | 376 |  |  |  |  | 37642 | dclone($self); | 
| 774 |  |  |  |  |  |  | } | 
| 775 |  |  |  |  |  |  |  | 
| 776 | 40710 |  |  | 40710 |  | 113186 | sub is_note { $_[0]->{isnote} }; | 
| 777 | 0 |  |  | 0 |  | 0 | sub is_flat { $_[0]->{isflat} }; | 
| 778 | 942 |  |  | 942 |  | 2590 | sub is_keyboard { $_[0]->{iskeyboard} }; | 
| 779 |  |  |  |  |  |  |  | 
| 780 |  |  |  |  |  |  | sub is_nc { | 
| 781 | 942 |  |  | 942 |  | 1887 | my ( $self ) = @_; | 
| 782 |  |  |  |  |  |  | # Keyboard... | 
| 783 | 942 | 50 | 0 |  |  | 2017 | return 1 if $self->is_keyboard && !@{ $self->kbkeys // [1] }; | 
|  | 0 |  | 33 |  |  | 0 |  | 
| 784 |  |  |  |  |  |  | # Strings... | 
| 785 | 942 | 100 | 100 |  |  | 1545 | return unless @{ $self->frets // [] }; | 
|  | 942 |  |  |  |  | 2068 |  | 
| 786 | 780 |  |  |  |  | 1265 | for ( @{ $self->frets } ) { | 
|  | 780 |  |  |  |  | 1359 |  | 
| 787 | 1621 | 100 |  |  |  | 5320 | return unless $_ < 0; | 
| 788 |  |  |  |  |  |  | } | 
| 789 | 14 |  |  |  |  | 66 | return 1;			# all -1 => N.C. | 
| 790 |  |  |  |  |  |  | } | 
| 791 |  |  |  |  |  |  |  | 
| 792 |  |  |  |  |  |  | # Can be transposed/transcoded. | 
| 793 |  |  |  |  |  |  | sub is_xpxc { | 
| 794 | 1339 | 100 | 66 | 1339 |  | 6725 | defined($_[0]->{root}) || defined($_[0]->{bass}) || $_[0]->is_nc; | 
| 795 |  |  |  |  |  |  | } | 
| 796 |  |  |  |  |  |  |  | 
| 797 |  |  |  |  |  |  | sub has_diagram { | 
| 798 | 814 |  |  | 814 |  | 1538 | my ( $self ) = @_; | 
| 799 |  |  |  |  |  |  | ( $::config->{instrument}->{type} eq "keyboard" ) | 
| 800 | 0 |  | 0 |  |  | 0 | ? @{ $self->kbkeys // []} | 
| 801 | 814 | 50 | 50 |  |  | 1812 | : @{ $self->frets  // []}; | 
|  | 814 |  |  |  |  | 1489 |  | 
| 802 |  |  |  |  |  |  | } | 
| 803 |  |  |  |  |  |  |  | 
| 804 |  |  |  |  |  |  | # For convenience. | 
| 805 | 170244 |  |  | 170244 |  | 583775 | sub is_chord      { defined $_[0]->{root_ord} } | 
| 806 | 79090 |  |  | 79090 |  | 250684 | sub is_rootless   { $_[0]->{rootless} } | 
| 807 | 590 |  |  | 590 |  | 1962 | sub is_annotation { 0 } | 
| 808 | 0 |  |  | 0 |  | 0 | sub is_movable    { $_[0]->{movable} } | 
| 809 |  |  |  |  |  |  |  | 
| 810 |  |  |  |  |  |  | # Common accessors. | 
| 811 |  |  |  |  |  |  | sub name          { | 
| 812 | 6347 |  |  | 6347 |  | 12128 | my ( $self, $np ) = @_; | 
| 813 |  |  |  |  |  |  | Carp::confess("Double parens") | 
| 814 | 6347 | 50 | 33 |  |  | 14139 | if $self->{parens} && $self->{name} =~ /^\(.*\)$/; | 
| 815 | 6347 | 50 | 33 |  |  | 41793 | return $self->{name} if $np || !$self->{parens}; | 
| 816 | 0 |  |  |  |  | 0 | "(" . $self->{name} . ")"; | 
| 817 |  |  |  |  |  |  | } | 
| 818 |  |  |  |  |  |  |  | 
| 819 | 0 |  |  | 0 |  | 0 | sub canon         { $_[0]->{name_canon} } | 
| 820 | 4 |  |  | 4 |  | 25 | sub root          { $_[0]->{root} } | 
| 821 | 4 |  |  | 4 |  | 28 | sub qual          { $_[0]->{qual} } | 
| 822 | 4 |  |  | 4 |  | 24 | sub ext           { $_[0]->{ext} } | 
| 823 | 4 |  |  | 4 |  | 141 | sub bass          { $_[0]->{bass} } | 
| 824 | 3 |  |  | 3 |  | 2029 | sub base          { $_[0]->{base} } | 
| 825 | 2537 |  |  | 2537 |  | 9556 | sub frets         { $_[0]->{frets} } | 
| 826 | 1 |  |  | 1 |  | 10 | sub fingers       { $_[0]->{fingers} } | 
| 827 | 0 |  |  | 0 |  | 0 | sub display       { $_[0]->{display} } | 
| 828 | 0 |  |  | 0 |  | 0 | sub format        { $_[0]->{format} } | 
| 829 | 7 |  |  | 7 |  | 57 | sub diagram       { $_[0]->{diagram} } | 
| 830 | 71 |  |  | 71 |  | 201 | sub parser        { $_[0]->{parser} } | 
| 831 |  |  |  |  |  |  |  | 
| 832 |  |  |  |  |  |  | sub strings { | 
| 833 | 0 |  |  | 0 |  | 0 | $_[0]->{parser}->{intervals}; | 
| 834 |  |  |  |  |  |  | } | 
| 835 |  |  |  |  |  |  |  | 
| 836 |  |  |  |  |  |  | sub kbkeys { | 
| 837 | 1 | 50 | 33 | 1 |  | 7 | return $_[0]->{keys} if $_[0]->{keys} && @{$_[0]->{keys}}; | 
|  | 1 |  |  |  |  | 11 |  | 
| 838 | 0 |  |  |  |  | 0 | $_[0]->{keys} = ChordPro::Chords::get_keys($_[0]); | 
| 839 |  |  |  |  |  |  | } | 
| 840 |  |  |  |  |  |  |  | 
| 841 | 711 |  |  | 711 |  | 1058 | sub flat_copy ( $self, $ret, $o, $pfx = "" ) { | 
|  | 711 |  |  |  |  | 1071 |  | 
|  | 711 |  |  |  |  | 1124 |  | 
|  | 711 |  |  |  |  | 1038 |  | 
|  | 711 |  |  |  |  | 1181 |  | 
|  | 711 |  |  |  |  | 955 |  | 
| 842 | 711 |  |  |  |  | 3009 | while ( my ( $k, $v ) = each %$o ) { | 
| 843 | 14226 | 100 |  |  |  | 24328 | if ( $k eq "orig" ) { | 
| 844 | 10 |  |  |  |  | 60 | $self->flat_copy( $ret, $v, "$k.$pfx"); | 
| 845 |  |  |  |  |  |  | } | 
| 846 |  |  |  |  |  |  | else { | 
| 847 | 14216 |  |  |  |  | 48189 | $ret->{"$pfx$k"} = $v; | 
| 848 |  |  |  |  |  |  | } | 
| 849 |  |  |  |  |  |  | } | 
| 850 | 711 |  |  |  |  | 1400 | $ret; | 
| 851 |  |  |  |  |  |  | } | 
| 852 |  |  |  |  |  |  |  | 
| 853 | 22 |  |  | 22 |  | 34 | sub fix_musicsyms ( $self, $str ) { | 
|  | 22 |  |  |  |  | 38 |  | 
|  | 22 |  |  |  |  | 30 |  | 
|  | 22 |  |  |  |  | 36 |  | 
| 854 |  |  |  |  |  |  |  | 
| 855 | 79 |  |  | 79 |  | 722 | use ChordPro::Utils qw( splitmarkup ); | 
|  | 79 |  |  |  |  | 192 |  | 
|  | 79 |  |  |  |  | 49202 |  | 
| 856 |  |  |  |  |  |  |  | 
| 857 | 22 | 50 |  |  |  | 52 | return $str unless $::config->{settings}->{truesf}; | 
| 858 |  |  |  |  |  |  |  | 
| 859 | 22 |  |  |  |  | 66 | my @c = splitmarkup($str); | 
| 860 | 22 |  |  |  |  | 45 | my $res = ''; | 
| 861 | 22 | 100 |  |  |  | 70 | push( @c, '' ) if @c % 2; | 
| 862 | 22 |  |  |  |  | 32 | my $did = 0;		# TODO: not for roman | 
| 863 | 22 |  |  |  |  | 46 | while ( @c ) { | 
| 864 | 37 |  |  |  |  | 66 | $_ = shift(@c); | 
| 865 | 37 | 100 |  |  |  | 90 | if ( $did ) { | 
| 866 | 15 |  |  |  |  | 35 | s/b/♭/g; | 
| 867 |  |  |  |  |  |  | } | 
| 868 |  |  |  |  |  |  | else { | 
| 869 | 22 |  |  |  |  | 81 | s/(?<=[[:alnum:]])b/♭/g; | 
| 870 | 22 |  |  |  |  | 41 | $did++; | 
| 871 |  |  |  |  |  |  | } | 
| 872 | 37 |  |  |  |  | 86 | s/#/♯/g; | 
| 873 | 37 |  |  |  |  | 114 | $res .= $_ . shift(@c); | 
| 874 |  |  |  |  |  |  | } | 
| 875 | 22 |  |  |  |  | 139 | $res; | 
| 876 |  |  |  |  |  |  | } | 
| 877 |  |  |  |  |  |  |  | 
| 878 | 0 |  |  | 0 |  | 0 | sub simplify ( $self ) { | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 879 | 0 |  |  |  |  | 0 | my $c = {}; | 
| 880 | 0 |  |  |  |  | 0 | for ( keys %$self ) { | 
| 881 | 0 | 0 |  |  |  | 0 | next unless defined $self->{$_}; | 
| 882 | 0 | 0 |  |  |  | 0 | next if defined $c->{$_}; | 
| 883 | 0 | 0 | 0 |  |  | 0 | if ( UNIVERSAL::can( $self->{$_}, "simplify" ) ) { | 
|  |  | 0 |  |  |  |  |  | 
| 884 | 0 |  |  |  |  | 0 | $c->{$_} = $self->{$_}->simplify; | 
| 885 |  |  |  |  |  |  | } | 
| 886 | 0 |  |  |  |  | 0 | elsif ( ref($self->{$_}) eq 'ARRAY' && @{$self->{$_}} ) { | 
| 887 | 0 |  |  |  |  | 0 | $c->{$_} = "[ " . join(" ", @{$self->{$_}}) . " ]"; | 
|  | 0 |  |  |  |  | 0 |  | 
| 888 |  |  |  |  |  |  | } | 
| 889 |  |  |  |  |  |  | else { | 
| 890 | 0 |  |  |  |  | 0 | $c->{$_} = $self->{$_}; | 
| 891 |  |  |  |  |  |  | } | 
| 892 |  |  |  |  |  |  | } | 
| 893 | 0 |  |  |  |  | 0 | $c; | 
| 894 |  |  |  |  |  |  | } | 
| 895 |  |  |  |  |  |  |  | 
| 896 | 0 |  |  | 0 |  | 0 | sub dump ( $self ) { | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 897 | 0 |  |  |  |  | 0 | ::dump($self->simplify); | 
| 898 |  |  |  |  |  |  | } | 
| 899 |  |  |  |  |  |  |  | 
| 900 |  |  |  |  |  |  | package ChordPro::Chord::Common; | 
| 901 |  |  |  |  |  |  |  | 
| 902 |  |  |  |  |  |  | our @ISA = qw( ChordPro::Chord::Base ); | 
| 903 |  |  |  |  |  |  |  | 
| 904 | 79 |  |  | 79 |  | 46704 | use String::Interpolate::Named; | 
|  | 79 |  |  |  |  | 115806 |  | 
|  | 79 |  |  |  |  | 104674 |  | 
| 905 |  |  |  |  |  |  |  | 
| 906 |  |  |  |  |  |  | # Show reconstructs the chord from its constituents. | 
| 907 |  |  |  |  |  |  | # Result is canonical. | 
| 908 |  |  |  |  |  |  | sub show { | 
| 909 | 0 |  |  | 0 |  | 0 | Carp::croak("call canonical instead of show"); | 
| 910 |  |  |  |  |  |  | } | 
| 911 |  |  |  |  |  |  |  | 
| 912 | 515 |  |  | 515 |  | 764 | sub canonical ( $self ) { | 
|  | 515 |  |  |  |  | 755 |  | 
|  | 515 |  |  |  |  | 691 |  | 
| 913 | 515 |  |  |  |  | 780 | my $res; | 
| 914 |  |  |  |  |  |  |  | 
| 915 |  |  |  |  |  |  | $res = | 
| 916 |  |  |  |  |  |  | $self->is_rootless | 
| 917 |  |  |  |  |  |  | ? "" | 
| 918 |  |  |  |  |  |  | : $self->is_chord | 
| 919 |  |  |  |  |  |  | ? $self->{parser}->root_canon( $self->{root_ord}, | 
| 920 |  |  |  |  |  |  | $self->{root_mod} >= 0, | 
| 921 |  |  |  |  |  |  | $self->{qual} eq '-', | 
| 922 |  |  |  |  |  |  | # !$self->is_flat ??? | 
| 923 |  |  |  |  |  |  | ) . $self->{qual} . $self->{ext} | 
| 924 | 515 | 50 |  |  |  | 1102 | : $self->{name}; | 
|  |  | 50 |  |  |  |  |  | 
| 925 |  |  |  |  |  |  |  | 
| 926 | 515 | 100 |  |  |  | 1312 | if ( $self->is_note ) { | 
| 927 | 4 |  |  |  |  | 19 | return lcfirst($res); | 
| 928 |  |  |  |  |  |  | } | 
| 929 | 511 | 100 | 66 |  |  | 1303 | if ( $self->{bass} && $self->{bass} ne "" ) { | 
| 930 |  |  |  |  |  |  | $res .= "/" . | 
| 931 | 3 | 50 |  |  |  | 14 | ($self->{system} eq "roman" ? lc($self->{bass}) : $self->{bass}); | 
| 932 |  |  |  |  |  |  | } | 
| 933 | 511 |  |  |  |  | 1371 | return $res; | 
| 934 |  |  |  |  |  |  | } | 
| 935 |  |  |  |  |  |  |  | 
| 936 |  |  |  |  |  |  | # Returns a representation indepent of notation system. | 
| 937 | 38554 |  |  | 38554 |  | 59171 | sub agnostic ( $self ) { | 
|  | 38554 |  |  |  |  | 59240 |  | 
|  | 38554 |  |  |  |  | 52454 |  | 
| 938 | 38554 | 100 | 66 |  |  | 72855 | return if $self->is_rootless || $self->is_note; | 
| 939 |  |  |  |  |  |  | join( " ", "", | 
| 940 |  |  |  |  |  |  | $self->{root_ord}, | 
| 941 |  |  |  |  |  |  | $self->{root_mod}, | 
| 942 |  |  |  |  |  |  | $self->{qual_canon}, | 
| 943 |  |  |  |  |  |  | $self->{ext_canon}, | 
| 944 | 38548 |  | 100 |  |  | 215817 | $self->{bass_ord} // () ); | 
| 945 |  |  |  |  |  |  | } | 
| 946 |  |  |  |  |  |  |  | 
| 947 | 382 |  |  | 382 |  | 661 | sub transpose ( $self, $xpose, $dir = 0 ) { | 
|  | 382 |  |  |  |  | 590 |  | 
|  | 382 |  |  |  |  | 599 |  | 
|  | 382 |  |  |  |  | 608 |  | 
|  | 382 |  |  |  |  | 538 |  | 
| 948 | 382 | 100 |  |  |  | 870 | return $self unless $xpose; | 
| 949 | 370 | 100 |  |  |  | 859 | return $self unless $self->is_chord; | 
| 950 | 368 |  | 33 |  |  | 897 | $dir //= $xpose <=> 0; | 
| 951 |  |  |  |  |  |  |  | 
| 952 | 368 |  |  |  |  | 893 | my $info = $self->clone; | 
| 953 | 368 |  |  |  |  | 272621 | my $p = $self->{parser}; | 
| 954 |  |  |  |  |  |  |  | 
| 955 | 368 | 50 |  |  |  | 1378 | unless ( $self->{rootless} ) { | 
| 956 | 368 |  |  |  |  | 1371 | $info->{root_ord} = ( $self->{root_ord} + $xpose ) % $p->intervals; | 
| 957 |  |  |  |  |  |  | $info->{root_canon} = $info->{root} = | 
| 958 |  |  |  |  |  |  | $p->root_canon( $info->{root_ord}, | 
| 959 |  |  |  |  |  |  | $dir > 0, | 
| 960 | 368 |  |  |  |  | 1304 | $info->{qual_canon} eq "-" ); | 
| 961 |  |  |  |  |  |  | } | 
| 962 | 368 | 50 | 66 |  |  | 1183 | if ( $self->{bass} && $self->{bass} ne "" && $self->{bass} !~ /^\d+$/ ) { | 
|  |  |  | 66 |  |  |  |  | 
| 963 | 3 |  |  |  |  | 11 | $info->{bass_ord} = ( $self->{bass_ord} + $xpose ) % $p->intervals; | 
| 964 |  |  |  |  |  |  | $info->{bass_canon} = $info->{bass} = | 
| 965 | 3 |  |  |  |  | 11 | $p->root_canon( $info->{bass_ord}, $xpose > 0 ); | 
| 966 | 3 |  |  |  |  | 8 | $info->{bass_mod} = $dir; | 
| 967 |  |  |  |  |  |  | } | 
| 968 | 368 |  |  |  |  | 609 | $info->{root_mod} = $dir; | 
| 969 | 368 |  |  |  |  | 951 | $info->{name} = $info->{name_canon} = $info->canonical; | 
| 970 |  |  |  |  |  |  |  | 
| 971 | 368 |  |  |  |  | 1840 | delete $info->{$_} for qw( copy base frets fingers keys display ); | 
| 972 |  |  |  |  |  |  |  | 
| 973 | 368 |  |  |  |  | 1297 | return $info; | 
| 974 |  |  |  |  |  |  | } | 
| 975 |  |  |  |  |  |  |  | 
| 976 | 143 |  |  | 143 |  | 243 | sub transcode ( $self, $xcode, $key_ord = 0 ) { | 
|  | 143 |  |  |  |  | 224 |  | 
|  | 143 |  |  |  |  | 245 |  | 
|  | 143 |  |  |  |  | 245 |  | 
|  | 143 |  |  |  |  | 186 |  | 
| 977 | 143 | 100 |  |  |  | 462 | return $self unless $xcode; | 
| 978 | 20 | 50 |  |  |  | 58 | return $self unless $self->is_chord; | 
| 979 | 20 | 50 |  |  |  | 102 | return $self if $self->{system} eq $xcode; | 
| 980 | 20 |  |  |  |  | 2013 | my $info = $self->dclone; | 
| 981 |  |  |  |  |  |  | #warn("_>_XCODE = $xcode, _SELF = $self->{system}, CHORD = $info->{name}"); | 
| 982 | 20 |  |  |  |  | 14771 | $info->{system} = $xcode; | 
| 983 | 20 |  |  |  |  | 112 | my $p = $self->{parser}->get_parser($xcode); | 
| 984 | 20 | 50 |  |  |  | 69 | die("OOPS ", $p->{system}, " $xcode") unless $p->{system} eq $xcode; | 
| 985 | 20 |  |  |  |  | 406 | $info->{parser} = $p; | 
| 986 | 20 | 100 | 100 |  |  | 102 | $info->{root_ord} -= $key_ord if $key_ord && $p->movable; | 
| 987 |  |  |  |  |  |  | #    $info->{$_} = $p->{$_} for qw( ns_tbl nf_tbl ns_canon nf_canon ); | 
| 988 |  |  |  |  |  |  | $info->{root_canon} = $info->{root} = | 
| 989 |  |  |  |  |  |  | $p->root_canon( $info->{root_ord}, | 
| 990 |  |  |  |  |  |  | $info->{root_mod} >= 0, | 
| 991 | 20 |  |  |  |  | 158 | $info->{qual_canon} eq "-" ); | 
| 992 | 20 | 50 | 66 |  |  | 72 | if ( $p->{system} eq "roman" && $info->{qual_canon} eq "-" ) { | 
| 993 |  |  |  |  |  |  | # Minor quality is in the root name. | 
| 994 | 0 |  |  |  |  | 0 | $info->{qual_canon} = $info->{qual} = ""; | 
| 995 |  |  |  |  |  |  | } | 
| 996 | 20 | 50 | 33 |  |  | 62 | if ( $self->{bass} && $self->{bass} ne "" ) { | 
| 997 | 0 | 0 | 0 |  |  | 0 | $info->{bass_ord} -= $key_ord if $key_ord && $p->movable; | 
| 998 |  |  |  |  |  |  | $info->{bass_canon} = $info->{bass} = | 
| 999 | 0 |  |  |  |  | 0 | $p->root_canon( $info->{bass_ord}, $info->{bass_mod} >= 0 ); | 
| 1000 |  |  |  |  |  |  | } | 
| 1001 | 20 |  |  |  |  | 57 | $info->{name} = $info->{name_canon} = $info->canonical; | 
| 1002 | 20 |  |  |  |  | 41 | $info->{system} = $p->{system}; | 
| 1003 | 20 |  |  |  |  | 52 | bless $info => $p->{target}; | 
| 1004 |  |  |  |  |  |  | #    ::dump($info); | 
| 1005 |  |  |  |  |  |  | #warn("_<_XCODE = $xcode, CHORD = ", $info->canonical); | 
| 1006 | 20 |  |  |  |  | 120 | return $info; | 
| 1007 |  |  |  |  |  |  | } | 
| 1008 |  |  |  |  |  |  |  | 
| 1009 | 701 |  |  | 701 |  | 1132 | sub chord_display ( $self ) { | 
|  | 701 |  |  |  |  | 1128 |  | 
|  | 701 |  |  |  |  | 956 |  | 
| 1010 |  |  |  |  |  |  |  | 
| 1011 |  |  |  |  |  |  | # $self->dump; | 
| 1012 |  |  |  |  |  |  |  | 
| 1013 | 701 |  |  |  |  | 1603 | my $res = $self->name; | 
| 1014 | 701 |  |  |  |  | 1559 | my $args = {}; | 
| 1015 | 701 |  | 33 |  |  | 3492 | $self->flat_copy( $args, $self->{display} // $self ); | 
| 1016 |  |  |  |  |  |  |  | 
| 1017 | 701 |  |  |  |  | 2298 | for my $fmt ( $::config->{settings}->{"chord-format"}, | 
| 1018 |  |  |  |  |  |  | $self->{format}, | 
| 1019 |  |  |  |  |  |  | $self->{chordformat} ) { | 
| 1020 | 2103 | 100 |  |  |  | 486442 | next unless $fmt; | 
| 1021 | 713 | 100 |  |  |  | 1799 | $args->{root} = lc($args->{root}) if $self->is_note; | 
| 1022 | 713 |  |  |  |  | 1689 | $args->{formatted} = $res; | 
| 1023 | 713 |  |  |  |  | 2873 | $res = interpolate( { args => $args }, $fmt ); | 
| 1024 |  |  |  |  |  |  | } | 
| 1025 |  |  |  |  |  |  |  | 
| 1026 |  |  |  |  |  |  | # Substitute musical symbols if wanted. | 
| 1027 | 701 | 100 |  |  |  | 5370 | return $::config->{settings}->{truesf} ? $self->fix_musicsyms($res) : $res; | 
| 1028 |  |  |  |  |  |  | } | 
| 1029 |  |  |  |  |  |  |  | 
| 1030 |  |  |  |  |  |  | ################ Chord objects: Nashville ################ | 
| 1031 |  |  |  |  |  |  |  | 
| 1032 |  |  |  |  |  |  | package ChordPro::Chord::Nashville; | 
| 1033 |  |  |  |  |  |  |  | 
| 1034 |  |  |  |  |  |  | our @ISA = 'ChordPro::Chord::Base'; | 
| 1035 |  |  |  |  |  |  |  | 
| 1036 | 79 |  |  | 79 |  | 794 | use String::Interpolate::Named; | 
|  | 79 |  |  |  |  | 223 |  | 
|  | 79 |  |  |  |  | 39970 |  | 
| 1037 |  |  |  |  |  |  |  | 
| 1038 | 0 |  |  | 0 |  | 0 | sub transpose ( $self ) { $self } | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 1039 |  |  |  |  |  |  |  | 
| 1040 |  |  |  |  |  |  | sub show { | 
| 1041 | 0 |  |  | 0 |  | 0 | Carp::croak("call canonical instead of show"); | 
| 1042 |  |  |  |  |  |  | } | 
| 1043 |  |  |  |  |  |  |  | 
| 1044 | 0 |  |  | 0 |  | 0 | sub canonical ( $self ) { | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 1045 | 0 |  |  |  |  | 0 | my $res = $self->{root_canon} . $self->{qual} . $self->{ext}; | 
| 1046 | 0 | 0 | 0 |  |  | 0 | if ( $self->{bass} && $self->{bass} ne "" ) { | 
| 1047 | 0 |  |  |  |  | 0 | $res .= "/" . lc($self->{bass}); | 
| 1048 |  |  |  |  |  |  | } | 
| 1049 | 0 |  |  |  |  | 0 | return $res; | 
| 1050 |  |  |  |  |  |  | } | 
| 1051 |  |  |  |  |  |  |  | 
| 1052 | 4 |  |  | 4 |  | 7 | sub chord_display ( $self ) { | 
|  | 4 |  |  |  |  | 5 |  | 
|  | 4 |  |  |  |  | 7 |  | 
| 1053 | 4 | 50 |  |  |  | 11 | if ( $self->{format} ) { | 
| 1054 |  |  |  |  |  |  | ####TODO | 
| 1055 | 0 |  | 0 |  |  | 0 | my $fmt = $self->{format} || $::config->{settings}->{"chord-format"}; | 
| 1056 | 0 | 0 |  |  |  | 0 | if ( $fmt ) { | 
| 1057 | 0 |  |  |  |  | 0 | my $args = {}; | 
| 1058 | 0 |  |  |  |  | 0 | $self->flat_copy( $args, $self ); | 
| 1059 | 0 |  |  |  |  | 0 | return interpolate( { args => $args }, $fmt ); | 
| 1060 |  |  |  |  |  |  | } | 
| 1061 |  |  |  |  |  |  | } | 
| 1062 |  |  |  |  |  |  |  | 
| 1063 |  |  |  |  |  |  | my $res = $self->{root_canon} . | 
| 1064 | 4 |  |  |  |  | 14 | "" . $self->{qual} . $self->{ext} . ""; | 
| 1065 | 4 | 50 | 33 |  |  | 13 | if ( $self->{bass} && $self->{bass} ne "" ) { | 
| 1066 | 0 |  |  |  |  | 0 | $res .= "/" . lc($self->{bass}) . ""; | 
| 1067 |  |  |  |  |  |  | } | 
| 1068 | 4 |  |  |  |  | 14 | return $res; | 
| 1069 |  |  |  |  |  |  | } | 
| 1070 |  |  |  |  |  |  |  | 
| 1071 |  |  |  |  |  |  | ################ Chord objects: Roman ################ | 
| 1072 |  |  |  |  |  |  |  | 
| 1073 |  |  |  |  |  |  | package ChordPro::Chord::Roman; | 
| 1074 |  |  |  |  |  |  |  | 
| 1075 |  |  |  |  |  |  | our @ISA = 'ChordPro::Chord::Base'; | 
| 1076 |  |  |  |  |  |  |  | 
| 1077 | 79 |  |  | 79 |  | 688 | use String::Interpolate::Named; | 
|  | 79 |  |  |  |  | 207 |  | 
|  | 79 |  |  |  |  | 37609 |  | 
| 1078 |  |  |  |  |  |  |  | 
| 1079 | 0 |  |  | 0 |  | 0 | sub transpose ( $self ) { $self } | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 1080 |  |  |  |  |  |  |  | 
| 1081 |  |  |  |  |  |  | sub show { | 
| 1082 | 0 |  |  | 0 |  | 0 | Carp::croak("call canonical instead of show"); | 
| 1083 |  |  |  |  |  |  | } | 
| 1084 |  |  |  |  |  |  |  | 
| 1085 | 0 |  |  | 0 |  | 0 | sub canonical ( $self ) { | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 1086 | 0 |  |  |  |  | 0 | my $res = $self->{root_canon} . $self->{qual} . $self->{ext}; | 
| 1087 | 0 | 0 | 0 |  |  | 0 | if ( $self->{bass} && $self->{bass} ne "" ) { | 
| 1088 | 0 |  |  |  |  | 0 | $res .= "/" . lc($self->{bass}); | 
| 1089 |  |  |  |  |  |  | } | 
| 1090 | 0 |  |  |  |  | 0 | return $res; | 
| 1091 |  |  |  |  |  |  | } | 
| 1092 |  |  |  |  |  |  |  | 
| 1093 | 4 |  |  | 4 |  | 8 | sub chord_display ( $self ) { | 
|  | 4 |  |  |  |  | 6 |  | 
|  | 4 |  |  |  |  | 7 |  | 
| 1094 | 4 | 50 |  |  |  | 10 | if ( $self->{format} ) { | 
| 1095 |  |  |  |  |  |  | ####TODO | 
| 1096 | 0 |  | 0 |  |  | 0 | my $fmt = $self->{format} || $::config->{settings}->{"chord-format"}; | 
| 1097 | 0 | 0 |  |  |  | 0 | if ( $fmt ) { | 
| 1098 | 0 |  |  |  |  | 0 | my $args = {}; | 
| 1099 | 0 |  |  |  |  | 0 | $self->flat_copy( $args, $self ); | 
| 1100 | 0 |  |  |  |  | 0 | return interpolate( { args => $args }, $fmt ); | 
| 1101 |  |  |  |  |  |  | } | 
| 1102 | 0 |  |  |  |  | 0 | return $self->canonical; | 
| 1103 |  |  |  |  |  |  | } | 
| 1104 |  |  |  |  |  |  |  | 
| 1105 | 4 |  |  |  |  | 10 | my $res = $self->{root_canon}; | 
| 1106 |  |  |  |  |  |  | $res .= "" . $self->{qual} . $self->{ext} . "" | 
| 1107 | 4 | 50 |  |  |  | 8 | if $self->{qual}; | 
| 1108 | 4 | 50 | 33 |  |  | 12 | if ( $self->{bass} && $self->{bass} ne "" ) { | 
| 1109 | 0 |  |  |  |  | 0 | $res .= "/" . lc($self->{bass}) . ""; | 
| 1110 |  |  |  |  |  |  | } | 
| 1111 | 4 |  |  |  |  | 14 | return $res; | 
| 1112 |  |  |  |  |  |  | } | 
| 1113 |  |  |  |  |  |  |  | 
| 1114 |  |  |  |  |  |  | ################ Chord objects: Annotations ################ | 
| 1115 |  |  |  |  |  |  |  | 
| 1116 |  |  |  |  |  |  | package ChordPro::Chord::Annotation; | 
| 1117 |  |  |  |  |  |  |  | 
| 1118 | 79 |  |  | 79 |  | 718 | use String::Interpolate::Named; | 
|  | 79 |  |  |  |  | 199 |  | 
|  | 79 |  |  |  |  | 58617 |  | 
| 1119 |  |  |  |  |  |  |  | 
| 1120 |  |  |  |  |  |  | our @ISA = 'ChordPro::Chord::Base'; | 
| 1121 |  |  |  |  |  |  |  | 
| 1122 | 0 |  |  | 0 |  | 0 | sub transpose ( $self ) { $self } | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 1123 | 0 |  |  | 0 |  | 0 | sub transcode ( $self ) { $self } | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 1124 |  |  |  |  |  |  |  | 
| 1125 | 0 |  |  | 0 |  | 0 | sub canonical ( $self ) { | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 1126 | 0 |  |  |  |  | 0 | my $res = $self->{text}; | 
| 1127 | 0 |  |  |  |  | 0 | return $res; | 
| 1128 |  |  |  |  |  |  | } | 
| 1129 |  |  |  |  |  |  |  | 
| 1130 | 2 |  |  | 2 |  | 6 | sub chord_display ( $self ) { | 
|  | 2 |  |  |  |  | 4 |  | 
|  | 2 |  |  |  |  | 4 |  | 
| 1131 | 2 |  |  |  |  | 12 | return interpolate( { args => $self }, $self->{text} ); | 
| 1132 |  |  |  |  |  |  | } | 
| 1133 |  |  |  |  |  |  |  | 
| 1134 |  |  |  |  |  |  | # For convenience. | 
| 1135 | 0 |  |  | 0 |  | 0 | sub is_chord      ( $self ) { 0 }; | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 1136 | 3 |  |  | 3 |  | 9 | sub is_annotation ( $self ) { 1 }; | 
|  | 3 |  |  |  |  | 9 |  | 
|  | 3 |  |  |  |  | 4 |  | 
|  | 3 |  |  |  |  | 20 |  | 
| 1137 |  |  |  |  |  |  |  | 
| 1138 |  |  |  |  |  |  | ################ Testing ################ | 
| 1139 |  |  |  |  |  |  |  | 
| 1140 |  |  |  |  |  |  | package main; | 
| 1141 |  |  |  |  |  |  |  | 
| 1142 |  |  |  |  |  |  | unless ( caller ) { | 
| 1143 |  |  |  |  |  |  | select(STDERR); | 
| 1144 |  |  |  |  |  |  | binmode(STDERR, ':utf8'); | 
| 1145 |  |  |  |  |  |  | $::config = { settings => { chordnames => "strict" } }; | 
| 1146 |  |  |  |  |  |  | $::options = { verbose => 2 }; | 
| 1147 |  |  |  |  |  |  | foreach ( @ARGV ) { | 
| 1148 |  |  |  |  |  |  | if ( $_ eq '-' ) { | 
| 1149 |  |  |  |  |  |  | $::config = { settings => { chordnames => "relaxed" } }; | 
| 1150 |  |  |  |  |  |  | ChordPro::Chords::Parser->reset_parsers("common"); | 
| 1151 |  |  |  |  |  |  | next; | 
| 1152 |  |  |  |  |  |  | } | 
| 1153 |  |  |  |  |  |  | my $p0 = ChordPro::Chords::Parser->default; | 
| 1154 |  |  |  |  |  |  | my $p1 = ChordPro::Chords::Parser->get_parser("common", 1); | 
| 1155 |  |  |  |  |  |  | die unless $p0 eq $p1; | 
| 1156 |  |  |  |  |  |  | my $p2 = ChordPro::Chords::Parser->get_parser("nashville", 1); | 
| 1157 |  |  |  |  |  |  | my $p3 = ChordPro::Chords::Parser->get_parser("roman", 1); | 
| 1158 |  |  |  |  |  |  | my $info = $p1->parse($_); | 
| 1159 |  |  |  |  |  |  | $info = $p2->parse($_) if !$info && $p2; | 
| 1160 |  |  |  |  |  |  | $info = $p3->parse($_) if !$info && $p3; | 
| 1161 |  |  |  |  |  |  | print( "$_ => OOPS\n" ), next unless $info; | 
| 1162 |  |  |  |  |  |  | print( "$_ ($info->{system}) =>" ); | 
| 1163 |  |  |  |  |  |  | print( " ", $info->transcode($_)->canonical, " ($_)" ) | 
| 1164 |  |  |  |  |  |  | for qw( common nashville roman ); | 
| 1165 |  |  |  |  |  |  | print( " '", $info->agnostic, "' (agnostic)\n" ); | 
| 1166 |  |  |  |  |  |  | print( "$_ =>" ); | 
| 1167 |  |  |  |  |  |  | print( " ", $info->transpose($_)->canonical, " ($_)" ) for -2..2; | 
| 1168 |  |  |  |  |  |  | print( "\n" ); | 
| 1169 |  |  |  |  |  |  | #	my $clone = $info->clone; | 
| 1170 |  |  |  |  |  |  | #	delete($clone->{parser}); | 
| 1171 |  |  |  |  |  |  | #	print( ::dump($clone), "\n" ); | 
| 1172 |  |  |  |  |  |  | } | 
| 1173 |  |  |  |  |  |  | } | 
| 1174 |  |  |  |  |  |  |  | 
| 1175 |  |  |  |  |  |  | 1; |