Class: Music::Transcription::Pitch
- Inherits:
-
Object
- Object
- Music::Transcription::Pitch
- Includes:
- Comparable, Parseable
- Defined in:
- lib/music-transcription/model/pitch.rb,
lib/music-transcription/parsing/convenience_methods.rb
Overview
Constant Summary collapse
- SEMITONES_PER_OCTAVE =
The default number of semitones per octave is 12, corresponding to the twelve-tone equal temperment tuning system.
12
- CENTS_PER_SEMITONE =
100
- CENTS_PER_OCTAVE =
SEMITONES_PER_OCTAVE * CENTS_PER_SEMITONE
- BASE_FREQ =
The base ferquency is C0
16.351597831287414
- PARSER =
Parsing::PitchParser.new
- CONVERSION_METHOD =
:to_pitch
Constants included from Parseable
Music::Transcription::Parseable::DEFAULT_SPLIT_PATTERN
Instance Attribute Summary collapse
-
#cent ⇒ Object
readonly
Returns the value of attribute cent.
-
#octave ⇒ Fixnum
readonly
The pitch octave.
-
#semitone ⇒ Fixnum
readonly
The pitch semitone.
-
#total_cents ⇒ Object
readonly
Returns the value of attribute total_cents.
Class Method Summary collapse
Instance Method Summary collapse
-
#<=>(other) ⇒ Object
Compare pitches.
-
#==(other) ⇒ Object
Compare pitch equality using total semitone.
- #clone ⇒ Object
-
#diff(other) ⇒ Object
diff in (rounded) semitones.
- #eql?(other) ⇒ Boolean
-
#freq ⇒ Object
Return the pitch’s frequency, which is determined by multiplying the base frequency and the pitch ratio.
-
#hash ⇒ Object
Override default hash method.
-
#initialize(octave: 0, semitone: 0, cent: 0) ⇒ Pitch
constructor
A new instance of Pitch.
-
#ratio ⇒ Float
Calculate the pitch ratio.
-
#round ⇒ Object
rounds to the nearest semitone.
- #to_s(sharpit = false) ⇒ Object
- #total_semitones ⇒ Object
- #transpose(semitones) ⇒ Object
Methods included from Parseable
Constructor Details
#initialize(octave: 0, semitone: 0, cent: 0) ⇒ Pitch
Returns a new instance of Pitch.
34 35 36 37 38 39 40 41 42 43 44 |
# File 'lib/music-transcription/model/pitch.rb', line 34 def initialize octave:0, semitone:0, cent: 0 raise NonIntegerError, "octave #{octave} is not an integer" unless octave.is_a?(Integer) raise NonIntegerError, "semitone #{semitone} is not an integer" unless semitone.is_a?(Integer) raise NonIntegerError, "cent #{cent} is not an integer" unless cent.is_a?(Integer) @octave = octave @semitone = semitone @cent = cent @total_cents = (@octave*SEMITONES_PER_OCTAVE + @semitone)*CENTS_PER_SEMITONE + @cent balance! end |
Instance Attribute Details
#cent ⇒ Object (readonly)
Returns the value of attribute cent.
23 24 25 |
# File 'lib/music-transcription/model/pitch.rb', line 23 def cent @cent end |
#octave ⇒ Fixnum (readonly)
Returns The pitch octave.
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 |
# File 'lib/music-transcription/model/pitch.rb', line 21 class Pitch include Comparable attr_reader :octave, :semitone, :cent, :total_cents #The default number of semitones per octave is 12, corresponding to # the twelve-tone equal temperment tuning system. SEMITONES_PER_OCTAVE = 12 CENTS_PER_SEMITONE = 100 CENTS_PER_OCTAVE = SEMITONES_PER_OCTAVE * CENTS_PER_SEMITONE # The base ferquency is C0 BASE_FREQ = 16.351597831287414 def initialize octave:0, semitone:0, cent: 0 raise NonIntegerError, "octave #{octave} is not an integer" unless octave.is_a?(Integer) raise NonIntegerError, "semitone #{semitone} is not an integer" unless semitone.is_a?(Integer) raise NonIntegerError, "cent #{cent} is not an integer" unless cent.is_a?(Integer) @octave = octave @semitone = semitone @cent = cent @total_cents = (@octave*SEMITONES_PER_OCTAVE + @semitone)*CENTS_PER_SEMITONE + @cent balance! end # Return the pitch's frequency, which is determined by multiplying the base # frequency and the pitch ratio. Base frequency defaults to DEFAULT_BASE_FREQ, # but can be set during initialization to something else by specifying the # :base_freq key. def freq return self.ratio() * BASE_FREQ end # Calculate the pitch ratio. Raises 2 to the power of the total cent # count divided by cents-per-octave. # @return [Float] ratio def ratio 2.0**(@total_cents.to_f / CENTS_PER_OCTAVE) end # Override default hash method. def hash return @total_cents end # Compare pitch equality using total semitone def ==(other) return (self.class == other.class && @total_cents == other.total_cents) end def eql?(other) self == other end # Compare pitches. A higher ratio or total semitone is considered larger. # @param [Pitch] other The pitch object to compare. def <=> (other) @total_cents <=> other.total_cents end # rounds to the nearest semitone def round if @cent == 0 self.clone else Pitch.new(semitone: (@total_cents / CENTS_PER_SEMITONE.to_f).round) end end # diff in (rounded) semitones def diff other Rational(@total_cents - other.total_cents, CENTS_PER_SEMITONE) end def transpose semitones Pitch.new(cent: (@total_cents + semitones * CENTS_PER_SEMITONE).round) end def total_semitones Rational(@total_cents, CENTS_PER_SEMITONE) end def self.from_semitones semitones Pitch.new(cent: (semitones * CENTS_PER_SEMITONE).round) end def clone Pitch.new(cent: @total_cents) end def to_s(sharpit = false) letter = case semitone when 0 then "C" when 1 then sharpit ? "C#" : "Db" when 2 then "D" when 3 then sharpit ? "D#" : "Eb" when 4 then "E" when 5 then "F" when 6 then sharpit ? "F#" : "Gb" when 7 then "G" when 8 then sharpit ? "G#" : "Ab" when 9 then "A" when 10 then sharpit ? "A#" : "Bb" when 11 then "B" end if @cent == 0 return letter + octave.to_s elsif @cent > 0 return letter + octave.to_s + "+" + @cent.to_s else return letter + octave.to_s + @cent.to_s end end def self.from_ratio ratio raise NonPositiveError, "ratio #{ratio} is not > 0" unless ratio > 0 x = Math.log2 ratio new(cent: (x * CENTS_PER_OCTAVE).round) end def self.from_freq freq from_ratio(freq / BASE_FREQ) end private # Balance out the octave and semitone count. def balance! centsTotal = @total_cents @octave = centsTotal / CENTS_PER_OCTAVE centsTotal -= @octave * CENTS_PER_OCTAVE @semitone = centsTotal / CENTS_PER_SEMITONE centsTotal -= @semitone * CENTS_PER_SEMITONE @cent = centsTotal return self end end |
#semitone ⇒ Fixnum (readonly)
Returns The pitch semitone.
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 |
# File 'lib/music-transcription/model/pitch.rb', line 21 class Pitch include Comparable attr_reader :octave, :semitone, :cent, :total_cents #The default number of semitones per octave is 12, corresponding to # the twelve-tone equal temperment tuning system. SEMITONES_PER_OCTAVE = 12 CENTS_PER_SEMITONE = 100 CENTS_PER_OCTAVE = SEMITONES_PER_OCTAVE * CENTS_PER_SEMITONE # The base ferquency is C0 BASE_FREQ = 16.351597831287414 def initialize octave:0, semitone:0, cent: 0 raise NonIntegerError, "octave #{octave} is not an integer" unless octave.is_a?(Integer) raise NonIntegerError, "semitone #{semitone} is not an integer" unless semitone.is_a?(Integer) raise NonIntegerError, "cent #{cent} is not an integer" unless cent.is_a?(Integer) @octave = octave @semitone = semitone @cent = cent @total_cents = (@octave*SEMITONES_PER_OCTAVE + @semitone)*CENTS_PER_SEMITONE + @cent balance! end # Return the pitch's frequency, which is determined by multiplying the base # frequency and the pitch ratio. Base frequency defaults to DEFAULT_BASE_FREQ, # but can be set during initialization to something else by specifying the # :base_freq key. def freq return self.ratio() * BASE_FREQ end # Calculate the pitch ratio. Raises 2 to the power of the total cent # count divided by cents-per-octave. # @return [Float] ratio def ratio 2.0**(@total_cents.to_f / CENTS_PER_OCTAVE) end # Override default hash method. def hash return @total_cents end # Compare pitch equality using total semitone def ==(other) return (self.class == other.class && @total_cents == other.total_cents) end def eql?(other) self == other end # Compare pitches. A higher ratio or total semitone is considered larger. # @param [Pitch] other The pitch object to compare. def <=> (other) @total_cents <=> other.total_cents end # rounds to the nearest semitone def round if @cent == 0 self.clone else Pitch.new(semitone: (@total_cents / CENTS_PER_SEMITONE.to_f).round) end end # diff in (rounded) semitones def diff other Rational(@total_cents - other.total_cents, CENTS_PER_SEMITONE) end def transpose semitones Pitch.new(cent: (@total_cents + semitones * CENTS_PER_SEMITONE).round) end def total_semitones Rational(@total_cents, CENTS_PER_SEMITONE) end def self.from_semitones semitones Pitch.new(cent: (semitones * CENTS_PER_SEMITONE).round) end def clone Pitch.new(cent: @total_cents) end def to_s(sharpit = false) letter = case semitone when 0 then "C" when 1 then sharpit ? "C#" : "Db" when 2 then "D" when 3 then sharpit ? "D#" : "Eb" when 4 then "E" when 5 then "F" when 6 then sharpit ? "F#" : "Gb" when 7 then "G" when 8 then sharpit ? "G#" : "Ab" when 9 then "A" when 10 then sharpit ? "A#" : "Bb" when 11 then "B" end if @cent == 0 return letter + octave.to_s elsif @cent > 0 return letter + octave.to_s + "+" + @cent.to_s else return letter + octave.to_s + @cent.to_s end end def self.from_ratio ratio raise NonPositiveError, "ratio #{ratio} is not > 0" unless ratio > 0 x = Math.log2 ratio new(cent: (x * CENTS_PER_OCTAVE).round) end def self.from_freq freq from_ratio(freq / BASE_FREQ) end private # Balance out the octave and semitone count. def balance! centsTotal = @total_cents @octave = centsTotal / CENTS_PER_OCTAVE centsTotal -= @octave * CENTS_PER_OCTAVE @semitone = centsTotal / CENTS_PER_SEMITONE centsTotal -= @semitone * CENTS_PER_SEMITONE @cent = centsTotal return self end end |
#total_cents ⇒ Object (readonly)
Returns the value of attribute total_cents.
23 24 25 |
# File 'lib/music-transcription/model/pitch.rb', line 23 def total_cents @total_cents end |
Class Method Details
.from_freq(freq) ⇒ Object
143 144 145 |
# File 'lib/music-transcription/model/pitch.rb', line 143 def self.from_freq freq from_ratio(freq / BASE_FREQ) end |
.from_ratio(ratio) ⇒ Object
137 138 139 140 141 |
# File 'lib/music-transcription/model/pitch.rb', line 137 def self.from_ratio ratio raise NonPositiveError, "ratio #{ratio} is not > 0" unless ratio > 0 x = Math.log2 ratio new(cent: (x * CENTS_PER_OCTAVE).round) end |
.from_semitones(semitones) ⇒ Object
104 105 106 |
# File 'lib/music-transcription/model/pitch.rb', line 104 def self.from_semitones semitones Pitch.new(cent: (semitones * CENTS_PER_SEMITONE).round) end |
Instance Method Details
#<=>(other) ⇒ Object
Compare pitches. A higher ratio or total semitone is considered larger.
78 79 80 |
# File 'lib/music-transcription/model/pitch.rb', line 78 def <=> (other) @total_cents <=> other.total_cents end |
#==(other) ⇒ Object
Compare pitch equality using total semitone
67 68 69 70 |
# File 'lib/music-transcription/model/pitch.rb', line 67 def ==(other) return (self.class == other.class && @total_cents == other.total_cents) end |
#clone ⇒ Object
108 109 110 |
# File 'lib/music-transcription/model/pitch.rb', line 108 def clone Pitch.new(cent: @total_cents) end |
#diff(other) ⇒ Object
diff in (rounded) semitones
92 93 94 |
# File 'lib/music-transcription/model/pitch.rb', line 92 def diff other Rational(@total_cents - other.total_cents, CENTS_PER_SEMITONE) end |
#eql?(other) ⇒ Boolean
72 73 74 |
# File 'lib/music-transcription/model/pitch.rb', line 72 def eql?(other) self == other end |
#freq ⇒ Object
Return the pitch’s frequency, which is determined by multiplying the base frequency and the pitch ratio. Base frequency defaults to DEFAULT_BASE_FREQ, but can be set during initialization to something else by specifying the :base_freq key.
50 51 52 |
# File 'lib/music-transcription/model/pitch.rb', line 50 def freq return self.ratio() * BASE_FREQ end |
#hash ⇒ Object
Override default hash method.
62 63 64 |
# File 'lib/music-transcription/model/pitch.rb', line 62 def hash return @total_cents end |
#ratio ⇒ Float
Calculate the pitch ratio. Raises 2 to the power of the total cent count divided by cents-per-octave.
57 58 59 |
# File 'lib/music-transcription/model/pitch.rb', line 57 def ratio 2.0**(@total_cents.to_f / CENTS_PER_OCTAVE) end |
#round ⇒ Object
rounds to the nearest semitone
83 84 85 86 87 88 89 |
# File 'lib/music-transcription/model/pitch.rb', line 83 def round if @cent == 0 self.clone else Pitch.new(semitone: (@total_cents / CENTS_PER_SEMITONE.to_f).round) end end |
#to_s(sharpit = false) ⇒ Object
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
# File 'lib/music-transcription/model/pitch.rb', line 112 def to_s(sharpit = false) letter = case semitone when 0 then "C" when 1 then sharpit ? "C#" : "Db" when 2 then "D" when 3 then sharpit ? "D#" : "Eb" when 4 then "E" when 5 then "F" when 6 then sharpit ? "F#" : "Gb" when 7 then "G" when 8 then sharpit ? "G#" : "Ab" when 9 then "A" when 10 then sharpit ? "A#" : "Bb" when 11 then "B" end if @cent == 0 return letter + octave.to_s elsif @cent > 0 return letter + octave.to_s + "+" + @cent.to_s else return letter + octave.to_s + @cent.to_s end end |
#total_semitones ⇒ Object
100 101 102 |
# File 'lib/music-transcription/model/pitch.rb', line 100 def total_semitones Rational(@total_cents, CENTS_PER_SEMITONE) end |
#transpose(semitones) ⇒ Object
96 97 98 |
# File 'lib/music-transcription/model/pitch.rb', line 96 def transpose semitones Pitch.new(cent: (@total_cents + semitones * CENTS_PER_SEMITONE).round) end |