File Coverage

blib/lib/GD/Chord/Piano.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             package GD::Chord::Piano;
2              
3 2     2   46241 use warnings;
  2         6  
  2         68  
4 2     2   10 use strict;
  2         4  
  2         74  
5 2     2   11 use Carp qw( croak );
  2         8  
  2         153  
6              
7 2     2   2235 use GD;
  0            
  0            
8              
9             use base qw(Class::Accessor::Fast);
10             __PACKAGE__->mk_accessors(
11             qw(bgcolor color pcolor tcolor interlaced)
12             );
13              
14             our $VERSION = '0.061';
15              
16             my $base_chord_list = {
17             'base' => '0,4,7',
18             '-5' => '0,4,6',
19             '6' => '0,4,7,9',
20             '6(9)' => '0,4,7,9,14', '69' => '0,4,7,9,14',
21             'M7' => '0,4,7,11',
22             'M7(9)' => '0,4,7,11,14', 'M79' => '0,4,7,11,14',
23             'M9' => '0,4,7,11,14',
24             'M11' => '0,4,7,11,14,17',
25             'M13' => '0,4,7,11,14,17,21',
26             '7' => '0,4,7,10',
27             '7(b5)' => '0,4,6,10', '7b5' => '0,4,6,10',
28             '7(-5)' => '0,4,6,10', '7-5' => '0,4,6,10',
29             '7(b9)' => '0,4,7,10,13', '7b9' => '0,4,7,10,13',
30             '7(-9)' => '0,4,7,10,13', '7-9' => '0,4,7,10,13',
31             '-9' => '0,4,7,10,13',
32             '-9(#5)' => '0,4,8,10,13', '-9#5' => '0,4,8,10,13',
33             '7(b9,13)' => '0,4,7,10,13,21', '7(-9,13)' => '0,4,7,10,13,21',
34             '7(9,13)' => '0,4,7,10,14,21',
35             '7(#9)' => '0,4,7,10,15', '7#9' => '0,4,7,10,15',
36             '7(#11)' => '0,4,7,10,15,18', '7#11' => '0,4,7,10,15,18',
37             '7(#13)' => '0,4,10,21', '7#13' => '0,4,10,21',
38             '9' => '0,4,7,10,14',
39             '9(b5)' => '0,4,6,10,14', '9b5' => '0,4,6,10,14',
40             '9(-5)' => '0,4,6,10,14', '9-5' => '0,4,6,10,14',
41             '11' => '0,4,7,10,14,17',
42             '13' => '0,4,7,10,14,17,21',
43             'm' => '0,3,7',
44             'm6' => '0,3,7,9',
45             'm6(9)' => '0,3,7,9,14', 'm69' => '0,3,7,9,14',
46             'mM7' => '0,3,7,11',
47             'm7' => '0,3,7,10',
48             'm7(b5)' => '0,3,6,10', 'm7b5' => '0,3,6,10',
49             'm7(-5)' => '0,3,6,10', 'm7-5' => '0,3,6,10',
50             'm7(9)' => '0,3,7,10,14', 'm79' => '0,3,7,10,14',
51             'm9' => '0,3,7,10,14',
52             'm7(9,11)' => '0,3,7,10,14,17',
53             'm11' => '0,3,7,10,14,17',
54             'm13' => '0,3,7,10,14,17,21',
55             'dim' => '0,3,6',
56             'dim7' => '0,3,6,9',
57             'aug' => '0,4,8',
58             'aug7' => '0,4,8,10',
59             'augM7' => '0,4,8,11',
60             'aug9' => '0,4,8,10,14',
61             'sus4' => '0,5,7',
62             '7sus4' => '0,5,7,10',
63             'add2' => '0,2,4,7',
64             'add4' => '0,4,5,7',
65             'add9' => '0,4,7,14',
66             };
67              
68             my $scalic_value = {
69             'C' => 0,
70             'C#' => 1, 'Db' => 1,
71             'D' => 2,
72             'D#' => 3, 'Eb' => 3,
73             'E' => 4,
74             'E#' => 5, 'Fb' => 4, # joke!
75             'F' => 5,
76             'F#' => 6, 'Gb' => 6,
77             'G' => 7,
78             'G#' => 8, 'Ab' => 8,
79             'A' => 9,
80             'A#' => 10, 'Bb' => 10,
81             'B' => 11,
82             'Cb' => 11, 'B#' => 0, # joke!
83             };
84              
85             my $black_keys;
86             for my $black_key (qw(1 3 6 8 10 13 15 18 20 22)){
87             $black_keys->{$black_key} = 1;
88             }
89              
90             sub new {
91             my $class = shift;
92             bless {
93             bgcolor => [255,255,255],
94             color => [0,0,0],
95             pcolor => [255,0,0],
96             tcolor => [0,0,0],
97             interlaced => 'true',
98             }, $class;
99             }
100              
101             sub chord {
102             my ($self, $chord_name) = @_;
103             return $self->generate($chord_name, $self->_get_keys($chord_name));
104             }
105              
106             sub gen {
107             my ($self, $chord_name, @keys) = @_;
108             return $self->generate($chord_name, @keys);
109             }
110             sub generate {
111             my ($self, $chord_name, @keys) = @_;
112             my $im = $self->_draw_keyboard;
113             my $pcolor = $im->colorAllocate(@{$self->pcolor});
114             my $tcolor = $im->colorAllocate(@{$self->color});
115             my $x = 3;
116             for my $key (0..23){
117             my $play = 0;
118             for my $i (@keys){
119             $play = 1 if $i == $key;
120             }
121             if($play){
122             my ($color, $y);
123             $y = $black_keys->{$key} || 0;
124             $im->filledRectangle(@{[$x, 24-$y*12, $x+3, 27-$y*12]}, $pcolor);
125             }
126             if($black_keys->{$key} and !$black_keys->{$key+1}){
127             $x += 4;
128             }elsif(!$black_keys->{$key} and $black_keys->{$key+1}){
129             $x += 5;
130             }else{
131             $x += 9;
132             }
133             }
134             $im->string(GD::Font->Small, 3, 31, $chord_name, $tcolor);
135             return $im;
136             }
137              
138             sub all_chords {
139             my $self = shift;
140             return [keys %{$base_chord_list}];
141             }
142              
143             sub _get_keys {
144             my ($self, $chord_name) = @_;
145             croak "no chord" unless $chord_name;
146             my ($tonic, $kind) = ($chord_name =~ /([A-G][b#]?)(.+)?/);
147             $kind = 'base' unless $kind;
148             croak "undefined chord $chord_name" unless defined $tonic;
149             my $scalic = $scalic_value->{$tonic};
150             croak "undefined kind of chord $chord_name ($kind)" unless defined $base_chord_list->{$kind};
151             my @keys;
152             for my $scale ( split /\,/, $base_chord_list->{$kind} ){
153             my $tone = $scale + $scalic;
154             $tone = int($tone % 24) + 12 if $tone > 23;
155             push @keys, $tone;
156             }
157             return @keys;
158             }
159              
160             sub _draw_keyboard {
161             my $self = shift;
162              
163             my $im = GD::Image->new(127,43);
164             my $bgcolor = $im->colorAllocate(@{$self->bgcolor});
165             my $color = $im->colorAllocate(@{$self->color});
166              
167             if($self->interlaced){
168             $im->transparent($bgcolor);
169             $im->interlaced('true');
170             }
171             for my $k (0..13){
172             $im->rectangle(@{[$k*9, 0, 9+$k*9, 30]}, $color);
173             }
174             for my $k (0..12){
175             next if $k == 2 or $k == 6 or $k == 9;
176             $im->filledRectangle(@{[7+$k*9, 0, 12+$k*9, 17]}, $color);
177             }
178             return $im;
179             }
180              
181             1;
182              
183             __END__