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.0704';
7              
8 1     1   1048 use strictures 2;
  1         1383  
  1         36  
9 1     1   190 use Carp qw(croak);
  1         1  
  1         62  
10 1     1   5 use List::Util qw( first );
  1         2  
  1         90  
11 1     1   498 use Moo;
  1         9769  
  1         6  
12 1     1   1642 use Music::Note ();
  1         1459  
  1         27  
13 1     1   402 use Music::Scales qw(get_scale_notes);
  1         4359  
  1         102  
14 1     1   463 use namespace::clean;
  1         10259  
  1         5  
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 4689 my ( $self, %args ) = @_;
56              
57 8         21 my $name = $args{note_name};
58 8   66     34 my $format = $args{note_format} || $self->note_format;
59 8   66     26 my $offset = $args{offset} || $self->offset;
60 8   66     41 my $flat = $args{flat} || $self->flat;
61              
62 8 50 66     22 croak 'note_name, note_format or offset not provided'
      33        
63             unless $name || $format || $offset;
64              
65 8         11 my $rev; # Going in reverse?
66              
67 8         26 my $note = Music::Note->new( $name, $format );
68              
69 8         240 my $equiv;
70 8 100 66     19 if ( $note->format('isobase') =~ /b/ || $note->format('isobase') =~ /#/ ) {
71 5         193 $equiv = Music::Note->new( $name, $format );
72 5 50       117 $equiv->en_eq( $note->format('isobase') =~ /b/ ? 'sharp' : 'flat' );
73             }
74              
75 8 0       253 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         36 my @scale = get_scale_notes( $self->scale_note, $self->scale_name );
80 8 50       1225 print "\tScale: @scale\n"
81             if $self->verbose;
82              
83 8 100       20 if ( $offset < 0 ) {
84 5         7 $rev++;
85 5         6 $offset = abs $offset;
86 5         7 @scale = reverse @scale;
87             }
88              
89             my $posn = first {
90 36 100 66 36   711 ( $scale[$_] eq $note->format('isobase') )
91             ||
92             ( $equiv && $scale[$_] eq $equiv->format('isobase') )
93 8         57 } 0 .. $#scale;
94              
95 8 100       153 if ( defined $posn ) {
96 6 50       16 printf "\tPosition: %d\n", $posn
97             if $self->verbose;
98 6         8 $offset += $posn;
99             }
100             else {
101 2         35 croak 'Scale position not defined!';
102             }
103              
104 6         14 my $octave = $note->octave;
105 6         38 my $factor = int( $offset / @scale );
106              
107 6 100       12 if ( $rev ) {
108 5         8 $octave -= $factor;
109             }
110             else {
111 1         2 $octave += $factor;
112             }
113              
114 6         27 $note = Music::Note->new( $scale[ $offset % @scale ] . $octave, 'ISO' );
115              
116 6 100 66     178 if ( $flat && $note->format('isobase') =~ /#/ ) {
117 1         19 $note->en_eq('flat');
118             }
119              
120 6 50       23 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         23 return $note;
125             }
126              
127              
128             sub step {
129 6     6 1 2426 my ( $self, %args ) = @_;
130              
131 6         12 my $name = $args{note_name};
132 6   100     16 my $steps = $args{steps} || 1;
133 6   66     21 my $flat = $args{flat} || $self->flat;
134              
135 6 50       11 croak 'note_name not provided'
136             unless $name;
137              
138 6         21 my $note = Music::Note->new( $name, $self->note_format );
139 6         201 my $num = $note->format('midinum');
140              
141 6 50       128 printf "Given note: %s, ISO: %s, Formatted: %d\n",
142             $name, $note->format('ISO'), $num
143             if $self->verbose;
144              
145 6         9 $num += $steps;
146 6         10 $note = Music::Note->new( $num, 'midinum' );
147              
148 6 100 100     156 if ( $flat && $note->format('isobase') =~ /#/ ) {
149 1         21 $note->en_eq('flat');
150             }
151              
152 6 50       41 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         16 return $note;
157             }
158              
159             1;
160              
161             __END__