App-Music-ChordPro
view release on metacpan or search on metacpan
lib/ChordPro/A2Crd.pm view on Meta::CPAN
Note that the output from the conversion will generally need some
additional editing to be useful as input to ChordPro.
B<a2crd> is a wrapper around L<ChordPro::A2Crd>, which
does all of the work.
B<chordpro> will read one or more text files containing the lyrics of
one or many songs plus chord information. B<chordpro> will then
generate a photo-ready, professional looking, impress-your-friends
sheet-music suitable for printing on your nearest printer.
B<chordpro> is a rewrite of the Chordii program.
For more information about the ChordPro file format, see
L<https://www.chordpro.org>.
=cut
################ Common stuff ################
use strict;
use warnings;
use utf8;
use Carp;
################ The Process ################
package main;
our $options;
our $config;
package ChordPro::A2Crd;
use ChordPro::Config;
my $local_debug;
# API: Main entry point.
sub a2crd {
my ($opts) = @_;
$options = { %$options, %$opts } if $opts;
# One configurator to bind them all.
$config = ChordPro::Config::configurator({});
$local_debug = $config->{debug}->{a2crd};
# Process input.
my $lines = $opts->{lines}
? delete($opts->{lines})
: fs_load( @ARGV ? $ARGV[0] : \*STDIN);
return [ a2cho($lines) ];
}
################ Subroutines ################
# Replace tabs with blanks, retaining layout.
my $tabstop;
sub expand {
my ( $line ) = @_;
return $line unless $line;
$tabstop //= $::config->{a2crd}->{tabstop};
return $line unless $tabstop > 0;
my ( @l ) = split( /\t/, $line, -1 );
return $l[0] if @l == 1;
$line = shift(@l);
$line .= " " x ($tabstop-length($line)%$tabstop) . shift(@l) while @l;
return $line;
}
# API: Produce ChordPro data from AsciiCRD lines.
sub a2cho {
my ( $lines ) = @_;
my $map = "";
my @lines_with_tabs_replaced ;
foreach ( @$lines ) {
if(/\t/) {
$_ = expand($_) ;
}
#s/=20/ /g ; # replace HTML coded space with ascii space, no, MUST LEAVE IN because it can mess up fingering diagrams like A/F#=202220
s/=3D/=/g ; # replace HTML coded equal with ascii =
# s/\s*$// ; # remove all trailing whitespace -- no, MUST LEAVE IN so chords indicated above trailing whitespace will be properly formatted
my $n_ch_chords=0 ;
#An odd format for chords, [ch]Chordname[\ch], possibly from reformated webpage
# need to strip out and consider it to be a chord line
while(s/\[ch\](.*?)\[\/ch\]/$1/) {
$n_ch_chords++ ;
}
push @lines_with_tabs_replaced, $_ ;
if($n_ch_chords < 1) {
$map .= classify($_);
} else {
$map .= "c" ;
}
}
maplines( $map, \@lines_with_tabs_replaced );
}
# Classify the line and return a single-char token.
my $classify;
sub classify {
my ( $line ) = @_;
return '_' if $line =~ /^\s*$/; # empty line
return '{' if $line =~ /^\{.+/; # directive
unless ( defined $classify ) {
my $classifier = $::config->{a2crd}->{classifier};
$classify = __PACKAGE__->can("classify_".$classifier);
unless ( $classify ) {
warn("No such classifier: $classifier, using classic\n");
$classify = \&classify_classic;
}
}
$classify->($line);
}
sub classify_classic {
my ( $line ) = @_;
# Lyrics or Chords heuristic.
my @words = split ( /\s+/, $line );
my $len = length($line);
$line =~ s/\s+//g;
my $type = ( $len / length($line) - 1 ) < 1 ? 'l' : 'c';
my $p = ChordPro::Chords::Parser->default;
if ( $type eq 'l') {
foreach (@words) {
if (length $_ > 0) {
if (!ChordPro::Chords::parse_chord($_)) {
return 'l';
}
}
}
( run in 0.490 second using v1.01-cache-2.11-cpan-5b529ec07f3 )