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.
@@ -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