xi-lang 0.1.4 → 0.1.5
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/README.md +24 -11
- data/bin/xi +0 -1
- data/lib/xi/bjorklund.rb +60 -0
- data/lib/xi/clock.rb +9 -1
- data/lib/xi/core_ext/array.rb +13 -0
- data/lib/xi/core_ext/enumerable.rb +6 -14
- data/lib/xi/core_ext/enumerator.rb +14 -0
- data/lib/xi/core_ext/fixnum.rb +3 -3
- data/lib/xi/core_ext/numeric.rb +4 -4
- data/lib/xi/core_ext/scalar.rb +16 -0
- data/lib/xi/core_ext/string.rb +46 -44
- data/lib/xi/core_ext.rb +3 -1
- data/lib/xi/pattern/generators.rb +157 -162
- data/lib/xi/pattern/transforms.rb +96 -79
- data/lib/xi/pattern.rb +424 -95
- data/lib/xi/step_sequencer.rb +40 -0
- data/lib/xi/stream.rb +56 -64
- data/lib/xi/tidal_clock.rb +2 -1
- data/lib/xi/version.rb +1 -1
- data/lib/xi.rb +12 -3
- data/xi.gemspec +0 -1
- metadata +7 -18
- data/lib/xi/core_ext/simple.rb +0 -15
- data/lib/xi/event.rb +0 -82
@@ -1,189 +1,184 @@
|
|
1
1
|
module Xi
|
2
2
|
class Pattern
|
3
3
|
module Generators
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
i += step
|
25
|
-
end
|
4
|
+
# Create an arithmetic series pattern of +length+ values, being +start+
|
5
|
+
# the starting value and +step+ the addition factor.
|
6
|
+
#
|
7
|
+
# @example
|
8
|
+
# peek P.series #=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
9
|
+
# peek P.series(3) #=> [3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
|
10
|
+
# peek P.series(0, 2) #=> [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
|
11
|
+
# peek P.series(0, 0.25, 8) #=> [0, 0.25, 0.5, 0.75, 1.0, 1.25, 1.5, 1.75]
|
12
|
+
#
|
13
|
+
# @param start [Numeric] (default: 0)
|
14
|
+
# @param step [Numeric] (default: 1)
|
15
|
+
# @param length [Numeric, Symbol] number or inf (default: inf)
|
16
|
+
# @return [Pattern]
|
17
|
+
#
|
18
|
+
def series(start=0, step=1, length=inf)
|
19
|
+
Pattern.new(size: length) do |y|
|
20
|
+
i = start
|
21
|
+
loop_n(length) do
|
22
|
+
y << i
|
23
|
+
i += step
|
26
24
|
end
|
27
25
|
end
|
26
|
+
end
|
28
27
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
end
|
28
|
+
# Create a geometric series pattern of +length+ values, being +start+ the
|
29
|
+
# starting value and +step+ the multiplication factor.
|
30
|
+
#
|
31
|
+
# @example
|
32
|
+
# peek P.geom #=> [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
33
|
+
# peek P.geom(3) #=> [3, 3, 3, 3, 3, 3, 3, 3, 3, 3]
|
34
|
+
# peek P.geom(1, 2) #=> [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
|
35
|
+
# peek P.geom(1, 1/2, 6) #=> [1, (1/2), (1/4), (1/8), (1/16), (1/32)]
|
36
|
+
# peek P.geom(1, -1, 8) #=> [1, -1, 1, -1, 1, -1, 1, -1]
|
37
|
+
#
|
38
|
+
# @param start [Numeric] (default: 0)
|
39
|
+
# @param grow [Numeric] (default: 1)
|
40
|
+
# @param length [Numeric, Symbol] number or inf (default: inf)
|
41
|
+
# @return [Pattern]
|
42
|
+
#
|
43
|
+
def geom(start=0, grow=1, length=inf)
|
44
|
+
Pattern.new(size: length) do |y|
|
45
|
+
i = start
|
46
|
+
loop_n(length) do
|
47
|
+
y << i
|
48
|
+
i *= grow
|
51
49
|
end
|
52
50
|
end
|
51
|
+
end
|
53
52
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
end
|
53
|
+
# Choose items from the +list+ randomly, +repeats+ number of times
|
54
|
+
#
|
55
|
+
# +list+ can be a *finite* enumerable or Pattern.
|
56
|
+
#
|
57
|
+
# @see Pattern::Transforms#rand
|
58
|
+
#
|
59
|
+
# @example
|
60
|
+
# peek P.rand([1, 2, 3]) #=> [2]
|
61
|
+
# peek P.rand([1, 2, 3, 4], 6) #=> [1, 3, 2, 2, 4, 3]
|
62
|
+
#
|
63
|
+
# @param list [#each] list of values
|
64
|
+
# @param repeats [Fixnum, Symbol] number or inf (default: 1)
|
65
|
+
# @return [Pattern]
|
66
|
+
#
|
67
|
+
def rand(list, repeats=1)
|
68
|
+
Pattern.new(size: repeats) do |y|
|
69
|
+
ls = list.to_a
|
70
|
+
loop_n(repeats) { y << ls.sample }
|
73
71
|
end
|
72
|
+
end
|
74
73
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
end
|
74
|
+
# Choose randomly, but only allow repeating the same item after yielding
|
75
|
+
# all items from the list.
|
76
|
+
#
|
77
|
+
# +list+ can be a *finite* enumerable or Pattern.
|
78
|
+
#
|
79
|
+
# @see Pattern::Transforms#xrand
|
80
|
+
#
|
81
|
+
# @example
|
82
|
+
# peek P.xrand([1, 2, 3, 4, 5]) #=> [4]
|
83
|
+
# peek P.xrand([1, 2, 3], 8) #=> [1, 3, 2, 3, 1, 2, 3, 2]
|
84
|
+
#
|
85
|
+
# @param list [#each] list of values
|
86
|
+
# @param repeats [Fixnum, Symbol] number or inf (default: 1)
|
87
|
+
# @return [Pattern]
|
88
|
+
#
|
89
|
+
def xrand(list, repeats=1)
|
90
|
+
Pattern.new(size: repeats) do |y|
|
91
|
+
ls = list.to_a
|
92
|
+
xs = nil
|
93
|
+
loop_n(repeats) do |i|
|
94
|
+
xs = ls.shuffle if i % ls.size == 0
|
95
|
+
y << xs[i % ls.size]
|
98
96
|
end
|
99
97
|
end
|
98
|
+
end
|
100
99
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
end
|
100
|
+
# Shuffle the list in random order, and use the same random order
|
101
|
+
# +repeats+ times
|
102
|
+
#
|
103
|
+
# +list+ can be a *finite* enumerable or Pattern.
|
104
|
+
#
|
105
|
+
# @see Pattern::Transforms#shuf
|
106
|
+
#
|
107
|
+
# @example
|
108
|
+
# peek P.shuf([1, 2, 3, 4, 5]) #=> [5, 3, 4, 1, 2]
|
109
|
+
# peek P.shuf([1, 2, 3], 3) #=> [2, 3, 1, 2, 3, 1, 2, 3, 1]
|
110
|
+
#
|
111
|
+
# @param list [#each] list of values
|
112
|
+
# @param repeats [Fixnum, Symbol] number or inf (default: 1)
|
113
|
+
# @return [Pattern]
|
114
|
+
#
|
115
|
+
def shuf(list, repeats=1)
|
116
|
+
Pattern.new(size: list.size * repeats) do |y|
|
117
|
+
xs = list.to_a.shuffle
|
118
|
+
loop_n(repeats) do |i|
|
119
|
+
xs.each { |x| y << x }
|
122
120
|
end
|
123
121
|
end
|
122
|
+
end
|
124
123
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
end
|
124
|
+
# Generates values from a sinewave discretized to +quant+ events
|
125
|
+
# for the duration of +delta+ cycles.
|
126
|
+
#
|
127
|
+
# Values range from -1 to 1
|
128
|
+
#
|
129
|
+
# @see #sin1 for the same function but constrained on 0 to 1 values
|
130
|
+
#
|
131
|
+
# @example
|
132
|
+
# P.sin(8).map { |i| i.round(2) }
|
133
|
+
# #=> [0.0, 0.71, 1.0, 0.71, 0.0, -0.71, -1.0, -0.71]
|
134
|
+
#
|
135
|
+
# @example +quant+ determines the size, +delta+ the total duration
|
136
|
+
# P.sin(8).size #=> 8
|
137
|
+
# P.sin(22).duration #=> (1/1)
|
138
|
+
# P.sin(19, 2).duration #=> (2/1)
|
139
|
+
#
|
140
|
+
# @param quant [Fixnum]
|
141
|
+
# @param delta [Fixnum] (default: 1)
|
142
|
+
# @return [Pattern]
|
143
|
+
#
|
144
|
+
def sin(quant, delta=1)
|
145
|
+
Pattern.new(size: quant, delta: delta / quant) do |y|
|
146
|
+
quant.times do |i|
|
147
|
+
y << Math.sin(i / quant * 2 * Math::PI)
|
150
148
|
end
|
151
149
|
end
|
150
|
+
end
|
152
151
|
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
152
|
+
# Generates values from a sinewave discretized to +quant+ events
|
153
|
+
# for the duration of +delta+ cycles.
|
154
|
+
#
|
155
|
+
# Values range from 0 to 1
|
156
|
+
#
|
157
|
+
# @see #sin
|
158
|
+
#
|
159
|
+
# @example
|
160
|
+
# P.sin1(8).map { |i| i.round(2) }
|
161
|
+
# #=> [0.5, 0.85, 1.0, 0.85, 0.5, 0.15, 0.0, 0.15]
|
162
|
+
#
|
163
|
+
# @param quant [Fixnum]
|
164
|
+
# @param delta [Fixnum] (default: 1)
|
165
|
+
# @return [Pattern]
|
166
|
+
#
|
167
|
+
def sin1(quant, delta=1)
|
168
|
+
sin(quant, delta).scale(-1, 1, 0, 1)
|
169
|
+
end
|
171
170
|
|
172
|
-
|
171
|
+
private
|
173
172
|
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
173
|
+
# @private
|
174
|
+
def loop_n(length)
|
175
|
+
i = 0
|
176
|
+
loop do
|
177
|
+
break if length != inf && i == length
|
178
|
+
yield i
|
179
|
+
i += 1
|
181
180
|
end
|
182
181
|
end
|
183
|
-
|
184
|
-
def self.included(receiver)
|
185
|
-
receiver.extend(ClassMethods)
|
186
|
-
end
|
187
182
|
end
|
188
183
|
end
|
189
184
|
end
|
@@ -12,12 +12,7 @@ module Xi
|
|
12
12
|
# @return [Pattern]
|
13
13
|
#
|
14
14
|
def -@
|
15
|
-
|
16
|
-
each_event { |e|
|
17
|
-
v = e.value
|
18
|
-
y << E[(v.respond_to?(:-@) ? -v : v), e.start, e.duration]
|
19
|
-
}
|
20
|
-
}
|
15
|
+
map { |v| v.respond_to?(:-@) ? -v : v }
|
21
16
|
end
|
22
17
|
|
23
18
|
# Concatenate +object+ pattern or perform a scalar sum with +object+
|
@@ -39,17 +34,12 @@ module Xi
|
|
39
34
|
#
|
40
35
|
def +(object)
|
41
36
|
if object.is_a?(Pattern)
|
42
|
-
Pattern.new(self, size: size + object.size) { |y|
|
37
|
+
Pattern.new(self, size: size + object.size) { |y, d|
|
43
38
|
each { |v| y << v }
|
44
39
|
object.each { |v| y << v }
|
45
40
|
}
|
46
41
|
else
|
47
|
-
|
48
|
-
each_event { |e|
|
49
|
-
v = e.value
|
50
|
-
y << E[(v.respond_to?(:+) ? v + object : v), e.start, e.duration]
|
51
|
-
}
|
52
|
-
}
|
42
|
+
map { |v| v.respond_to?(:+) ? v + object : v }
|
53
43
|
end
|
54
44
|
end
|
55
45
|
|
@@ -66,12 +56,7 @@ module Xi
|
|
66
56
|
# @return [Pattern]
|
67
57
|
#
|
68
58
|
def -(numeric)
|
69
|
-
|
70
|
-
each_event { |e|
|
71
|
-
v = e.value
|
72
|
-
y << E[(v.respond_to?(:-) ? v - numeric : v), e.start, e.duration]
|
73
|
-
}
|
74
|
-
}
|
59
|
+
map { |v| v.respond_to?(:-) ? v - numeric : v }
|
75
60
|
end
|
76
61
|
|
77
62
|
# Performs a scalar multiplication with +numeric+
|
@@ -87,12 +72,7 @@ module Xi
|
|
87
72
|
# @return [Pattern]
|
88
73
|
#
|
89
74
|
def *(numeric)
|
90
|
-
|
91
|
-
each_event { |e|
|
92
|
-
v = e.value
|
93
|
-
y << E[(v.respond_to?(:*) ? v * numeric : v), e.start, e.duration]
|
94
|
-
}
|
95
|
-
}
|
75
|
+
map { |v| v.respond_to?(:*) ? v * numeric : v }
|
96
76
|
end
|
97
77
|
|
98
78
|
# Performs a scalar division by +numeric+
|
@@ -108,12 +88,7 @@ module Xi
|
|
108
88
|
# @return [Pattern]
|
109
89
|
#
|
110
90
|
def /(numeric)
|
111
|
-
|
112
|
-
each_event { |e|
|
113
|
-
v = e.value
|
114
|
-
y << E[(v.respond_to?(:/) ? v / numeric : v), e.start, e.duration]
|
115
|
-
}
|
116
|
-
}
|
91
|
+
map { |v| v.respond_to?(:/) ? v / numeric : v }
|
117
92
|
end
|
118
93
|
|
119
94
|
# Performs a scalar modulo against +numeric+
|
@@ -129,12 +104,7 @@ module Xi
|
|
129
104
|
# @return [Pattern]
|
130
105
|
#
|
131
106
|
def %(numeric)
|
132
|
-
|
133
|
-
each_event { |e|
|
134
|
-
v = e.value
|
135
|
-
y << E[(v.respond_to?(:%) ? v % numeric : v), e.start, e.duration]
|
136
|
-
}
|
137
|
-
}
|
107
|
+
map { |v| v.respond_to?(:%) ? v % numeric : v }
|
138
108
|
end
|
139
109
|
|
140
110
|
# Raises each value to the power of +numeric+, which may be negative or
|
@@ -150,12 +120,7 @@ module Xi
|
|
150
120
|
# @return [Pattern]
|
151
121
|
#
|
152
122
|
def **(numeric)
|
153
|
-
|
154
|
-
each_event { |e|
|
155
|
-
v = e.value
|
156
|
-
y << E[(v.respond_to?(:**) ? v ** numeric : v), e.start, e.duration]
|
157
|
-
}
|
158
|
-
}
|
123
|
+
map { |v| v.respond_to?(:**) ? v ** numeric : v }
|
159
124
|
end
|
160
125
|
alias_method :^, :**
|
161
126
|
|
@@ -166,16 +131,16 @@ module Xi
|
|
166
131
|
# peek [1, 2, 3].p.seq(2) #=> [1, 2, 3, 1, 2, 3]
|
167
132
|
# peek [1, 2, 3].p.seq(1, 1) #=> [2, 3, 1]
|
168
133
|
# peek [1, 2, 3].p.seq(2, 2) #=> [3, 2, 1, 3, 2, 1]
|
169
|
-
# peek [1, 2].p.seq(inf, 1) #=> [2, 1, 2, 1, 2, 1, 2, 1, 2, 1]
|
170
134
|
#
|
171
|
-
# @param repeats [Fixnum
|
135
|
+
# @param repeats [Fixnum] number (defaut: 1)
|
172
136
|
# @param offset [Fixnum] (default: 0)
|
173
137
|
# @return [Pattern]
|
174
138
|
#
|
175
139
|
def seq(repeats=1, offset=0)
|
176
|
-
unless
|
177
|
-
fail ArgumentError, "repeats must be a non-negative Fixnum
|
140
|
+
unless repeats.is_a?(Fixnum) && repeats >= 0
|
141
|
+
fail ArgumentError, "repeats must be a non-negative Fixnum"
|
178
142
|
end
|
143
|
+
|
179
144
|
unless offset.is_a?(Fixnum) && offset >= 0
|
180
145
|
fail ArgumentError, "offset must be a non-negative Fixnum"
|
181
146
|
end
|
@@ -227,14 +192,12 @@ module Xi
|
|
227
192
|
def bounce(skip_extremes=true)
|
228
193
|
return self if size == 0 || size == 1
|
229
194
|
|
230
|
-
|
231
|
-
|
232
|
-
each
|
233
|
-
|
234
|
-
last_id = i
|
235
|
-
}
|
195
|
+
new_size = skip_extremes ? size * 2 - 2 : size * 2
|
196
|
+
Pattern.new(self, size: new_size) { |y|
|
197
|
+
each { |v| y << v }
|
198
|
+
last_i = size - 1
|
236
199
|
reverse_each.with_index { |v, i|
|
237
|
-
y << v unless skip_extremes && (i == 0 || i ==
|
200
|
+
y << v unless skip_extremes && (i == 0 || i == last_i)
|
238
201
|
}
|
239
202
|
}
|
240
203
|
end
|
@@ -254,12 +217,7 @@ module Xi
|
|
254
217
|
# @return [Pattern]
|
255
218
|
#
|
256
219
|
def normalize(min, max)
|
257
|
-
|
258
|
-
each_event { |e|
|
259
|
-
v = e.value
|
260
|
-
y << E[(v.respond_to?(:-) ? (v - min) / (max - min) : v), e.start, e.duration]
|
261
|
-
}
|
262
|
-
}
|
220
|
+
map { |v| v.respond_to?(:-) ? (v - min) / (max - min) : v }
|
263
221
|
end
|
264
222
|
|
265
223
|
# Scales a pattern of normalized values (0..1) to a custom range
|
@@ -279,12 +237,7 @@ module Xi
|
|
279
237
|
# @return [Pattern]
|
280
238
|
#
|
281
239
|
def denormalize(min, max)
|
282
|
-
|
283
|
-
each_event { |e|
|
284
|
-
v = e.value
|
285
|
-
y << E[(v.respond_to?(:*) ? (max - min) * v + min : v), e.start, e.duration]
|
286
|
-
}
|
287
|
-
}
|
240
|
+
map { |v| v.respond_to?(:*) ? (max - min) * v + min : v }
|
288
241
|
end
|
289
242
|
|
290
243
|
# Scale from one range of values to another range of values
|
@@ -308,18 +261,17 @@ module Xi
|
|
308
261
|
#
|
309
262
|
# It is the inverse operation of #accelerate
|
310
263
|
#
|
264
|
+
# @see #accelerate
|
265
|
+
#
|
311
266
|
# @example
|
312
267
|
# peek_events %w(a b c d).p([1/4, 1/8, 1/6]).decelerate(2)
|
313
268
|
# #=> [E["a",0,1/2], E["b",1/2,1/4], E["c",3/4,1/3], E["d",13/12,1/2]]
|
314
269
|
#
|
315
270
|
# @param num [Numeric]
|
316
271
|
# @return [Pattern]
|
317
|
-
# @see #accelerate
|
318
272
|
#
|
319
273
|
def decelerate(num)
|
320
|
-
Pattern.new(self
|
321
|
-
each_event { |e| y << E[e.value, e.start * num, e.duration * num] }
|
322
|
-
}
|
274
|
+
Pattern.new(self, delta: delta.p * num)
|
323
275
|
end
|
324
276
|
|
325
277
|
# Advance a pattern by shrinking start and duration of events
|
@@ -327,18 +279,17 @@ module Xi
|
|
327
279
|
#
|
328
280
|
# It is the inverse operation of #decelerate
|
329
281
|
#
|
282
|
+
# @see #decelerate
|
283
|
+
#
|
330
284
|
# @example
|
331
285
|
# peek_events %w(a b c d).p([1/2, 1/4]).accelerate(2)
|
332
286
|
# #=> [E["a",0,1/4], E["b",1/4,1/8], E["c",3/8,1/4], E["d",5/8,1/8]]
|
333
287
|
#
|
334
288
|
# @param num [Numeric]
|
335
289
|
# @return [Pattern]
|
336
|
-
# @see #decelerate
|
337
290
|
#
|
338
291
|
def accelerate(num)
|
339
|
-
Pattern.new(self
|
340
|
-
each_event { |e| y << E[e.value, e.start / num, e.duration / num] }
|
341
|
-
}
|
292
|
+
Pattern.new(self, delta: delta.p / num)
|
342
293
|
end
|
343
294
|
|
344
295
|
# Based on +probability+, it yields original value or nil
|
@@ -362,12 +313,12 @@ module Xi
|
|
362
313
|
prob_pat = probability.p
|
363
314
|
|
364
315
|
if prob_pat.infinite?
|
365
|
-
fail ArgumentError, 'times must be
|
316
|
+
fail ArgumentError, 'times must be finite'
|
366
317
|
end
|
367
318
|
|
368
|
-
Pattern.new(self, size: size * prob_pat.
|
319
|
+
Pattern.new(self, size: size * prob_pat.size) do |y|
|
369
320
|
prob_pat.each do |prob|
|
370
|
-
each { |v| y << (rand < prob ? v : nil) }
|
321
|
+
each { |v| y << (Kernel.rand < prob ? v : nil) }
|
371
322
|
end
|
372
323
|
end
|
373
324
|
end
|
@@ -393,10 +344,10 @@ module Xi
|
|
393
344
|
times_pat = times.p
|
394
345
|
|
395
346
|
if times_pat.infinite?
|
396
|
-
fail ArgumentError, 'times must be
|
347
|
+
fail ArgumentError, 'times must be finite'
|
397
348
|
end
|
398
349
|
|
399
|
-
Pattern.new(self, size: size * times_pat.
|
350
|
+
Pattern.new(self, size: size * times_pat.size) do |y|
|
400
351
|
times_pat.each do |t|
|
401
352
|
each { |v| t.times { y << v } }
|
402
353
|
end
|
@@ -449,6 +400,72 @@ module Xi
|
|
449
400
|
def shuf(repeats=1)
|
450
401
|
P.shuf(self, repeats)
|
451
402
|
end
|
403
|
+
|
404
|
+
# Returns a new Pattern where values for which +test_proc+ are true are
|
405
|
+
# yielded as a pattern to another +block+
|
406
|
+
#
|
407
|
+
# If no block is given, an Enumerator is returned.
|
408
|
+
#
|
409
|
+
# These values are grouped together as a "subpattern", then yielded to
|
410
|
+
# +block+ for further transformation and finally spliced into the original
|
411
|
+
# pattern. +test_proc+ will be called with +value+, +start+ and +duration+
|
412
|
+
# as parameters.
|
413
|
+
#
|
414
|
+
# @param test_proc [#call]
|
415
|
+
# @yield [Pattern] subpattern
|
416
|
+
# @yieldreturn [Pattern] transformed subpattern
|
417
|
+
# @return [Pattern, Enumerator]
|
418
|
+
#
|
419
|
+
def when(test_proc, &block)
|
420
|
+
return enum_for(__method__, test_proc) if block.nil?
|
421
|
+
|
422
|
+
Pattern.new(self) do |y|
|
423
|
+
each_event do |v, s, d, i|
|
424
|
+
if test_proc.call(v, s, d, i)
|
425
|
+
new_pat = block.call(self)
|
426
|
+
new_pat.each_event(s)
|
427
|
+
.take_while { |_, s_, d_| s_ + d_ <= s + d }
|
428
|
+
.each { |v_, _| y << v_ }
|
429
|
+
else
|
430
|
+
y << v
|
431
|
+
end
|
432
|
+
end
|
433
|
+
end
|
434
|
+
end
|
435
|
+
|
436
|
+
# Splices a new pattern returned from +block+ every +n+ cycles
|
437
|
+
#
|
438
|
+
# @see #every_iter
|
439
|
+
#
|
440
|
+
# @param n [Numeric]
|
441
|
+
# @yield [Pattern] subpattern
|
442
|
+
# @yieldreturn [Pattern] transformed subpattern
|
443
|
+
# @return [Pattern]
|
444
|
+
#
|
445
|
+
def every(n, &block)
|
446
|
+
fn = proc { |_, s, _|
|
447
|
+
m = (s + 1) % n
|
448
|
+
m >= 0 && m < 1
|
449
|
+
}
|
450
|
+
self.when(fn, &block)
|
451
|
+
end
|
452
|
+
|
453
|
+
# Splices a new pattern returned from +block+ every +n+ iterations
|
454
|
+
#
|
455
|
+
# @see #every
|
456
|
+
#
|
457
|
+
# @param n [Numeric]
|
458
|
+
# @yield [Pattern] subpattern
|
459
|
+
# @yieldreturn [Pattern] transformed subpattern
|
460
|
+
# @return [Pattern]
|
461
|
+
#
|
462
|
+
def every_iter(n, &block)
|
463
|
+
fn = proc { |_, _, _, i|
|
464
|
+
m = (i + 1) % n
|
465
|
+
m >= 0 && m < 1
|
466
|
+
}
|
467
|
+
self.when(fn, &block)
|
468
|
+
end
|
452
469
|
end
|
453
470
|
end
|
454
471
|
end
|