File Coverage

blib/lib/GD/Tab/Guitar.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::Tab::Guitar;
2 2     2   73142 use strict;
  2         6  
  2         524  
3 2     2   14 use warnings;
  2         4  
  2         58  
4 2     2   23 use Carp;
  2         5  
  2         162  
5 2     2   4455 use GD;
  0            
  0            
6             use base qw(Class::Accessor::Fast);
7             use List::Util qw(max);
8              
9             __PACKAGE__->mk_accessors(qw(bgcolor color interlaced));
10              
11             our $VERSION = '0.03';
12             my @lines = (
13             [5,15,46,15],
14             [5,21,46,21],
15             [5,27,46,27],
16             [5,33,46,33],
17             [5,39,46,39],
18             [5,45,46,45],
19             [4,15,4,45],
20             [6,15,6,45],
21             [14,15,14,45],
22             [22,15,22,45],
23             [30,15,30,45],
24             [38,15,38,45],
25             );
26              
27             my %chord_lists = (
28             'C' => 'x32010',
29             'C6' => 'x32210',
30             'C6(9)' => 'x32233',
31             'CM7' => 'x32000',
32             'CM7(9)' => 'x30000',
33             'C7' => 'x32310',
34             'C7(b5)' => 'x34310',
35             'C7(b9)' => 'x32323',
36             'C7(b9,13)' => 'x21214',
37             'C7(9)' => 'x3233x',
38             'C7(9,13)' => 'x32335',
39             'C7(#9)' => 'x3234x',
40             'C7(#11)' => 'x1213x',
41             'C7(b13)' => 'x1211x',
42             'C7(13)' => 'x1221x',
43             'Cm' => 'x35543',
44             'Cm6' => 'x31213',
45             'Cm6(9)' => 'x3124x',
46             'CmM7' => 'x31003',
47             'Cm7' => 'x31313',
48             'Cm7(b5)' => 'x3434x',
49             'Cm7(9)' => 'x3133x',
50             'Cm7(9,11)' => 'x31331',
51             'Cdim' => 'x3424x',
52             'Caug' => 'x3211x',
53             'Caug7' => 'x323x4',
54             'Csus4' => 'x33011',
55             'C7sus4' => 'x33311',
56             'Cadd9' => 'x32030',
57             'C#' => 'x43121',
58             'C#6' => 'x4332x',
59             'C#6(9)' => 'x43344',
60             'C#M7' => 'x43111',
61             'C#M7(9)' => 'x4354x',
62             'C#7' => 'x4342x',
63             'C#7(b5)' => 'x4542x',
64             'C#7(b9)' => 'x43434',
65             'C#7(b9,13)' => 'x43436',
66             'C#7(9)' => 'x4344x',
67             'C#7(9,13)' => 'x43445',
68             'C#7(#9)' => 'x4345x',
69             'C#7(#11)' => 'x4546x',
70             'C#7(b13)' => 'x2322x',
71             'C#7(13)' => 'x2332x',
72             'C#m' => 'x46654',
73             'C#m6' => 'x42324',
74             'C#m6(9)' => 'x4234x',
75             'C#mM7' => 'x4211x',
76             'C#m7' => 'x42100',
77             'C#m7(b5)' => 'x4545x',
78             'C#m7(9)' => 'x4244x',
79             'C#m7(9,11)' => 'x42442',
80             'C#dim' => 'x4535x',
81             'C#aug' => 'x4322x',
82             'C#aug7' => 'x2322x',
83             'C#sus4' => 'x46674',
84             'C#7sus4' => 'x44422',
85             'C#add9' => 'x43141',
86             'D' => 'xx0232',
87             'D6' => 'xx0202',
88             'D6(9)' => 'x54455',
89             'DM7' => 'xx0222',
90             'DM7(9)' => 'xx0220',
91             'D7' => 'xx0212',
92             'D7(b5)' => 'xx0112',
93             'D7(b9)' => 'x5454x',
94             'D7(b9,13)' => 'x54547',
95             'D7(9)' => 'xx0210',
96             'D7(9,13)' => 'x54557',
97             'D7(#9)' => 'x5456x',
98             'D7(#11)' => 'xx0112',
99             'D7(b13)' => 'x3433x',
100             'D7(13)' => 'x3443x',
101             'Dm' => 'xx0231',
102             'Dm6' => 'xx0201',
103             'Dm6(9)' => 'x5345x',
104             'DmM7' => 'x5322x',
105             'Dm7' => 'xx0211',
106             'Dm7(b5)' => 'xx0111',
107             'Dm7(9)' => 'x5355x',
108             'Dm7(9,11)' => 'x53553',
109             'Ddim' => 'xx0101',
110             'Daug' => 'xx0332',
111             'Daug7' => 'xx0312',
112             'Dsus4' => 'x55033',
113             'D7sus4' => 'xx0213',
114             'Dadd9' => 'xx0230',
115             'Eb' => 'xx1343',
116             'Eb6' => 'xx1313',
117             'Eb6(9)' => 'x65566',
118             'EbM7' => 'xx1333',
119             'EbM7(9)' => 'xx1331',
120             'Eb7' => 'xx1323',
121             'Eb7(b5)' => 'xx1223',
122             'Eb7(b9)' => 'xx1020',
123             'Eb7(b9,13)' => 'x65658',
124             'Eb7(9)' => 'xx1021',
125             'Eb7(9,13)' => 'x65668',
126             'Eb7(#9)' => 'xx1022',
127             'Eb7(#11)' => 'xx1223',
128             'Eb7(b13)' => 'x4544x',
129             'Eb7(13)' => 'x4554x',
130             'Ebm' => 'xx1342',
131             'Ebm6' => 'xx1312',
132             'Ebm6(9)' => 'x6456x',
133             'EbmM7' => 'xx1332',
134             'Ebm7' => 'xx1322',
135             'Ebm7(b5)' => 'xx1222',
136             'Ebm7(9)' => 'x6466x',
137             'Ebm7(9,11)' => 'xx1121',
138             'Ebdim' => 'xx1212',
139             'Ebaug' => 'xx1003',
140             'Ebaug7' => 'x2102x',
141             'Ebsus4' => 'xx1344',
142             'Eb7sus4' => 'xx1324',
143             'Ebadd9' => 'xx1341',
144             'E' => '022100',
145             'E6' => '022120',
146             'E6(9)' => 'xx2122',
147             'EM7' => '02110x',
148             'EM7(9)' => '021102',
149             'E7' => '020100',
150             'E7(b5)' => 'xx2334',
151             'E7(b9)' => '020101',
152             'E7(b9,13)' => '020131',
153             'E7(9)' => '020132',
154             'E7(9,13)' => '020122',
155             'E7(#9)' => '020103',
156             'E7(#11)' => '6x675x',
157             'E7(b13)' => '020110',
158             'E7(13)' => '020120',
159             'Em' => '022000',
160             'Em6' => '022020',
161             'Em6(9)' => '022022',
162             'EmM7' => '021000',
163             'Em7' => '020000',
164             'Em7(b5)' => '0x2333',
165             'Em7(9)' => '020002',
166             'Em7(9,11)' => 'xx2232',
167             'Edim' => '012020',
168             'Eaug' => '03211x',
169             'Eaug7' => '032130',
170             'Esus4' => '022200',
171             'E7sus4' => '020200',
172             'Eadd9' => '024100',
173             'F' => '133211',
174             'F6' => '1x323x',
175             'F6(9)' => '100011',
176             'FM7' => 'xx3210',
177             'FM7(9)' => '1x2010',
178             'F7' => '131211',
179             'F7(b5)' => '1x120x',
180             'F7(b9)' => 'xx1212',
181             'F7(b9,13)' => '1x1232',
182             'F7(9)' => '131213',
183             'F7(9,13)' => '1x123x',
184             'F7(#9)' => '131214',
185             'F7(#11)' => '101201',
186             'F7(b13)' => '1x122x',
187             'F7(13)' => '1x123x',
188             'Fm' => '133111',
189             'Fm6' => '133131',
190             'Fm6(9)' => '1xx133',
191             'FmM7' => '13211x',
192             'Fm7' => '131111',
193             'Fm7(b5)' => '1x110x',
194             'Fm7(9)' => '131113',
195             'Fm7(9,11)' => '131313',
196             'Fdim' => '1x0101',
197             'Faug' => 'xx3221',
198             'Faug7' => '1x1221',
199             'Fsus4' => '133311',
200             'F7sus4' => '131311',
201             'Fadd9' => 'xx3213',
202             'F#' => '244322',
203             'F#6' => '2x434x',
204             'F#6(9)' => '2x112x',
205             'F#M7' => 'xx4321',
206             'F#M7(9)' => '2x312x',
207             'F#7' => '242322',
208             'F#7(b5)' => '2x231x',
209             'F#7(b9)' => '212020',
210             'F#7(b9,13)' => 'x1204x',
211             'F#7(9)' => '21212x',
212             'F#7(9,13)' => '21213x',
213             'F#7(#9)' => '242325',
214             'F#7(#11)' => '2x231x',
215             'F#7(b13)' => '2x233x',
216             'F#7(13)' => '2x234x',
217             'F#m' => '244222',
218             'F#m6' => '244242',
219             'F#m6(9)' => 'xx1224',
220             'F#mM7' => '24322x',
221             'F#m7' => '242222',
222             'F#m7(b5)' => '2x221x',
223             'F#m7(9)' => '242224',
224             'F#m7(9,11)' => '20210x',
225             'F#dim' => '2x121x',
226             'F#aug' => 'xx4332',
227             'F#aug7' => '2x2332',
228             'F#sus4' => '244422',
229             'F#7sus4' => '242422',
230             'F#add9' => 'xx4324',
231             'G' => '320003',
232             'G6' => '320000',
233             'G6(9)' => '3x223x',
234             'GM7' => '320002',
235             'GM7(9)' => '3x423x',
236             'G7' => '320001',
237             'G7(b5)' => '3x342x',
238             'G7(b9)' => 'x2313x',
239             'G7(b9,13)' => '3x3100',
240             'G7(9)' => '353435',
241             'G7(9,13)' => '3x3200',
242             'G7(#9)' => '353436',
243             'G7(#11)' => '3x342x',
244             'G7(b13)' => '3x344x',
245             'G7(13)' => '323000',
246             'Gm' => '355333',
247             'Gm6' => '3x233x',
248             'Gm6(9)' => 'xx2335',
249             'GmM7' => '354333',
250             'Gm7' => '353333',
251             'Gm7(b5)' => '3x332x',
252             'Gm7(9)' => '353335',
253             'Gm7(9,11)' => '3x321x',
254             'Gdim' => '3x232x',
255             'Gaug' => '321003',
256             'Gaug7' => '3x3443',
257             'Gsus4' => '330013',
258             'G7sus4' => '330011',
259             'Gadd9' => '3x0233',
260             'G#' => '431114',
261             'G#6' => '431111',
262             'G#6(9)' => '4x334x',
263             'G#M7' => 'xx6543',
264             'G#M7(9)' => '4x534x',
265             'G#7' => '464544',
266             'G#7(b5)' => '4x453x',
267             'G#7(b9)' => 'x3424x',
268             'G#7(b9,13)' => '4x4211',
269             'G#7(9)' => '464546',
270             'G#7(9,13)' => '4x4311',
271             'G#7(#9)' => '464547',
272             'G#7(#11)' => '4x453x',
273             'G#7(b13)' => '4x455x',
274             'G#7(13)' => '434111',
275             'G#m' => '466444',
276             'G#m6' => 'xx1101',
277             'G#m6(9)' => 'xx3446',
278             'G#mM7' => 'xx1103',
279             'G#m7' => '464444',
280             'G#m7(b5)' => 'x20102',
281             'G#m7(9)' => '464446',
282             'G#m7(9,11)' => '4x432x',
283             'G#dim' => '4x343x',
284             'G#aug' => 'xx6554',
285             'G#aug7' => '4x4554',
286             'G#sus4' => '466644',
287             'G#7sus4' => '464644',
288             'G#add9' => 'xx6546',
289             'A' => 'x02220',
290             'A6' => 'x02222',
291             'A6(9)' => 'x02202',
292             'AM7' => 'x02120',
293             'AM7(9)' => 'x02100',
294             'A7' => 'x02020',
295             'A7(b5)' => 'x0102x',
296             'A7(b9)' => 'x02323',
297             'A7(b9,13)' => 'x05322',
298             'A7(9)' => 'x02423',
299             'A7(9,13)' => 'x05422',
300             'A7(#9)' => '575658',
301             'A7(#11)' => 'x01023',
302             'A7(b13)' => 'x02021',
303             'A7(13)' => 'x02022',
304             'Am' => 'x02210',
305             'Am6' => 'x02212',
306             'Am6(9)' => 'xx4557',
307             'AmM7' => 'x02110',
308             'Am7' => 'x02010',
309             'Am7(b5)' => 'x01213',
310             'Am7(9)' => 'x02000',
311             'Am7(9,11)' => 'x02433',
312             'Adim' => 'x01212',
313             'Aaug' => 'x03221',
314             'Aaug7' => 'x03021',
315             'Asus4' => 'x02230',
316             'A7sus4' => 'x02030',
317             'Aadd9' => 'x02200',
318             'Bb' => 'x13331',
319             'Bb6' => 'x13333',
320             'Bb6(9)' => 'x10011',
321             'BbM7' => 'x13231',
322             'BbM7(9)' => 'x10211',
323             'Bb7' => 'x13131',
324             'Bb7(b5)' => 'x12131',
325             'Bb7(b9)' => 'x10101',
326             'Bb7(b9,13)' => 'x10103',
327             'Bb7(9)' => 'x1011x',
328             'Bb7(9,13)' => 'x10113',
329             'Bb7(#9)' => 'x1012x',
330             'Bb7(#11)' => 'x10130',
331             'Bb7(b13)' => 'x13132',
332             'Bb7(13)' => 'x13133',
333             'Bbm' => 'x13321',
334             'Bbm6' => 'x1302x',
335             'Bbm6(9)' => 'xx5668',
336             'BbmM7' => 'x13221',
337             'Bbm7' => 'x13121',
338             'Bbm7(b5)' => 'x1212x',
339             'Bbm7(9)' => 'x13111',
340             'Bbm7(9,11)' => '6x654x',
341             'Bbdim' => 'x12020',
342             'Bbaug' => 'x10443',
343             'Bbaug7' => 'x14132',
344             'Bbsus4' => 'x13341',
345             'Bb7sus4' => 'x13141',
346             'Bbadd9' => 'x13311',
347             'B' => 'x24442',
348             'B6' => 'x24444',
349             'B6(9)' => 'x21122',
350             'BM7' => 'x24342',
351             'BM7(9)' => 'x2132x',
352             'B7' => 'x21202',
353             'B7(b5)' => 'x2324x',
354             'B7(b9)' => 'x2121x',
355             'B7(b9,13)' => 'x21214',
356             'B7(9)' => 'x2122x',
357             'B7(9,13)' => 'x21224',
358             'B7(#9)' => 'x2123x',
359             'B7(#11)' => 'x2324x',
360             'B7(b13)' => 'x24243',
361             'B7(13)' => 'x24244',
362             'Bm' => 'x24432',
363             'Bm6' => 'x2413x',
364             'Bm6(9)' => 'x2012x',
365             'BmM7' => 'x20332',
366             'Bm7' => 'x20202',
367             'Bm7(b5)' => 'x2323x',
368             'Bm7(9)' => 'x2022x',
369             'Bm7(9,11)' => 'x20220',
370             'Bdim' => 'x2313x',
371             'Baug' => 'x2100x',
372             'Baug7' => 'x25243',
373             'Bsus4' => 'x24452',
374             'B7sus4' => 'x22202',
375             'Badd9' => 'x24422',
376             );
377              
378             my %synonyms = (
379             'C#' => 'Db',
380             'Eb' => 'D#',
381             'F#' => 'Gb',
382             'G#' => 'Ab',
383             'Bb' => 'A#',
384             );
385              
386             my $synonyms_re = qr<^([CFG]#|[EB]b)>;
387              
388             for my $chord (keys %chord_lists) {
389             if ($chord =~ $synonyms_re) {
390             my $match = $1;
391             (my $same_chord = $chord) =~ s/^$match/$synonyms{$match}/;
392             $chord_lists{$same_chord} = $chord_lists{$chord}; # copy
393             }
394             }
395              
396             sub new {
397             my $class = shift;
398             bless {
399             bgcolor => [255, 255, 255],
400             color => [0, 0, 0],
401             interlaced => 'true',
402             }, $class;
403             }
404              
405             sub chord {
406             my ($self, $chord) = @_;
407             return $self->generate($chord, $self->get_frets($chord));
408             }
409              
410             sub get_frets {
411             my ($self, $chord) = @_;
412             my $frets = $chord_lists{$chord} or croak("undefined chord $chord");
413             return [reverse split //, $frets];
414             }
415              
416             sub generate {
417             my ($self, $chord, $frets) = @_;
418             my @frets = ref $frets eq 'ARRAY' ? @$frets : reverse split //, $frets;
419              
420             my $im = GD::Image->new(52, 56);
421             my $bgcolor = $im->colorAllocate(@{$self->bgcolor});
422             my $color = $im->colorAllocate(@{$self->color});
423              
424             if ($self->interlaced) {
425             $im->transparent($bgcolor);
426             $im->interlaced('true');
427             }
428              
429             $self->_draw_line($im, $color);
430              
431             my $fret_max = max( grep { /^\d+$/ } @frets );
432              
433             if ($fret_max > 5) {
434             $im->filledRectangle(4, 15, 6, 45, $bgcolor);
435             my $fret_num = $fret_max - 5;
436              
437             for my $fret (@frets) {
438             next if $fret eq 'x';
439             $fret -= $fret_num;
440             }
441              
442             for my $n (0..4) {
443             $im->string(GD::Font->Tiny, 9 * $n + 4, 47, $fret_num + 1, $color);
444             $fret_num++;
445             }
446             }
447              
448             my $i = 0;
449             for my $fret (@frets) {
450             if (lc $fret eq 'x') {
451             $im->line(0, 14 + 6 * $i, 2, 16 + 6 * $i, $color);
452             $im->line(2, 14 + 6 * $i, 0, 16 + 6 * $i, $color);
453             }
454             elsif ($fret > 0) {
455             $im->filledRectangle(
456             9 + 8 * ($fret - 1),
457             14 + 6 * $i,
458             11 + 8 * ($fret - 1),
459             16 + 6 * $i, $color
460             );
461             }
462             $i++;
463             }
464              
465             $im->string(GD::Font->Small, 4, 0, $chord, $color);
466             return $im;
467             }
468              
469             sub all_chords { return [keys(%chord_lists)] }
470              
471             sub _draw_line {
472             my ($self, $im, $color) = @_;
473             for my $line (@lines) {
474             $im->line(@$line, $color);
475             }
476             return $im;
477             }
478              
479             1;
480              
481             __END__