xi-lang 0.1.4 → 0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- 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
|