File Coverage

blib/lib/Music/Tension.pm
Criterion Covered Total %
statement 42 42 100.0
branch 16 16 100.0
condition 27 27 100.0
subroutine 8 8 100.0
pod 4 4 100.0
total 97 97 100.0


line stmt bran cond sub pod time code
1             # -*- Perl -*-
2             #
3             # Parent class for music tension analysis modules
4              
5             package Music::Tension;
6              
7             our $VERSION = '1.03';
8              
9 5     5   252751 use strict;
  5         21  
  5         142  
10 5     5   28 use warnings;
  5         8  
  5         136  
11 5     5   24 use Carp qw/croak/;
  5         8  
  5         233  
12 5     5   26 use Scalar::Util qw/looks_like_number/;
  5         10  
  5         2689  
13              
14             ########################################################################
15             #
16             # METHODS
17              
18             sub new {
19 28     28 1 917 my ( $class, %param ) = @_;
20 28         47 my $self = {};
21              
22             # just MIDI support here, see Music::Scala for scala scale file support
23 28 100       78 if ( exists $param{reference_frequency} ) {
24             croak "reference_frequency must be a number"
25             if !defined $param{reference_frequency}
26 5 100 100     66 or !looks_like_number $param{reference_frequency};
27 3         11 $self->{_reference_frequency} = $param{reference_frequency};
28             } else {
29 23         61 $self->{_reference_frequency} = 440;
30             }
31              
32 26         48 bless $self, $class;
33 26         67 return $self;
34             }
35              
36             sub freq2pitch {
37 7     7 1 762 my ( $self, $freq ) = @_;
38 7 100 100     74 croak "frequency must be a positive number"
      100        
39             if !defined $freq
40             or !looks_like_number $freq
41             or $freq <= 0;
42              
43             return sprintf "%.0f",
44 4         43 69 + 12 * ( log( $freq / $self->{_reference_frequency} ) / log(2) );
45             }
46              
47             # accumulate tension values for two phrases at each possible offset
48             sub offset_tensions {
49 11     11 1 905 my ( $self, $phrase1, $phrase2 ) = @_;
50              
51 11 100       69 die "pitches method is unimplemented" unless $self->can('pitches');
52              
53             croak "phrase1 is too short"
54             unless defined $phrase1
55             and ref $phrase1 eq 'ARRAY'
56 10 100 100     65 and $#{$phrase1} > 1;
  8   100     33  
57 7 100 100     70 croak "phrase2 is too short"
      100        
58             unless defined $phrase2
59             and ref $phrase2 eq 'ARRAY'
60             and @$phrase2;
61              
62 4         8 my $max = $#{$phrase1};
  4         8  
63              
64 4         7 my @tensions;
65 4         11 for my $offset ( 0 .. $max ) {
66 15         19 for my $i ( 0 .. $#{$phrase2} ) {
  15         32  
67 47         80 my $delta = $i + $offset;
68 47 100       85 last if $delta > $max;
69 36         48 push @{ $tensions[$offset] },
  36         110  
70             $self->pitches( $phrase1->[$delta], $phrase2->[$i] );
71             }
72             }
73 4         42 return @tensions;
74             }
75              
76             sub pitch2freq {
77 15     15 1 168 my ( $self, $pitch ) = @_;
78 15 100 100     141 croak "pitch must be MIDI number"
      100        
79             if !defined $pitch
80             or !looks_like_number $pitch
81             or $pitch < 0;
82              
83 12         68 return $self->{_reference_frequency} * ( 2**( ( $pitch - 69 ) / 12 ) );
84             }
85              
86             1;
87             __END__