musa-dsl 0.30.2 → 0.40.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +3 -1
- data/.version +6 -0
- data/.yardopts +7 -0
- data/README.md +227 -6
- data/docs/README.md +83 -0
- data/docs/api-reference.md +86 -0
- data/docs/getting-started/quick-start.md +93 -0
- data/docs/getting-started/tutorial.md +58 -0
- data/docs/subsystems/core-extensions.md +316 -0
- data/docs/subsystems/datasets.md +465 -0
- data/docs/subsystems/generative.md +290 -0
- data/docs/subsystems/matrix.md +63 -0
- data/docs/subsystems/midi.md +123 -0
- data/docs/subsystems/music.md +233 -0
- data/docs/subsystems/musicxml-builder.md +264 -0
- data/docs/subsystems/neumas.md +71 -0
- data/docs/subsystems/repl.md +135 -0
- data/docs/subsystems/sequencer.md +98 -0
- data/docs/subsystems/series.md +302 -0
- data/docs/subsystems/transcription.md +152 -0
- data/docs/subsystems/transport.md +177 -0
- data/lib/musa-dsl/core-ext/array-explode-ranges.rb +68 -0
- data/lib/musa-dsl/core-ext/arrayfy.rb +110 -0
- data/lib/musa-dsl/core-ext/attribute-builder.rb +91 -30
- data/lib/musa-dsl/core-ext/deep-copy.rb +125 -2
- data/lib/musa-dsl/core-ext/dynamic-proxy.rb +78 -0
- data/lib/musa-dsl/core-ext/extension.rb +53 -0
- data/lib/musa-dsl/core-ext/hashify.rb +162 -1
- data/lib/musa-dsl/core-ext/inspect-nice.rb +154 -0
- data/lib/musa-dsl/core-ext/smart-proc-binder.rb +117 -0
- data/lib/musa-dsl/core-ext/with.rb +114 -0
- data/lib/musa-dsl/datasets/dataset.rb +109 -0
- data/lib/musa-dsl/datasets/delta-d.rb +78 -0
- data/lib/musa-dsl/datasets/e.rb +186 -2
- data/lib/musa-dsl/datasets/gdv.rb +279 -2
- data/lib/musa-dsl/datasets/gdvd.rb +201 -0
- data/lib/musa-dsl/datasets/helper.rb +75 -0
- data/lib/musa-dsl/datasets/p.rb +177 -2
- data/lib/musa-dsl/datasets/packed-v.rb +91 -0
- data/lib/musa-dsl/datasets/pdv.rb +136 -1
- data/lib/musa-dsl/datasets/ps.rb +134 -4
- data/lib/musa-dsl/datasets/score/queriable.rb +143 -1
- data/lib/musa-dsl/datasets/score/render.rb +105 -1
- data/lib/musa-dsl/datasets/score/to-mxml/process-pdv.rb +138 -1
- data/lib/musa-dsl/datasets/score/to-mxml/process-ps.rb +111 -0
- data/lib/musa-dsl/datasets/score/to-mxml/process-time.rb +200 -1
- data/lib/musa-dsl/datasets/score/to-mxml/to-mxml.rb +145 -1
- data/lib/musa-dsl/datasets/score.rb +279 -0
- data/lib/musa-dsl/datasets/v.rb +88 -0
- data/lib/musa-dsl/generative/darwin.rb +180 -1
- data/lib/musa-dsl/generative/generative-grammar.rb +359 -0
- data/lib/musa-dsl/generative/markov.rb +133 -3
- data/lib/musa-dsl/generative/rules.rb +258 -4
- data/lib/musa-dsl/generative/variatio.rb +217 -2
- data/lib/musa-dsl/logger/logger.rb +267 -2
- data/lib/musa-dsl/matrix/matrix.rb +256 -10
- data/lib/musa-dsl/midi/midi-recorder.rb +108 -1
- data/lib/musa-dsl/midi/midi-voices.rb +265 -4
- data/lib/musa-dsl/music/chord-definition.rb +233 -1
- data/lib/musa-dsl/music/chord-definitions.rb +33 -6
- data/lib/musa-dsl/music/chords.rb +308 -2
- data/lib/musa-dsl/music/equally-tempered-12-tone-scale-system.rb +315 -0
- data/lib/musa-dsl/music/scales.rb +957 -40
- data/lib/musa-dsl/musicxml/builder/attributes.rb +483 -3
- data/lib/musa-dsl/musicxml/builder/backup-forward.rb +166 -1
- data/lib/musa-dsl/musicxml/builder/direction.rb +243 -0
- data/lib/musa-dsl/musicxml/builder/helper.rb +240 -0
- data/lib/musa-dsl/musicxml/builder/measure.rb +284 -0
- data/lib/musa-dsl/musicxml/builder/note-complexities.rb +324 -8
- data/lib/musa-dsl/musicxml/builder/note.rb +285 -0
- data/lib/musa-dsl/musicxml/builder/part-group.rb +108 -1
- data/lib/musa-dsl/musicxml/builder/part.rb +139 -0
- data/lib/musa-dsl/musicxml/builder/pitched-note.rb +124 -0
- data/lib/musa-dsl/musicxml/builder/rest.rb +93 -0
- data/lib/musa-dsl/musicxml/builder/score-partwise.rb +276 -0
- data/lib/musa-dsl/musicxml/builder/typed-text.rb +62 -1
- data/lib/musa-dsl/musicxml/builder/unpitched-note.rb +83 -0
- data/lib/musa-dsl/neumalang/neumalang.rb +675 -0
- data/lib/musa-dsl/neumas/array-to-neumas.rb +149 -0
- data/lib/musa-dsl/neumas/neuma-decoder.rb +253 -0
- data/lib/musa-dsl/neumas/neuma-gdv-decoder.rb +142 -2
- data/lib/musa-dsl/neumas/neuma-gdvd-decoder.rb +82 -0
- data/lib/musa-dsl/neumas/neumas.rb +67 -0
- data/lib/musa-dsl/neumas/string-to-neumas.rb +233 -1
- data/lib/musa-dsl/repl/repl.rb +550 -0
- data/lib/musa-dsl/sequencer/base-sequencer-implementation-every.rb +118 -2
- data/lib/musa-dsl/sequencer/base-sequencer-implementation-move.rb +149 -2
- data/lib/musa-dsl/sequencer/base-sequencer-implementation-play-helper.rb +296 -0
- data/lib/musa-dsl/sequencer/base-sequencer-implementation-play-timed.rb +88 -2
- data/lib/musa-dsl/sequencer/base-sequencer-implementation-play.rb +161 -0
- data/lib/musa-dsl/sequencer/base-sequencer-implementation.rb +263 -0
- data/lib/musa-dsl/sequencer/base-sequencer-tick-based.rb +173 -1
- data/lib/musa-dsl/sequencer/base-sequencer-tickless-based.rb +177 -0
- data/lib/musa-dsl/sequencer/base-sequencer.rb +710 -10
- data/lib/musa-dsl/sequencer/sequencer-dsl.rb +210 -0
- data/lib/musa-dsl/sequencer/timeslots.rb +79 -0
- data/lib/musa-dsl/series/array-to-serie.rb +37 -1
- data/lib/musa-dsl/series/base-series.rb +843 -5
- data/lib/musa-dsl/series/buffer-serie.rb +48 -0
- data/lib/musa-dsl/series/hash-or-array-serie-splitter.rb +41 -0
- data/lib/musa-dsl/series/main-serie-constructors.rb +398 -2
- data/lib/musa-dsl/series/main-serie-operations.rb +538 -16
- data/lib/musa-dsl/series/proxy-serie.rb +67 -0
- data/lib/musa-dsl/series/quantizer-serie.rb +45 -7
- data/lib/musa-dsl/series/queue-serie.rb +65 -0
- data/lib/musa-dsl/series/series-composer.rb +701 -0
- data/lib/musa-dsl/series/timed-serie.rb +473 -28
- data/lib/musa-dsl/transcription/from-gdv-to-midi.rb +404 -1
- data/lib/musa-dsl/transcription/from-gdv-to-musicxml.rb +118 -0
- data/lib/musa-dsl/transcription/from-gdv.rb +84 -1
- data/lib/musa-dsl/transcription/transcription.rb +265 -0
- data/lib/musa-dsl/transport/clock.rb +125 -0
- data/lib/musa-dsl/transport/dummy-clock.rb +89 -2
- data/lib/musa-dsl/transport/external-tick-clock.rb +91 -0
- data/lib/musa-dsl/transport/input-midi-clock.rb +133 -1
- data/lib/musa-dsl/transport/timer-clock.rb +183 -1
- data/lib/musa-dsl/transport/timer.rb +83 -0
- data/lib/musa-dsl/transport/transport.rb +318 -0
- data/lib/musa-dsl/version.rb +1 -1
- data/lib/musa-dsl.rb +132 -25
- data/musa-dsl.gemspec +12 -10
- metadata +87 -8
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
module Musa::Sequencer
|
|
2
2
|
class BaseSequencer
|
|
3
|
+
# Implements recurring event execution at regular intervals.
|
|
4
|
+
#
|
|
5
|
+
# Recursively schedules block execution at interval-based positions.
|
|
6
|
+
# Handles stopping conditions, callbacks, and precise timing without drift.
|
|
7
|
+
#
|
|
8
|
+
# ## Timing Precision
|
|
9
|
+
#
|
|
10
|
+
# Next iteration is always scheduled at:
|
|
11
|
+
# start_position + execution_counter * interval
|
|
12
|
+
#
|
|
13
|
+
# This ensures iterations align to exact positions regardless of execution
|
|
14
|
+
# time or accumulated delays.
|
|
15
|
+
#
|
|
16
|
+
# @param interval [Rational, nil] bars between iterations (nil = one-shot)
|
|
17
|
+
# @param control [EveryControl] control object managing lifecycle
|
|
18
|
+
# @param block_procedure_binder [SmartProcBinder, nil] cached binder (for recursion)
|
|
19
|
+
# @yield block to execute each iteration (receives control:)
|
|
20
|
+
#
|
|
21
|
+
# @return [nil]
|
|
22
|
+
#
|
|
23
|
+
# @api private
|
|
3
24
|
private def _every(interval, control, block_procedure_binder: nil, &block)
|
|
4
25
|
block ||= proc {}
|
|
5
26
|
|
|
@@ -41,12 +62,65 @@ module Musa::Sequencer
|
|
|
41
62
|
nil
|
|
42
63
|
end
|
|
43
64
|
|
|
65
|
+
# Control object for every loops.
|
|
66
|
+
#
|
|
67
|
+
# Manages lifecycle of every loop including stopping conditions, callbacks,
|
|
68
|
+
# and execution tracking. Extends EventHandler to support event-based
|
|
69
|
+
# control (e.g., launching custom events).
|
|
70
|
+
#
|
|
71
|
+
# ## Stopping Conditions
|
|
72
|
+
#
|
|
73
|
+
# - **duration**: Maximum loop duration in bars
|
|
74
|
+
# - **till**: Absolute position to stop at
|
|
75
|
+
# - **condition**: Proc returning true to continue, false to stop
|
|
76
|
+
# - **manual stop**: Call `control.stop` to halt loop
|
|
77
|
+
#
|
|
78
|
+
# ## Callbacks
|
|
79
|
+
#
|
|
80
|
+
# - **on_stop**: Called when loop stops (any reason)
|
|
81
|
+
# - **after**: Called after loop stops, with optional delay in bars
|
|
82
|
+
#
|
|
83
|
+
# ## Execution Tracking
|
|
84
|
+
#
|
|
85
|
+
# - **_start_position**: Position when loop started
|
|
86
|
+
# - **_execution_counter**: Number of iterations executed
|
|
87
|
+
#
|
|
88
|
+
# @example Dynamic control
|
|
89
|
+
# control = sequencer.every(1r) { |control| puts control._execution_counter }
|
|
90
|
+
# control.duration(4r) # Stop after 4 bars
|
|
91
|
+
# control.on_stop { puts "Finished!" }
|
|
92
|
+
# control.after(2r) { puts "2 bars after finish" }
|
|
93
|
+
#
|
|
44
94
|
class EveryControl < EventHandler
|
|
45
|
-
|
|
46
|
-
|
|
95
|
+
# @return [Rational, nil] maximum duration in bars
|
|
96
|
+
attr_reader :duration_value
|
|
97
|
+
# @return [Rational, nil] absolute position to stop at
|
|
98
|
+
attr_reader :till_value
|
|
99
|
+
# @return [Proc, nil] condition block (returns true to continue)
|
|
100
|
+
attr_reader :condition_block
|
|
101
|
+
# @return [Array<Proc>] callbacks when loop stops
|
|
102
|
+
attr_reader :do_on_stop
|
|
103
|
+
# @return [Array<Hash>] after callbacks with delays
|
|
104
|
+
attr_reader :do_after
|
|
105
|
+
|
|
106
|
+
# @return [Rational] position when loop started
|
|
107
|
+
# @api private
|
|
47
108
|
attr_accessor :_start_position
|
|
109
|
+
# @return [Integer] number of iterations executed
|
|
110
|
+
# @api private
|
|
48
111
|
attr_accessor :_execution_counter
|
|
49
112
|
|
|
113
|
+
# Creates every loop control.
|
|
114
|
+
#
|
|
115
|
+
# @param parent [EventHandler] parent event handler
|
|
116
|
+
# @param duration [Rational, nil] maximum duration in bars
|
|
117
|
+
# @param till [Rational, nil] absolute stop position
|
|
118
|
+
# @param condition [Proc, nil] continuation condition
|
|
119
|
+
# @param on_stop [Proc, nil] stop callback
|
|
120
|
+
# @param after_bars [Rational, nil] delay for after callback
|
|
121
|
+
# @param after [Proc, nil] after callback block
|
|
122
|
+
#
|
|
123
|
+
# @api private
|
|
50
124
|
def initialize(parent, duration: nil, till: nil, condition: nil, on_stop: nil, after_bars: nil, after: nil)
|
|
51
125
|
super parent
|
|
52
126
|
|
|
@@ -62,22 +136,64 @@ module Musa::Sequencer
|
|
|
62
136
|
self.after after_bars, &after if after
|
|
63
137
|
end
|
|
64
138
|
|
|
139
|
+
# Sets maximum loop duration.
|
|
140
|
+
#
|
|
141
|
+
# @param value [Numeric] duration in bars
|
|
142
|
+
#
|
|
143
|
+
# @return [void]
|
|
144
|
+
#
|
|
145
|
+
# @api private
|
|
65
146
|
def duration(value)
|
|
66
147
|
@duration_value = value.rationalize
|
|
67
148
|
end
|
|
68
149
|
|
|
150
|
+
# Sets absolute stop position.
|
|
151
|
+
#
|
|
152
|
+
# @param value [Numeric] position to stop at
|
|
153
|
+
#
|
|
154
|
+
# @return [void]
|
|
155
|
+
#
|
|
156
|
+
# @api private
|
|
69
157
|
def till(value)
|
|
70
158
|
@till_value = value.rationalize
|
|
71
159
|
end
|
|
72
160
|
|
|
161
|
+
# Sets continuation condition.
|
|
162
|
+
#
|
|
163
|
+
# @yield condition block (returns true to continue, false to stop)
|
|
164
|
+
#
|
|
165
|
+
# @return [void]
|
|
166
|
+
#
|
|
167
|
+
# @api private
|
|
73
168
|
def condition(&block)
|
|
74
169
|
@condition_block = block
|
|
75
170
|
end
|
|
76
171
|
|
|
172
|
+
# Registers callback for when loop stops.
|
|
173
|
+
#
|
|
174
|
+
# @yield stop callback block
|
|
175
|
+
#
|
|
176
|
+
# @return [void]
|
|
177
|
+
#
|
|
178
|
+
# @api private
|
|
77
179
|
def on_stop(&block)
|
|
78
180
|
@do_on_stop << block
|
|
79
181
|
end
|
|
80
182
|
|
|
183
|
+
# Registers callback to execute after loop stops.
|
|
184
|
+
#
|
|
185
|
+
# @param bars [Numeric, nil] delay in bars after stop (default: 0)
|
|
186
|
+
# @yield after callback block
|
|
187
|
+
#
|
|
188
|
+
# @return [void]
|
|
189
|
+
#
|
|
190
|
+
# @example Immediate after callback
|
|
191
|
+
# control.after { puts "Done" }
|
|
192
|
+
#
|
|
193
|
+
# @example Delayed after callback
|
|
194
|
+
# control.after(2r) { puts "2 bars after stop" }
|
|
195
|
+
#
|
|
196
|
+
# @api private
|
|
81
197
|
def after(bars = nil, &block)
|
|
82
198
|
bars ||= 0
|
|
83
199
|
@do_after << { bars: bars.rationalize, block: block }
|
|
@@ -6,6 +6,50 @@ module Musa::Sequencer
|
|
|
6
6
|
using Musa::Extension::Arrayfy
|
|
7
7
|
using Musa::Extension::InspectNice
|
|
8
8
|
|
|
9
|
+
# Animates value(s) over time with flexible parameter control.
|
|
10
|
+
#
|
|
11
|
+
# Implements smooth value transitions supporting single values, arrays,
|
|
12
|
+
# and hashes. Handles complex parameter calculation to derive missing
|
|
13
|
+
# timing or step information. Uses _every for iteration scheduling.
|
|
14
|
+
#
|
|
15
|
+
# ## Yield Block Parameters
|
|
16
|
+
#
|
|
17
|
+
# Block receives current value(s), next value(s), and metadata:
|
|
18
|
+
# - In single mode: `|value, next_value, control:, duration:, ...|`
|
|
19
|
+
# - In array mode: `|values, next_values, control:, duration:, ...|`
|
|
20
|
+
# - In hash mode: `|values_hash, next_values_hash, control:, ...|`
|
|
21
|
+
#
|
|
22
|
+
# Optional parameters: control, duration, quantized_duration,
|
|
23
|
+
# position_jitter, duration_jitter, started_ago, right_open
|
|
24
|
+
#
|
|
25
|
+
# ## Implementation Details
|
|
26
|
+
#
|
|
27
|
+
# 1. Validates and normalizes parameters
|
|
28
|
+
# 2. Calculates missing parameters (step or every) from others
|
|
29
|
+
# 3. Creates MoveControl wrapping EveryControl
|
|
30
|
+
# 4. Uses _every with common interval (GCD of all intervals)
|
|
31
|
+
# 5. Calculates values, applies function, checks stop conditions
|
|
32
|
+
# 6. Yields to user block with values and metadata
|
|
33
|
+
#
|
|
34
|
+
# @param every [Rational, Array, Hash, nil] interval(s) between iterations
|
|
35
|
+
# @param from [Numeric, Array, Hash] starting value(s)
|
|
36
|
+
# @param to [Numeric, Array, Hash, nil] target value(s)
|
|
37
|
+
# @param step [Numeric, Array, Hash, nil] increment(s) per iteration
|
|
38
|
+
# @param duration [Rational, nil] total duration in bars
|
|
39
|
+
# @param till [Rational, nil] absolute end position
|
|
40
|
+
# @param function [Proc, Array<Proc>, nil] interpolation function(s)
|
|
41
|
+
# @param right_open [Boolean, Array, Hash, nil] exclude final value
|
|
42
|
+
# @param on_stop [Proc, nil] callback when movement stops
|
|
43
|
+
# @param after_bars [Rational, nil] delay for after callback
|
|
44
|
+
# @param after [Proc, nil] callback after movement completes
|
|
45
|
+
# @yield block to execute at each iteration with current/next values
|
|
46
|
+
#
|
|
47
|
+
# @return [MoveControl] control object for managing movement
|
|
48
|
+
#
|
|
49
|
+
# @raise [ArgumentError] if incompatible parameters used together
|
|
50
|
+
# @raise [ArgumentError] if insufficient parameters to calculate movement
|
|
51
|
+
#
|
|
52
|
+
# @api private
|
|
9
53
|
private def _move(every: nil,
|
|
10
54
|
from:, to: nil,
|
|
11
55
|
step: nil,
|
|
@@ -356,6 +400,18 @@ module Musa::Sequencer
|
|
|
356
400
|
control
|
|
357
401
|
end
|
|
358
402
|
|
|
403
|
+
# Calculates time elapsed since each value last changed.
|
|
404
|
+
#
|
|
405
|
+
# Returns array where each element is position delta since that value
|
|
406
|
+
# was last updated, or nil if value is being updated this iteration.
|
|
407
|
+
#
|
|
408
|
+
# @param last_positions [Array<Rational, nil>] positions of last updates
|
|
409
|
+
# @param position [Rational] current position
|
|
410
|
+
# @param affected_indexes [Array<Integer>] indexes being updated now
|
|
411
|
+
#
|
|
412
|
+
# @return [Array<Rational, nil>] deltas or nils
|
|
413
|
+
#
|
|
414
|
+
# @api private
|
|
359
415
|
private def _started_ago(last_positions, position, affected_indexes)
|
|
360
416
|
Array.new(last_positions.size).tap do |a|
|
|
361
417
|
last_positions.each_index do |i|
|
|
@@ -366,6 +422,16 @@ module Musa::Sequencer
|
|
|
366
422
|
end
|
|
367
423
|
end
|
|
368
424
|
|
|
425
|
+
# Calculates duration for each value based on interval grouping.
|
|
426
|
+
#
|
|
427
|
+
# Returns array of durations, one per value, based on its interval group.
|
|
428
|
+
#
|
|
429
|
+
# @param every_groups [Hash{Rational => Array<Integer>}] interval groups
|
|
430
|
+
# @param largest_duration [Rational] fallback duration
|
|
431
|
+
#
|
|
432
|
+
# @return [Array<Rational>] durations per value
|
|
433
|
+
#
|
|
434
|
+
# @api private
|
|
369
435
|
private def _durations(every_groups, largest_duration)
|
|
370
436
|
[].tap do |a|
|
|
371
437
|
if every_groups.any?
|
|
@@ -380,10 +446,28 @@ module Musa::Sequencer
|
|
|
380
446
|
end
|
|
381
447
|
end
|
|
382
448
|
|
|
449
|
+
# Reconstructs hash from separate key and value arrays.
|
|
450
|
+
#
|
|
451
|
+
# @param keys [Array<Symbol>] hash keys
|
|
452
|
+
# @param values [Array] hash values
|
|
453
|
+
#
|
|
454
|
+
# @return [Hash] reconstructed hash
|
|
455
|
+
#
|
|
456
|
+
# @api private
|
|
383
457
|
private def _hash_from_keys_and_values(keys, values)
|
|
384
458
|
{}.tap { |h| keys.each_index { |i| h[keys[i]] = values[i] } }
|
|
385
459
|
end
|
|
386
460
|
|
|
461
|
+
# Calculates greatest common divisor of intervals for scheduling.
|
|
462
|
+
#
|
|
463
|
+
# Computes GCD of all intervals to find common iteration frequency
|
|
464
|
+
# that hits all interval positions. Uses rational arithmetic.
|
|
465
|
+
#
|
|
466
|
+
# @param intervals [Array<Rational>] intervals to find GCD of
|
|
467
|
+
#
|
|
468
|
+
# @return [Rational, nil] common interval or nil if empty
|
|
469
|
+
#
|
|
470
|
+
# @api private
|
|
387
471
|
private def _common_interval(intervals)
|
|
388
472
|
intervals = intervals.compact
|
|
389
473
|
return nil if intervals.empty?
|
|
@@ -397,9 +481,48 @@ module Musa::Sequencer
|
|
|
397
481
|
Rational(gcd_numerators, lcm_denominators)
|
|
398
482
|
end
|
|
399
483
|
|
|
484
|
+
# Control object for move operations.
|
|
485
|
+
#
|
|
486
|
+
# Wraps EveryControl to provide move-specific lifecycle management.
|
|
487
|
+
# Delegates timing control to EveryControl while adding move-specific
|
|
488
|
+
# callbacks and state.
|
|
489
|
+
#
|
|
490
|
+
# ## Delegation Pattern
|
|
491
|
+
#
|
|
492
|
+
# MoveControl delegates to EveryControl for:
|
|
493
|
+
# - Duration and till timing control
|
|
494
|
+
# - Iteration scheduling
|
|
495
|
+
# - Stop conditions
|
|
496
|
+
#
|
|
497
|
+
# Adds move-specific features:
|
|
498
|
+
# - on_stop callbacks when movement completes
|
|
499
|
+
# - after callbacks with delays
|
|
500
|
+
# - Stopped state synchronization
|
|
501
|
+
#
|
|
502
|
+
# @example Basic move control
|
|
503
|
+
# control = sequencer.move(from: 0, to: 100, duration: 4r, every: 1/4r) { |v| }
|
|
504
|
+
# control.on_stop { puts "Movement finished!" }
|
|
505
|
+
# control.after(2r) { puts "2 bars after finish" }
|
|
506
|
+
# control.stop # Manually halt movement
|
|
507
|
+
#
|
|
400
508
|
class MoveControl < EventHandler
|
|
401
|
-
|
|
402
|
-
|
|
509
|
+
# @return [EveryControl] underlying every control for timing
|
|
510
|
+
attr_reader :every_control
|
|
511
|
+
# @return [Array<Proc>] stop callbacks
|
|
512
|
+
attr_reader :do_on_stop
|
|
513
|
+
# @return [Array<Hash>] after callbacks with delays
|
|
514
|
+
attr_reader :do_after
|
|
515
|
+
|
|
516
|
+
# Creates move control with timing parameters.
|
|
517
|
+
#
|
|
518
|
+
# @param parent [EventHandler] parent event handler
|
|
519
|
+
# @param duration [Rational, nil] maximum duration in bars
|
|
520
|
+
# @param till [Rational, nil] absolute stop position
|
|
521
|
+
# @param on_stop [Proc, nil] stop callback
|
|
522
|
+
# @param after_bars [Rational, nil] delay for after callback
|
|
523
|
+
# @param after [Proc, nil] after callback block
|
|
524
|
+
#
|
|
525
|
+
# @api private
|
|
403
526
|
def initialize(parent, duration: nil, till: nil, on_stop: nil, after_bars: nil, after: nil)
|
|
404
527
|
super parent
|
|
405
528
|
|
|
@@ -417,19 +540,43 @@ module Musa::Sequencer
|
|
|
417
540
|
end
|
|
418
541
|
end
|
|
419
542
|
|
|
543
|
+
# Registers callback for when movement stops.
|
|
544
|
+
#
|
|
545
|
+
# @yield stop callback block
|
|
546
|
+
#
|
|
547
|
+
# @return [void]
|
|
548
|
+
#
|
|
420
549
|
def on_stop(&block)
|
|
421
550
|
@do_on_stop << block
|
|
422
551
|
end
|
|
423
552
|
|
|
553
|
+
# Registers callback to execute after movement stops.
|
|
554
|
+
#
|
|
555
|
+
# @param bars [Numeric, nil] delay in bars after stop (default: 0)
|
|
556
|
+
# @yield after callback block
|
|
557
|
+
#
|
|
558
|
+
# @return [void]
|
|
559
|
+
#
|
|
560
|
+
# @example Delayed callback
|
|
561
|
+
# control.after(2r) { puts "2 bars after movement ends" }
|
|
562
|
+
#
|
|
424
563
|
def after(bars = nil, &block)
|
|
425
564
|
bars ||= 0
|
|
426
565
|
@do_after << { bars: bars.rationalize, block: block }
|
|
427
566
|
end
|
|
428
567
|
|
|
568
|
+
# Stops movement.
|
|
569
|
+
#
|
|
570
|
+
# @return [void]
|
|
571
|
+
#
|
|
429
572
|
def stop
|
|
430
573
|
@every_control.stop
|
|
431
574
|
end
|
|
432
575
|
|
|
576
|
+
# Checks if movement is stopped.
|
|
577
|
+
#
|
|
578
|
+
# @return [Boolean] true if stopped
|
|
579
|
+
#
|
|
433
580
|
def stopped?
|
|
434
581
|
@stop
|
|
435
582
|
end
|