File Coverage

blib/lib/Music/ScaleNote.pm
Criterion Covered Total %
statement 67 67 100.0
branch 17 26 65.3
condition 18 26 69.2
subroutine 11 11 100.0
pod 2 2 100.0
total 115 132 87.1


line stmt bran cond sub pod time code
1             package Music::ScaleNote;
2             our $AUTHORITY = 'cpan:GENE';
3              
4             # ABSTRACT: Manipulate the position of a note in a scale
5              
6             our $VERSION = '0.0804';
7              
8 2     2   730788 use Moo;
  2         22283  
  2         45  
9 2     2   4996 use strictures 2;
  2         4117  
  2         180  
10 2     2   1580 use Carp qw(croak);
  2         3  
  2         137  
11 2     2   1307 use Array::Circular ();
  2         3148  
  2         89  
12 2     2   1338 use List::SomeUtils qw( first_index );
  2         43847  
  2         295  
13 2     2   1408 use Music::Note ();
  2         5206  
  2         98  
14 2     2   1333 use Music::Scales qw( get_scale_notes );
  2         15298  
  2         268  
15 2     2   1575 use namespace::clean;
  2         30961  
  2         32  
16              
17              
18             has scale_note => (
19             is => 'ro',
20             default => sub { 'C' },
21             );
22              
23              
24             has scale_name => (
25             is => 'ro',
26             default => sub { 'major' },
27             );
28              
29              
30             has note_format => (
31             is => 'ro',
32             default => sub { 'ISO' },
33             );
34              
35              
36             has offset => (
37             is => 'ro',
38             isa => sub { die 'Not a negative or positive integer' unless $_[0] =~ /^-?\d+$/ },
39             default => sub { 1 },
40             );
41              
42              
43             has flat => (
44             is => 'ro',
45             default => sub { 0 },
46             );
47              
48              
49             has verbose => (
50             is => 'ro',
51             default => sub { 0 },
52             );
53              
54              
55             sub get_offset {
56 8     8 1 8835 my ( $self, %args ) = @_;
57              
58 8         17 my $name = $args{note_name};
59 8   66     39 my $format = $args{note_format} || $self->note_format;
60 8   66     33 my $offset = $args{offset} || $self->offset;
61 8   66     31 my $flat = $args{flat} || $self->flat;
62              
63 8 50 66     22 croak 'note_name, note_format or offset not provided'
      33        
64             unless $name || $format || $offset;
65              
66 8         36 my $note = Music::Note->new( $name, $format );
67 8 50 66     347 $note->en_eq('flat') if $flat && $note->format('isobase') =~ /#/;
68              
69 8 50       68 printf "Given note: %s, Format: %s, ISO: %s, Offset: %d\n",
70             $name, $format, $note->format('ISO'), $offset
71             if $self->verbose;
72              
73 8         35 my @scale = get_scale_notes( $self->scale_note, $self->scale_name );
74 8 100       1602 if ( $flat ) {
75 1         2 for ( @scale ) {
76 5 100       26 if ( $_ =~ /#/ ) {
77 2         29 my $equiv = Music::Note->new( $_, $format );
78 2         53 $equiv->en_eq('flat');
79 2         25 $_ = $equiv->format('isobase');
80             }
81             }
82             }
83 8 50       36 print "\tScale: @scale\n"
84             if $self->verbose;
85              
86 8         34 my $ac = Array::Circular->new( @scale );
87              
88 8     22   173 my $posn = first_index { $note->format('isobase') eq $_ } @scale;
  22         363  
89 8 100       196 if ( $posn >= 0 ) {
90 6 50       14 printf "\tPosition: %d\n", $posn
91             if $self->verbose;
92 6         17 $ac->index( $posn );
93             }
94             else {
95 2         29 croak 'Scale position not defined!';
96             }
97              
98 6         127 $ac->next( $offset );
99              
100 6         310 my $octave = $note->octave;
101 6         30 $octave += $ac->loops;
102              
103 6         50 $note = Music::Note->new( $ac->current . $octave, 'ISO' );
104              
105 6 50       207 printf "\tOctave: %d, ISO: %s, Formatted: %s\n",
106             $octave, $note->format('ISO'), $note->format($format)
107             if $self->verbose;
108              
109 6         13 return $note;
110             }
111              
112              
113             sub step {
114 6     6 1 4260 my ( $self, %args ) = @_;
115              
116 6         11 my $name = $args{note_name};
117 6   100     18 my $steps = $args{steps} || 1;
118 6   66     24 my $flat = $args{flat} || $self->flat;
119              
120 6 50       14 croak 'note_name not provided'
121             unless $name;
122              
123 6         26 my $note = Music::Note->new( $name, $self->note_format );
124 6         234 my $num = $note->format('midinum');
125              
126 6 50       184 printf "Given note: %s, ISO: %s, Formatted: %d\n",
127             $name, $note->format('ISO'), $num
128             if $self->verbose;
129              
130 6         10 $num += $steps;
131 6         14 $note = Music::Note->new( $num, 'midinum' );
132 6 100 100     184 $note->en_eq('flat') if $flat && $note->format('isobase') =~ /#/;
133              
134 6 50       124 printf "\tNew steps: %d, ISO: %s, Formatted: %s\n",
135             $steps, $note->format('ISO'), $note->format( $self->note_format )
136             if $self->verbose;
137              
138 6         23 return $note;
139             }
140              
141             1;
142              
143             __END__