Module: Webmidi::Music::Rhythm

Defined in:
lib/webmidi/music/rhythm.rb

Constant Summary collapse

DURATIONS =
{
  whole: 4.0,
  half: 2.0,
  quarter: 1.0,
  eighth: 0.5,
  sixteenth: 0.25,
  thirty_second: 0.125,
  dotted_whole: 6.0,
  dotted_half: 3.0,
  dotted_quarter: 1.5,
  dotted_eighth: 0.75,
  triplet_quarter: 2.0 / 3.0,
  triplet_eighth: 1.0 / 3.0,
  triplet_sixteenth: 1.0 / 6.0
}.freeze

Class Method Summary collapse

Class Method Details

.beats_to_ticks(beats, ppqn: 480) ⇒ Object



38
39
40
41
42
# File 'lib/webmidi/music/rhythm.rb', line 38

def beats_to_ticks(beats, ppqn: 480)
  validate_beats!(beats)
  validate_ppqn!(ppqn)
  (beats * ppqn).round
end

.dotted(name, dots: 1) ⇒ Object



50
51
52
53
54
55
# File 'lib/webmidi/music/rhythm.rb', line 50

def dotted(name, dots: 1)
  raise InvalidMessageError, "dots must be a non-negative integer" unless dots.is_a?(Integer) && dots >= 0

  base = duration_in_beats(name)
  dots.times.reduce(base) { |sum, index| sum + (base / (2**(index + 1))) }
end

.duration_in_beats(name) ⇒ Object



24
25
26
# File 'lib/webmidi/music/rhythm.rb', line 24

def duration_in_beats(name)
  DURATIONS[name] || raise(InvalidMessageError, "Unknown duration: #{name}")
end

.duration_in_seconds(name, bpm: 120) ⇒ Object



33
34
35
36
# File 'lib/webmidi/music/rhythm.rb', line 33

def duration_in_seconds(name, bpm: 120)
  validate_bpm!(bpm)
  duration_in_beats(name) * (60.0 / bpm)
end

.duration_in_ticks(name, ppqn: 480) ⇒ Object



28
29
30
31
# File 'lib/webmidi/music/rhythm.rb', line 28

def duration_in_ticks(name, ppqn: 480)
  validate_ppqn!(ppqn)
  (duration_in_beats(name) * ppqn).round
end

.ticks_to_beats(ticks, ppqn: 480) ⇒ Object



44
45
46
47
48
# File 'lib/webmidi/music/rhythm.rb', line 44

def ticks_to_beats(ticks, ppqn: 480)
  validate_ticks!(ticks)
  validate_ppqn!(ppqn)
  ticks.to_f / ppqn
end

.tuplet(name, in_time_of:, notes:) ⇒ Object



57
58
59
60
61
62
63
# File 'lib/webmidi/music/rhythm.rb', line 57

def tuplet(name, in_time_of:, notes:)
  unless in_time_of.is_a?(Integer) && in_time_of.positive? && notes.is_a?(Integer) && notes.positive?
    raise InvalidMessageError, "Tuplet values must be positive integers"
  end

  duration_in_beats(name) * (in_time_of.to_f / notes)
end