File Coverage

blib/lib/Music/ScaleNote.pm
Criterion Covered Total %
statement 71 71 100.0
branch 17 26 65.3
condition 18 26 69.2
subroutine 12 12 100.0
pod 2 2 100.0
total 120 137 87.5


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.0805';
7              
8 2     2   447063 use Moo;
  2         13231  
  2         9  
9 2     2   3401 use strictures 2;
  2         2624  
  2         85  
10 2     2   685 use Carp qw(croak);
  2         7  
  2         88  
11 2     2   715 use Array::Circular ();
  2         1932  
  2         47  
12 2     2   838 use List::SomeUtils qw( first_index );
  2         19707  
  2         139  
13 2     2   887 use MIDI::Util qw(midi_format);
  2         47870  
  2         118  
14 2     2   805 use Music::Note ();
  2         2886  
  2         56  
15 2     2   791 use Music::Scales qw( get_scale_notes );
  2         8011  
  2         125  
16 2     2   872 use namespace::clean;
  2         20669  
  2         11  
17              
18              
19             has scale_note => (
20             is => 'ro',
21             default => sub { 'C' },
22             );
23              
24              
25             has scale_name => (
26             is => 'ro',
27             default => sub { 'major' },
28             );
29              
30              
31             has note_format => (
32             is => 'ro',
33             default => sub { 'ISO' },
34             );
35              
36              
37             has offset => (
38             is => 'ro',
39             isa => sub { die 'Not a negative or positive integer' unless $_[0] =~ /^-?\d+$/ },
40             default => sub { 1 },
41             );
42              
43              
44             has flat => (
45             is => 'ro',
46             default => sub { 0 },
47             );
48              
49              
50             has verbose => (
51             is => 'ro',
52             default => sub { 0 },
53             );
54              
55              
56             sub get_offset {
57 8     8 1 6225 my ( $self, %args ) = @_;
58              
59 8         21 my $name = $args{note_name};
60 8   66     27 my $format = $args{note_format} || $self->note_format;
61 8   66     20 my $offset = $args{offset} || $self->offset;
62 8   66     22 my $flat = $args{flat} || $self->flat;
63              
64 8 50 66     37 croak 'note_name, note_format or offset not provided'
      33        
65             unless $name || $format || $offset;
66              
67 8         25 my $note = Music::Note->new( $name, $format );
68 8 50 66     248 $note->en_eq('flat') if $flat && $note->format('isobase') =~ /#/;
69              
70 8 50       32 printf "Given note: %s, Format: %s, ISO: %s, Offset: %d\n",
71             $name, $format, $note->format('ISO'), $offset
72             if $self->verbose;
73              
74 8         29 my @scale = get_scale_notes( $self->scale_note, $self->scale_name );
75 8         1074 @scale = midi_format(0, @scale);
76 8 100       467 if ( $flat ) {
77 1         2 for ( @scale ) {
78 5 100       25 if ( $_ =~ /#/ ) {
79 2         4 my $equiv = Music::Note->new( $_, $format );
80 2         43 $equiv->en_eq('flat');
81 2         23 $_ = $equiv->format('isobase');
82             }
83             }
84             }
85 8 50       27 print "\tScale: @scale\n"
86             if $self->verbose;
87              
88 8         30 my $ac = Array::Circular->new( @scale );
89              
90 8     22   145 my $posn = first_index { $note->format('isobase') eq $_ } @scale;
  22         237  
91 8 100       141 if ( $posn >= 0 ) {
92 6 50       14 printf "\tPosition: %d\n", $posn
93             if $self->verbose;
94 6         12 $ac->index( $posn );
95             }
96             else {
97 2         22 croak 'Scale position not defined!';
98             }
99              
100 6         83 $ac->next( $offset );
101              
102 6         290 my $octave = $note->octave;
103 6         30 $octave += $ac->loops;
104              
105 6         46 $note = Music::Note->new( $ac->current . $octave, 'ISO' );
106              
107 6 50       186 printf "\tOctave: %d, ISO: %s, Formatted: %s\n",
108             $octave, $note->format('ISO'), $note->format($format)
109             if $self->verbose;
110              
111 6         13 return $note;
112             }
113              
114              
115             sub step {
116 6     6 1 3507 my ( $self, %args ) = @_;
117              
118 6         9 my $name = $args{note_name};
119 6   100     18 my $steps = $args{steps} || 1;
120 6   66     20 my $flat = $args{flat} || $self->flat;
121              
122 6 50       12 croak 'note_name not provided'
123             unless $name;
124              
125 6         32 my $note = Music::Note->new( $name, $self->note_format );
126 6         165 my $num = $note->format('midinum');
127              
128 6 50       145 printf "Given note: %s, ISO: %s, Formatted: %d\n",
129             $name, $note->format('ISO'), $num
130             if $self->verbose;
131              
132 6         7 $num += $steps;
133 6         11 $note = Music::Note->new( $num, 'midinum' );
134 6 100 100     128 $note->en_eq('flat') if $flat && $note->format('isobase') =~ /#/;
135              
136 6 50       59 printf "\tNew steps: %d, ISO: %s, Formatted: %s\n",
137             $steps, $note->format('ISO'), $note->format( $self->note_format )
138             if $self->verbose;
139              
140 6         17 return $note;
141             }
142              
143             1;
144              
145             __END__