Class: MTK::Core::Duration

Inherits:
Object
  • Object
show all
Includes:
Comparable
Defined in:
lib/mtk/core/duration.rb

Overview

A measure of time in musical beats. May be negative to indicate a rest, which uses the absolute value for the effective duration.

See Also:

Constant Summary collapse

NAMES =

The names of the base durations. See Lang::Durations for more info.

%w[w h q e s r x].freeze
VALUES_BY_NAME =
{
  'w' => 4,
  'h' => 2,
  'q' => 1,
  'e' => Rational(1,2),
  's' => Rational(1,4),
  'r' => Rational(1,8),
  'x' => Rational(1,16)
}

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(length_in_beats) ⇒ Duration

Returns a new instance of Duration.



30
31
32
# File 'lib/mtk/core/duration.rb', line 30

def initialize( length_in_beats )
  @value = length_in_beats
end

Instance Attribute Details

#valueObject (readonly)

The number of beats, typically represented as a Rational



28
29
30
# File 'lib/mtk/core/duration.rb', line 28

def value
  @value
end

Class Method Details

.[](length_in_beats) ⇒ Object Also known as: from_f, from_i

Return a duration, only constructing a new instance when not already in the flyweight cache



35
36
37
38
39
40
41
42
# File 'lib/mtk/core/duration.rb', line 35

def self.[](length_in_beats)
  if length_in_beats.is_a? Fixnum
    value = length_in_beats
  else
    value = Rational(length_in_beats)
  end
  @flyweight[value] ||= new(value)
end

.from_s(s) ⇒ Object Also known as: from_name

Lookup a duration by name. This method supports appending any combination of ‘.’ and ‘t’ for more fine-grained values. each ‘.’ multiplies by 3/2, and each ‘t’ multiplies by 2/3. You may use the prefix ‘-’ to negate the duration (which turns it into a rest of the same length). You may also prefix (after the ‘-’ if present) the base duration name with an integer, float, or rational number to multiply the base duration value. Rationals are in the form “#{numerator_integer}/#{denominator_integer}”.

Examples:

lookup value of ‘q.’, which is 1.5 times a quarter note (1.5 beats):

MTK::Core::Duration.from_s('q.')

lookup the value of 3/4w, which three-quarters of a whole note (3 beats):

MTK::Core::Duration.from_s('3/4w')


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
# File 'lib/mtk/core/duration.rb', line 59

def self.from_s(s)
  if s =~ /^(-)?(\d+([\.\/]\d+)?)?([whqesrx])((\.|t)*)$/i
    name = $4.downcase
    modifier = $5.downcase
    modifier << $1 if $1 # negation
    multiplier = $2
  else
    raise ArgumentError.new("Invalid Duration string '#{s}'")
  end

  value = VALUES_BY_NAME[name]
  modifier.each_char do |mod|
    case mod
      when '-' then value *= -1
      when '.' then value *= Rational(3,2)
      when 't' then value *= Rational(2,3)
    end
  end

  if multiplier
    case multiplier
      when /\./
        value *= multiplier.to_f
      when /\//
        numerator, denominator = multiplier.split('/')
        value *= Rational(numerator.to_i, denominator.to_i)
      else
        value *= multiplier.to_i
    end
  end

  self[value]
end

Instance Method Details

#*(duration) ⇒ Object

Multiply this duration with another.

Returns:

  • a new Duration that has a value of the product of the arguments.



182
183
184
185
186
187
188
# File 'lib/mtk/core/duration.rb', line 182

def * duration
  if duration.is_a? MTK::Core::Duration
    MTK::Core::Duration[@value * duration.value]
  else
    MTK::Core::Duration[@value * duration]
  end
end

#+(duration) ⇒ Object

Add this duration to another.

Returns:

  • a new Duration that has a value of the sum of the arguments.



162
163
164
165
166
167
168
# File 'lib/mtk/core/duration.rb', line 162

def + duration
  if duration.is_a? MTK::Core::Duration
    MTK::Core::Duration[@value + duration.value]
  else
    MTK::Core::Duration[@value + duration]
  end
end

#-(duration) ⇒ Object

Subtract another duration from this one.

Returns:

  • a new Duration that has a value of the difference of the arguments.



172
173
174
175
176
177
178
# File 'lib/mtk/core/duration.rb', line 172

def - duration
  if duration.is_a? MTK::Core::Duration
    MTK::Core::Duration[@value - duration.value]
  else
    MTK::Core::Duration[@value - duration]
  end
end

#-@Object

Negate the duration value. Turns normal durations into rests and vice versa.

Returns:

  • a new Duration that has a negated value.

See Also:



204
205
206
# File 'lib/mtk/core/duration.rb', line 204

def -@
  MTK::Core::Duration[@value * -1]
end

#/(duration) ⇒ Object

Divide this duration with another.

Returns:

  • a new Duration that has a value of the division of the arguments.



192
193
194
195
196
197
198
# File 'lib/mtk/core/duration.rb', line 192

def / duration
  if duration.is_a? MTK::Core::Duration
    MTK::Core::Duration[to_f / duration.value]
  else
    MTK::Core::Duration[to_f / duration]
  end
end

#<=>(other) ⇒ Object



152
153
154
155
156
157
158
# File 'lib/mtk/core/duration.rb', line 152

def <=> other
  if other.respond_to? :value
    @value <=> other.value
  else
    @value <=> other
  end
end

#==(other) ⇒ Object



144
145
146
147
148
149
150
# File 'lib/mtk/core/duration.rb', line 144

def ==( other )
  if other.is_a? MTK::Core::Duration
    other.value == @value
  else
    other == @value
  end
end

#absObject

Force resets to be non-rests, otherwise don’t change the duration.

See Also:



116
117
118
119
120
121
122
# File 'lib/mtk/core/duration.rb', line 116

def abs
  if @value < 0
    -self
  else
    self
  end
end

#coerce(other) ⇒ Object

Allow basic math operations with Numeric objects.



209
210
211
# File 'lib/mtk/core/duration.rb', line 209

def coerce(other)
  return MTK::Core::Duration[other], self
end

#inspectObject



140
141
142
# File 'lib/mtk/core/duration.rb', line 140

def inspect
  "#<#{self.class}:#{object_id} @value=#{@value}>"
end

#lengthObject

The magnitude (absolute value) of the duration. This is the actual duration for rests.

See Also:



101
102
103
# File 'lib/mtk/core/duration.rb', line 101

def length
  @value < 0 ? -@value : @value
end

#rest?Boolean

Durations with negative values are rests.

Returns:

  • (Boolean)

See Also:



108
109
110
# File 'lib/mtk/core/duration.rb', line 108

def rest?
  @value < 0
end

#to_fObject

The number of beats as a floating point number



125
126
127
# File 'lib/mtk/core/duration.rb', line 125

def to_f
  @value.to_f
end

#to_iObject

The numerical value for the nearest whole number of beats



130
131
132
# File 'lib/mtk/core/duration.rb', line 130

def to_i
  @value.round
end

#to_sObject



134
135
136
137
138
# File 'lib/mtk/core/duration.rb', line 134

def to_s
  value = @value.to_s
  value = sprintf '%.2f', @value if value.length > 6 # threshold is 6 for no particular reason...
  "#{value} #{@value.abs > 1 || @value==0 ? 'beats' : 'beat'}"
end