File Coverage

blib/lib/Music/ScaleNote.pm
Criterion Covered Total %
statement 70 70 100.0
branch 23 34 67.6
condition 22 32 68.7
subroutine 10 10 100.0
pod 2 2 100.0
total 127 148 85.8


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.0703';
7              
8 1     1   746 use Carp;
  1         3  
  1         63  
9 1     1   7 use List::Util qw( first );
  1         2  
  1         86  
10 1     1   569 use Moo;
  1         11240  
  1         4  
11 1     1   1908 use Music::Note;
  1         1582  
  1         32  
12 1     1   443 use Music::Scales;
  1         5168  
  1         84  
13              
14 1     1   466 use strictures 2;
  1         1554  
  1         46  
15 1     1   655 use namespace::clean;
  1         11267  
  1         8  
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 5560 my ( $self, %args ) = @_;
57              
58 8         32 my $name = $args{note_name};
59 8   66     34 my $format = $args{note_format} || $self->note_format;
60 8   66     22 my $offset = $args{offset} || $self->offset;
61 8   66     28 my $flat = $args{flat} || $self->flat;
62              
63 8 50 66     25 croak 'note_name, note_format or offset not provided'
      33        
64             unless $name || $format || $offset;
65              
66 8         9 my $rev; # Going in reverse?
67              
68 8         27 my $note = Music::Note->new( $name, $format );
69              
70 8         267 my $equiv;
71 8 100 66     20 if ( $note->format('isobase') =~ /b/ || $note->format('isobase') =~ /#/ ) {
72 5         251 $equiv = Music::Note->new( $name, $format );
73 5 50       143 $equiv->en_eq( $note->format('isobase') =~ /b/ ? 'sharp' : 'flat' );
74             }
75              
76 8 0       340 printf "Given note: %s, ISO: %s/%s, Offset: %d\n",
    50          
77             $name, $note->format('ISO'), ( $equiv ? $equiv->format('ISO') : '' ), $offset
78             if $self->verbose;
79              
80 8         27 my @scale = get_scale_notes( $self->scale_note, $self->scale_name );
81 8 50       1340 print "\tScale: @scale\n"
82             if $self->verbose;
83              
84 8 100       16 if ( $offset < 0 ) {
85 5         9 $rev++;
86 5         7 $offset = abs $offset;
87 5         8 @scale = reverse @scale;
88             }
89              
90             my $posn = first {
91 36 100 66 36   810 ( $scale[$_] eq $note->format('isobase') )
92             ||
93             ( $equiv && $scale[$_] eq $equiv->format('isobase') )
94 8         51 } 0 .. $#scale;
95              
96 8 100       183 if ( defined $posn ) {
97 6 50       14 printf "\tPosition: %d\n", $posn
98             if $self->verbose;
99 6         11 $offset += $posn;
100             }
101             else {
102 2         28 croak 'Scale position not defined!';
103             }
104              
105 6         13 my $octave = $note->octave;
106 6         42 my $factor = int( $offset / @scale );
107              
108 6 100       14 if ( $rev ) {
109 5         8 $octave -= $factor;
110             }
111             else {
112 1         2 $octave += $factor;
113             }
114              
115 6         19 $note = Music::Note->new( $scale[ $offset % @scale ] . $octave, 'ISO' );
116              
117 6 100 66     202 if ( $flat && $note->format('isobase') =~ /#/ ) {
118 1         23 $note->en_eq('flat');
119             }
120              
121 6 50       27 printf "\tNew offset: %d, octave: %d, ISO: %s, Formatted: %s\n",
122             $offset, $octave, $note->format('ISO'), $note->format($format)
123             if $self->verbose;
124              
125 6         25 return $note;
126             }
127              
128              
129             sub step {
130 6     6 1 3111 my ( $self, %args ) = @_;
131              
132 6         12 my $name = $args{note_name};
133 6   100     16 my $steps = $args{steps} || 1;
134 6   66     23 my $flat = $args{flat} || $self->flat;
135              
136 6 50       14 croak 'note_name not provided'
137             unless $name;
138              
139 6         27 my $note = Music::Note->new( $name, $self->note_format );
140 6         215 my $num = $note->format('midinum');
141              
142 6 50       148 printf "Given note: %s, ISO: %s, Formatted: %d\n",
143             $name, $note->format('ISO'), $num
144             if $self->verbose;
145              
146 6         9 $num += $steps;
147 6         12 $note = Music::Note->new( $num, 'midinum' );
148              
149 6 100 100     174 if ( $flat && $note->format('isobase') =~ /#/ ) {
150 1         22 $note->en_eq('flat');
151             }
152              
153 6 50       46 printf "\tNew steps: %d, ISO: %s, Formatted: %s\n",
154             $steps, $note->format('ISO'), $note->format( $self->note_format )
155             if $self->verbose;
156              
157 6         20 return $note;
158             }
159              
160             1;
161              
162             __END__