zeroclickinfo-goodies/lib/DDG/Goodie/NoteFrequency.pm

78 lines
2.5 KiB
Perl

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 $_;
# 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"
return unless ($_ =~ /^([A-Ga-g])([b#])?([0-8])(\s+[0-9]{1,4})?(\s?[hH][zZ])?$/ );
my( $letter, $accidental, $octave, $tuning, $pitchClass, $midi, $frequency );
# 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; }
# assume 440Hz tuning unless otherwise specified
if ( $tuning == 0 ) { $tuning = 440; }
# 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; }
# apply accidental to pitch class number
if ( $accidental eq "b" ) { $pitchClass -= 1; }
elsif ( $accidental eq "#" ) { $pitchClass += 1; }
# calculate MIDI number
$midi = ( 12 * ($octave + 1) ) + $pitchClass;
# fix pitch class number
$pitchClass %= 12;
# validate note is between C0 and B8
return unless ( $midi >= 12 && $midi <= 119 );
# calculate frequency
$frequency = $tuning * ( 2 ** (($midi-69)/12) );
# round to two decimal places (is never negative anyway and avoids libs)
$frequency = int(100 * ($frequency + 0.005)) / 100;
# result
return $frequency,
structured_answer => {
data => {
title => $frequency." Hz",
subtitle => "Note Frequency: $letter$accidental$octave in A$tuning tuning",
},
templates => {
group => 'text',
},
};
};
1;