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.

Parameters:

  • do_log (Boolean, nil) (defaults to: nil)

    enable logging



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
98
# File 'lib/musa-dsl/transport/external-tick-clock.rb', line 93

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

#stopvoid

This method returns an undefined value.

Stops the clock and fires on_stop callbacks.



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

def stop
  @run = false
  super
end

#terminatevoid

This method returns an undefined value.

Terminates the clock.

Delegates to #stop which fires on_stop callbacks and sets @run to false.



128
129
130
# File 'lib/musa-dsl/transport/external-tick-clock.rb', line 128

def terminate
  stop
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.



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

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