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.1101';
3             our $AUTHORITY = 'cpan:GENE';
4              
5             # ABSTRACT: Breakdown of numeric musical intervals
6              
7 2     2   830776 use strict;
  2         6  
  2         98  
8 2     2   13 use warnings;
  2         5  
  2         169  
9              
10 2     2   1247 use Algorithm::Combinatorics qw( combinations );
  2         10432  
  2         193  
11 2     2   1140 use Math::Factor::XS qw( prime_factors );
  2         80416  
  2         174  
12 2     2   1379 use Number::Fraction ();
  2         470843  
  2         112  
13 2     2   1708 use Music::Intervals::Ratios;
  2         14  
  2         227  
14 2     2   21 use Moo;
  2         4  
  2         19  
15 2     2   2506 use strictures 2;
  2         6056  
  2         123  
16 2     2   2435 use namespace::clean;
  2         27084  
  2         22  
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 5     5   36 my ($self) = @_;
31 2     2   1003 no warnings 'once';
  2         7  
  2         2345  
32             my $ratios = { map {
33 5         626 $Music::Intervals::Ratios::ratio->{$_}{ratio} => $Music::Intervals::Ratios::ratio->{$_}{name}
34 2540         9211 } keys %$Music::Intervals::Ratios::ratio };
35 5         516 return $ratios;
36             }
37              
38             has _dyads => (
39             is => 'ro',
40             lazy => 1,
41             builder => 1,
42             );
43             sub _build__dyads {
44 4     4   48 my $self = shift;
45 4         28 my %dyads = $self->dyads($self->notes);
46 4         33 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 1546 my ($self) = @_;
57              
58 2         5 my %frequencies = map { $_ => $self->ratios->{$_} } @{ $self->notes };
  4         23  
  2         11  
59              
60 2         16 return \%frequencies;
61             }
62              
63             sub intervals {
64 2     2 1 1299 my ($self) = @_;
65              
66 2         6 my %dyads = %{ $self->_dyads };
  2         78  
67              
68             my %intervals = map {
69 2         8 $_ => {
70 3         19 $dyads{$_} => $self->ratios->{ $dyads{$_} }
71             }
72             } keys %dyads;
73              
74 2         21 return \%intervals;
75             }
76              
77             sub cent_vals {
78 4     4 1 1269 my ($self) = @_;
79              
80 4         10 my %dyads = %{ $self->_dyads };
  4         144  
81              
82             my %cent_vals = map {
83 4         47 $_ => log( eval $dyads{$_} ) * $self->_temper
  9         777  
84             } keys %dyads;
85            
86 4         97 return \%cent_vals;
87             }
88              
89             sub prime_factor {
90 2     2 1 1254 my ($self) = @_;
91              
92 2         7 my %dyads = %{ $self->_dyads };
  2         73  
93              
94             my %prime_factor = map {
95 2         18 $_ => {
96 3         12 $dyads{$_} => scalar ratio_factorize( $dyads{$_} )
97             }
98             } keys %dyads;
99              
100 2         20 return \%prime_factor;
101             }
102              
103              
104             sub dyads {
105 4     4 1 9 my $self = shift;
106 4         11 my ($c) = @_;
107              
108 4 100       20 return () if @$c <= 1;
109              
110 3         21 my @pairs = combinations( $c, 2 );
111              
112 3         447 my %dyads;
113 3         11 for my $i (@pairs) {
114             # Construct our "dyadic" fraction.
115 9         749 my $numerator = Number::Fraction->new( $i->[1] );
116 9         8310 my $denominator = Number::Fraction->new( $i->[0] );
117 9         2515 my $fraction = $numerator / $denominator;
118              
119 9         2279 $dyads{"@$i"} = $fraction->to_string;
120             }
121              
122 3         81 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         14 $numerator = [ prime_factors($numerator) ];
131 3         9 $denominator = [ prime_factors($denominator) ];
132              
133             return wantarray
134 3 50       27 ? ( $numerator, $denominator )
135             : sprintf( '(%s) / (%s)',
136             join( '*', @$numerator ),
137             join( '*', @$denominator )
138             );
139             }
140              
141             1;
142              
143             __END__