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 )