lib/xi/pattern/transforms.rb in xi-lang-0.1.4 vs lib/xi/pattern/transforms.rb in xi-lang-0.1.5
- old
+ new
@@ -10,16 +10,11 @@
# peek -[1, -2, 3].p #=> [-1, 2, -3]
#
# @return [Pattern]
#
def -@
- Pattern.new(self) { |y|
- each_event { |e|
- v = e.value
- y << E[(v.respond_to?(:-@) ? -v : v), e.start, e.duration]
- }
- }
+ map { |v| v.respond_to?(:-@) ? -v : v }
end
# Concatenate +object+ pattern or perform a scalar sum with +object+
#
# If +object+ is a Pattern, concatenate the two patterns.
@@ -37,21 +32,16 @@
# @param object [Pattern, Numeric] pattern or numeric
# @return [Pattern]
#
def +(object)
if object.is_a?(Pattern)
- Pattern.new(self, size: size + object.size) { |y|
+ Pattern.new(self, size: size + object.size) { |y, d|
each { |v| y << v }
object.each { |v| y << v }
}
else
- Pattern.new(self) { |y|
- each_event { |e|
- v = e.value
- y << E[(v.respond_to?(:+) ? v + object : v), e.start, e.duration]
- }
- }
+ map { |v| v.respond_to?(:+) ? v + object : v }
end
end
# Performs a scalar substraction with +numeric+
#
@@ -64,16 +54,11 @@
#
# @param numeric [Numeric]
# @return [Pattern]
#
def -(numeric)
- Pattern.new(self) { |y|
- each_event { |e|
- v = e.value
- y << E[(v.respond_to?(:-) ? v - numeric : v), e.start, e.duration]
- }
- }
+ map { |v| v.respond_to?(:-) ? v - numeric : v }
end
# Performs a scalar multiplication with +numeric+
#
# For each value from pattern, multiplicate with +numeric+.
@@ -85,16 +70,11 @@
#
# @param numeric [Numeric]
# @return [Pattern]
#
def *(numeric)
- Pattern.new(self) { |y|
- each_event { |e|
- v = e.value
- y << E[(v.respond_to?(:*) ? v * numeric : v), e.start, e.duration]
- }
- }
+ map { |v| v.respond_to?(:*) ? v * numeric : v }
end
# Performs a scalar division by +numeric+
#
# For each value from pattern, divide by +numeric+.
@@ -106,16 +86,11 @@
#
# @param numeric [Numeric]
# @return [Pattern]
#
def /(numeric)
- Pattern.new(self) { |y|
- each_event { |e|
- v = e.value
- y << E[(v.respond_to?(:/) ? v / numeric : v), e.start, e.duration]
- }
- }
+ map { |v| v.respond_to?(:/) ? v / numeric : v }
end
# Performs a scalar modulo against +numeric+
#
# For each value from pattern, return modulo of value divided by +numeric+.
@@ -127,16 +102,11 @@
#
# @param numeric [Numeric]
# @return [Pattern]
#
def %(numeric)
- Pattern.new(self) { |y|
- each_event { |e|
- v = e.value
- y << E[(v.respond_to?(:%) ? v % numeric : v), e.start, e.duration]
- }
- }
+ map { |v| v.respond_to?(:%) ? v % numeric : v }
end
# Raises each value to the power of +numeric+, which may be negative or
# fractional.
#
@@ -148,36 +118,31 @@
#
# @param numeric [Numeric]
# @return [Pattern]
#
def **(numeric)
- Pattern.new(self) { |y|
- each_event { |e|
- v = e.value
- y << E[(v.respond_to?(:**) ? v ** numeric : v), e.start, e.duration]
- }
- }
+ map { |v| v.respond_to?(:**) ? v ** numeric : v }
end
alias_method :^, :**
# Cycles pattern +repeats+ number of times, shifted by +offset+
#
# @example
# peek [1, 2, 3].p.seq #=> [1, 2, 3]
# peek [1, 2, 3].p.seq(2) #=> [1, 2, 3, 1, 2, 3]
# peek [1, 2, 3].p.seq(1, 1) #=> [2, 3, 1]
# peek [1, 2, 3].p.seq(2, 2) #=> [3, 2, 1, 3, 2, 1]
- # peek [1, 2].p.seq(inf, 1) #=> [2, 1, 2, 1, 2, 1, 2, 1, 2, 1]
#
- # @param repeats [Fixnum, Symbol] number or inf (defaut: 1)
+ # @param repeats [Fixnum] number (defaut: 1)
# @param offset [Fixnum] (default: 0)
# @return [Pattern]
#
def seq(repeats=1, offset=0)
- unless (repeats.is_a?(Fixnum) && repeats >= 0) || repeats == inf
- fail ArgumentError, "repeats must be a non-negative Fixnum or inf"
+ unless repeats.is_a?(Fixnum) && repeats >= 0
+ fail ArgumentError, "repeats must be a non-negative Fixnum"
end
+
unless offset.is_a?(Fixnum) && offset >= 0
fail ArgumentError, "offset must be a non-negative Fixnum"
end
Pattern.new(self, size: size * repeats) do |y|
@@ -225,18 +190,16 @@
# @return [Pattern]
#
def bounce(skip_extremes=true)
return self if size == 0 || size == 1
- Pattern.new(self, size: size * 2 - 1) { |y|
- last_id = 0
- each.with_index { |v, i|
- y << v
- last_id = i
- }
+ new_size = skip_extremes ? size * 2 - 2 : size * 2
+ Pattern.new(self, size: new_size) { |y|
+ each { |v| y << v }
+ last_i = size - 1
reverse_each.with_index { |v, i|
- y << v unless skip_extremes && (i == 0 || i == last_id)
+ y << v unless skip_extremes && (i == 0 || i == last_i)
}
}
end
# Normalizes a pattern of values that range from +min+ to +max+ to 0..1
@@ -252,16 +215,11 @@
# @param min [Numeric]
# @param max [Numeric]
# @return [Pattern]
#
def normalize(min, max)
- Pattern.new(self) { |y|
- each_event { |e|
- v = e.value
- y << E[(v.respond_to?(:-) ? (v - min) / (max - min) : v), e.start, e.duration]
- }
- }
+ map { |v| v.respond_to?(:-) ? (v - min) / (max - min) : v }
end
# Scales a pattern of normalized values (0..1) to a custom range
# +min+..+max+
#
@@ -277,16 +235,11 @@
# @param min [Numeric]
# @param max [Numeric]
# @return [Pattern]
#
def denormalize(min, max)
- Pattern.new(self) { |y|
- each_event { |e|
- v = e.value
- y << E[(v.respond_to?(:*) ? (max - min) * v + min : v), e.start, e.duration]
- }
- }
+ map { |v| v.respond_to?(:*) ? (max - min) * v + min : v }
end
# Scale from one range of values to another range of values
#
# @example
@@ -306,41 +259,39 @@
# Slows down a pattern by stretching start and duration of events
# +num+ times.
#
# It is the inverse operation of #accelerate
#
+ # @see #accelerate
+ #
# @example
# peek_events %w(a b c d).p([1/4, 1/8, 1/6]).decelerate(2)
# #=> [E["a",0,1/2], E["b",1/2,1/4], E["c",3/4,1/3], E["d",13/12,1/2]]
#
# @param num [Numeric]
# @return [Pattern]
- # @see #accelerate
#
def decelerate(num)
- Pattern.new(self) { |y|
- each_event { |e| y << E[e.value, e.start * num, e.duration * num] }
- }
+ Pattern.new(self, delta: delta.p * num)
end
# Advance a pattern by shrinking start and duration of events
# +num+ times.
#
# It is the inverse operation of #decelerate
#
+ # @see #decelerate
+ #
# @example
# peek_events %w(a b c d).p([1/2, 1/4]).accelerate(2)
# #=> [E["a",0,1/4], E["b",1/4,1/8], E["c",3/8,1/4], E["d",5/8,1/8]]
#
# @param num [Numeric]
# @return [Pattern]
- # @see #decelerate
#
def accelerate(num)
- Pattern.new(self) { |y|
- each_event { |e| y << E[e.value, e.start / num, e.duration / num] }
- }
+ Pattern.new(self, delta: delta.p / num)
end
# Based on +probability+, it yields original value or nil
#
# +probability+ can also be an enumerable or a *finite* Pattern. In this
@@ -360,16 +311,16 @@
#
def sometimes(probability=0.5)
prob_pat = probability.p
if prob_pat.infinite?
- fail ArgumentError, 'times must be a finite pattern'
+ fail ArgumentError, 'times must be finite'
end
- Pattern.new(self, size: size * prob_pat.reduce(:+)) do |y|
+ Pattern.new(self, size: size * prob_pat.size) do |y|
prob_pat.each do |prob|
- each { |v| y << (rand < prob ? v : nil) }
+ each { |v| y << (Kernel.rand < prob ? v : nil) }
end
end
end
# Repeats each value +times+
@@ -391,14 +342,14 @@
#
def repeat_each(times)
times_pat = times.p
if times_pat.infinite?
- fail ArgumentError, 'times must be a finite pattern'
+ fail ArgumentError, 'times must be finite'
end
- Pattern.new(self, size: size * times_pat.reduce(:+)) do |y|
+ Pattern.new(self, size: size * times_pat.size) do |y|
times_pat.each do |t|
each { |v| t.times { y << v } }
end
end
end
@@ -446,9 +397,75 @@
# @param repeats [Fixnum, Symbol] number or inf (default: 1)
# @return [Pattern]
#
def shuf(repeats=1)
P.shuf(self, repeats)
+ end
+
+ # Returns a new Pattern where values for which +test_proc+ are true are
+ # yielded as a pattern to another +block+
+ #
+ # If no block is given, an Enumerator is returned.
+ #
+ # These values are grouped together as a "subpattern", then yielded to
+ # +block+ for further transformation and finally spliced into the original
+ # pattern. +test_proc+ will be called with +value+, +start+ and +duration+
+ # as parameters.
+ #
+ # @param test_proc [#call]
+ # @yield [Pattern] subpattern
+ # @yieldreturn [Pattern] transformed subpattern
+ # @return [Pattern, Enumerator]
+ #
+ def when(test_proc, &block)
+ return enum_for(__method__, test_proc) if block.nil?
+
+ Pattern.new(self) do |y|
+ each_event do |v, s, d, i|
+ if test_proc.call(v, s, d, i)
+ new_pat = block.call(self)
+ new_pat.each_event(s)
+ .take_while { |_, s_, d_| s_ + d_ <= s + d }
+ .each { |v_, _| y << v_ }
+ else
+ y << v
+ end
+ end
+ end
+ end
+
+ # Splices a new pattern returned from +block+ every +n+ cycles
+ #
+ # @see #every_iter
+ #
+ # @param n [Numeric]
+ # @yield [Pattern] subpattern
+ # @yieldreturn [Pattern] transformed subpattern
+ # @return [Pattern]
+ #
+ def every(n, &block)
+ fn = proc { |_, s, _|
+ m = (s + 1) % n
+ m >= 0 && m < 1
+ }
+ self.when(fn, &block)
+ end
+
+ # Splices a new pattern returned from +block+ every +n+ iterations
+ #
+ # @see #every
+ #
+ # @param n [Numeric]
+ # @yield [Pattern] subpattern
+ # @yieldreturn [Pattern] transformed subpattern
+ # @return [Pattern]
+ #
+ def every_iter(n, &block)
+ fn = proc { |_, _, _, i|
+ m = (i + 1) % n
+ m >= 0 && m < 1
+ }
+ self.when(fn, &block)
end
end
end
end