File Coverage

blib/lib/MIDI/Pitch.pm
Criterion Covered Total %
statement 30 39 76.9
branch 14 24 58.3
condition 23 45 51.1
subroutine 10 11 90.9
pod 8 8 100.0
total 85 127 66.9


line stmt bran cond sub pod time code
1             package MIDI::Pitch;
2              
3 6     6   138733 use 5.00503;
  6         24  
  6         199  
4 6     6   31 use strict;
  6         13  
  6         262  
5              
6             require Exporter;
7 6         5406 use vars qw($VERSION @ISA @EXPORT_OK
8 6     6   43 %name2pitch_lut @pitch2name_table $base_freq);
  6         21  
9              
10             @ISA = qw(Exporter);
11             @EXPORT_OK =
12             qw(name2pitch pitch2name freq2pitch pitch2freq basefreq name2freq freq2name findsemitone);
13             $VERSION = '0.7';
14              
15             $base_freq = 440;
16              
17             =head1 NAME
18              
19             MIDI::Pitch - Converts MIDI pitches, note names and frequencies into each other
20              
21             =head1 SYNOPSIS
22              
23             use MIDI::Pitch qw(name2pitch pitch2name freq2pitch pitch2freq basefreq);
24              
25             my $pitch = name2pitch($name);
26            
27            
28             =head1 DESCRIPTION
29              
30             This module converts MIDI pitches between 0 and 127 (called 'note numbers'
31             in the MIDI standard) and note names into each other. The octave
32             numbers are based on the table found in the MIDI standard (see
33             L):
34              
35             The MIDI specification only defines note number 60 as "Middle C", and
36             all other notes are relative. The absolute octave number designations
37             shown here are based on Middle C = C4, which is an arbitrary
38             assignment.
39              
40             The note names are C, C/C, C, ..., followed by an octave
41             number from -1 to 9. Thus, the valid notes range between C and
42             C.
43              
44             =head1 FUNCTIONS
45              
46             =head2 name2pitch
47              
48             my $pitch = name2pitch($name);
49              
50             Converts a note name into a pitch.
51              
52             =cut
53              
54             %name2pitch_lut = (
55             'b#' => 0,
56             c => 0,
57             'c#' => 1,
58             'db' => 1,
59             d => 2,
60             'd#' => 3,
61             'eb' => 3,
62             e => 4,
63             'fb' => 4,
64             'e#' => 5,
65             f => 5,
66             'f#' => 6,
67             'gb' => 6,
68             g => 7,
69             'g#' => 8,
70             'ab' => 8,
71             a => 9,
72             'a#' => 10,
73             'bb' => 10,
74             b => 11,
75             'cb' => 11);
76              
77             sub name2pitch {
78 669     669 1 8491 my $n = shift;
79              
80 669 100 100     4866 return undef unless defined $n && lc($n) =~ /^([a-g][b#]?)(-?\d\d?)$/;
81              
82 663         2228 my $p = $name2pitch_lut{$1} + ($2 + 1) * 12;
83 663 100 66     2510 return undef unless $p >= 0 && $p <= 127;
84 662         1812 return $p;
85             }
86              
87             =head2 pitch2name
88              
89             my $name = pitch2name($pitch);
90              
91             Converts a pitch between 0 and 127 into a note name. pitch2name returns
92             the lowercase version with a sharp, if necessary (e.g. it will return
93             'g#', not 'Ab').
94              
95             =cut
96              
97             @pitch2name_table =
98             ('c', 'c#', 'd', 'd#', 'e', 'f', 'f#', 'g', 'g#', 'a', 'a#', 'b');
99              
100             sub pitch2name {
101 666     666 1 142677 my $p = shift;
102              
103 666 100 100     6200 return undef unless defined $p && $p =~ /^-?(\d+|\d*(\.\d+))$/;
104 660         1295 $p = int($p + .5 * ($p <=> 0));
105 660 100 100     2420 return undef unless $p >= 0 && $p <= 127;
106              
107 657         2722 return $pitch2name_table[$p % 12] . (int($p / 12) - 1);
108             }
109              
110             =head2 freq2pitch
111              
112             my $pitch = freq2pitch($440);
113              
114             Converts a frequency >= 0 Hz to a pitch, using the base frequency set.
115              
116             =cut
117              
118             sub freq2pitch {
119 722     722 1 50558 my $f = shift;
120              
121 722 100 100     10691 return undef unless defined $f && $f =~ /^(\d+|\d*(\.\d+))$/ && $f > 0;
      100        
122 716         2827 return 69 + 12 * log($f / $base_freq) / log(2);
123             }
124              
125             =head2 pitch2freq
126              
127             my $freq = pitch2freq(69);
128              
129             Converts a pitch to a frequency, using the base frequency set.
130              
131             =cut
132              
133             sub pitch2freq {
134 592     592 1 91075 my $p = shift;
135              
136 592 100 100     5288 return undef unless defined $p && $p =~ /^-?(\d+|\d*(\.\d+))$/;
137 588         2559 return exp((($p - 69) / 12) * log(2)) * $base_freq;
138             }
139              
140             =head2 name2freq
141              
142             my $freq = name2freq('c2');
143              
144             This is just an alias for C.
145              
146             =cut
147              
148             sub name2freq {
149 130     130 1 386 return pitch2freq(name2pitch(@_));
150             }
151              
152             =head2 freq2name
153              
154             my $name = freq2name('c2');
155              
156             This is just an alias for C.
157              
158             =cut
159              
160             sub freq2name {
161 131     131 1 467 return pitch2name(freq2pitch(@_));
162             }
163              
164             =head2 findsemitone {
165              
166             my $pitch = findsemitone('d#', 60);
167              
168             Finds the nearest pitch that expresses the semitone given around the
169             pitch given. The example above would return 63, since the d# at pitch 63 is
170             nearer to 60 than the d# at pitch 51.
171              
172             The semitone can be specified in the same format as a note name (without
173             the octave) or as an integer between 0 and 11.
174              
175             If there are two possibilities for the nearest pitch, findsemitone returns
176             the lower one.
177              
178             =cut
179              
180             sub findsemitone {
181 0     0 1 0 my ($semitone, $pitch) = @_;
182              
183 0 0 0     0 return undef unless defined $semitone &&
      0        
184             (($semitone =~ /^\d+$/
185             && $semitone >= 0
186             && $semitone <= 11) || exists $name2pitch_lut{$semitone});
187             return undef
188 0 0 0     0 unless defined $pitch
      0        
      0        
189             && $pitch =~ /^\d+$/
190             && $pitch >= 0
191             && $pitch <= 127;
192              
193 0 0       0 $semitone = $name2pitch_lut{$semitone} if exists $name2pitch_lut{$semitone};
194              
195 0         0 my $m = $pitch % 12;
196 0         0 my $result = $pitch - $m + $semitone;
197 0 0 0     0 $result += 12 if ($pitch - $result > 6 && $result < 116);
198 0 0 0     0 $result -= 12 if ($result - $pitch > 6 && $result > 11);
199              
200 0         0 return $result;
201             }
202              
203             =head2 basefreq
204              
205             my $basefreq = basefreq;
206             basefreq(432);
207              
208             Sets/returns current base frequency for frequency/pitch conversion. The
209             standard base frequency set is 440 (Hz). Note that the base frequency
210             does not affect the pitch/name conversion.
211              
212             =cut
213              
214             sub basefreq {
215 4     4 1 12 my $f = shift;
216              
217 4 100 100     28 $base_freq = $f if defined $f && $f > 0;
218 4         21 return $base_freq;
219             }
220              
221             =head1 HISTORY
222              
223             =over 8
224              
225             =item 0.7
226              
227             Added Changes file.
228              
229             =item 0.6
230              
231             findsemitone now also understands semitones specified as integers between 0 and 11.
232             Fixed bug in findsemitone.
233              
234             =item 0.5
235              
236             Added findsemitone function
237              
238             =item 0.2
239              
240             Added pitch rounding (60.49 and 59.5 will both be considered 60/'C4').
241              
242             Added frequency/pitch conversion.
243              
244             Added POD tests.
245              
246             =item 0.1
247              
248             Original version; created by h2xs 1.22 with options
249              
250             -A -C -X -n MIDI::Pitch -v 0.1 -b 5.5.3
251              
252             =back
253              
254             =head1 SEE ALSO
255              
256             L. L.
257              
258             L
259              
260             =head1 AUTHOR
261              
262             Christian Renz, Ecrenz @ web42.comE
263              
264             =head1 COPYRIGHT AND LICENSE
265              
266             Copyright 2004-2005 by Christian Renz Ecrenz @ web42.comE. All Rights Reserved.
267              
268             This library is free software; you can redistribute it and/or modify
269             it under the same terms as Perl itself.
270              
271             =cut
272              
273             1;