File Coverage

blib/lib/Music/Intervals/Numeric.pm
Criterion Covered Total %
statement 76 76 100.0
branch 3 4 75.0
condition n/a
subroutine 18 18 100.0
pod 6 6 100.0
total 103 104 99.0


line stmt bran cond sub pod time code
1             package Music::Intervals::Numeric;
2             $Music::Intervals::Numeric::VERSION = '0.0903';
3             our $AUTHORITY = 'cpan:GENE';
4              
5             # ABSTRACT: Breakdown of numeric musical intervals
6              
7 1     1   667 use strict;
  1         2  
  1         26  
8 1     1   4 use warnings;
  1         2  
  1         28  
9              
10 1     1   448 use Algorithm::Combinatorics qw( combinations );
  1         3148  
  1         56  
11 1     1   377 use Math::Factor::XS qw( prime_factors );
  1         26041  
  1         60  
12 1     1   466 use Number::Fraction ();
  1         50019  
  1         26  
13 1     1   609 use Music::Intervals::Ratios;
  1         11  
  1         37  
14 1     1   6 use Moo;
  1         2  
  1         7  
15 1     1   834 use strictures 2;
  1         2087  
  1         41  
16 1     1   635 use namespace::clean;
  1         7322  
  1         23  
17              
18              
19             has notes => (
20             is => 'ro',
21             default => sub { [qw( 1/1 5/4 3/2 )] },
22             );
23              
24              
25             has ratios => (
26             is => 'ro',
27             builder => 1,
28             );
29             sub _build_ratios {
30 2     2   14 my ($self) = @_;
31 1     1   327 no warnings 'once';
  1         2  
  1         758  
32             my $ratios = { map {
33 2         141 $Music::Intervals::Ratios::ratio->{$_}{ratio} => $Music::Intervals::Ratios::ratio->{$_}{name}
34 894         2421 } keys %$Music::Intervals::Ratios::ratio };
35 2         79 return $ratios;
36             }
37              
38             has _dyads => (
39             is => 'ro',
40             lazy => 1,
41             builder => 1,
42             );
43             sub _build__dyads {
44 2     2   18 my $self = shift;
45 2         9 my %dyads = $self->dyads($self->notes);
46 2         10 return \%dyads;
47             }
48              
49             has _semitones => ( is => 'ro', default => sub { 12 } );
50             has _temper => ( is => 'ro', lazy => 1, default => sub { my $self = shift;
51             $self->_semitones * 100 / log(2) },
52             );
53              
54              
55             sub frequencies {
56 2     2 1 1108 my ($self) = @_;
57              
58 2         4 my %frequencies = map { $_ => $self->ratios->{$_} } @{ $self->notes };
  4         16  
  2         12  
59              
60 2         14 return \%frequencies;
61             }
62              
63             sub intervals {
64 2     2 1 286 my ($self) = @_;
65              
66 2         5 my %dyads = %{ $self->_dyads };
  2         45  
67              
68             my %intervals = map {
69 2         6 $_ => {
70 3         16 $dyads{$_} => $self->ratios->{ $dyads{$_} }
71             }
72             } keys %dyads;
73              
74 2         17 return \%intervals;
75             }
76              
77             sub cent_vals {
78 4     4 1 277 my ($self) = @_;
79              
80 4         6 my %dyads = %{ $self->_dyads };
  4         83  
81              
82             my %cent_vals = map {
83 4         40 $_ => log( eval $dyads{$_} ) * $self->_temper
  9         398  
84             } keys %dyads;
85            
86 4         70 return \%cent_vals;
87             }
88              
89             sub prime_factor {
90 2     2 1 285 my ($self) = @_;
91              
92 2         3 my %dyads = %{ $self->_dyads };
  2         42  
93              
94             my %prime_factor = map {
95 2         21 $_ => {
96 3         10 $dyads{$_} => scalar ratio_factorize( $dyads{$_} )
97             }
98             } keys %dyads;
99              
100 2         14 return \%prime_factor;
101             }
102              
103              
104             sub dyads {
105 2     2 1 3 my $self = shift;
106 2         4 my ($c) = @_;
107              
108 2 100       10 return () if @$c <= 1;
109              
110 1         6 my @pairs = combinations( $c, 2 );
111              
112 1         149 my %dyads;
113 1         3 for my $i (@pairs) {
114             # Construct our "dyadic" fraction.
115 3         77 my $numerator = Number::Fraction->new( $i->[1] );
116 3         3252 my $denominator = Number::Fraction->new( $i->[0] );
117 3         590 my $fraction = $numerator / $denominator;
118              
119 3         555 $dyads{"@$i"} = $fraction->to_string;
120             }
121              
122 1         18 return %dyads;
123             }
124              
125              
126             sub ratio_factorize {
127 3     3 1 5 my $dyad = shift;
128              
129 3         9 my ( $numerator, $denominator ) = split /\//, $dyad;
130 3         12 $numerator = [ prime_factors($numerator) ];
131 3         6 $denominator = [ prime_factors($denominator) ];
132              
133             return wantarray
134 3 50       23 ? ( $numerator, $denominator )
135             : sprintf( '(%s) / (%s)',
136             join( '*', @$numerator ),
137             join( '*', @$denominator )
138             );
139             }
140              
141             1;
142              
143             __END__