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.0705';
7              
8 1     1   1281 use Moo;
  1         11599  
  1         4  
9 1     1   1933 use strictures 2;
  1         1639  
  1         40  
10 1     1   212 use Carp qw(croak);
  1         2  
  1         48  
11 1     1   6 use List::Util qw( first );
  1         1  
  1         99  
12 1     1   488 use Music::Note ();
  1         1726  
  1         28  
13 1     1   464 use Music::Scales qw(get_scale_notes);
  1         5258  
  1         93  
14 1     1   458 use namespace::clean;
  1         11565  
  1         11  
15              
16              
17             has scale_note => (
18             is => 'ro',
19             default => sub { 'C' },
20             );
21              
22              
23             has scale_name => (
24             is => 'ro',
25             default => sub { 'major' },
26             );
27              
28              
29             has note_format => (
30             is => 'ro',
31             default => sub { 'ISO' },
32             );
33              
34              
35             has offset => (
36             is => 'ro',
37             isa => sub { die 'Not a negative or positive integer' unless $_[0] =~ /^-?\d+$/ },
38             default => sub { 1 },
39             );
40              
41              
42             has flat => (
43             is => 'ro',
44             default => sub { 0 },
45             );
46              
47              
48             has verbose => (
49             is => 'ro',
50             default => sub { 0 },
51             );
52              
53              
54             sub get_offset {
55 8     8 1 6211 my ( $self, %args ) = @_;
56              
57 8         23 my $name = $args{note_name};
58 8   66     31 my $format = $args{note_format} || $self->note_format;
59 8   66     38 my $offset = $args{offset} || $self->offset;
60 8   66     29 my $flat = $args{flat} || $self->flat;
61              
62 8 50 66     26 croak 'note_name, note_format or offset not provided'
      33        
63             unless $name || $format || $offset;
64              
65 8         10 my $rev; # Going in reverse?
66              
67 8         28 my $note = Music::Note->new( $name, $format );
68              
69 8         289 my $equiv;
70 8 100 66     22 if ( $note->format('isobase') =~ /b/ || $note->format('isobase') =~ /#/ ) {
71 5         231 $equiv = Music::Note->new( $name, $format );
72 5 50       145 $equiv->en_eq( $note->format('isobase') =~ /b/ ? 'sharp' : 'flat' );
73             }
74              
75 8 0       296 printf "Given note: %s, ISO: %s/%s, Offset: %d\n",
    50          
76             $name, $note->format('ISO'), ( $equiv ? $equiv->format('ISO') : '' ), $offset
77             if $self->verbose;
78              
79 8         44 my @scale = get_scale_notes( $self->scale_note, $self->scale_name );
80 8 50       1555 print "\tScale: @scale\n"
81             if $self->verbose;
82              
83 8 100       19 if ( $offset < 0 ) {
84 5         9 $rev++;
85 5         8 $offset = abs $offset;
86 5         8 @scale = reverse @scale;
87             }
88              
89             my $posn = first {
90 36 100 66 36   804 ( $scale[$_] eq $note->format('isobase') )
91             ||
92             ( $equiv && $scale[$_] eq $equiv->format('isobase') )
93 8         56 } 0 .. $#scale;
94              
95 8 100       182 if ( defined $posn ) {
96 6 50       16 printf "\tPosition: %d\n", $posn
97             if $self->verbose;
98 6         11 $offset += $posn;
99             }
100             else {
101 2         26 croak 'Scale position not defined!';
102             }
103              
104 6         15 my $octave = $note->octave;
105 6         55 my $factor = int( $offset / @scale );
106              
107 6 100       15 if ( $rev ) {
108 5         9 $octave -= $factor;
109             }
110             else {
111 1         2 $octave += $factor;
112             }
113              
114 6         22 $note = Music::Note->new( $scale[ $offset % @scale ] . $octave, 'ISO' );
115              
116 6 100 66     211 if ( $flat && $note->format('isobase') =~ /#/ ) {
117 1         24 $note->en_eq('flat');
118             }
119              
120 6 50       32 printf "\tNew offset: %d, octave: %d, ISO: %s, Formatted: %s\n",
121             $offset, $octave, $note->format('ISO'), $note->format($format)
122             if $self->verbose;
123              
124 6         28 return $note;
125             }
126              
127              
128             sub step {
129 6     6 1 3623 my ( $self, %args ) = @_;
130              
131 6         14 my $name = $args{note_name};
132 6   100     19 my $steps = $args{steps} || 1;
133 6   66     23 my $flat = $args{flat} || $self->flat;
134              
135 6 50       14 croak 'note_name not provided'
136             unless $name;
137              
138 6         30 my $note = Music::Note->new( $name, $self->note_format );
139 6         201 my $num = $note->format('midinum');
140              
141 6 50       152 printf "Given note: %s, ISO: %s, Formatted: %d\n",
142             $name, $note->format('ISO'), $num
143             if $self->verbose;
144              
145 6         11 $num += $steps;
146 6         13 $note = Music::Note->new( $num, 'midinum' );
147              
148 6 100 100     221 if ( $flat && $note->format('isobase') =~ /#/ ) {
149 1         24 $note->en_eq('flat');
150             }
151              
152 6 50       49 printf "\tNew steps: %d, ISO: %s, Formatted: %s\n",
153             $steps, $note->format('ISO'), $note->format( $self->note_format )
154             if $self->verbose;
155              
156 6         21 return $note;
157             }
158              
159             1;
160              
161             __END__