Module: Cyclotone::Transforms::Alteration

Included in:
Pattern
Defined in:
lib/cyclotone/transforms/alteration.rb

Constant Summary collapse

MAX_SEGMENTS =
4096

Instance Method Summary collapse

Instance Method Details

#almost_always(&block) ⇒ Object



53
54
55
# File 'lib/cyclotone/transforms/alteration.rb', line 53

def almost_always(&block)
  sometimes_by(0.9, &block)
end

#almost_never(&block) ⇒ Object



57
58
59
# File 'lib/cyclotone/transforms/alteration.rb', line 57

def almost_never(&block)
  sometimes_by(0.1, &block)
end

#chunk(count, &block) ⇒ Object

Raises:

  • (ArgumentError)


61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/cyclotone/transforms/alteration.rb', line 61

def chunk(count, &block)
  normalized_count = validate_segment_count(count, "chunk count")
  raise ArgumentError, "chunk requires a block" unless block

  Pattern.new do |span|
    selected = span.cycle_number % normalized_count
    pieces = Array.new(normalized_count) do |index|
      segment = zoom(Rational(index, normalized_count), Rational(index + 1, normalized_count))
      index == selected ? block.call(segment) : segment
    end

    Pattern.fastcat(pieces).query_span(span)
  end
end

#degradeObject



114
115
116
# File 'lib/cyclotone/transforms/alteration.rb', line 114

def degrade
  degrade_by(0.5)
end

#degrade_by(probability) ⇒ Object



104
105
106
107
108
109
110
111
112
# File 'lib/cyclotone/transforms/alteration.rb', line 104

def degrade_by(probability)
  normalized_probability = validate_probability(probability, "degrade probability")

  select_events do |event|
    cycle = (event.onset || event.part.start).floor
    seed = [:degrade, normalized_probability, cycle, event.value, event.part.start]
    Support::Deterministic.float(seed) >= normalized_probability
  end
end

#every(period, &block) ⇒ Object



8
9
10
# File 'lib/cyclotone/transforms/alteration.rb', line 8

def every(period, &block)
  every_with_offset(period, 0, &block)
end

#every_with_offset(period, offset, &block) ⇒ Object

Raises:

  • (ArgumentError)


12
13
14
15
16
17
18
19
20
21
22
23
24
# File 'lib/cyclotone/transforms/alteration.rb', line 12

def every_with_offset(period, offset, &block)
  normalized_period = validate_positive_rational(period, "every period")
  normalized_offset = Pattern.to_rational(offset)
  raise ArgumentError, "every requires a block" unless block

  Pattern.new do |span|
    if ((span.cycle_number - normalized_offset) % normalized_period).zero?
      block.call(self).query_span(span)
    else
      query_span(span)
    end
  end
end

#fastspread(function, values) ⇒ Object

Raises:

  • (ArgumentError)


180
181
182
183
184
185
# File 'lib/cyclotone/transforms/alteration.rb', line 180

def fastspread(function, values)
  normalized_values = validate_values(values, "fastspread values")
  raise ArgumentError, "fastspread requires a callable function" unless function.respond_to?(:call)

  Pattern.fastcat(normalized_values.map { |value| function.call(self, value) })
end

#fold_every(periods, &block) ⇒ Object



26
27
28
29
30
# File 'lib/cyclotone/transforms/alteration.rb', line 26

def fold_every(periods, &block)
  Array(periods).reduce(self) do |pattern, period|
    pattern.every(period, &block)
  end
end

#iter(count) ⇒ Object



88
89
90
91
92
93
94
# File 'lib/cyclotone/transforms/alteration.rb', line 88

def iter(count)
  normalized_count = validate_segment_count(count, "iter count")

  reorder_segments(normalized_count) do |cycle|
    Array.new(normalized_count) { |index| (index + cycle) % normalized_count }
  end
end

#iter_back(count) ⇒ Object



96
97
98
99
100
101
102
# File 'lib/cyclotone/transforms/alteration.rb', line 96

def iter_back(count)
  normalized_count = validate_segment_count(count, "iter_back count")

  reorder_segments(normalized_count) do |cycle|
    Array.new(normalized_count) { |index| (index - cycle) % normalized_count }
  end
end

#linger(amount) ⇒ Object



139
140
141
# File 'lib/cyclotone/transforms/alteration.rb', line 139

def linger(amount)
  zoom(0, amount)
end

#rarely(&block) ⇒ Object



49
50
51
# File 'lib/cyclotone/transforms/alteration.rb', line 49

def rarely(&block)
  sometimes_by(0.25, &block)
end

#scramble(count) ⇒ Object



76
77
78
79
80
81
82
# File 'lib/cyclotone/transforms/alteration.rb', line 76

def scramble(count)
  normalized_count = validate_segment_count(count, "scramble count")

  reorder_segments(normalized_count) do |cycle|
    Array.new(normalized_count) { |index| index }.shuffle(random: Support::Deterministic.random(:scramble, cycle))
  end
end

#shuffle(count) ⇒ Object



84
85
86
# File 'lib/cyclotone/transforms/alteration.rb', line 84

def shuffle(count)
  scramble(count)
end

#slowstripe(count) ⇒ Object



168
169
170
# File 'lib/cyclotone/transforms/alteration.rb', line 168

def slowstripe(count)
  slow(count)
end

#sometimes(&block) ⇒ Object



32
33
34
# File 'lib/cyclotone/transforms/alteration.rb', line 32

def sometimes(&block)
  sometimes_by(0.5, &block)
end

#sometimes_by(probability, namespace: :sometimes, &block) ⇒ Object

Raises:

  • (ArgumentError)


36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/cyclotone/transforms/alteration.rb', line 36

def sometimes_by(probability, namespace: :sometimes, &block)
  normalized_probability = validate_probability(probability, "sometimes probability")
  raise ArgumentError, "sometimes_by requires a block" unless block

  Pattern.new do |span|
    if Support::Deterministic.float(namespace, normalized_probability, span.cycle_number) < normalized_probability
      block.call(self).query_span(span)
    else
      query_span(span)
    end
  end
end

#spread(function, values) ⇒ Object

Raises:

  • (ArgumentError)


172
173
174
175
176
177
178
# File 'lib/cyclotone/transforms/alteration.rb', line 172

def spread(function, values)
  normalized_values = validate_values(values, "spread values")
  raise ArgumentError, "spread requires a callable function" unless function.respond_to?(:call)

  sequence = Pattern.cat(normalized_values.map { |value| function.call(self, value) })
  Pattern.new { |span| sequence.query_span(span) }
end

#stripe(count) ⇒ Object



164
165
166
# File 'lib/cyclotone/transforms/alteration.rb', line 164

def stripe(count)
  fast(count)
end

#trunc(amount) ⇒ Object

Raises:

  • (ArgumentError)


118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/cyclotone/transforms/alteration.rb', line 118

def trunc(amount)
  limit = Pattern.to_rational(amount)
  raise ArgumentError, "trunc amount must be between 0 and 1" if limit.negative? || limit > 1

  Pattern.new do |span|
    cycle_start = Rational(span.cycle_number)
    allowed = TimeSpan.new(cycle_start, cycle_start + limit)
    overlap = span.intersection(allowed)
    next [] unless overlap

    query_span(overlap).map do |event|
      clipped_part = event.part.intersection(allowed)
      next unless clipped_part

      clipped_whole = event.whole&.intersection(allowed) || event.whole

      event.with_span(new_whole: clipped_whole, new_part: clipped_part)
    end.compact
  end
end

#zoom(start_point, end_point) ⇒ Object

Raises:

  • (ArgumentError)


143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/cyclotone/transforms/alteration.rb', line 143

def zoom(start_point, end_point)
  window_start = Pattern.to_rational(start_point)
  window_end = Pattern.to_rational(end_point)
  window_length = window_end - window_start
  raise ArgumentError, "zoom end must be greater than start" unless window_length.positive?

  Pattern.new do |span|
    cycle_start = Rational(span.cycle_number)
    source_span = TimeSpan.new(
      cycle_start + window_start + ((span.start - cycle_start) * window_length),
      cycle_start + window_start + ((span.stop - cycle_start) * window_length)
    )

    query_span(source_span).map do |event|
      Pattern.map_event(event) do |time|
        cycle_start + ((time - cycle_start - window_start) / window_length)
      end
    end
  end
end