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
@@ -4,9 +4,62 @@ require_relative '../core-ext/with'
4
4
 
5
5
  module Musa
6
6
  module Sequencer
7
+ # High-level DSL wrapper for BaseSequencer.
8
+ #
9
+ # Provides user-friendly interface with block context management via
10
+ # `with` method. Wraps BaseSequencer methods to automatically evaluate
11
+ # blocks in DSL context, enabling clean musical composition code.
12
+ #
13
+ # ## DSL Context
14
+ #
15
+ # Blocks passed to scheduling methods (now, at, wait, play, every, move)
16
+ # are evaluated in DSL context via `with`, providing access to sequencer
17
+ # methods and allowing for cleaner composition syntax.
18
+ #
19
+ # ## Delegation
20
+ #
21
+ # Delegates most methods to either BaseSequencer or DSLContext:
22
+ #
23
+ # - Timing: beats_per_bar, ticks_per_beat, position, tick, reset
24
+ # - Scheduling: now, at, wait, play, play_timed, every, move
25
+ # - Events: launch, on
26
+ # - Inspection: size, empty?, everying, playing, moving
27
+ #
28
+ # ## Musical Applications
29
+ #
30
+ # Provides composer-friendly API for:
31
+ #
32
+ # - Musical composition scripts
33
+ # - Interactive sequencing
34
+ # - Live coding
35
+ # - Algorithmic composition
36
+ #
37
+ # @example Basic DSL usage
38
+ # sequencer = Musa::Sequencer::Sequencer.new(4, 96) do
39
+ # at(1r) { puts "Bar 1" }
40
+ # at(2r) { puts "Bar 2" }
41
+ #
42
+ # every(1r, duration: 4r) do
43
+ # puts "Every beat"
44
+ # end
45
+ # end
46
+ #
47
+ # sequencer.run
48
+ #
49
+ # @example DSL context access
50
+ # sequencer = Musa::Sequencer::Sequencer.new(4, 96) do
51
+ # at(1r) do
52
+ # puts "Position: #{position}" # DSL context method
53
+ #
54
+ # wait(1r) { puts "One bar later" } # Nested scheduling
55
+ # end
56
+ # end
57
+ #
58
+ # @api public
7
59
  class Sequencer
8
60
  extend Forwardable
9
61
 
62
+ # Delegates to BaseSequencer
10
63
  def_delegators :@sequencer,
11
64
  :beats_per_bar, :ticks_per_beat, :ticks_per_bar, :tick_duration,
12
65
  :offset,
@@ -18,12 +71,38 @@ module Musa
18
71
  :position=,
19
72
  :event_handler
20
73
 
74
+ # Delegates to DSLContext
21
75
  def_delegators :@dsl, :position, :quantize_position, :logger, :debug
22
76
  def_delegators :@dsl, :now, :at, :wait, :play, :play_timed, :every, :move
23
77
  def_delegators :@dsl, :everying, :playing, :moving
24
78
  def_delegators :@dsl, :launch, :on
25
79
  def_delegators :@dsl, :run
26
80
 
81
+ # Creates sequencer with optional initialization block.
82
+ #
83
+ # @param beats_per_bar [Integer, nil] beats per bar (tick-based mode)
84
+ # @param ticks_per_beat [Integer, nil] ticks per beat (tick-based mode)
85
+ # @param offset [Rational, nil] starting position offset
86
+ # @param sequencer [BaseSequencer, nil] use existing sequencer
87
+ # @param logger [Logger, nil] logger instance
88
+ # @param do_log [Boolean, nil] enable logging
89
+ # @param do_error_log [Boolean, nil] enable error logging
90
+ # @param log_position_format [Symbol, nil] position format for logs
91
+ # @param dsl_context_class [Class, nil] custom DSL context class
92
+ # @param keep_block_context [Boolean, nil] preserve block's original binding
93
+ # @yield initialization block evaluated in DSL context
94
+ #
95
+ # @example Tick-based sequencer
96
+ # seq = Sequencer.new(4, 96) do
97
+ # at(1r) { puts "Start" }
98
+ # end
99
+ #
100
+ # @example Tickless sequencer
101
+ # seq = Sequencer.new do
102
+ # at(1r) { puts "Start" }
103
+ # end
104
+ #
105
+ # @api public
27
106
  def initialize(beats_per_bar = nil,
28
107
  ticks_per_beat = nil,
29
108
  offset: nil,
@@ -49,16 +128,89 @@ module Musa
49
128
  @dsl.with &block if block_given?
50
129
  end
51
130
 
131
+ # Evaluates block in DSL context.
132
+ #
133
+ # Provides `with` method for evaluating blocks with DSL context access.
134
+ # The block is executed in the DSL context, giving it direct access to
135
+ # sequencer methods like at, wait, play, every, move without needing to
136
+ # reference the sequencer object.
137
+ #
138
+ # @param value_parameters [Array] positional parameters
139
+ # @param key_parameters [Hash] keyword parameters
140
+ # @yield block to evaluate in DSL context
141
+ #
142
+ # @return [Object] block return value
143
+ #
144
+ # @example Evaluating blocks in DSL context
145
+ # seq = Musa::Sequencer::Sequencer.new(4, 96)
146
+ #
147
+ # executed = []
148
+ #
149
+ # # Use 'with' to evaluate block in DSL context
150
+ # seq.with do
151
+ # # Inside this block, we have direct access to DSL methods
152
+ # at(1) { executed << "bar 1" }
153
+ # at(2) { executed << "bar 2" }
154
+ #
155
+ # every(1, duration: 4) do
156
+ # executed << "beat at #{position}"
157
+ # end
158
+ # end
159
+ #
160
+ # seq.run
161
+ #
162
+ # # executed contains ["bar 1", "beat at 1", "bar 2", "beat at 2", ...]
163
+ #
164
+ # @example Passing parameters to with block
165
+ # seq = Musa::Sequencer::Sequencer.new(4, 96)
166
+ #
167
+ # notes = []
168
+ #
169
+ # seq.with(60, 64, 67) do |c, e, g|
170
+ # at(1) { notes << c } # Uses parameter c = 60
171
+ # at(2) { notes << e } # Uses parameter e = 64
172
+ # at(3) { notes << g } # Uses parameter g = 67
173
+ # end
174
+ #
175
+ # seq.run
176
+ #
177
+ # # notes contains [60, 64, 67]
178
+ #
179
+ # @example Comparison: with DSL context vs external context
180
+ # seq = Musa::Sequencer::Sequencer.new(4, 96)
181
+ #
182
+ # # Without 'with': need to reference seq explicitly
183
+ # seq.at(1) { seq.at(2) { puts "nested" } }
184
+ #
185
+ # # With 'with': DSL methods available directly
186
+ # seq.with do
187
+ # at(1) { at(2) { puts "nested" } } # Cleaner syntax
188
+ # end
189
+ #
190
+ # @api public
52
191
  def with(*value_parameters, **key_parameters, &block)
53
192
  @dsl.with(*value_parameters, **key_parameters, &block)
54
193
  end
55
194
 
195
+ # DSL context providing block evaluation with sequencer method access.
196
+ #
197
+ # Wraps BaseSequencer methods to evaluate user blocks in controlled
198
+ # context via `with`. Enables clean DSL syntax by providing direct
199
+ # access to sequencer methods within block scopes.
200
+ #
201
+ # ## Block Context Modes
202
+ #
203
+ # - **keep_block_context: false** (default): Evaluate blocks in DSL context
204
+ # - **keep_block_context: true**: Preserve block's original binding
205
+ #
56
206
  class DSLContext
57
207
  extend Forwardable
58
208
  include Musa::Extension::With
59
209
 
210
+ # @return [BaseSequencer] underlying sequencer
60
211
  attr_reader :sequencer
61
212
 
213
+ # Delegates to BaseSequencer
62
214
  def_delegators :@sequencer,
63
215
  :launch, :on,
64
216
  :position, :quantize_position,
@@ -67,11 +219,20 @@ module Musa
67
219
  :ticks_per_bar, :logger, :debug, :inspect,
68
220
  :run
69
221
 
222
+ # @api private
70
223
  def initialize(sequencer, keep_block_context:)
71
224
  @sequencer = sequencer
72
225
  @keep_block_context_on_with = keep_block_context
73
226
  end
74
227
 
228
+ # Schedules block at current position via DSL context.
229
+ #
230
+ # Wraps BaseSequencer#now, evaluating block in DSL context.
231
+ #
232
+ # @param value_parameters [Array] parameters to pass to block
233
+ # @param key_parameters [Hash] keyword parameters
234
+ # @yield block to execute at current position
235
+ # @return [void]
75
236
  def now(*value_parameters, **key_parameters, &block)
76
237
  block ||= proc {}
77
238
 
@@ -80,6 +241,14 @@ module Musa
80
241
  end
81
242
  end
82
243
 
244
+ # Schedules block at absolute position via DSL context.
245
+ #
246
+ # Wraps BaseSequencer#at, evaluating block in DSL context.
247
+ #
248
+ # @param value_parameters [Array] parameters (first is position)
249
+ # @param key_parameters [Hash] keyword parameters
250
+ # @yield block to execute at position
251
+ # @return [void]
83
252
  def at(*value_parameters, **key_parameters, &block)
84
253
  block ||= proc {}
85
254
 
@@ -88,6 +257,14 @@ module Musa
88
257
  end
89
258
  end
90
259
 
260
+ # Schedules block after wait duration via DSL context.
261
+ #
262
+ # Wraps BaseSequencer#wait, evaluating block in DSL context.
263
+ #
264
+ # @param value_parameters [Array] parameters (first is duration)
265
+ # @param key_parameters [Hash] keyword parameters
266
+ # @yield block to execute after wait
267
+ # @return [void]
91
268
  def wait(*value_parameters, **key_parameters, &block)
92
269
  block ||= proc {}
93
270
  @sequencer.wait *value_parameters, **key_parameters do |*values, **key_values|
@@ -95,6 +272,14 @@ module Musa
95
272
  end
96
273
  end
97
274
 
275
+ # Plays series via DSL context.
276
+ #
277
+ # Wraps BaseSequencer#play, evaluating block in DSL context.
278
+ #
279
+ # @param value_parameters [Array] parameters (series, etc.)
280
+ # @param key_parameters [Hash] keyword parameters
281
+ # @yield block to execute for each element
282
+ # @return [PlayControl] control object
98
283
  def play(*value_parameters, **key_parameters, &block)
99
284
  block ||= proc {}
100
285
 
@@ -103,6 +288,14 @@ module Musa
103
288
  end
104
289
  end
105
290
 
291
+ # Plays timed series via DSL context.
292
+ #
293
+ # Wraps BaseSequencer#play_timed, evaluating block in DSL context.
294
+ #
295
+ # @param value_parameters [Array] parameters (timed series, etc.)
296
+ # @param key_parameters [Hash] keyword parameters
297
+ # @yield block to execute for each element
298
+ # @return [PlayTimedControl] control object
106
299
  def play_timed(*value_parameters, **key_parameters, &block)
107
300
  block ||= proc {}
108
301
 
@@ -111,6 +304,15 @@ module Musa
111
304
  end
112
305
  end
113
306
 
307
+ # Repeats block at intervals via DSL context.
308
+ #
309
+ # Wraps BaseSequencer#every, evaluating block in DSL context.
310
+ # Uses SmartProcBinder to apply parameters before with.
311
+ #
312
+ # @param value_parameters [Array] parameters (interval, etc.)
313
+ # @param key_parameters [Hash] keyword parameters
314
+ # @yield block to execute each iteration
315
+ # @return [EveryControl] control object
114
316
  def every(*value_parameters, **key_parameters, &block)
115
317
  block ||= proc {}
116
318
 
@@ -120,6 +322,14 @@ module Musa
120
322
  end
121
323
  end
122
324
 
325
+ # Animates values over time via DSL context.
326
+ #
327
+ # Wraps BaseSequencer#move, evaluating block in DSL context.
328
+ #
329
+ # @param value_parameters [Array] parameters (from, to, etc.)
330
+ # @param key_parameters [Hash] keyword parameters
331
+ # @yield block to execute each iteration with current value
332
+ # @return [MoveControl] control object
123
333
  def move(*value_parameters, **key_parameters, &block)
124
334
  block ||= proc {}
125
335
 
@@ -3,24 +3,101 @@ require 'set'
3
3
  module Musa
4
4
  module Sequencer
5
5
  class BaseSequencer
6
+ # Sorted time-indexed event storage for sequencer.
7
+ #
8
+ # Timeslots extends Hash to maintain scheduled events indexed by time,
9
+ # with efficient sorted access for sequential playback. Each timeslot
10
+ # (key) maps to an array of events scheduled at that time.
11
+ #
12
+ # ## Implementation
13
+ #
14
+ # Uses a SortedSet to track keys in temporal order, enabling fast
15
+ # lookup of next scheduled event without full hash traversal.
16
+ #
17
+ # ## Usage
18
+ #
19
+ # - **Store**: `timeslots[time] = events` schedules events at time
20
+ # - **Retrieve**: `timeslots[time]` gets events at specific time
21
+ # - **Next**: `first_after(pos)` finds next scheduled time >= position
22
+ # - **Delete**: `delete(time)` removes timeslot and updates sort index
23
+ #
24
+ # ## Time Representation
25
+ #
26
+ # Times are Rational numbers representing musical beats/ticks.
27
+ # Granularity depends on sequencer configuration (tick-based or tickless).
28
+ #
29
+ # ## Internal Usage Examples
30
+ #
31
+ # Note: These are illustrative examples for internal implementation.
32
+ # Timeslots is private and cannot be instantiated from public API.
33
+ #
34
+ # # Within BaseSequencer context:
35
+ # timeslots = Timeslots.new
36
+ # timeslots[0r] = [event1, event2] # Events at time 0
37
+ # timeslots[1/2r] = [event3] # Event at beat 0.5
38
+ # timeslots[1r] = [event4, event5] # Events at beat 1
39
+ #
40
+ # next_time = timeslots.first_after(0.5) # => 1r
41
+ # events = timeslots[next_time] # => [event4, event5]
42
+ #
43
+ # @api private
6
44
  class Timeslots < Hash
7
45
 
46
+ # Creates empty timeslots storage.
47
+ #
48
+ # @param several_variants [Array] optional Hash initialization parameters
49
+ # @api private
8
50
  def initialize(*several_variants)
9
51
  super
10
52
  @sorted_keys = SortedSet.new
11
53
  end
12
54
 
55
+ # Stores events at time, maintaining sort index.
56
+ #
57
+ # @param key [Rational] time position
58
+ # @param value [Array, Object] events at this time
59
+ #
60
+ # @return [Array, Object] stored value
61
+ # @api private
13
62
  def []=(key, value)
14
63
  super
15
64
  @sorted_keys << key
16
65
  end
17
66
 
67
+ # Removes timeslot, updating sort index.
68
+ #
69
+ # @param key [Rational] time position to remove
70
+ #
71
+ # @return [Array, Object, nil] removed value
72
+ # @api private
18
73
  def delete(key)
19
74
  super
20
75
  @sorted_keys.delete key
21
76
 
22
77
  end
23
78
 
79
+ # Finds first scheduled time at or after position.
80
+ #
81
+ # Used by sequencer to find next event to execute during playback.
82
+ #
83
+ # Internal behavior (illustrative, not executable from public API):
84
+ #
85
+ # timeslots[1r] = [:event_a]
86
+ # timeslots[2r] = [:event_b]
87
+ # timeslots[3r] = [:event_c]
88
+ #
89
+ # timeslots.first_after(nil) # => 1r
90
+ # timeslots.first_after(0r) # => 1r
91
+ # timeslots.first_after(1r) # => 1r
92
+ # timeslots.first_after(1.5r) # => 2r
93
+ # timeslots.first_after(3r) # => 3r
94
+ # timeslots.first_after(4r) # => nil
95
+ #
96
+ # @param position [Rational, nil] search position (nil for first overall)
97
+ #
98
+ # @return [Rational, nil] next scheduled time, or nil if none
99
+ #
100
+ # @api private
24
101
  def first_after(position)
25
102
  if position.nil?
26
103
  @sorted_keys.first
@@ -29,6 +106,8 @@ module Musa
29
106
  end
30
107
  end
31
108
  end
109
+
110
+ private_constant :Timeslots
32
111
  end
33
112
  end
34
113
  end
@@ -1,8 +1,36 @@
1
1
  require_relative '../series'
2
2
 
3
- # TODO: esto sería un refinement, no?
4
3
 
5
4
  class Array
5
+ # TODO: esto sería un refinement, no?
6
+
7
+ # Converts array to Serie.
8
+ #
9
+ # Three conversion modes:
10
+ #
11
+ # - **Basic**: Direct conversion to serie
12
+ # - **of_series**: Each element of the array becomes a new serie (for array of arrays)
13
+ # - **recursive**: Recursive conversion of nested arrays
14
+ #
15
+ # @param of_series [Boolean, nil] convert each element to a serie (default: false)
16
+ # @param recursive [Boolean, nil] recursively convert nested arrays (default: false)
17
+ #
18
+ # @return [Serie] converted serie
19
+ #
20
+ # @raise [ArgumentError] if both of_series and recursive are true
21
+ #
22
+ # @example Basic conversion
23
+ # [60, 64, 67].to_serie.i.to_a # => [60, 64, 67]
24
+ #
25
+ # @example Serie of series
26
+ # [[1, 2], [3, 4]].to_serie(of_series: true)
27
+ # # Each [1,2], [3,4] becomes S(1,2), S(3,4)
28
+ #
29
+ # @example Recursive conversion
30
+ # [[1, [2, 3]], [4, 5]].to_serie(recursive: true)
31
+ # # Nested arrays become nested series
32
+ #
33
+ # @api public
6
34
  def to_serie(of_series: nil, recursive: nil)
7
35
  of_series ||= false
8
36
  recursive ||= false
@@ -18,5 +46,13 @@ class Array
18
46
  end
19
47
  end
20
48
 
49
+ # Short alias for {#to_serie}.
50
+ #
51
+ # @return [Serie] converted serie
52
+ #
53
+ # @example Short form
54
+ # [1, 2, 3].s # => S(1, 2, 3)
55
+ #
56
+ # @api public
21
57
  alias_method :s, :to_serie
22
58
  end