Class: Musa::Sequencer::BaseSequencer::PlayControl Private

Inherits:
EventHandler show all
Defined in:
lib/musa-dsl/sequencer/base-sequencer-implementation-play.rb

Overview

This class is part of a private API. You should avoid using this class if possible, as it may be removed or be changed in the future.

Control object for play operations.

Manages play lifecycle including pause/continue and after callbacks. Extends EventHandler to support custom events and hierarchical control.

Pause/Continue

When paused:

  1. Stores continuation parameters (series state, evaluator, etc.)
  2. Stops processing series
  3. Awaits continue call

When continued:

  1. Restores continuation parameters
  2. Resumes play from stored position

After Callbacks

Executed after play completes, with optional delay in bars.

Examples:

Basic play control

seq = Musa::Sequencer::BaseSequencer.new(4, 24)

# play consumes datasets, not bare values: each element is a hash, and
# its :duration is how far the player advances before the next one.
series = Musa::Series::Constructors.S({ pitch: 60, duration: 1r },
                                      { pitch: 62, duration: 1r })
played_notes = []
after_executed = []

control = seq.play(series) do |pitch:, duration:|
  played_notes << { pitch: pitch, position: seq.position }
end

control.after(2r) { after_executed << seq.position }

seq.run
played_notes.collect { |n| n[:pitch] }  # => [60, 62]
# after_executed holds the position 2 bars after play completes

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(parent, on_stop: nil, after_bars: nil, after: nil) ⇒ PlayControl

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Creates play control with optional callbacks.

Parameters:

  • parent (EventHandler)

    parent event handler

  • on_stop (Proc, nil) (defaults to: nil)

    stop callback (fires on any termination)

  • after_bars (Rational, nil) (defaults to: nil)

    delay for after callback

  • after (Proc, nil) (defaults to: nil)

    after callback block (fires only on natural termination)



297
298
299
300
301
302
303
304
305
# File 'lib/musa-dsl/sequencer/base-sequencer-implementation-play.rb', line 297

def initialize(parent, on_stop: nil, after_bars: nil, after: nil)
  super parent

  @do_on_stop = []
  @do_after = []

  @do_on_stop << on_stop if on_stop
  after(after_bars, &after) if after
end

Instance Attribute Details

#do_afterArray<Hash> (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns after callbacks with delays (only on natural termination).

Returns:

  • (Array<Hash>)

    after callbacks with delays (only on natural termination)



287
288
289
# File 'lib/musa-dsl/sequencer/base-sequencer-implementation-play.rb', line 287

def do_after
  @do_after
end

#do_on_stopArray<Proc> (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns callbacks when play stops (any reason, including manual stop).

Returns:

  • (Array<Proc>)

    callbacks when play stops (any reason, including manual stop)



285
286
287
# File 'lib/musa-dsl/sequencer/base-sequencer-implementation-play.rb', line 285

def do_on_stop
  @do_on_stop
end

Instance Method Details

#_finished!(sequencer, position) ⇒ void

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Records that the play has ended, and where.

Only the termination branch of _play calls this. It exists because the control can reach the caller already dead -- see #after.

Parameters:

  • sequencer (BaseSequencer)

    the sequencer that was playing

  • position (Rational)

    where the play ended



385
386
387
388
# File 'lib/musa-dsl/sequencer/base-sequencer-implementation-play.rb', line 385

def _finished!(sequencer, position)
  @finished_sequencer = sequencer
  @finished_at = position
end

#after(bars = nil) { ... } ⇒ void

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Registers callback to execute after play completes naturally (series exhausted). Not called on manual stop.

Examples:

Delayed callback

control = seq.play(series) { |pitch:, duration:| }
control.after(4r) { puts "4 bars after play ends" }

Parameters:

  • bars (Numeric, nil) (defaults to: nil)

    delay in bars after completion (default: 0)

Yields:

  • after callback block



403
404
405
406
407
408
409
410
411
412
413
414
415
416
# File 'lib/musa-dsl/sequencer/base-sequencer-implementation-play.rb', line 403

def after(bars = nil, &block)
  bars ||= 0

  # Already over: `after(2)` means "two bars after it ends", and it ended
  # at @finished_at, so that is where this goes. Registering it in the
  # list would be registering it after the only moment anything reads the
  # list, which is what used to happen to every play that resolved within
  # one instant -- a chord written as a serie of `forward_duration: 0`
  # elements, a lone event with no duration -- and the callback simply
  # never ran (issue #84).
  return @finished_sequencer.at(@finished_at + bars.rationalize, &block) if @finished_at

  @do_after << { bars: bars.rationalize, block: block }
end

#continuevoid

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Continues from pause.

Restores paused state and resumes play using stored continuation.



354
355
356
357
# File 'lib/musa-dsl/sequencer/base-sequencer-implementation-play.rb', line 354

def continue
  super
  @continuation_sequencer&.continuation_play(@continuation_parameters)
end

#on_stop { ... } ⇒ void

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Registers callback for when play stops (any reason, including manual stop).

Yields:

  • stop callback block



366
367
368
369
370
371
372
# File 'lib/musa-dsl/sequencer/base-sequencer-implementation-play.rb', line 366

def on_stop(&block)
  # Already over: on_stop means "when it terminates", and it has. The
  # normal path calls these directly too, rather than scheduling them.
  return block.call if @finished_at

  @do_on_stop << block
end

#pausevoid

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Pauses play and stores continuation state.

Sets paused flag. Continuation must be stored separately via store_continuation.



315
316
317
# File 'lib/musa-dsl/sequencer/base-sequencer-implementation-play.rb', line 315

def pause
  @paused = true
end

#store_continuation(sequencer:, serie:, neumalang_context:, mode:, decoder:, play_eval:, mode_args:) ⇒ void

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Stores state for continue operation.

Saves all parameters needed to resume play from current position. Called automatically by _play when paused.

Parameters:

  • sequencer (BaseSequencer)

    sequencer instance

  • serie (Series)

    series being played

  • neumalang_context (Object, nil)

    neumalang context

  • mode (Symbol, nil)

    evaluation mode

  • decoder (Object, nil)

    decoder

  • play_eval (PlayEval)

    evaluator

  • mode_args (Hash)

    mode arguments



335
336
337
338
339
340
341
342
343
344
345
# File 'lib/musa-dsl/sequencer/base-sequencer-implementation-play.rb', line 335

def store_continuation(sequencer:, serie:, neumalang_context:, mode:, decoder:, play_eval:, mode_args:)
  @continuation_sequencer = sequencer
  @continuation_parameters = {
    serie: serie,
    control: self,
    neumalang_context: neumalang_context,
    mode: mode,
    decoder: decoder,
    play_eval: play_eval,
    mode_args: mode_args }
end