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.
Files changed (123) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -1
  3. data/.version +6 -0
  4. data/.yardopts +7 -0
  5. data/README.md +227 -6
  6. data/docs/README.md +83 -0
  7. data/docs/api-reference.md +86 -0
  8. data/docs/getting-started/quick-start.md +93 -0
  9. data/docs/getting-started/tutorial.md +58 -0
  10. data/docs/subsystems/core-extensions.md +316 -0
  11. data/docs/subsystems/datasets.md +465 -0
  12. data/docs/subsystems/generative.md +290 -0
  13. data/docs/subsystems/matrix.md +63 -0
  14. data/docs/subsystems/midi.md +123 -0
  15. data/docs/subsystems/music.md +233 -0
  16. data/docs/subsystems/musicxml-builder.md +264 -0
  17. data/docs/subsystems/neumas.md +71 -0
  18. data/docs/subsystems/repl.md +135 -0
  19. data/docs/subsystems/sequencer.md +98 -0
  20. data/docs/subsystems/series.md +302 -0
  21. data/docs/subsystems/transcription.md +152 -0
  22. data/docs/subsystems/transport.md +177 -0
  23. data/lib/musa-dsl/core-ext/array-explode-ranges.rb +68 -0
  24. data/lib/musa-dsl/core-ext/arrayfy.rb +110 -0
  25. data/lib/musa-dsl/core-ext/attribute-builder.rb +91 -30
  26. data/lib/musa-dsl/core-ext/deep-copy.rb +125 -2
  27. data/lib/musa-dsl/core-ext/dynamic-proxy.rb +78 -0
  28. data/lib/musa-dsl/core-ext/extension.rb +53 -0
  29. data/lib/musa-dsl/core-ext/hashify.rb +162 -1
  30. data/lib/musa-dsl/core-ext/inspect-nice.rb +154 -0
  31. data/lib/musa-dsl/core-ext/smart-proc-binder.rb +117 -0
  32. data/lib/musa-dsl/core-ext/with.rb +114 -0
  33. data/lib/musa-dsl/datasets/dataset.rb +109 -0
  34. data/lib/musa-dsl/datasets/delta-d.rb +78 -0
  35. data/lib/musa-dsl/datasets/e.rb +186 -2
  36. data/lib/musa-dsl/datasets/gdv.rb +279 -2
  37. data/lib/musa-dsl/datasets/gdvd.rb +201 -0
  38. data/lib/musa-dsl/datasets/helper.rb +75 -0
  39. data/lib/musa-dsl/datasets/p.rb +177 -2
  40. data/lib/musa-dsl/datasets/packed-v.rb +91 -0
  41. data/lib/musa-dsl/datasets/pdv.rb +136 -1
  42. data/lib/musa-dsl/datasets/ps.rb +134 -4
  43. data/lib/musa-dsl/datasets/score/queriable.rb +143 -1
  44. data/lib/musa-dsl/datasets/score/render.rb +105 -1
  45. data/lib/musa-dsl/datasets/score/to-mxml/process-pdv.rb +138 -1
  46. data/lib/musa-dsl/datasets/score/to-mxml/process-ps.rb +111 -0
  47. data/lib/musa-dsl/datasets/score/to-mxml/process-time.rb +200 -1
  48. data/lib/musa-dsl/datasets/score/to-mxml/to-mxml.rb +145 -1
  49. data/lib/musa-dsl/datasets/score.rb +279 -0
  50. data/lib/musa-dsl/datasets/v.rb +88 -0
  51. data/lib/musa-dsl/generative/darwin.rb +180 -1
  52. data/lib/musa-dsl/generative/generative-grammar.rb +359 -0
  53. data/lib/musa-dsl/generative/markov.rb +133 -3
  54. data/lib/musa-dsl/generative/rules.rb +258 -4
  55. data/lib/musa-dsl/generative/variatio.rb +217 -2
  56. data/lib/musa-dsl/logger/logger.rb +267 -2
  57. data/lib/musa-dsl/matrix/matrix.rb +256 -10
  58. data/lib/musa-dsl/midi/midi-recorder.rb +108 -1
  59. data/lib/musa-dsl/midi/midi-voices.rb +265 -4
  60. data/lib/musa-dsl/music/chord-definition.rb +233 -1
  61. data/lib/musa-dsl/music/chord-definitions.rb +33 -6
  62. data/lib/musa-dsl/music/chords.rb +308 -2
  63. data/lib/musa-dsl/music/equally-tempered-12-tone-scale-system.rb +315 -0
  64. data/lib/musa-dsl/music/scales.rb +957 -40
  65. data/lib/musa-dsl/musicxml/builder/attributes.rb +483 -3
  66. data/lib/musa-dsl/musicxml/builder/backup-forward.rb +166 -1
  67. data/lib/musa-dsl/musicxml/builder/direction.rb +243 -0
  68. data/lib/musa-dsl/musicxml/builder/helper.rb +240 -0
  69. data/lib/musa-dsl/musicxml/builder/measure.rb +284 -0
  70. data/lib/musa-dsl/musicxml/builder/note-complexities.rb +324 -8
  71. data/lib/musa-dsl/musicxml/builder/note.rb +285 -0
  72. data/lib/musa-dsl/musicxml/builder/part-group.rb +108 -1
  73. data/lib/musa-dsl/musicxml/builder/part.rb +139 -0
  74. data/lib/musa-dsl/musicxml/builder/pitched-note.rb +124 -0
  75. data/lib/musa-dsl/musicxml/builder/rest.rb +93 -0
  76. data/lib/musa-dsl/musicxml/builder/score-partwise.rb +276 -0
  77. data/lib/musa-dsl/musicxml/builder/typed-text.rb +62 -1
  78. data/lib/musa-dsl/musicxml/builder/unpitched-note.rb +83 -0
  79. data/lib/musa-dsl/neumalang/neumalang.rb +675 -0
  80. data/lib/musa-dsl/neumas/array-to-neumas.rb +149 -0
  81. data/lib/musa-dsl/neumas/neuma-decoder.rb +253 -0
  82. data/lib/musa-dsl/neumas/neuma-gdv-decoder.rb +142 -2
  83. data/lib/musa-dsl/neumas/neuma-gdvd-decoder.rb +82 -0
  84. data/lib/musa-dsl/neumas/neumas.rb +67 -0
  85. data/lib/musa-dsl/neumas/string-to-neumas.rb +233 -1
  86. data/lib/musa-dsl/repl/repl.rb +550 -0
  87. data/lib/musa-dsl/sequencer/base-sequencer-implementation-every.rb +118 -2
  88. data/lib/musa-dsl/sequencer/base-sequencer-implementation-move.rb +149 -2
  89. data/lib/musa-dsl/sequencer/base-sequencer-implementation-play-helper.rb +296 -0
  90. data/lib/musa-dsl/sequencer/base-sequencer-implementation-play-timed.rb +88 -2
  91. data/lib/musa-dsl/sequencer/base-sequencer-implementation-play.rb +161 -0
  92. data/lib/musa-dsl/sequencer/base-sequencer-implementation.rb +263 -0
  93. data/lib/musa-dsl/sequencer/base-sequencer-tick-based.rb +173 -1
  94. data/lib/musa-dsl/sequencer/base-sequencer-tickless-based.rb +177 -0
  95. data/lib/musa-dsl/sequencer/base-sequencer.rb +710 -10
  96. data/lib/musa-dsl/sequencer/sequencer-dsl.rb +210 -0
  97. data/lib/musa-dsl/sequencer/timeslots.rb +79 -0
  98. data/lib/musa-dsl/series/array-to-serie.rb +37 -1
  99. data/lib/musa-dsl/series/base-series.rb +843 -5
  100. data/lib/musa-dsl/series/buffer-serie.rb +48 -0
  101. data/lib/musa-dsl/series/hash-or-array-serie-splitter.rb +41 -0
  102. data/lib/musa-dsl/series/main-serie-constructors.rb +398 -2
  103. data/lib/musa-dsl/series/main-serie-operations.rb +538 -16
  104. data/lib/musa-dsl/series/proxy-serie.rb +67 -0
  105. data/lib/musa-dsl/series/quantizer-serie.rb +45 -7
  106. data/lib/musa-dsl/series/queue-serie.rb +65 -0
  107. data/lib/musa-dsl/series/series-composer.rb +701 -0
  108. data/lib/musa-dsl/series/timed-serie.rb +473 -28
  109. data/lib/musa-dsl/transcription/from-gdv-to-midi.rb +404 -1
  110. data/lib/musa-dsl/transcription/from-gdv-to-musicxml.rb +118 -0
  111. data/lib/musa-dsl/transcription/from-gdv.rb +84 -1
  112. data/lib/musa-dsl/transcription/transcription.rb +265 -0
  113. data/lib/musa-dsl/transport/clock.rb +125 -0
  114. data/lib/musa-dsl/transport/dummy-clock.rb +89 -2
  115. data/lib/musa-dsl/transport/external-tick-clock.rb +91 -0
  116. data/lib/musa-dsl/transport/input-midi-clock.rb +133 -1
  117. data/lib/musa-dsl/transport/timer-clock.rb +183 -1
  118. data/lib/musa-dsl/transport/timer.rb +83 -0
  119. data/lib/musa-dsl/transport/transport.rb +318 -0
  120. data/lib/musa-dsl/version.rb +1 -1
  121. data/lib/musa-dsl.rb +132 -25
  122. data/musa-dsl.gemspec +12 -10
  123. 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
- attr_reader :duration_value, :till_value, :condition_block, :do_on_stop, :do_after
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
- attr_reader :every_control, :do_on_stop, :do_after
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