File Coverage

blib/lib/Music/ScaleNote.pm
Criterion Covered Total %
statement 72 72 100.0
branch 14 22 63.6
condition 22 35 62.8
subroutine 14 14 100.0
pod 2 2 100.0
total 124 145 85.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.0900';
7              
8 2     2   468488 use Moo;
  2         13421  
  2         10  
9 2     2   3773 use strictures 2;
  2         2775  
  2         67  
10 2     2   645 use Carp qw(croak);
  2         8  
  2         107  
11 2     2   946 use Data::Dumper::Compact qw( ddc );
  2         23062  
  2         7  
12 2     2   1135 use Array::Circular ();
  2         1948  
  2         61  
13 2     2   896 use List::SomeUtils qw( first_index );
  2         21974  
  2         192  
14 2     2   860 use MIDI::Util qw(midi_format);
  2         47809  
  2         125  
15 2     2   798 use Music::Note ();
  2         2873  
  2         73  
16 2     2   796 use Music::Scales qw( get_scale_notes get_scale_nums );
  2         7881  
  2         128  
17 2     2   890 use namespace::clean;
  2         19938  
  2         10  
18              
19              
20             has scale_note => (
21             is => 'ro',
22             default => sub { 'C' },
23             );
24              
25              
26             has scale_name => (
27             is => 'ro',
28             default => sub { 'major' },
29             );
30              
31             has _scale => (
32             is => 'lazy',
33             builder => '_build__scale',
34             );
35             sub _build__scale {
36 4     4   25 my ($self) = @_;
37 4         13 my @base = get_scale_nums( $self->scale_name );
38 4         97 my @scale;
39 4         9 for my $i (0 .. 9) {
40 40         41 for my $degree (@base) {
41 240         315 push @scale, $degree + ($i * 12)
42             }
43             }
44             # print ddc \@scale;
45 4         35 return \@scale;
46             }
47              
48              
49             has note_format => (
50             is => 'ro',
51             default => sub { 'midinum' },
52             );
53              
54              
55             has offset => (
56             is => 'ro',
57             isa => sub { die 'Not a negative or positive integer' unless $_[0] =~ /^-?\d+$/ },
58             default => sub { 1 },
59             );
60              
61              
62             has flat => (
63             is => 'ro',
64             default => sub { 0 },
65             );
66              
67              
68             has verbose => (
69             is => 'ro',
70             default => sub { 0 },
71             );
72              
73              
74             sub get_offset {
75 11     11 1 5743 my ( $self, %args ) = @_;
76              
77 11   66     37 my $name = $args{note_name} || $self->scale_note;
78 11   66     41 my $format = $args{note_format} || $self->note_format;
79 11   66     41 my $offset = $args{offset} || $self->offset;
80 11   66     29 my $flat = $args{flat} || $self->flat;
81              
82 11 0 33     22 croak 'note_name, note_format or offset not provided'
      33        
83             unless $name || $format || $offset;
84              
85 11         38 my $note = Music::Note->new( $name, $format );
86 11 100       297 croak 'Note not defined!' unless $note->format($format) eq $name;
87              
88 10         212 my $octave = $note->octave;
89              
90 10 50 66     72 $note->en_eq('flat') if $flat && $note->format('isobase') =~ /#/;
91              
92 10 50       36 printf "Given note: %s, Format: %s, ISO: %s, Offset: %d\n",
93             $name, $format, $note->format('ISO'), $offset
94             if $self->verbose;
95              
96 10     328   57 my $posn = first_index { $note->format('midinum') == $_ } @{ $self->_scale };
  328         4334  
  10         199  
97 10 100       160 if ( $posn >= 0 ) {
98 9 50       24 printf "\tPosition: %d, Offset position: %d\n", $posn, $posn + $offset
99             if $self->verbose;
100             }
101             else {
102 1         14 croak 'Scale position not defined!';
103             }
104              
105 9         168 my $n = $self->_scale->[ $posn + $offset ];
106 9         58 $note = Music::Note->new( $n, 'midinum' );
107              
108 9 100       193 $note->en_eq('flat') if $flat;
109              
110 9 50       27 printf "\tOctave: %d, ISO: %s, Formatted: %s\n",
111             $octave, $note->format('ISO'), $note->format($format)
112             if $self->verbose;
113              
114 9         26 return $note;
115             }
116              
117              
118             sub step {
119 7     7 1 4843 my ( $self, %args ) = @_;
120              
121 7   33     19 my $name = $args{note_name} || $self->scale_note;
122 7   66     28 my $format = $args{note_format} || $self->note_format;
123 7   100     16 my $steps = $args{steps} || 1;
124 7   66     16 my $flat = $args{flat} || $self->flat;
125              
126 7         38 my $note = Music::Note->new( $name, $format );
127 7         211 my $num = $note->format('midinum');
128              
129 7 50       162 printf "Given note: %s, ISO: %s, Formatted: %d\n",
130             $name, $note->format('ISO'), $num
131             if $self->verbose;
132              
133 7         9 $num += $steps;
134 7         28 $note = Music::Note->new( $num, 'midinum' );
135 7 100 100     174 $note->en_eq('flat') if $flat && $note->format('isobase') =~ /#/;
136              
137 7 50       97 printf "\tNew steps: %d, ISO: %s, Formatted: %s\n",
138             $steps, $note->format('ISO'), $note->format( $self->note_format )
139             if $self->verbose;
140              
141 7         21 return $note;
142             }
143              
144             1;
145              
146             __END__