class Topaz::MIDIClockInput
Trigger an event based on received midi clock messages
Attributes
Public Class Methods
@param [UniMIDI::Input] input @param [Hash] options @option options [Clock::Event] :event @option options [Boolean] :midi_transport Whether to respect start/stop MIDI commands from a MIDI input
# File lib/topaz/midi_clock_input.rb, line 16 def initialize(input, options = {}) @event = options[:event] @use_transport = !!options[:midi_transport] @tick_counter = 0 @pause = false @listening = false @running = false @tempo_calculator = TempoCalculator.new @tick_threshold = interval_to_ticks(options.fetch(:interval, 4)) initialize_listener(input) end
Public Instance Methods
Return the interval at which the tick event is fired @return [Fixnum]
# File lib/topaz/midi_clock_input.rb, line 85 def interval ticks_to_interval(@tick_threshold) end
Change the clock interval Defaults to 4, which means click once every 24 ticks or one quarter note (per MIDI spec). Therefore, to fire the on_tick event twice as often, pass 8
1 = whole note 2 = half note 4 = quarter note 6 = dotted quarter 8 = eighth note 16 = sixteenth note etc
@param [Fixnum] interval @return [Fixnum]
# File lib/topaz/midi_clock_input.rb, line 79 def interval=(interval) @tick_threshold = interval_to_ticks(interval) end
Join the listener thread @return [MIDIInputClock] self
# File lib/topaz/midi_clock_input.rb, line 60 def join @listener.join self end
Start the listener @param [Hash] options @option options [Boolean] :background Whether to run the listener in a background process @option options [Boolean] :focus (or :blocking) Whether to run the listener in a foreground process @return [MIDIInputClock] self
# File lib/topaz/midi_clock_input.rb, line 40 def start(options = {}) @listening = true blocking = options[:focus] || options[:blocking] background = !blocking unless blocking.nil? background = options[:background] if background.nil? background = false if background.nil? @listener.start(:background => background) self end
Stop the listener @return [MIDIInputClock] self
# File lib/topaz/midi_clock_input.rb, line 52 def stop(*a) @listening = false @listener.stop self end
This will return a calculated tempo @return [Fixnum]
# File lib/topaz/midi_clock_input.rb, line 31 def tempo @tempo_calculator.calculate end
Private Instance Methods
Advance the tick counter @return [Fixnum]
# File lib/topaz/midi_clock_input.rb, line 152 def advance @tick_counter += 1 end
Handle a received clock message @param [Hash] message @return [Fixnum] The current counter
# File lib/topaz/midi_clock_input.rb, line 141 def handle_clock_message(message) if @running || !@use_transport @running ||= true thru log(message) tick? ? tick : advance end end
Handle a received start message @return [Boolean]
# File lib/topaz/midi_clock_input.rb, line 120 def handle_start_message @running = true if !@event.nil? @event.do_start true end end
Handle a received stop message @return [Boolean]
# File lib/topaz/midi_clock_input.rb, line 130 def handle_stop_message @running = false if !@event.nil? @event.do_stop true end end
Initialize the MIDI input listener @param [UniMIDI::Input] input @return [MIDIEye::Listener]
# File lib/topaz/midi_clock_input.rb, line 110 def initialize_listener(input) @listener = MIDIEye::Listener.new(input) @listener.listen_for(:name => "Clock") { |message| handle_clock_message(message) } @listener.listen_for(:name => "Start") { handle_start_message } @listener.listen_for(:name => "Stop") { handle_stop_message } @listener end
Convert a note interval to number of ticks @param [Fixnum] interval @param [Fixnum]
# File lib/topaz/midi_clock_input.rb, line 94 def interval_to_ticks(interval) per_qn = interval / 4 24 / per_qn end
Log the timestamp of a message for tempo calculation @param [Hash] message @return [Array<Fixnum>]
# File lib/topaz/midi_clock_input.rb, line 159 def log(message) time = message[:timestamp] / 1000.0 @tempo_calculator.timestamps << time end
Fire the clock event (this results in MIDI output sending clock, thus thru) @return [Boolean]
# File lib/topaz/midi_clock_input.rb, line 167 def thru if !@event.nil? @event.do_clock true end end
Fire the tick event @return [Boolean]
# File lib/topaz/midi_clock_input.rb, line 176 def tick @tick_counter = 0 if !@event.nil? && !@pause @event.do_tick true end end
Should the tick event be fired given the current state? @return [Boolean]
# File lib/topaz/midi_clock_input.rb, line 186 def tick? @tick_counter >= @tick_threshold - 1 end
Convert a number of ticks to a note interval @param [Fixnum] ticks @param [Fixnum]
# File lib/topaz/midi_clock_input.rb, line 102 def ticks_to_interval(ticks) note_value = 24 / ticks 4 * note_value end