Music::Image::Chord - Perl extension for generating guitar tab chords


Music-Image-Chord documentation Contained in the Music-Image-Chord distribution.

Index


Code Index:

NAME

Top

Music::Image::Chord - Perl extension for generating guitar tab chords

SYNOPSIS

Top

  use Music::Image::Chord;
  $image = new Music::Image::Chord();
  $old_font = $image->font('/path/to/my/TrueType/font.ttf');
  $old_file = $image->file('/path/to/file/to/save.png');
  $image->draw(name => 'D'); # Write the actual file.

DESCRIPTION

Top

Image::Chord is a simple package for creating images of guitar chords in any format that the Imager module can produce.

The object's API is as follows:

new Image::Chord()

Creates and returns a new Image::Chord object.

bar_thickness

Returns and optionally sets the thickness of the fret bar. Only used if on the first fret (which is the default).

bounds

Returns and optionally sets the image boundaries. It accepts several formats:

 $image->bounds($width, $height);
 $image->bounds(width=>$wid, height=>$hgt);
 $image->bounds(xmin=>$xmin, xmax=>$xmax, yMIN=>$ymin, YMax=>$ymax);

crop_width

Returns and optionally sets the width/height of the crop marks on the image. If this value is not set or is <= 0, then crop marks will not be drawn.

 $image->crop_width(5); # Crop marks will be 5 pixels wide.
 print $image->crop_width(); # Display the current crop mark width.

debug

Returns and optionally sets the Imager debugging flag.

 $image->debug(1); # Set debugging
 print $image->debug(); # Get the current debugging value

font

Returns and optionally sets the font used to render the chord's title.

 $image->font('/home/jgoff/Bach.ttf'); # Display in the Bach TrueType font
 $foo = $image->font(); # Assign $foo to the image's font

file

Returns and optionally sets the file name to save the rendered image to. It also reads the extension of the file to determine how to save the image.

 $image->file('/home/jgoff/chord.png');

fret

Returns and optionally sets the beginning fret in the image. Defaults to 1.

 $image->fret(5);
 print $image->fret(); # prints the current fret setting.

draw([optional named parameters])

Renders the chord described into the appropriate file. Optional named parameters are:

  name - The name of the chord
  fret - Beginning fret
  barres - Chord barres, not implemented yet.
  chord - If the chord isn't represented in the list, describe it like 'xx0232'.

$image->draw( name => 'D' ); # Draw a D chord

grid

Returns and optionally sets the grid coordinates. X and Y are the UL corner of the grid, and w and h control (for the moment, this will change) the inter-string and inter-fret spacing.

 $image->grid($x,$y,$w,$h); # Set explicitly
 $image->grid(x=>$x,y=>$y,w=>$w,h=>$h); # Set through named parameters.

SEE ALSO

Top

Imager perl(1).

AUTHOR

Top

Jeffrey Goff, <jgoff@cpan.org<gt> Inspiration by #perl

COPYRIGHT AND LICENSE

Top


Music-Image-Chord documentation Contained in the Music-Image-Chord distribution.

package Music::Image::Chord;

use strict;
use warnings;
use vars qw($VERSION);

$VERSION = '0.006';

use Imager;

my $standard_6 =
	{
	c => 'x32010',
	d => 'xxO232',
	e => '022100',
	g => '210002',
	a => 'x02220',
	f => 'xx3211',
	b => 'xx4442',

	cm => 'xx5543',
	dm => 'xx0231',
	em => '022000',
	gm => 'xx5333',
	am => 'x02210',
	fm => 'xx3111',
	bm => 'xx4432',

	c7 => 'x32310',
	d7 => 'xx0212',
	e7 => '020100',
	g7 => '320001',
	a7 => 'x02020',
	f7 => 'xx1211',
	b7 => 'x21202',
	};

my $black = Imager::Color->new(0,0,0);
my $white = Imager::Color->new(255,255,255);

sub new
	{
	my $class = shift;
	bless {}, $class;
	}

sub bar_thickness { shift->{bar_thickness} ||= shift; }
sub crop_width    { shift->{crop_width}    ||= shift; }
sub debug         { shift->{debug}         ||= shift; }
sub font          { shift->{font}          ||= shift; }
sub file          { shift->{file}          ||= shift; }
sub fret          { shift->{fret}          ||= shift; }

sub bounds
	{
	my $self = shift;
	if(@_>0)
		{
		if($_[0]=~/\D/)
			{
			while(@_)
				{
				$_ = shift;
				if(/^w/i)
					{ $self->{bounds}->{w}    = shift; }
				elsif(/^h/i)
					{ $self->{bounds}->{h}    = shift; }
				elsif(/^xmin/i)
					{ $self->{bounds}->{xmin} = shift; }
				elsif(/^xmax/i)
					{ $self->{bounds}->{xmax} = shift; }
				elsif(/^ymin/i)
					{ $self->{bounds}->{ymin} = shift; }
				elsif(/^ymax/i)
					{ $self->{bounds}->{ymax} = shift; }
				}
			}
		else
			{
			$self->{bounds}->{w} = shift;
			$self->{bounds}->{h} = shift;
			}
		$self->{bounds}->{w} ||=
			$self->{bounds}->{xmax} - $self->{bounds}->{xmin};
		$self->{bounds}->{h} ||=
			$self->{bounds}->{ymax} - $self->{bounds}->{ymin};
		}
	return $self->{bounds};
	}

sub grid
	{
	my $self = shift;
	if(@_>0)
		{
		if($_[0]=~/\D/)
			{
			while(@_)
				{
				$_ = shift;
				if(/^w/i)    { $self->{grid}->{w} = shift; }
				elsif(/^h/i) { $self->{grid}->{h} = shift; }
				elsif(/^x/i) { $self->{grid}->{x} = shift; }
				elsif(/^y/i) { $self->{grid}->{y} = shift; }
				}
			}
		else
			{
			$self->{grid}->{x} = shift;
			$self->{grid}->{y} = shift;
			$self->{grid}->{w} = shift;
			$self->{grid}->{h} = shift;
			}
		}
	return %{$self->{grid}};
	}

sub draw
	{
	my $self = shift;
	while(@_)
		{
		my ($k,$v)=(shift(),shift());
		$self->{$k}=$v;
		}
	$self->{chord}  ||= $standard_6->{lc $self->{name}};

	if(length($self->{chord})<6)
		{
		$self->{chord} .= 'x' x 6-length($self->{chord});
		}

	my $i = 0;

	for(split //,lc $self->{chord})
		{
		if(/x/i)
			{
			push @{$self->{closed}},$i;
			}
		elsif(/[o0]/i)
			{
			push @{$self->{open}},$i;
			}
		else
			{
			push @{$self->{fingering}->[$_-1]},$i if $_>0 and $_<5;
			}
		$i++;
		}

	$self->{open_r}=($self->{grid}->{w}/2)-1;
	$self->{image} = Imager->new
		(
		xsize    => $self->bounds()->{w},
		ysize    => $self->bounds()->{h},
		channels => 1,
		);
	$self->{image}->{DEBUG}=1 if $self->debug();
	$self->{image}->box
		(
		color  => $white,
		xmin   => 0,
		ymin   => 0,
		xmax   => $self->bounds()->{w},
		ymax   => $self->bounds()->{h},
		filled => 1,
		);

	$self->_cropmarks() if $self->crop_width() > 0;
	$self->_grid();
	$self->_open_strings();
	$self->_closed_strings();
	$self->_fingering();
	$self->_top_label();

	$self->file()=~/\.(.*)$/;
	$self->{type} = $1;
	$self->{image}->write(file=>$self->{file},type=>$self->{type});
	}

sub _top_label
	{
	my $self = shift;
	my $text = $self->{name} || $self->{labels}{top};
	my $font = new Imager::Font(file => $self->font());
	$self->{image}->string
		(
		font  => $font,
		text  => $text,
		x     => 0,
		y     => 20,
		size  => 20,
		color => $black
		);
	}

sub _cropmarks
	{
	my $self       = shift;
	my $bounds     = $self->bounds();
	my $crop_width = $self->crop_width();
	$self->{image}->polyline
		(
		points=>
			[
			[0,$crop_width],
			[0,0],
			[$crop_width,0]
			],
		color=>$black
		);
	$self->{image}->polyline
		(
		points=>
			[
			[$bounds->{w}-$crop_width-1,$bounds->{h}-1],
			[$bounds->{w}-1,$bounds->{h}-1],
			[$bounds->{w}-1,$bounds->{h}-$crop_width-1]
			],
		color=>$black
		);
	}

sub _closed_strings
	{
	my $self   = shift;
	my $y      = $self->{grid}->{y}+$self->{grid}->{h}-$self->{grid}->{w};

	for(@{$self->{closed}})
		{
		my $x = $self->{grid}->{x}+$self->{open_r}+($self->{grid}->{w}*$_);
		$self->{image}->polyline
			(
			points=>
				[
				[$x-$self->{open_r},$y-$self->{open_r}],
				[$x+$self->{open_r},$y+$self->{open_r}-1],
				],
			color => $black
			);
		$self->{image}->polyline
			(
			points=>
				[
				[$x+$self->{open_r},$y-$self->{open_r}],
				[$x-$self->{open_r},$y+$self->{open_r}],
				],
			color => $black
			);
		}
	}

sub _open_strings
	{
	my $self   = shift;
	my $y = $self->{grid}->{y}+$self->{grid}->{h}-$self->{grid}->{w};

	for(@{$self->{open}})
		{
		my $x = $self->{grid}->{x}+$self->{open_r}+($self->{grid}->{w}*$_);
		$self->{image}->circle
			(
			r      => $self->{open_r},
			x      => $x,
			y      => $y,
			color  => $black,
			filled => 0
			);
		$self->{image}->circle
			(
			r      => $self->{open_r} - 1,
			x      => $x,
			y      => $y,
			color  => $white,
			filled => 0
			);
		}
	}

sub _fingering
	{
	my $self   = shift;
	my $row    = 0;
	my $grid_y = $self->{grid}->{y}+$self->{grid}->{h}+
		($self->{grid}->{h}/2)+($self->{open_r}/2)-$self->{open_r};

	for my $fret_ref(@{$self->{fingering}})
		{
		for(@{$fret_ref})
			{
			$self->{image}->circle
				(
				r      => $self->{open_r},
				x      => $self->{grid}->{x}+$self->{open_r}+($self->{grid}->{w}*($_)),
				y      => $grid_y+($row*($self->{grid}->{h}+2)),
				color  => $black,
				filled => 0
				);
			}
		$row++;
		}
	}

sub _grid
	{
	my $self = shift;

	for(0..5)
		{
		my $x = $self->{grid}->{x}+$self->{open_r}+($self->{grid}->{w}*$_);

		$self->{image}->polyline
			(
			points=>
				[
				[$x,$self->{grid}->{y}+$self->{grid}->{h}],
				[$x,$self->{grid}->{y}+$self->{grid}->{h}+($self->{grid}->{h}*5)],
				],
			color=>$black
			);
		}
	for(0..4)
		{
		my $y = $self->{grid}->{y}+($self->{grid}->{h}*($_+1));
		$self->{image}->polyline
			(
			points=>
				[
				[$self->{grid}->{x}+$self->{open_r},$y],
				[$self->{grid}->{x}+$self->{open_r}+($self->{grid}->{w}*5),$y],
				],
			color=>$black
			);
		}

	if ($self->fret() == 1)
		{
		$self->{image}->box
			(
			color  => $black,
			xmin   => $self->{grid}->{x}+$self->{open_r},
			ymin   => $self->{grid}->{y}+$self->{grid}->{h}-$self->bar_thickness(),
			xmax   => $self->{grid}->{x}+$self->{open_r}+(5*$self->{grid}->{w}),
			ymax   => $self->{grid}->{y}+$self->{grid}->{h},
			filled => 1,
			);
		}

	$self->{image}->polyline
		(
		points=>
			[
			[$self->{grid}->{x}+$self->{open_r},
			$self->{grid}->{y}+$self->{grid}->{h}+($self->{grid}->{h}*5)],
			[$self->{grid}->{x}+$self->{open_r}+(5*$self->{grid}->{w}),
			$self->{grid}->{y}+$self->{grid}->{h}+($self->{grid}->{h}*5)],
			],
		color=>$black
		);
	}

1;
__END__