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,53 @@
|
|
|
1
1
|
module Musa
|
|
2
2
|
module Series::Operations
|
|
3
|
+
# Creates a buffered serie allowing multiple independent iterations over same source.
|
|
4
|
+
#
|
|
5
|
+
# Provides buffering mechanism enabling multiple "readers" to independently
|
|
6
|
+
# iterate over the same serie source without interfering with each other.
|
|
7
|
+
#
|
|
8
|
+
# ## Buffering Modes
|
|
9
|
+
#
|
|
10
|
+
# - **Async (default)**: Buffers fill independently, each progresses at own pace
|
|
11
|
+
# - **Sync**: All buffers synchronized, restart affects all
|
|
12
|
+
#
|
|
13
|
+
# ## Use Cases
|
|
14
|
+
#
|
|
15
|
+
# - Multiple voices reading same melodic sequence at different speeds
|
|
16
|
+
# - Polyphonic playback from single source
|
|
17
|
+
# - Canonic structures (rounds, fugues)
|
|
18
|
+
# - Independent transformations of same base material
|
|
19
|
+
#
|
|
20
|
+
# ## Memory Management
|
|
21
|
+
#
|
|
22
|
+
# History is automatically cleaned when all buffers have progressed past
|
|
23
|
+
# old values, preventing unbounded memory growth.
|
|
24
|
+
#
|
|
25
|
+
# @param sync [Boolean] synchronized mode (default: false)
|
|
26
|
+
#
|
|
27
|
+
# @return [BufferSerie, SyncBufferSerie] buffered serie
|
|
28
|
+
#
|
|
29
|
+
# @example Create buffered serie
|
|
30
|
+
# buffered = S(1, 2, 3, 4).buffered
|
|
31
|
+
# reader1 = buffered.buffer.i
|
|
32
|
+
# reader2 = buffered.buffer.i
|
|
33
|
+
#
|
|
34
|
+
# @example Multiple independent readers
|
|
35
|
+
# source = S(1, 2, 3, 4).buffered
|
|
36
|
+
# reader1 = source.buffer.i
|
|
37
|
+
# reader2 = source.buffer.i
|
|
38
|
+
#
|
|
39
|
+
# reader1.next_value # => 1
|
|
40
|
+
# reader2.next_value # => 1 (independent)
|
|
41
|
+
# reader1.next_value # => 2
|
|
42
|
+
# reader2.next_value # => 2
|
|
43
|
+
#
|
|
44
|
+
# @example Canon structure
|
|
45
|
+
# melody = S(60, 64, 67, 72).buffered
|
|
46
|
+
# voice1 = melody.buffer
|
|
47
|
+
# voice2 = melody.buffer
|
|
48
|
+
# # Play voice2 delayed by N beats
|
|
49
|
+
#
|
|
50
|
+
# @api public
|
|
3
51
|
def buffered(sync: false)
|
|
4
52
|
if sync
|
|
5
53
|
SyncBufferSerie.new(self)
|
|
@@ -1,5 +1,46 @@
|
|
|
1
1
|
module Musa
|
|
2
2
|
module Series::Operations
|
|
3
|
+
|
|
4
|
+
# Serie splitter for decomposing hash/array values into component series.
|
|
5
|
+
#
|
|
6
|
+
# Splits series of hash or array values into individual component series,
|
|
7
|
+
# enabling independent access to each component.
|
|
8
|
+
#
|
|
9
|
+
# ## Splitting Modes
|
|
10
|
+
#
|
|
11
|
+
# - **Hash mode**: Split `{pitch: 60, velocity: 96}` into separate series
|
|
12
|
+
# for `:pitch` and `:velocity`
|
|
13
|
+
# - **Array mode**: Split `[60, 96]` into separate series for indices 0, 1
|
|
14
|
+
#
|
|
15
|
+
# ## Component Access
|
|
16
|
+
#
|
|
17
|
+
# - **Hash**: `splitter[:pitch]`, `splitter[:velocity]`
|
|
18
|
+
# - **Array**: `splitter[0]`, `splitter[1]`
|
|
19
|
+
# - **Enumerable**: `splitter.each { |component| ... }`
|
|
20
|
+
#
|
|
21
|
+
# ## Use Cases
|
|
22
|
+
#
|
|
23
|
+
# - Separate polyphonic voices from single source
|
|
24
|
+
# - Independent processing of musical parameters
|
|
25
|
+
# - Extract specific components (pitch, duration, velocity, etc.)
|
|
26
|
+
# - Multi-track decomposition
|
|
27
|
+
#
|
|
28
|
+
# @example Split hash values
|
|
29
|
+
# notes = S({pitch: 60, vel: 96}, {pitch: 64, vel: 80})
|
|
30
|
+
# splitter = notes.split.i
|
|
31
|
+
#
|
|
32
|
+
# pitches = splitter[:pitch]
|
|
33
|
+
# velocities = splitter[:vel]
|
|
34
|
+
#
|
|
35
|
+
# pitches.next_value # => 60
|
|
36
|
+
# velocities.next_value # => 96
|
|
37
|
+
#
|
|
38
|
+
# @example Split array values
|
|
39
|
+
# pairs = S([1, 10], [2, 20], [3, 30]).split.i
|
|
40
|
+
# first = pairs[0]
|
|
41
|
+
# second = pairs[1]
|
|
42
|
+
#
|
|
43
|
+
# @api public
|
|
3
44
|
def split
|
|
4
45
|
Splitter.new(self)
|
|
5
46
|
end
|
|
@@ -3,50 +3,314 @@ require_relative '../core-ext/smart-proc-binder'
|
|
|
3
3
|
|
|
4
4
|
require_relative 'base-series'
|
|
5
5
|
|
|
6
|
-
# TODO: añadir en for: steps: (nº de pasos en los que repartir el incremento)
|
|
7
|
-
|
|
8
6
|
module Musa
|
|
7
|
+
# Series constructor methods for creating series from various sources.
|
|
8
|
+
#
|
|
9
|
+
# Provides factory methods for common serie types:
|
|
10
|
+
#
|
|
11
|
+
# ## Basic Constructors
|
|
12
|
+
#
|
|
13
|
+
# - **UNDEFINED** - Undefined serie (unresolved state)
|
|
14
|
+
# - **NIL** - Serie that always returns nil
|
|
15
|
+
# - **S** - Serie from array of values
|
|
16
|
+
# - **E** - Serie from evaluation block
|
|
17
|
+
#
|
|
18
|
+
# ## Collection Constructors
|
|
19
|
+
#
|
|
20
|
+
# - **H/HC** - Hash of series (hash/combined mode)
|
|
21
|
+
# - **A/AC** - Array of series (array/combined mode)
|
|
22
|
+
# - **MERGE** - Sequential merge of multiple series
|
|
23
|
+
#
|
|
24
|
+
# ## Numeric Generators
|
|
25
|
+
#
|
|
26
|
+
# - **FOR** - For-loop style numeric sequence
|
|
27
|
+
# - **RND** - Random values (from array or range)
|
|
28
|
+
# - **RND1** - Single random value
|
|
29
|
+
# - **SIN** - Sine wave function
|
|
30
|
+
# - **FIBO** - Fibonacci sequence
|
|
31
|
+
#
|
|
32
|
+
# ## Musical Generators
|
|
33
|
+
#
|
|
34
|
+
# - **HARMO** - Harmonic note series
|
|
35
|
+
#
|
|
36
|
+
# ## Usage Patterns
|
|
37
|
+
#
|
|
38
|
+
# ### Array Serie
|
|
39
|
+
#
|
|
40
|
+
# ```ruby
|
|
41
|
+
# notes = S(60, 64, 67, 72)
|
|
42
|
+
# notes.i.next_value # => 60
|
|
43
|
+
# ```
|
|
44
|
+
#
|
|
45
|
+
# ### Evaluation Block
|
|
46
|
+
#
|
|
47
|
+
# ```ruby
|
|
48
|
+
# counter = E(1) { |v, last_value:| last_value + 1 unless last_value == 10 }
|
|
49
|
+
# counter.i.to_a # => [1, 2, 3, ..., 10]
|
|
50
|
+
# ```
|
|
51
|
+
#
|
|
52
|
+
# ### Random Values
|
|
53
|
+
#
|
|
54
|
+
# ```ruby
|
|
55
|
+
# dice = RND(1, 2, 3, 4, 5, 6)
|
|
56
|
+
# dice.i.next_value # => random 1-6
|
|
57
|
+
# ```
|
|
58
|
+
#
|
|
59
|
+
# ### Numeric Sequences
|
|
60
|
+
#
|
|
61
|
+
# ```ruby
|
|
62
|
+
# sequence = FOR(from: 0, to: 10, step: 2)
|
|
63
|
+
# sequence.i.to_a # => [0, 2, 4, 6, 8, 10]
|
|
64
|
+
# ```
|
|
65
|
+
#
|
|
66
|
+
# ### Combining Series
|
|
67
|
+
#
|
|
68
|
+
# ```ruby
|
|
69
|
+
# melody = MERGE(S(60, 64), S(67, 72))
|
|
70
|
+
# melody.i.to_a # => [60, 64, 67, 72]
|
|
71
|
+
# ```
|
|
72
|
+
#
|
|
73
|
+
# @see Musa::Series::Operations Serie transformation operations
|
|
74
|
+
#
|
|
75
|
+
# @api public
|
|
9
76
|
module Series::Constructors
|
|
10
77
|
using Musa::Extension::ExplodeRanges
|
|
11
78
|
|
|
79
|
+
# Creates undefined serie.
|
|
80
|
+
#
|
|
81
|
+
# Returns serie in undefined state. Useful as placeholder that will
|
|
82
|
+
# be resolved later (e.g., in PROXY).
|
|
83
|
+
#
|
|
84
|
+
# @return [UndefinedSerie] serie in undefined state
|
|
85
|
+
#
|
|
86
|
+
# @example Undefined placeholder
|
|
87
|
+
# proxy = PROXY() # Uses UNDEFINED internally
|
|
88
|
+
# proxy.undefined? # => true
|
|
89
|
+
#
|
|
90
|
+
# @api public
|
|
12
91
|
def UNDEFINED
|
|
13
92
|
UndefinedSerie.new
|
|
14
93
|
end
|
|
15
94
|
|
|
95
|
+
# Creates serie that always returns nil.
|
|
96
|
+
#
|
|
97
|
+
# Returns nil on every next_value call. Useful for padding or as
|
|
98
|
+
# placeholder in composite structures.
|
|
99
|
+
#
|
|
100
|
+
# @return [NilSerie] serie returning nil
|
|
101
|
+
#
|
|
102
|
+
# @example Nil serie
|
|
103
|
+
# s = NIL().i
|
|
104
|
+
# s.next_value # => nil
|
|
105
|
+
# s.next_value # => nil
|
|
106
|
+
#
|
|
107
|
+
# @api public
|
|
16
108
|
def NIL
|
|
17
109
|
NilSerie.new
|
|
18
110
|
end
|
|
19
111
|
|
|
112
|
+
# Creates serie from array of values.
|
|
113
|
+
#
|
|
114
|
+
# Most common constructor. Values can include ranges which will be
|
|
115
|
+
# expanded automatically via ExplodeRanges extension.
|
|
116
|
+
#
|
|
117
|
+
# @param values [Array] values to iterate (supports ranges)
|
|
118
|
+
#
|
|
119
|
+
# @return [FromArray] serie from array
|
|
120
|
+
#
|
|
121
|
+
# @example Basic array
|
|
122
|
+
# notes = S(60, 64, 67, 72)
|
|
123
|
+
# notes.i.to_a # => [60, 64, 67, 72]
|
|
124
|
+
#
|
|
125
|
+
# @example With ranges
|
|
126
|
+
# scale = S(60..67)
|
|
127
|
+
# scale.i.to_a # => [60, 61, 62, 63, 64, 65, 66, 67]
|
|
128
|
+
#
|
|
129
|
+
# @api public
|
|
20
130
|
def S(*values)
|
|
21
131
|
FromArray.new values.explode_ranges
|
|
22
132
|
end
|
|
23
133
|
|
|
134
|
+
# Creates hash-mode serie from hash of series.
|
|
135
|
+
#
|
|
136
|
+
# Combines multiple series into hash-structured values. Returns hash
|
|
137
|
+
# with same keys, values from respective series. Stops when first
|
|
138
|
+
# serie exhausts.
|
|
139
|
+
#
|
|
140
|
+
# @param series_hash [Hash] hash of series (key => serie)
|
|
141
|
+
#
|
|
142
|
+
# @return [FromHashOfSeries] combined hash serie
|
|
143
|
+
#
|
|
144
|
+
# @example Hash of series
|
|
145
|
+
# h = H(pitch: S(60, 64, 67), velocity: S(96, 80, 64))
|
|
146
|
+
# h.i.next_value # => {pitch: 60, velocity: 96}
|
|
147
|
+
# h.i.next_value # => {pitch: 64, velocity: 80}
|
|
148
|
+
#
|
|
149
|
+
# @api public
|
|
24
150
|
def H(**series_hash)
|
|
25
151
|
FromHashOfSeries.new series_hash, false
|
|
26
152
|
end
|
|
27
153
|
|
|
154
|
+
# Creates hash-mode combined serie from hash of series.
|
|
155
|
+
#
|
|
156
|
+
# Like H but cycles all series. When a serie exhausts, it restarts from
|
|
157
|
+
# the beginning, continuing until all series complete their cycles.
|
|
158
|
+
#
|
|
159
|
+
# @param series_hash [Hash] hash of series (key => serie)
|
|
160
|
+
#
|
|
161
|
+
# @return [FromHashOfSeries] combined hash serie that cycles all series
|
|
162
|
+
#
|
|
163
|
+
# @example Combined cycling all series
|
|
164
|
+
# hc = HC(a: S(1, 2), b: S(10, 20, 30))
|
|
165
|
+
# hc.max_size(6).i.to_a # => [{a:1, b:10}, {a:2, b:20}, {a:1, b:30},
|
|
166
|
+
# # {a:2, b:10}, {a:1, b:20}, {a:2, b:30}]
|
|
167
|
+
#
|
|
168
|
+
# @api public
|
|
28
169
|
def HC(**series_hash)
|
|
29
170
|
FromHashOfSeries.new series_hash, true
|
|
30
171
|
end
|
|
31
172
|
|
|
173
|
+
# Creates array-mode serie from array of series.
|
|
174
|
+
#
|
|
175
|
+
# Combines multiple series into array-structured values. Returns array
|
|
176
|
+
# of values from respective series. Stops when first serie exhausts.
|
|
177
|
+
#
|
|
178
|
+
# @param series [Array] array of series
|
|
179
|
+
#
|
|
180
|
+
# @return [FromArrayOfSeries] combined array serie
|
|
181
|
+
#
|
|
182
|
+
# @example Array of series
|
|
183
|
+
# a = A(S(1, 2, 3), S(10, 20, 30))
|
|
184
|
+
# a.i.next_value # => [1, 10]
|
|
185
|
+
# a.i.next_value # => [2, 20]
|
|
186
|
+
#
|
|
187
|
+
# @api public
|
|
32
188
|
def A(*series)
|
|
33
189
|
FromArrayOfSeries.new series, false
|
|
34
190
|
end
|
|
35
191
|
|
|
192
|
+
# Creates array-mode combined serie from array of series.
|
|
193
|
+
#
|
|
194
|
+
# Like A but cycles all series. When a serie exhausts, it restarts from
|
|
195
|
+
# the beginning, continuing until all series complete their cycles.
|
|
196
|
+
#
|
|
197
|
+
# @param series [Array] array of series
|
|
198
|
+
#
|
|
199
|
+
# @return [FromArrayOfSeries] combined array serie that cycles all series
|
|
200
|
+
#
|
|
201
|
+
# @example Combined cycling all series
|
|
202
|
+
# ac = AC(S(1, 2), S(10, 20, 30))
|
|
203
|
+
# ac.max_size(6).i.to_a # => [[1, 10], [2, 20], [1, 30],
|
|
204
|
+
# # [2, 10], [1, 20], [2, 30]]
|
|
205
|
+
#
|
|
206
|
+
# @api public
|
|
36
207
|
def AC(*series)
|
|
37
208
|
FromArrayOfSeries.new series, true
|
|
38
209
|
end
|
|
39
210
|
|
|
211
|
+
# Creates serie from evaluation block.
|
|
212
|
+
#
|
|
213
|
+
# Calls block repeatedly with parameters and last_value. Block returns
|
|
214
|
+
# next value or nil to stop. Enables stateful generators and algorithms.
|
|
215
|
+
#
|
|
216
|
+
# ## Block Parameters
|
|
217
|
+
#
|
|
218
|
+
# - **value_args**: Initial positional parameters
|
|
219
|
+
# - **last_value**: Previous return value (nil on first call)
|
|
220
|
+
# - **caller**: Serie instance (access to parameters attribute)
|
|
221
|
+
# - **key_args**: Initial keyword parameters
|
|
222
|
+
#
|
|
223
|
+
# @param value_args [Array] initial positional parameters
|
|
224
|
+
# @param key_args [Hash] initial keyword parameters
|
|
225
|
+
# @yield block called for each value
|
|
226
|
+
# @yieldparam value_args [Array] current positional parameters
|
|
227
|
+
# @yieldparam last_value [Object, nil] previous return value
|
|
228
|
+
# @yieldparam caller [FromEvalBlockWithParameters] serie instance
|
|
229
|
+
# @yieldparam key_args [Hash] current keyword parameters
|
|
230
|
+
# @yieldreturn [Object, nil] next value or nil to stop
|
|
231
|
+
#
|
|
232
|
+
# @return [FromEvalBlockWithParameters] evaluation-based serie
|
|
233
|
+
#
|
|
234
|
+
# @example Counter
|
|
235
|
+
# counter = E(1) { |v, last_value:| last_value + 1 unless last_value == 5 }
|
|
236
|
+
# counter.i.to_a # => [1, 2, 3, 4, 5]
|
|
237
|
+
#
|
|
238
|
+
# @example Fibonacci
|
|
239
|
+
# fib = E { |last_value:, caller:|
|
|
240
|
+
# a, b = caller.parameters
|
|
241
|
+
# caller.parameters = [b, a + b]
|
|
242
|
+
# a
|
|
243
|
+
# }
|
|
244
|
+
# fib.parameters = [0, 1]
|
|
245
|
+
# fib.i.to_a(limit: 10) # => [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
|
|
246
|
+
#
|
|
247
|
+
# @api public
|
|
40
248
|
def E(*value_args, **key_args, &block)
|
|
41
249
|
FromEvalBlockWithParameters.new *value_args, **key_args, &block
|
|
42
250
|
end
|
|
43
251
|
|
|
252
|
+
# Creates for-loop style numeric sequence.
|
|
253
|
+
#
|
|
254
|
+
# Generates sequence from `from` to `to` (inclusive) with `step` increment.
|
|
255
|
+
# Automatically adjusts step sign based on from/to relationship.
|
|
256
|
+
#
|
|
257
|
+
# @param from [Numeric, nil] starting value (default: 0)
|
|
258
|
+
# @param to [Numeric, nil] ending value (nil for infinite)
|
|
259
|
+
# @param step [Numeric, nil] increment (default: 1, sign auto-adjusted)
|
|
260
|
+
#
|
|
261
|
+
# @return [ForLoop] numeric sequence serie
|
|
262
|
+
#
|
|
263
|
+
# @example Ascending sequence
|
|
264
|
+
# s = FOR(from: 0, to: 10, step: 2)
|
|
265
|
+
# s.i.to_a # => [0, 2, 4, 6, 8, 10]
|
|
266
|
+
#
|
|
267
|
+
# @example Descending sequence
|
|
268
|
+
# s = FOR(from: 10, to: 0, step: 2)
|
|
269
|
+
# s.i.to_a # => [10, 8, 6, 4, 2, 0]
|
|
270
|
+
#
|
|
271
|
+
# @example Infinite sequence
|
|
272
|
+
# s = FOR(from: 0, step: 1) # to: nil
|
|
273
|
+
# s.infinite? # => true
|
|
274
|
+
#
|
|
275
|
+
# @api public
|
|
44
276
|
def FOR(from: nil, to: nil, step: nil)
|
|
45
277
|
from ||= 0
|
|
46
278
|
step ||= 1
|
|
47
279
|
ForLoop.new from, to, step
|
|
48
280
|
end
|
|
49
281
|
|
|
282
|
+
# Creates random value serie from array or range.
|
|
283
|
+
#
|
|
284
|
+
# Two modes:
|
|
285
|
+
# - **Array mode**: Random values from provided array
|
|
286
|
+
# - **Range mode**: Random numbers from range (from, to, step)
|
|
287
|
+
#
|
|
288
|
+
# Infinite serie - never exhausts.
|
|
289
|
+
#
|
|
290
|
+
# @param _values [Array] values to choose from (positional)
|
|
291
|
+
# @param values [Array, nil] values to choose from (named)
|
|
292
|
+
# @param from [Numeric, nil] range start (range mode)
|
|
293
|
+
# @param to [Numeric, nil] range end (range mode, required)
|
|
294
|
+
# @param step [Numeric, nil] range step (default: 1)
|
|
295
|
+
# @param random [Random, Integer, nil] Random instance or seed
|
|
296
|
+
#
|
|
297
|
+
# @return [RandomValuesFromArray, RandomNumbersFromRange] random serie
|
|
298
|
+
#
|
|
299
|
+
# @raise [ArgumentError] if using both positional and named values
|
|
300
|
+
# @raise [ArgumentError] if mixing array and range parameters
|
|
301
|
+
#
|
|
302
|
+
# @example Random from array
|
|
303
|
+
# dice = RND(1, 2, 3, 4, 5, 6)
|
|
304
|
+
# dice.i.next_value # => random 1-6
|
|
305
|
+
#
|
|
306
|
+
# @example Random from range
|
|
307
|
+
# rand = RND(from: 0, to: 100, step: 10)
|
|
308
|
+
# rand.i.next_value # => random 0, 10, 20, ..., 100
|
|
309
|
+
#
|
|
310
|
+
# @example With seed
|
|
311
|
+
# rnd = RND(1, 2, 3, random: 42) # Reproducible
|
|
312
|
+
#
|
|
313
|
+
# @api public
|
|
50
314
|
def RND(*_values, values: nil, from: nil, to: nil, step: nil, random: nil)
|
|
51
315
|
raise ArgumentError, "Can't use both direct values #{_values} and values named parameter #{values} at the same time." if values && !_values.empty?
|
|
52
316
|
|
|
@@ -66,10 +330,55 @@ module Musa
|
|
|
66
330
|
end
|
|
67
331
|
end
|
|
68
332
|
|
|
333
|
+
# Merges multiple series sequentially.
|
|
334
|
+
#
|
|
335
|
+
# Plays series in sequence: first serie until exhausted, then second,
|
|
336
|
+
# etc. Restarts each serie (except first) before playing.
|
|
337
|
+
#
|
|
338
|
+
# @param series [Array<Serie>] series to merge sequentially
|
|
339
|
+
#
|
|
340
|
+
# @return [Sequence] sequential merge serie
|
|
341
|
+
#
|
|
342
|
+
# @example Merge sequences
|
|
343
|
+
# merged = MERGE(S(1, 2, 3), S(10, 20, 30))
|
|
344
|
+
# merged.i.to_a # => [1, 2, 3, 10, 20, 30]
|
|
345
|
+
#
|
|
346
|
+
# @example Melodic phrases
|
|
347
|
+
# phrase1 = S(60, 64, 67)
|
|
348
|
+
# phrase2 = S(72, 69, 65)
|
|
349
|
+
# melody = MERGE(phrase1, phrase2)
|
|
350
|
+
#
|
|
351
|
+
# @api public
|
|
69
352
|
def MERGE(*series)
|
|
70
353
|
Sequence.new(series)
|
|
71
354
|
end
|
|
72
355
|
|
|
356
|
+
# Creates single random value serie from array or range.
|
|
357
|
+
#
|
|
358
|
+
# Like RND but returns only one random value then exhausts.
|
|
359
|
+
# Two modes: array mode and range mode.
|
|
360
|
+
#
|
|
361
|
+
# @param _values [Array] values to choose from (positional)
|
|
362
|
+
# @param values [Array, nil] values to choose from (named)
|
|
363
|
+
# @param from [Numeric, nil] range start (range mode)
|
|
364
|
+
# @param to [Numeric, nil] range end (range mode, required)
|
|
365
|
+
# @param step [Numeric, nil] range step (default: 1)
|
|
366
|
+
# @param random [Random, Integer, nil] Random instance or seed
|
|
367
|
+
#
|
|
368
|
+
# @return [RandomValueFromArray, RandomNumberFromRange] single random value serie
|
|
369
|
+
#
|
|
370
|
+
# @raise [ArgumentError] if using both positional and named values
|
|
371
|
+
# @raise [ArgumentError] if mixing array and range parameters
|
|
372
|
+
#
|
|
373
|
+
# @example Single random value
|
|
374
|
+
# rnd = RND1(1, 2, 3, 4, 5)
|
|
375
|
+
# rnd.i.next_value # => random 1-5
|
|
376
|
+
# rnd.i.next_value # => nil (exhausted)
|
|
377
|
+
#
|
|
378
|
+
# @example Random seed selection
|
|
379
|
+
# seed = RND1(10, 20, 30, random: 42)
|
|
380
|
+
#
|
|
381
|
+
# @api public
|
|
73
382
|
def RND1(*_values, values: nil, from: nil, to: nil, step: nil, random: nil)
|
|
74
383
|
raise ArgumentError, "Can't use both direct values #{_values} and values named parameter #{values} at the same time." if values && !_values.empty?
|
|
75
384
|
|
|
@@ -89,6 +398,36 @@ module Musa
|
|
|
89
398
|
end
|
|
90
399
|
end
|
|
91
400
|
|
|
401
|
+
# Creates sine wave function serie.
|
|
402
|
+
#
|
|
403
|
+
# Generates values following sine curve. Useful for smooth oscillations,
|
|
404
|
+
# LFO-style modulation, and periodic variations.
|
|
405
|
+
#
|
|
406
|
+
# ## Wave Parameters
|
|
407
|
+
#
|
|
408
|
+
# - **start_value**: Initial value (default: center)
|
|
409
|
+
# - **steps**: Period in steps (nil for continuous)
|
|
410
|
+
# - **amplitude**: Wave amplitude (default: 1.0)
|
|
411
|
+
# - **center**: Center/offset value (default: 0.0)
|
|
412
|
+
#
|
|
413
|
+
# Wave equation: `center + amplitude * sin(progress)`
|
|
414
|
+
#
|
|
415
|
+
# @param start_value [Numeric, nil] initial value
|
|
416
|
+
# @param steps [Numeric, nil] full period in steps
|
|
417
|
+
# @param amplitude [Numeric, nil] wave amplitude (default: 1.0)
|
|
418
|
+
# @param center [Numeric, nil] center offset (default: 0.0)
|
|
419
|
+
#
|
|
420
|
+
# @return [SinFunction] sine wave serie
|
|
421
|
+
#
|
|
422
|
+
# @example Basic sine wave
|
|
423
|
+
# wave = SIN(steps: 8, amplitude: 10, center: 50)
|
|
424
|
+
# wave.i.to_a # => oscillates around 50 ± 10
|
|
425
|
+
#
|
|
426
|
+
# @example LFO modulation
|
|
427
|
+
# lfo = SIN(steps: 16, amplitude: 0.5, center: 0.5)
|
|
428
|
+
# # Use for amplitude modulation
|
|
429
|
+
#
|
|
430
|
+
# @api public
|
|
92
431
|
def SIN(start_value: nil, steps: nil, amplitude: nil, center: nil)
|
|
93
432
|
amplitude ||= 1.0
|
|
94
433
|
center ||= 0.0
|
|
@@ -96,10 +435,53 @@ module Musa
|
|
|
96
435
|
SinFunction.new start_value, steps, amplitude, center
|
|
97
436
|
end
|
|
98
437
|
|
|
438
|
+
# Creates Fibonacci sequence serie.
|
|
439
|
+
#
|
|
440
|
+
# Generates classic Fibonacci sequence: 0, 1, 1, 2, 3, 5, 8, 13, 21, ...
|
|
441
|
+
# Infinite serie.
|
|
442
|
+
#
|
|
443
|
+
# @return [Fibonacci] Fibonacci sequence serie
|
|
444
|
+
#
|
|
445
|
+
# @example Fibonacci numbers
|
|
446
|
+
# fib = FIBO()
|
|
447
|
+
# fib.infinite? # => true
|
|
448
|
+
# inst = fib.i
|
|
449
|
+
# 10.times.map { inst.next_value }
|
|
450
|
+
# # => [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
|
|
451
|
+
#
|
|
452
|
+
# @example Rhythmic proportions
|
|
453
|
+
# durations = FIBO().i.map { |n| Rational(n, 16) }
|
|
454
|
+
#
|
|
455
|
+
# @api public
|
|
99
456
|
def FIBO()
|
|
100
457
|
Fibonacci.new
|
|
101
458
|
end
|
|
102
459
|
|
|
460
|
+
# Creates harmonic notes serie from fundamental.
|
|
461
|
+
#
|
|
462
|
+
# Generates MIDI note numbers for harmonic series based on listened
|
|
463
|
+
# fundamental. Approximates harmonics to nearest semitone within error
|
|
464
|
+
# tolerance.
|
|
465
|
+
#
|
|
466
|
+
# ## Parameters
|
|
467
|
+
#
|
|
468
|
+
# - **error**: Maximum cents deviation to accept harmonic (default: 0.5)
|
|
469
|
+
# - **extended**: Include extended harmonics beyond audible range
|
|
470
|
+
#
|
|
471
|
+
# @param error [Numeric, nil] maximum deviation in semitones (default: 0.5)
|
|
472
|
+
# @param extended [Boolean, nil] include extended harmonics (default: false)
|
|
473
|
+
#
|
|
474
|
+
# @return [HarmonicNotes] harmonic series serie
|
|
475
|
+
#
|
|
476
|
+
# @example Harmonic series
|
|
477
|
+
# # Listen to fundamental, serie returns harmonic notes
|
|
478
|
+
# harmonics = HARMO(error: 0.5)
|
|
479
|
+
# harmonics.i # Waits for fundamental input
|
|
480
|
+
#
|
|
481
|
+
# @example Extended harmonics
|
|
482
|
+
# harm = HARMO(error: 0.3, extended: true)
|
|
483
|
+
#
|
|
484
|
+
# @api public
|
|
103
485
|
def HARMO(error: nil, extended: nil)
|
|
104
486
|
error ||= 0.5
|
|
105
487
|
extended ||= false
|
|
@@ -133,6 +515,20 @@ module Musa
|
|
|
133
515
|
|
|
134
516
|
private_constant :NilSerie
|
|
135
517
|
|
|
518
|
+
# Serie constructor that creates a serie from an array of values.
|
|
519
|
+
#
|
|
520
|
+
# Iterates through array elements, returning nil when exhausted.
|
|
521
|
+
# Supports optional module extensions for enhanced functionality.
|
|
522
|
+
#
|
|
523
|
+
# @example Basic array serie
|
|
524
|
+
# serie = FromArray.new([1, 2, 3, 4, 5])
|
|
525
|
+
# serie.next_value # => 1
|
|
526
|
+
# serie.next_value # => 2
|
|
527
|
+
#
|
|
528
|
+
# @example With extensions
|
|
529
|
+
# serie = FromArray.new([60, 62, 64], [SomeExtension])
|
|
530
|
+
#
|
|
531
|
+
# @api private
|
|
136
532
|
class FromArray
|
|
137
533
|
include Series::Serie.base
|
|
138
534
|
|