Class: Fet::Note
Overview
Class responsible for parsing and validating musical notes from a string, e.g. “Eb”
Constant Summary collapse
- ACCIDENTAL_TO_SEMITONES_MAP =
{ "b" => -1, "#" => 1, "x" => 2, }.deep_freeze
Instance Attribute Summary collapse
-
#accidental ⇒ Object
TODO: this class could also potentially handle the octave number - note that e.g.
-
#full_note ⇒ Object
TODO: this class could also potentially handle the octave number - note that e.g.
-
#natural_note ⇒ Object
TODO: this class could also potentially handle the octave number - note that e.g.
Class Method Summary collapse
Instance Method Summary collapse
- #accidental_to_semitone_offset ⇒ Object
-
#degree(root_name) ⇒ Object
NOTE: Note.new(“E”).change_natural_note(“D”) -> Note.new(“Dx”) def change_natural_note(new_natural_note) semitone_offset = accidental_to_semitone_offset return Note.new(“##natural_note#selfself.classself.class.accidental_from_semitone_offset(semitone_offset)”) if new_natural_note == natural_note end.
- #flattened? ⇒ Boolean
-
#flattened_note ⇒ Object
NOTE: performs the following conversions: Fxx -> F#x -> Fx -> F# -> F -> Fb -> Fbb.
-
#initialize(note) ⇒ Note
constructor
A new instance of Note.
- #natural? ⇒ Boolean
-
#normalized_note ⇒ Object
NOTE: normalizing the note means: - converting the natural note + accidentals such that the remaining accidental is either “b”, “”, or “#”, or - if a note name is provided, then convert the accidental such that the natural note matches the pitch of the original.
- #sharpened? ⇒ Boolean
-
#sharpened_note ⇒ Object
NOTE: performs the following conversions: Fbb -> Fb -> F -> F# ->Fx -> F#x -> Fxx.
Constructor Details
#initialize(note) ⇒ Note
Returns a new instance of Note.
21 22 23 24 25 26 27 28 29 30 |
# File 'lib/fet/note.rb', line 21 def initialize(note) self.full_note = note validate_full_note! self.natural_note = note[0] validate_natural_note! self.accidental = note[1..] validate_accidental! end |
Instance Attribute Details
#accidental ⇒ Object
TODO: this class could also potentially handle the octave number - note that e.g. Bb,B,Bx… are in octave 3 while Cb,C,C# are in octave 4
17 18 19 |
# File 'lib/fet/note.rb', line 17 def accidental @accidental end |
#full_note ⇒ Object
TODO: this class could also potentially handle the octave number - note that e.g. Bb,B,Bx… are in octave 3 while Cb,C,C# are in octave 4
17 18 19 |
# File 'lib/fet/note.rb', line 17 def full_note @full_note end |
#natural_note ⇒ Object
TODO: this class could also potentially handle the octave number - note that e.g. Bb,B,Bx… are in octave 3 while Cb,C,C# are in octave 4
17 18 19 |
# File 'lib/fet/note.rb', line 17 def natural_note @natural_note end |
Class Method Details
.accidental_from_semitone_offset(semitone_offset) ⇒ Object
32 33 34 35 36 37 38 39 |
# File 'lib/fet/note.rb', line 32 def self.accidental_from_semitone_offset(semitone_offset) return "" if semitone_offset.zero? return "b" * -semitone_offset if semitone_offset.negative? number_of_hashes = (semitone_offset % 2).zero? ? 0 : 1 number_of_xs = semitone_offset / 2 return "#" * number_of_hashes + "x" * number_of_xs end |
Instance Method Details
#accidental_to_semitone_offset ⇒ Object
100 101 102 |
# File 'lib/fet/note.rb', line 100 def accidental_to_semitone_offset return accidental.chars.map { |char| ACCIDENTAL_TO_SEMITONES_MAP[char] }.sum end |
#degree(root_name) ⇒ Object
89 90 91 92 93 94 95 96 97 98 |
# File 'lib/fet/note.rb', line 89 def degree(root_name) notes_array = Fet::MusicTheory.notes_of_mode(root_name, "major") index = notes_array.index { |note| Note.new(note).natural_note == natural_note } degree = index + 1 degree_note = Note.new(notes_array[index]) accidental_difference = accidental_to_semitone_offset - degree_note.accidental_to_semitone_offset return "#{self.class.accidental_from_semitone_offset(accidental_difference)}#{degree}" end |
#flattened? ⇒ Boolean
108 109 110 |
# File 'lib/fet/note.rb', line 108 def flattened? return accidental.chars.include?("b") end |
#flattened_note ⇒ Object
NOTE: performs the following conversions: Fxx -> F#x -> Fx -> F# -> F -> Fb -> Fbb
58 59 60 61 62 63 64 65 66 67 68 69 |
# File 'lib/fet/note.rb', line 58 def flattened_note note_as_string = case when accidental.start_with?("x") "#{natural_note}##{accidental[1..]}" when accidental.start_with?("#") "#{natural_note}#{accidental[1..]}" else "#{natural_note}#{accidental}b" end return Note.new(note_as_string) end |
#natural? ⇒ Boolean
104 105 106 |
# File 'lib/fet/note.rb', line 104 def natural? return accidental.chars.empty? end |
#normalized_note ⇒ Object
NOTE: normalizing the note means:
-
converting the natural note + accidentals such that the remaining accidental is either “b”, “”, or “#”, or
-
if a note name is provided, then convert the accidental such that the natural note matches the pitch of the original
74 75 76 77 78 79 80 81 |
# File 'lib/fet/note.rb', line 74 def normalized_note remaining_semitones = accidental_to_semitone_offset next_note = remaining_semitones.positive? ? next_natural_note : previous_natural_note next_note_offset = remaining_semitones.positive? ? semitone_offset_to_next_natural_note : semitone_offset_to_previous_natural_note return Note.new(full_note) if next_note_offset.abs > remaining_semitones.abs return Note.new("#{next_note}#{self.class.accidental_from_semitone_offset(remaining_semitones - next_note_offset)}").normalized_note end |
#sharpened? ⇒ Boolean
112 113 114 |
# File 'lib/fet/note.rb', line 112 def sharpened? return accidental.chars.include?("#") || accidental.chars.include?("x") end |
#sharpened_note ⇒ Object
NOTE: performs the following conversions: Fbb -> Fb -> F -> F# ->Fx -> F#x -> Fxx
43 44 45 46 47 48 49 50 51 52 53 54 |
# File 'lib/fet/note.rb', line 43 def sharpened_note note_as_string = case when accidental.start_with?("b") "#{natural_note}#{accidental[1..]}" when accidental.start_with?("#") "#{natural_note}x#{accidental[1..]}" else "#{natural_note}##{accidental}" end return Note.new(note_as_string) end |