Class: Musa::Clock::ExternalTickClock

Inherits:
Clock show all
Defined in:
lib/musa-dsl/transport/external-tick-clock.rb

Overview

Clock driven by external tick() calls for integration and testing.

ExternalTickClock doesn't generate its own ticks. Instead, ticks are triggered manually by calling the #tick method.

Activation Model

IMPORTANT: ExternalTickClock requires manual tick generation. After calling transport.start (which returns immediately, doesn't block), you must call clock.tick() repeatedly from your external system to generate ticks.

This activation model is appropriate for:

  • Testing: Precise control over timing for step-by-step debugging
  • Game engine integration: Game loop controls tick timing
  • Frame-based systems: One tick per frame or custom logic
  • Offline rendering: Generate ticks as fast as needed

Operation

  1. Call #run to initialize (doesn't block, returns immediately)
  2. Call #tick repeatedly to generate ticks manually
  3. Call #terminate when done

Differences from Other Clocks

Unlike TimerClock and InputMidiClock, ExternalTickClock's run method does not block - it returns immediately. This allows your external system to control the timing loop.

Examples:

Manual stepping for testing

clock = ExternalTickClock.new
transport = Transport.new(clock)

# Schedule some events
transport.sequencer.at 1 { puts "Tick 1" }
transport.sequencer.at 2 { puts "Tick 2" }

# Start in background (non-blocking for ExternalTickClock)
thread = Thread.new { transport.start }
sleep 0.1  # Let transport initialize

# Generate ticks manually
clock.tick  # => (nothing, position 0)
clock.tick  # => "Tick 1"
clock.tick  # => "Tick 2"

transport.stop
thread.join

Integration with game loop

clock = ExternalTickClock.new
transport = Transport.new(clock)
thread = Thread.new { transport.start }
sleep 0.1

# In game update loop:
def update(delta_time)
  if should_tick?(delta_time)
    clock.tick  # Advance sequencer by one tick
  end
end

See Also:

Instance Method Summary collapse

Constructor Details

#initialize(do_log: nil) ⇒ ExternalTickClock

Creates a new externally-controlled clock.



75
76
77
78
79
80
81
# File 'lib/musa-dsl/transport/external-tick-clock.rb', line 75

def initialize(do_log: nil)
  do_log ||= false

  super()

  @do_log = do_log
end

Instance Method Details

#run { ... } ⇒ void

Note:

This method does NOT block

This method returns an undefined value.

Initializes the clock (non-blocking).

Unlike other clocks, this method doesn't block. It stores the block and calls on_start callbacks, then returns immediately. Ticks are generated by calling #tick.

Yields:

  • Called for each tick triggered by #tick



93
94
95
96
97
# File 'lib/musa-dsl/transport/external-tick-clock.rb', line 93

def run(&block)
  @on_start.each(&:call)
  @run = true
  @block = block
end

#terminatevoid

This method returns an undefined value.

Terminates the clock and calls on_stop callbacks.



117
118
119
120
# File 'lib/musa-dsl/transport/external-tick-clock.rb', line 117

def terminate
  @on_stop.each(&:call)
  @run = false
end

#tickvoid

Note:

Only works if #run has been called

Note:

Thread-safe for integration with external event loops

This method returns an undefined value.

Generates one tick manually.

If the clock is running, calls the registered block (typically sequencer.tick). Has no effect if clock is not running.



108
109
110
111
112
# File 'lib/musa-dsl/transport/external-tick-clock.rb', line 108

def tick
  if @run
    @block.call if @block
  end
end