module Xi::Pattern::Transforms
Public Instance Methods
Performs a scalar modulo against numeric
For each value from pattern, return modulo of value divided by numeric
. Values from pattern that do not respond to %
are ignored.
@example
peek (1..5).p % 2 #=> [1, 0, 1, 0, 1] peek [0, 1, 2, :bar, 4, 5, 6].p % 3 #=> [0, 1, 2, :bar, l, 2, 0]
@param numeric [Numeric] @return [Pattern]
# File lib/xi/pattern/transforms.rb, line 106 def %(numeric) map { |v| v.respond_to?(:%) ? v % numeric : v } end
Performs a scalar multiplication with numeric
For each value from pattern, multiplicate with numeric
. Values that do not respond to *
are ignored.
@example
peek [1, 2, 4].p * 2 #=> [2, 4, 8] peek [1, :foo].p * 2 #=> [2, :foo]
@param numeric [Numeric] @return [Pattern]
# File lib/xi/pattern/transforms.rb, line 74 def *(numeric) map { |v| v.respond_to?(:*) ? v * numeric : v } end
Raises each value to the power of numeric
, which may be negative or fractional.
Values from pattern that do not respond to *
* are ignored.
@example
peek (0..5).p ** 2 #=> [0, 1, 4, 9, 16, 25] peek [1, 2, 3].p ** -2 #=> [1, (1/4), (1/9)]
@param numeric [Numeric] @return [Pattern]
# File lib/xi/pattern/transforms.rb, line 122 def **(numeric) map { |v| v.respond_to?(:**) ? v ** numeric : v } end
Concatenate object
pattern or perform a scalar sum with object
If object
is a Pattern
, concatenate the two patterns. Else, for each value from pattern, sum with object
. Values that do not respond to +
are ignored.
@example Concatenation of patterns
peek [1, 2, 3].p + [4, 5, 6].p #=> [1, 2, 3, 4, 5, 6]
@example Scalar sum
peek [1, 2, 3].p + 60 #=> [61, 62, 63] peek [0.25, 0.5].p + 0.125 #=> [0.375, 0.625] peek [0, :foo, 2].p + 1 #=> [1, :foo, 3]
@param object [Pattern, Numeric] pattern or numeric @return [Pattern]
# File lib/xi/pattern/transforms.rb, line 35 def +(object) if object.is_a?(Pattern) Pattern.new(self, size: size + object.size) { |y, d| each { |v| y << v } object.each { |v| y << v } } else map { |v| v.respond_to?(:+) ? v + object : v } end end
Performs a scalar substraction with numeric
For each value from pattern, substract with numeric
. Values that do not respond to -
are ignored.
@example
peek [1, 2, 3].p - 10 #=> [-9, -8, -7] peek [1, :foo, 3].p - 10 #=> [-9, :foo, -7]
@param numeric [Numeric] @return [Pattern]
# File lib/xi/pattern/transforms.rb, line 58 def -(numeric) map { |v| v.respond_to?(:-) ? v - numeric : v } end
Negates every number in the pattern
Non-numeric values are ignored.
@example
peek -[10, 20, 30].p #=> [-10, -20, -30] peek -[1, -2, 3].p #=> [-1, 2, -3]
@return [Pattern]
# File lib/xi/pattern/transforms.rb, line 14 def -@ map { |v| v.respond_to?(:-@) ? -v : v } end
Performs a scalar division by numeric
For each value from pattern, divide by numeric
. Values that do not respond to #/ are ignored.
@example
peek [1, 2, 4].p / 2 #=> [(1/2), (1/1), (2/1)] peek [0.5, :foo].p / 2 #=> [0.25, :foo]
@param numeric [Numeric] @return [Pattern]
# File lib/xi/pattern/transforms.rb, line 90 def /(numeric) map { |v| v.respond_to?(:/) ? v / numeric : v } end
Traverses the pattern in order and then in reverse order, skipping first and last values if skip_extremes
is true.
@example
peek (0..3).p.bounce #=> [0, 1, 2, 3, 2, 1] peek 10.p.bounce #=> [10]
@example with skip_extremes=false
peek (0..3).p.bounce(false) #=> [0, 1, 2, 3, 3, 2, 1, 0]
@param skip_extremes [Boolean] Skip first and last values
to avoid repeated values (default: true)
@return [Pattern]
# File lib/xi/pattern/transforms.rb, line 192 def bounce(skip_extremes=true) return self if size == 0 || size == 1 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_i) } } end
Scales a pattern of normalized values (0..1) to a custom range min
..max
This is inverse of {#normalize} Values from pattern that do not respond to *
are ignored.
@example
peek [0.01, 0.02, 0.03, 0.04, 0.05].p.denormalize(0, 100) #=> [1.0, 2.0, 3.0, 4.0, 5.0] peek [0, 0.25, 0.50, 0.75].p.denormalize(0, 0x100) #=> [0, 64.0, 128.0, 192.0]
@param min [Numeric] @param max [Numeric] @return [Pattern]
# File lib/xi/pattern/transforms.rb, line 240 def denormalize(min, max) map { |v| v.respond_to?(:*) ? (max - min) * v + min : v } 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]
# File lib/xi/pattern/transforms.rb, line 447 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]
# File lib/xi/pattern/transforms.rb, line 464 def every_iter(n, &block) fn = proc { |_, _, _, i| m = (i + 1) % n m >= 0 && m < 1 } self.when(fn, &block) end
Advance a pattern by shrinking start and duration of events num
times.
It is the inverse operation of slow
@see slow
@example
peek_events %w(a b c d).p([1/2, 1/4]).fast(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]
# File lib/xi/pattern/transforms.rb, line 293 def fast(num) Pattern.new(self, delta: delta.p / num) end
Normalizes a pattern of values that range from min
to max
to 0..1
Values from pattern that do not respond to -
are ignored.
@example
peek (1..5).p.normalize(0, 100) #=> [(1/100), (1/50), (3/100), (1/25), (1/20)] peek [0, 0x40, 0x80, 0xc0].p.normalize(0, 0x100) #=> [(0/1), (1/4), (1/2), (3/4)]
@param min [Numeric] @param max [Numeric] @return [Pattern]
# File lib/xi/pattern/transforms.rb, line 219 def normalize(min, max) map { |v| v.respond_to?(:-) ? (v - min) / (max - min) : v } end
Choose items from the list randomly, repeats
number of times
@see Pattern::Generators::ClassMethods#rand
@example
peek [1, 2, 3].p.rand #=> [2] peek [1, 2, 3, 4].p.rand(6) #=> [1, 3, 2, 2, 4, 3]
@param repeats [Integer, Symbol] number or inf (default: 1) @return [Pattern]
# File lib/xi/pattern/transforms.rb, line 370 def rand(repeats=1) P.rand(self, repeats) end
Repeats each value times
times
can also be an enumerable or a finite Pattern
. In this case, for each value in times
, it will yield each value of original pattern repeated a number of times based on that times
value.
@example
peek [1, 2, 3].p.repeat_each(2) #=> [1, 1, 2, 2, 3, 3] peek [1, 2, 3].p.repeat_each(3) #=> [1, 1, 1, 2, 2, 2, 3, 3, 3]
@example
peek [1, 2, 3].p.repeat_each([3,2]), 15 #=> [1, 1, 1, 2, 2, 2, 3, 3, 3, 1, 1, 2, 2, 3, 3]
@param times [Numeric, each] @return [Pattern]
# File lib/xi/pattern/transforms.rb, line 345 def repeat_each(times) times_pat = times.p if times_pat.infinite? fail ArgumentError, 'times must be finite' end Pattern.new(self, size: size * times_pat.size) do |y| times_pat.each do |t| each { |v| t.times { y << v } } end end end
Scale
from one range of values to another range of values
@example
peek [0,2,4,1,3,6].p.scale(0, 6, 0, 0x7f) #=> [(0/1), (127/3), (254/3), (127/6), (127/2), (127/1)]
@param min_from [Numeric] @param max_from [Numeric] @param min_to [Numeric] @param max_to [Numeric] @return [Pattern]
# File lib/xi/pattern/transforms.rb, line 257 def scale(min_from, max_from, min_to, max_to) normalize(min_from, max_from).denormalize(min_to, max_to) end
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]
@param repeats [Integer] number (defaut: 1) @param offset [Integer] (default: 0) @return [Pattern]
# File lib/xi/pattern/transforms.rb, line 139 def seq(repeats=1, offset=0) unless repeats.is_a?(Integer) && repeats >= 0 fail ArgumentError, "repeats must be a non-negative Integer" end unless offset.is_a?(Integer) && offset >= 0 fail ArgumentError, "offset must be a non-negative Integer" end Pattern.new(self, size: size * repeats) do |y| rep = repeats loop do if rep != inf rep -= 1 break if rep < 0 end c = offset offset_items = [] is_empty = true each do |v| is_empty = false if c > 0 offset_items << v c -= 1 else y << v end end offset_items.each { |v| y << v } break if is_empty end end end
Shuffle the list in random order, and use the same random order repeats
times
@see Pattern::Generators::ClassMethods#shuf
@example
peek [1, 2, 3, 4, 5].p.xrand #=> [4] peek [1, 2, 3].p.xrand(8) #=> [1, 3, 2, 3, 1, 2, 3, 2]
@param repeats [Integer, Symbol] number or inf (default: 1) @return [Pattern]
# File lib/xi/pattern/transforms.rb, line 402 def shuf(repeats=1) P.shuf(self, repeats) end
Slows down a pattern by stretching start and duration of events num
times.
It is the inverse operation of fast
@see fast
@example
peek_events %w(a b c d).p([1/4, 1/8, 1/6]).slow(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]
# File lib/xi/pattern/transforms.rb, line 275 def slow(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 case, for each value in probability
it will enumerate original pattern based on that probability value.
@example
peek (1..6).p.sometimes #=> [1, nil, 3, nil, 5, 6] peek (1..6).p.sometimes(1/4) #=> [nil, nil, nil, 4, nil, 6]
@example
peek (1..6).p.sometimes([0.5, 1]), 12 #=> [1, 2, nil, nil, 5, 6, 1, 2, 3, 4, 5, 6]
@param probability [Numeric, each] (default=0.5) @return [Pattern]
# File lib/xi/pattern/transforms.rb, line 314 def sometimes(probability=0.5) prob_pat = probability.p if prob_pat.infinite? fail ArgumentError, 'times must be finite' end Pattern.new(self, size: size * prob_pat.size) do |y| prob_pat.each do |prob| each { |v| y << (Kernel.rand < prob ? v : nil) } end end 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]
# File lib/xi/pattern/transforms.rb, line 421 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
Choose randomly, but only allow repeating the same item after yielding all items from the list.
@see Pattern::Generators::ClassMethods#xrand
@example
peek [1, 2, 3, 4, 5].p.xrand #=> [4] peek [1, 2, 3].p.xrand(8) #=> [1, 3, 2, 3, 1, 2, 3, 2]
@param repeats [Integer, Symbol] number or inf (default: 1) @return [Pattern]
# File lib/xi/pattern/transforms.rb, line 386 def xrand(repeats=1) P.xrand(self, repeats) end