Class: Webmidi::SMF::MetaEvent

Inherits:
Event
  • Object
show all
Defined in:
lib/webmidi/smf/event.rb

Constant Summary collapse

META_TYPES =
{
  sequence_number: 0x00,
  text: 0x01,
  copyright: 0x02,
  track_name: 0x03,
  instrument_name: 0x04,
  lyric: 0x05,
  marker: 0x06,
  cue_point: 0x07,
  channel_prefix: 0x20,
  end_of_track: 0x2F,
  tempo: 0x51,
  smpte_offset: 0x54,
  time_signature: 0x58,
  key_signature: 0x59,
  sequencer_specific: 0x7F
}.freeze

Instance Attribute Summary collapse

Attributes inherited from Event

#absolute_time, #delta_time

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(type:, data: [], **kwargs) ⇒ MetaEvent

Returns a new instance of MetaEvent.



68
69
70
71
72
73
74
# File 'lib/webmidi/smf/event.rb', line 68

def initialize(type:, data: [], **kwargs)
  super(**kwargs)
  validate_type!(type)
  validate_data!(data)
  @type = type
  @data = data.frozen? ? data : data.dup.freeze
end

Instance Attribute Details

#dataObject (readonly)

Returns the value of attribute data.



48
49
50
# File 'lib/webmidi/smf/event.rb', line 48

def data
  @data
end

#typeObject (readonly)

Returns the value of attribute type.



48
49
50
# File 'lib/webmidi/smf/event.rb', line 48

def type
  @type
end

Class Method Details

.end_of_track(**kwargs) ⇒ Object



121
122
123
# File 'lib/webmidi/smf/event.rb', line 121

def self.end_of_track(**kwargs)
  new(type: META_TYPES[:end_of_track], data: [], **kwargs)
end

.key_signature(key: 0, scale: 0, **kwargs) ⇒ Object

Raises:



140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/webmidi/smf/event.rb', line 140

def self.key_signature(key: 0, scale: 0, **kwargs)
  raise InvalidSMFError, "Key signature must be between -7 and 7, got #{key.inspect}" unless key.is_a?(Integer) && key.between?(-7, 7)

  scale = case scale
  when :major then 0
  when :minor then 1
  else scale
  end
  raise InvalidSMFError, "Key signature scale must be 0/:major or 1/:minor" unless [0, 1].include?(scale)

  sf = (key < 0) ? (256 + key) : key
  new(type: META_TYPES[:key_signature], data: [sf, scale], **kwargs)
end

.tempo(bpm, **kwargs) ⇒ Object

Raises:



100
101
102
103
104
105
106
107
108
109
110
# File 'lib/webmidi/smf/event.rb', line 100

def self.tempo(bpm, **kwargs)
  raise InvalidSMFError, "Tempo BPM must be positive, got #{bpm.inspect}" unless bpm.is_a?(Numeric) && bpm.positive?

  microseconds = (60_000_000.0 / bpm).round
  data = [
    (microseconds >> 16) & 0xFF,
    (microseconds >> 8) & 0xFF,
    microseconds & 0xFF
  ]
  new(type: META_TYPES[:tempo], data: data, **kwargs)
end

.text(value, type: :text, encoding: Encoding::UTF_8, **kwargs) ⇒ Object



112
113
114
115
# File 'lib/webmidi/smf/event.rb', line 112

def self.text(value, type: :text, encoding: Encoding::UTF_8, **kwargs)
  meta_type = type.is_a?(Symbol) ? META_TYPES.fetch(type) : type
  new(type: meta_type, data: value.encode(encoding).bytes, **kwargs)
end

.time_signature(numerator: 4, denominator: 4, clocks_per_click: 24, notes_per_quarter: 8, **kwargs) ⇒ Object



125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/webmidi/smf/event.rb', line 125

def self.time_signature(numerator: 4, denominator: 4, clocks_per_click: 24, notes_per_quarter: 8, **kwargs)
  unless numerator.is_a?(Integer) && numerator.positive?
    raise InvalidSMFError, "Time signature numerator must be positive, got #{numerator.inspect}"
  end
  unless denominator.is_a?(Integer) && denominator.positive? && (denominator & (denominator - 1)).zero?
    raise InvalidSMFError, "Time signature denominator must be a power of two, got #{denominator.inspect}"
  end
  [clocks_per_click, notes_per_quarter].each do |value|
    raise InvalidSMFError, "Time signature values must be bytes" unless value.is_a?(Integer) && value.between?(0, 255)
  end

  dd = Math.log2(denominator).to_i
  new(type: META_TYPES[:time_signature], data: [numerator, dd, clocks_per_click, notes_per_quarter], **kwargs)
end

.track_name(name, encoding: Encoding::UTF_8, **kwargs) ⇒ Object



117
118
119
# File 'lib/webmidi/smf/event.rb', line 117

def self.track_name(name, encoding: Encoding::UTF_8, **kwargs)
  text(name, type: :track_name, encoding: encoding, **kwargs)
end

Instance Method Details

#bpmObject



93
94
95
96
97
98
# File 'lib/webmidi/smf/event.rb', line 93

def bpm
  t = tempo
  return nil unless t

  60_000_000.0 / t
end

#tempoObject



86
87
88
89
90
91
# File 'lib/webmidi/smf/event.rb', line 86

def tempo
  return nil unless @type == META_TYPES[:tempo]
  return nil unless @data.size == 3

  (@data[0] << 16) | (@data[1] << 8) | @data[2]
end

#text(encoding: Encoding::UTF_8) ⇒ Object



76
77
78
79
80
# File 'lib/webmidi/smf/event.rb', line 76

def text(encoding: Encoding::UTF_8)
  return nil unless text_event?

  @data.pack("C*").force_encoding(encoding)
end

#text_event?Boolean

Returns:

  • (Boolean)


82
83
84
# File 'lib/webmidi/smf/event.rb', line 82

def text_event?
  @type.between?(0x01, 0x07)
end