Skip to content

Instantly share code, notes, and snippets.

@fhpriamo
Created April 2, 2014 00:32
Show Gist options
  • Save fhpriamo/9925841 to your computer and use it in GitHub Desktop.
Save fhpriamo/9925841 to your computer and use it in GitHub Desktop.
class Note
class MalformedNotationError < StandardError
end
A4_FREQUENCY = 440
STEP_FREQUENCY_FACTOR = 1.059463094359
INTERVALS = {'C' => 0, 'C#' => 1, 'Db' => 1, 'D' => 2,
'D#' => 3, 'Eb' => 3, 'E' => 4, 'F' => 5,
'F#' => 6, 'Gb' => 6, 'G' => 7, 'G#' => 8,
'Ab' => 8, 'A' => 9, 'A#' => 10,'Bb' => 10,
'B' => 11}
STEPS_PER_OCTAVE = 12
attr_reader :cypher, :octave
def initialize(notation)
@cypher, @octave = parse_notation(notation)
@octave = @octave.to_i
end
def freq
A4_FREQUENCY * (STEP_FREQUENCY_FACTOR ** distance_from_a4)
end
def distance_from(another)
self.distance_from_c0 - another.distance_from_c0
end
protected
def distance_from_a4
distance_from(Note.new('A4'))
end
def distance_from_c0
(STEPS_PER_OCTAVE * octave) + distance_in_same_octave
end
def distance_in_same_octave
INTERVALS[cypher]
end
def parse_notation(notation)
match = notation.match /\A([A-G][b#]?)(\d)\z/
if match.nil?
raise MalformedNotationError.new("Malformed notation `#{notation}`")
end
match.captures
end
end
class NoteTest < MiniTest::Test
def frequencies
[['C0', 16.35], ['C#0', 17.32], ['Db0', 17.32],
['D0', 18.35], ['D#0', 19.45], ['Eb0', 19.45],
['E0', 20.60], ['F0', 21.83], ['F#0', 23.12],
['Gb0', 23.12], ['G0', 24.50], ['G#0', 25.96],
['Ab0', 25.96], ['A0', 27.50], ['A#0', 29.14],
['Bb0', 29.14], ['B0', 30.87], ['C1', 32.70],
['C#1', 34.65], ['Db1', 34.65], ['D1', 36.71],
['D#1', 38.89], ['Eb1', 38.89], ['E1', 41.20],
['F1', 43.65], ['F#1', 46.25], ['Gb1', 46.25],
['G1', 49.00], ['G#1', 51.91], ['Ab1', 51.91],
['A1', 55.00], ['A#1', 58.27], ['Bb1', 58.27],
['B1', 61.74], ['C2', 65.41], ['C#2', 69.30],
['Db2', 69.30], ['D2', 73.42], ['D#2', 77.78],
['Eb2', 77.78], ['E2', 82.41], ['F2', 87.31],
['F#2', 92.50], ['Gb2', 92.50], ['G2', 98.00],
['G#2', 103.83], ['Ab2', 103.83], ['A2', 110.00],
['A#2', 116.54], ['Bb2', 116.54], ['B2', 123.47],
['C3', 130.81], ['C#3', 138.59], ['Db3', 138.59],
['D3', 146.83], ['D#3', 155.56], ['Eb3', 155.56],
['E3', 164.81], ['F3', 174.61], ['F#3', 185.00],
['Gb3', 185.00], ['G3', 196.00], ['G#3', 207.65],
['Ab3', 207.65], ['A3', 220.00], ['A#3', 233.08],
['Bb3', 233.08], ['B3', 246.94], ['C4', 261.63],
['C#4', 277.18], ['Db4', 277.18], ['D4', 293.66],
['D#4', 311.13], ['Eb4', 311.13], ['E4', 329.63],
['F4', 349.23], ['F#4', 369.99], ['Gb4', 369.99],
['G4', 392.00], ['G#4', 415.30], ['Ab4', 415.30],
['A4', 440.00], ['A#4', 466.16], ['Bb4', 466.16],
['B4', 493.88], ['C5', 523.25], ['C#5', 554.37],
['Db5', 554.37], ['D5', 587.33], ['D#5', 622.25],
['Eb5', 622.25], ['E5', 659.25], ['F5', 698.46],
['F#5', 739.99], ['Gb5', 739.99], ['G5', 783.99],
['G#5', 830.61], ['Ab5', 830.61], ['A5', 880.00],
['A#5', 932.33], ['Bb5', 932.33], ['B5', 987.77],
['C6', 1046.50], ['C#6', 1108.73], ['Db6', 1108.73],
['D6', 1174.66], ['D#6', 1244.51], ['Eb6', 1244.51],
['E6', 1318.51], ['F6', 1396.91], ['F#6', 1479.98],
['Gb6', 1479.98], ['G6', 1567.98], ['G#6', 1661.22],
['Ab6', 1661.22], ['A6', 1760.00], ['A#6', 1864.66],
['Bb6', 1864.66], ['B6', 1975.53], ['C7', 2093.00],
['C#7', 2217.46], ['Db7', 2217.46], ['D7', 2349.32],
['D#7', 2489.02], ['Eb7', 2489.02], ['E7', 2637.02],
['F7', 2793.83], ['F#7', 2959.96], ['Gb7', 2959.96],
['G7', 3135.96], ['G#7', 3322.44], ['Ab7', 3322.44],
['A7', 3520.00], ['A#7', 3729.31], ['Bb7', 3729.31],
['B7', 3951.07], ['C8', 4186.01], ['C#8', 4434.92],
['Db8', 4434.92], ['D8', 4698.63], ['D#8', 4978.03],
['Eb8', 4978.03], ['E8', 5274.04], ['F8', 5587.65],
['F#8', 5919.91], ['Gb8', 5919.91], ['G8', 6271.93],
['G#8', 6644.88], ['Ab8', 6644.88], ['A8', 7040.00],
['A#8', 7458.62], ['Bb8', 7458.62], ['B8', 7902.13]]
end
def test_frequencies
frequencies.each do |f|
assert_in_delta f[1], Note.new(f[0]).freq, 0.01
end
end
def test_distance_from
note = Note.new('A4')
assert_equal -1, note.distance_from(Note.new('A#4'))
assert_equal 1, note.distance_from(Note.new('Ab4'))
assert_equal 57, note.distance_from(Note.new('C0'))
assert_equal -39, note.distance_from(Note.new('C8'))
assert_equal 0, note.distance_from(Note.new('A4'))
end
def test_raises_malformed_notation_error
assert_raises(Note::MalformedNotationError) do
Note.new('4A')
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment