2015-11-06 09:13:37 -08:00
|
|
|
package DDG::Goodie::NoteFrequency;
|
|
|
|
# ABSTRACT: Return the frequency (Hz) of the note given in the query
|
|
|
|
|
|
|
|
use DDG::Goodie;
|
|
|
|
use strict;
|
|
|
|
|
|
|
|
zci answer_type => "note_frequency";
|
|
|
|
zci is_cached => 1;
|
|
|
|
|
|
|
|
# Triggers
|
|
|
|
triggers any => "notefreq", "notefrequency", "note frequency", "note frequency of", "frequency of note";
|
|
|
|
|
|
|
|
# Handle statement
|
|
|
|
handle remainder => sub {
|
|
|
|
|
|
|
|
return unless $_;
|
|
|
|
|
2015-11-09 13:42:18 -08:00
|
|
|
# must be a note letter, optional sharp or flat,
|
|
|
|
# octave number, optional tuning frequency for A4,
|
|
|
|
# and optional case-insensitive "hz" with or without preceding whitespace
|
|
|
|
# e.g. "g#3 432 hz" or "ab5 435Hz"
|
2015-11-06 09:13:37 -08:00
|
|
|
|
2015-11-09 13:42:18 -08:00
|
|
|
return unless ($_ =~ /^([A-Ga-g])([b#])?([0-8])(\s+[0-9]{1,4})?(\s?[hH][zZ])?$/ );
|
2015-11-06 09:13:37 -08:00
|
|
|
|
2015-11-09 10:44:59 -08:00
|
|
|
my( $letter, $accidental, $octave, $tuning, $pitchClass, $midi, $frequency );
|
2015-11-06 09:13:37 -08:00
|
|
|
|
2015-11-09 10:44:59 -08:00
|
|
|
# regex captures
|
|
|
|
if (defined $1) { $letter = uc($1); } else { $letter = ""; }
|
|
|
|
if (defined $2) { $accidental = $2; } else { $accidental = ""; }
|
|
|
|
if (defined $3) { $octave = $3 + 0; } else { $octave = 0; }
|
|
|
|
if (defined $4) { $tuning = $4 + 0; } else { $tuning = 0; }
|
2015-11-06 09:13:37 -08:00
|
|
|
|
2015-11-09 10:44:59 -08:00
|
|
|
# assume 440Hz tuning unless otherwise specified
|
|
|
|
if ( $tuning == 0 ) { $tuning = 440; }
|
2015-11-06 09:13:37 -08:00
|
|
|
|
2015-11-09 10:44:59 -08:00
|
|
|
# convert note letter to pitch class number
|
|
|
|
if ( $letter eq "C" ) { $pitchClass = 0; }
|
|
|
|
elsif ( $letter eq "D" ) { $pitchClass = 2; }
|
|
|
|
elsif ( $letter eq "E" ) { $pitchClass = 4; }
|
|
|
|
elsif ( $letter eq "F" ) { $pitchClass = 5; }
|
|
|
|
elsif ( $letter eq "G" ) { $pitchClass = 7; }
|
|
|
|
elsif ( $letter eq "A" ) { $pitchClass = 9; }
|
|
|
|
else { $pitchClass = 11; }
|
2015-11-06 09:13:37 -08:00
|
|
|
|
2015-11-09 10:44:59 -08:00
|
|
|
# apply accidental to pitch class number
|
|
|
|
if ( $accidental eq "b" ) { $pitchClass -= 1; }
|
|
|
|
elsif ( $accidental eq "#" ) { $pitchClass += 1; }
|
2015-11-06 09:13:37 -08:00
|
|
|
|
2015-11-09 10:44:59 -08:00
|
|
|
# calculate MIDI number
|
|
|
|
$midi = ( 12 * ($octave + 1) ) + $pitchClass;
|
2015-11-06 09:13:37 -08:00
|
|
|
|
2015-11-09 10:44:59 -08:00
|
|
|
# fix pitch class number
|
|
|
|
$pitchClass %= 12;
|
2015-11-06 09:13:37 -08:00
|
|
|
|
2015-11-09 10:44:59 -08:00
|
|
|
# validate note is between C0 and B8
|
|
|
|
return unless ( $midi >= 12 && $midi <= 119 );
|
2015-11-06 09:13:37 -08:00
|
|
|
|
2015-11-09 10:44:59 -08:00
|
|
|
# calculate frequency
|
|
|
|
$frequency = $tuning * ( 2 ** (($midi-69)/12) );
|
2015-11-09 13:42:18 -08:00
|
|
|
|
|
|
|
# round to two decimal places (is never negative anyway and avoids libs)
|
|
|
|
$frequency = int(100 * ($frequency + 0.005)) / 100;
|
2015-11-06 09:13:37 -08:00
|
|
|
|
2015-11-09 10:44:59 -08:00
|
|
|
# result
|
|
|
|
return $frequency,
|
|
|
|
structured_answer => {
|
2016-04-28 11:32:23 -07:00
|
|
|
data => {
|
|
|
|
title => $frequency." Hz",
|
2016-09-02 01:52:59 -07:00
|
|
|
subtitle => "Note Frequency: $letter$accidental$octave in A$tuning tuning",
|
2016-04-28 11:32:23 -07:00
|
|
|
},
|
|
|
|
templates => {
|
|
|
|
group => 'text',
|
|
|
|
},
|
2015-11-09 10:44:59 -08:00
|
|
|
};
|
2015-11-06 09:13:37 -08:00
|
|
|
};
|
|
|
|
|
|
|
|
1;
|