File Coverage

blib/lib/Music/ScaleNote.pm
Criterion Covered Total %
statement 67 67 100.0
branch 15 24 62.5
condition 24 38 63.1
subroutine 12 12 100.0
pod 2 2 100.0
total 120 143 83.9


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.0905';
7              
8 2     2   590770 use Moo;
  2         13957  
  2         13  
9 2     2   3566 use strictures 2;
  2         2841  
  2         73  
10 2     2   842 use Carp qw(croak);
  2         6  
  2         104  
11             # use Data::Dumper::Compact qw( ddc );
12 2     2   985 use List::SomeUtils qw( first_index );
  2         32499  
  2         236  
13 2     2   1441 use MIDI::Util qw(midi_format);
  2         85008  
  2         212  
14 2     2   1266 use Music::Note ();
  2         5407  
  2         88  
15 2     2   1198 use Music::Scales qw( get_scale_nums );
  2         14717  
  2         192  
16 2     2   1351 use namespace::clean;
  2         28963  
  2         15  
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             has _scale => (
31             is => 'lazy',
32             builder => '_build__scale',
33             );
34             sub _build__scale {
35 4     4   40 my ($self) = @_;
36 4         21 my @base = get_scale_nums( $self->scale_name );
37 4         120 my @scale;
38 4         13 for my $i (0 .. 10) {
39 44         79 for my $degree (@base) {
40 264         537 push @scale, $degree + ($i * 12)
41             }
42             }
43 4 50       18 print "@scale\n" if $self->verbose;
44 4         40 return \@scale;
45             }
46              
47              
48             has note_format => (
49             is => 'ro',
50             default => sub { 'midinum' },
51             );
52              
53              
54             has offset => (
55             is => 'ro',
56             isa => sub { die 'Not a negative or positive integer' unless $_[0] =~ /^-?\d+$/ },
57             default => sub { 1 },
58             );
59              
60              
61             has flat => (
62             is => 'ro',
63             default => sub { 0 },
64             );
65              
66              
67             has verbose => (
68             is => 'ro',
69             default => sub { 0 },
70             );
71              
72              
73             sub get_offset {
74 11     11 1 12786 my ( $self, %args ) = @_;
75              
76 11   66     53 my $name = $args{note_name} || $self->scale_note;
77 11   66     54 my $format = $args{note_format} || $self->note_format;
78 11   66     43 my $offset = $args{offset} || $self->offset;
79 11   66     50 my $flat = $args{flat} || $self->flat;
80              
81 11 0 33     35 croak 'note_name, note_format or offset not provided'
      33        
82             unless $name || $format || $offset;
83              
84 11         49 my $note = Music::Note->new( $name, $format );
85              
86 11 100       475 croak "Note not defined for $name and $format!" unless $note->format($format) eq $name;
87              
88 10         381 my $octave = $note->octave;
89              
90 10 50 66     93 $note->en_eq('flat') if $flat && $note->format('isobase') =~ /#/;
91              
92 10 50       77 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     333   49 my $posn = first_index { $note->format('midinum') == $_ } @{ $self->_scale };
  333         8550  
  10         321  
97 10 100       300 if ( $posn >= 0 ) {
98 9 50       40 printf "\tPosition: %d, Offset position: %d\n", $posn, $posn + $offset
99             if $self->verbose;
100             }
101             else {
102 1         19 croak 'Scale position not defined!';
103             }
104              
105 9         332 my $n = $self->_scale->[ $posn + $offset ];
106 9         100 $note = Music::Note->new( $n, 'midinum' );
107              
108 9 100 66     339 $note->en_eq('flat') if $flat && $note->format('ISO') =~ /#/;
109              
110 9 50       79 printf "\tOctave: %d, ISO: %s, Formatted: %s\n",
111             $octave, $note->format('ISO'), $note->format($format)
112             if $self->verbose;
113              
114 9         46 return $note;
115             }
116              
117              
118             sub step {
119 7     7 1 9701 my ( $self, %args ) = @_;
120              
121 7   33     29 my $name = $args{note_name} || $self->scale_note;
122 7   66     44 my $format = $args{note_format} || $self->note_format;
123 7   100     20 my $steps = $args{steps} || 1;
124 7   66     27 my $flat = $args{flat} || $self->flat;
125              
126 7         31 my $note = Music::Note->new( $name, $format );
127 7         345 my $num = $note->format('midinum');
128              
129 7 50       275 printf "Given note: %s, ISO: %s, Formatted: %d\n",
130             $name, $note->format('ISO'), $num
131             if $self->verbose;
132              
133 7         14 $num += $steps;
134 7         20 $note = Music::Note->new( $num, 'midinum' );
135 7 100 100     292 $note->en_eq('flat') if $flat && $note->format('isobase') =~ /#/;
136              
137 7 50       153 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         33 return $note;
142             }
143              
144             1;
145              
146             __END__