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.
@@ -1,189 +1,184 @@
1
1
  module Xi
2
2
  class Pattern
3
3
  module Generators
4
- module ClassMethods
5
- # Create an arithmetic series pattern of +length+ values, being +start+
6
- # the starting value and +step+ the addition factor.
7
- #
8
- # @example
9
- # peek P.series #=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
10
- # peek P.series(3) #=> [3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
11
- # peek P.series(0, 2) #=> [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
12
- # peek P.series(0, 0.25, 8) #=> [0, 0.25, 0.5, 0.75, 1.0, 1.25, 1.5, 1.75]
13
- #
14
- # @param start [Numeric] (default: 0)
15
- # @param step [Numeric] (default: 1)
16
- # @param length [Numeric, Symbol] number or inf (default: inf)
17
- # @return [Pattern]
18
- #
19
- def series(start=0, step=1, length=inf)
20
- Pattern.new(size: length) do |y|
21
- i = start
22
- loop_n(length) do
23
- y << i
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
- # Create a geometric series pattern of +length+ values, being +start+ the
30
- # starting value and +step+ the multiplication factor.
31
- #
32
- # @example
33
- # peek P.geom #=> [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
34
- # peek P.geom(3) #=> [3, 3, 3, 3, 3, 3, 3, 3, 3, 3]
35
- # peek P.geom(1, 2) #=> [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
36
- # peek P.geom(1, 1/2, 6) #=> [1, (1/2), (1/4), (1/8), (1/16), (1/32)]
37
- # peek P.geom(1, -1, 8) #=> [1, -1, 1, -1, 1, -1, 1, -1]
38
- #
39
- # @param start [Numeric] (default: 0)
40
- # @param grow [Numeric] (default: 1)
41
- # @param length [Numeric, Symbol] number or inf (default: inf)
42
- # @return [Pattern]
43
- #
44
- def geom(start=0, grow=1, length=inf)
45
- Pattern.new(size: length) do |y|
46
- i = start
47
- loop_n(length) do
48
- y << i
49
- i *= grow
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
- # Choose items from the +list+ randomly, +repeats+ number of times
55
- #
56
- # +list+ can be a *finite* enumerable or Pattern.
57
- #
58
- # @see Pattern::Transforms#rand
59
- #
60
- # @example
61
- # peek P.rand([1, 2, 3]) #=> [2]
62
- # peek P.rand([1, 2, 3, 4], 6) #=> [1, 3, 2, 2, 4, 3]
63
- #
64
- # @param list [#each] list of values
65
- # @param repeats [Fixnum, Symbol] number or inf (default: 1)
66
- # @return [Pattern]
67
- #
68
- def rand(list, repeats=1)
69
- Pattern.new(size: repeats) do |y|
70
- ls = list.to_a
71
- loop_n(repeats) { y << ls.sample }
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
- # Choose randomly, but only allow repeating the same item after yielding
76
- # all items from the list.
77
- #
78
- # +list+ can be a *finite* enumerable or Pattern.
79
- #
80
- # @see Pattern::Transforms#xrand
81
- #
82
- # @example
83
- # peek P.xrand([1, 2, 3, 4, 5]) #=> [4]
84
- # peek P.xrand([1, 2, 3], 8) #=> [1, 3, 2, 3, 1, 2, 3, 2]
85
- #
86
- # @param list [#each] list of values
87
- # @param repeats [Fixnum, Symbol] number or inf (default: 1)
88
- # @return [Pattern]
89
- #
90
- def xrand(list, repeats=1)
91
- Pattern.new(size: repeats) do |y|
92
- ls = list.to_a
93
- xs = nil
94
- loop_n(repeats) do |i|
95
- xs = ls.shuffle if i % ls.size == 0
96
- y << xs[i % ls.size]
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
- # Shuffle the list in random order, and use the same random order
102
- # +repeats+ times
103
- #
104
- # +list+ can be a *finite* enumerable or Pattern.
105
- #
106
- # @see Pattern::Transforms#shuf
107
- #
108
- # @example
109
- # peek P.shuf([1, 2, 3, 4, 5]) #=> [5, 3, 4, 1, 2]
110
- # peek P.shuf([1, 2, 3], 3) #=> [2, 3, 1, 2, 3, 1, 2, 3, 1]
111
- #
112
- # @param list [#each] list of values
113
- # @param repeats [Fixnum, Symbol] number or inf (default: 1)
114
- # @return [Pattern]
115
- #
116
- def shuf(list, repeats=1)
117
- Pattern.new(size: list.size * repeats) do |y|
118
- xs = list.to_a.shuffle
119
- loop_n(repeats) do |i|
120
- xs.each { |x| y << x }
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
- # Generates values from a sinewave discretized to +quant+ events
126
- # for the duration of +dur+ cycles.
127
- #
128
- # Values range from -1 to 1
129
- #
130
- # @see #sin1 for the same function but constrained on 0 to 1 values
131
- #
132
- # @example
133
- # P.sin(8).map { |i| i.round(2) }
134
- # #=> [0.0, 0.71, 1.0, 0.71, 0.0, -0.71, -1.0, -0.71]
135
- #
136
- # @example +quant+ determines the size, +dur+ the total duration
137
- # P.sin(8).size #=> 8
138
- # P.sin(22).total_duration #=> (1/1)
139
- # P.sin(19, 2).total_duration #=> (2/1)
140
- #
141
- # @param quant [Fixnum]
142
- # @param dur [Fixnum] (default: 1)
143
- # @return [Pattern]
144
- #
145
- def sin(quant, dur=1)
146
- Pattern.new(size: quant, dur: dur / quant) do |y|
147
- quant.times do |i|
148
- y << Math.sin(i / quant * 2 * Math::PI)
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
- # Generates values from a sinewave discretized to +quant+ events
154
- # for the duration of +dur+ cycles.
155
- #
156
- # Values range from 0 to 1
157
- #
158
- # @see #sin
159
- #
160
- # @example
161
- # P.sin1(8).map { |i| i.round(2) }
162
- # #=> [0.5, 0.85, 1.0, 0.85, 0.5, 0.15, 0.0, 0.15]
163
- #
164
- # @param quant [Fixnum]
165
- # @param dur [Fixnum] (default: 1)
166
- # @return [Pattern]
167
- #
168
- def sin1(quant, dur=1)
169
- sin(quant, dur).scale(-1, 1, 0, 1)
170
- end
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
- private
171
+ private
173
172
 
174
- def loop_n(length)
175
- i = 0
176
- loop do
177
- break if length != inf && i == length
178
- yield i
179
- i += 1
180
- end
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
- Pattern.new(self) { |y|
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
- Pattern.new(self) { |y|
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
- Pattern.new(self) { |y|
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
- Pattern.new(self) { |y|
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
- Pattern.new(self) { |y|
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
- Pattern.new(self) { |y|
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
- Pattern.new(self) { |y|
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, Symbol] number or inf (defaut: 1)
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 (repeats.is_a?(Fixnum) && repeats >= 0) || repeats == inf
177
- fail ArgumentError, "repeats must be a non-negative Fixnum or inf"
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
- Pattern.new(self, size: size * 2 - 1) { |y|
231
- last_id = 0
232
- each.with_index { |v, i|
233
- y << v
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 == last_id)
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
- Pattern.new(self) { |y|
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
- Pattern.new(self) { |y|
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) { |y|
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) { |y|
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 a finite pattern'
316
+ fail ArgumentError, 'times must be finite'
366
317
  end
367
318
 
368
- Pattern.new(self, size: size * prob_pat.reduce(:+)) do |y|
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 a finite pattern'
347
+ fail ArgumentError, 'times must be finite'
397
348
  end
398
349
 
399
- Pattern.new(self, size: size * times_pat.reduce(:+)) do |y|
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