File Coverage

blib/lib/Music/ScaleNote.pm
Criterion Covered Total %
statement 66 66 100.0
branch 14 22 63.6
condition 24 38 63.1
subroutine 12 12 100.0
pod 2 2 100.0
total 118 140 84.2


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.0904';
7              
8 2     2   522865 use Moo;
  2         14061  
  2         8  
9 2     2   3779 use strictures 2;
  2         2851  
  2         67  
10 2     2   727 use Carp qw(croak);
  2         4  
  2         94  
11             # use Data::Dumper::Compact qw( ddc );
12 2     2   892 use List::SomeUtils qw( first_index );
  2         21256  
  2         182  
13 2     2   989 use MIDI::Util qw(midi_format);
  2         54443  
  2         123  
14 2     2   866 use Music::Note ();
  2         3123  
  2         61  
15 2     2   838 use Music::Scales qw( get_scale_notes get_scale_nums );
  2         8977  
  2         157  
16 2     2   948 use namespace::clean;
  2         21478  
  2         12  
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   31 my ($self) = @_;
36 4         19 my @base = get_scale_nums( $self->scale_name );
37 4         143 my @scale;
38 4         11 for my $i (0 .. 9) {
39 40         37 for my $degree (@base) {
40 240         299 push @scale, $degree + ($i * 12)
41             }
42             }
43             # print ddc \@scale;
44 4         52 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 5931 my ( $self, %args ) = @_;
75              
76 11   66     40 my $name = $args{note_name} || $self->scale_note;
77 11   66     37 my $format = $args{note_format} || $self->note_format;
78 11   66     25 my $offset = $args{offset} || $self->offset;
79 11   66     56 my $flat = $args{flat} || $self->flat;
80              
81 11 0 33     24 croak 'note_name, note_format or offset not provided'
      33        
82             unless $name || $format || $offset;
83              
84 11         41 my $note = Music::Note->new( $name, $format );
85              
86 11 100       380 croak "Note not defined for $name and $format!" unless $note->format($format) eq $name;
87              
88 10         288 my $octave = $note->octave;
89              
90 10 50 66     67 $note->en_eq('flat') if $flat && $note->format('isobase') =~ /#/;
91              
92 10 50       42 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   39 my $posn = first_index { $note->format('midinum') == $_ } @{ $self->_scale };
  328         4659  
  10         232  
97 10 100       171 if ( $posn >= 0 ) {
98 9 50       45 printf "\tPosition: %d, Offset position: %d\n", $posn, $posn + $offset
99             if $self->verbose;
100             }
101             else {
102 1         16 croak 'Scale position not defined!';
103             }
104              
105 9         143 my $n = $self->_scale->[ $posn + $offset ];
106 9         60 $note = Music::Note->new( $n, 'midinum' );
107              
108 9 100 66     203 $note->en_eq('flat') if $flat && $note->format('ISO') =~ /#/;
109              
110 9 50       46 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 4723 my ( $self, %args ) = @_;
120              
121 7   33     25 my $name = $args{note_name} || $self->scale_note;
122 7   66     33 my $format = $args{note_format} || $self->note_format;
123 7   100     18 my $steps = $args{steps} || 1;
124 7   66     19 my $flat = $args{flat} || $self->flat;
125              
126 7         26 my $note = Music::Note->new( $name, $format );
127 7         289 my $num = $note->format('midinum');
128              
129 7 50       197 printf "Given note: %s, ISO: %s, Formatted: %d\n",
130             $name, $note->format('ISO'), $num
131             if $self->verbose;
132              
133 7         8 $num += $steps;
134 7         27 $note = Music::Note->new( $num, 'midinum' );
135 7 100 100     205 $note->en_eq('flat') if $flat && $note->format('isobase') =~ /#/;
136              
137 7 50       132 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         25 return $note;
142             }
143              
144             1;
145              
146             __END__