Module: Cyclotone::Transforms::Time

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

Instance Method Summary collapse

Instance Method Details

#early(amount) ⇒ Object



26
27
28
29
30
31
32
33
34
# File 'lib/cyclotone/transforms/time.rb', line 26

def early(amount)
  offset = Pattern.to_rational(amount)

  Pattern.new do |span|
    query_span(span.shift(offset)).map do |event|
      Pattern.shift_event(event, -offset)
    end
  end
end

#fast(amount) ⇒ Object

Raises:

  • (ArgumentError)


6
7
8
9
10
11
12
13
14
15
16
17
# File 'lib/cyclotone/transforms/time.rb', line 6

def fast(amount)
  factor = Pattern.to_rational(amount)
  raise ArgumentError, "fast amount must be positive" unless factor.positive?

  Pattern.new do |span|
    source_span = TimeSpan.new(span.start * factor, span.stop * factor)

    query_span(source_span).map do |event|
      Pattern.map_event(event) { |time| time / factor }
    end
  end
end

#hurry(amount) ⇒ Object



60
61
62
63
64
65
66
67
# File 'lib/cyclotone/transforms/time.rb', line 60

def hurry(amount)
  fast(amount).fmap do |value|
    next value unless value.is_a?(Hash)

    current_speed = value.fetch(:speed, 1.0) || 1.0
    value.merge(speed: current_speed * amount.to_f)
  end
end

#inside(amount, &block) ⇒ Object

Raises:

  • (ArgumentError)


99
100
101
102
103
# File 'lib/cyclotone/transforms/time.rb', line 99

def inside(amount, &block)
  raise ArgumentError, "inside requires a block" unless block

  slow(amount).then(&block).fast(amount)
end

#late(amount) ⇒ Object



36
37
38
# File 'lib/cyclotone/transforms/time.rb', line 36

def late(amount)
  early(-Pattern.to_rational(amount))
end

#off(amount, &block) ⇒ Object

Raises:

  • (ArgumentError)


69
70
71
72
73
74
# File 'lib/cyclotone/transforms/time.rb', line 69

def off(amount, &block)
  raise ArgumentError, "off requires a block" unless block

  transformed = block.call(self).early(amount)
  Pattern.stack([self, transformed])
end

#outside(amount, &block) ⇒ Object

Raises:

  • (ArgumentError)


105
106
107
108
109
# File 'lib/cyclotone/transforms/time.rb', line 105

def outside(amount, &block)
  raise ArgumentError, "outside requires a block" unless block

  fast(amount).then(&block).slow(amount)
end

#palindromeObject



54
55
56
57
58
# File 'lib/cyclotone/transforms/time.rb', line 54

def palindrome
  Pattern.new do |span|
    span.cycle_number.even? ? rev.query_span(span) : query_span(span)
  end
end

#revObject



40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/cyclotone/transforms/time.rb', line 40

def rev
  Pattern.new do |span|
    cycle_start = Rational(span.cycle_number)
    reversed_span = span.reverse_within(cycle_start)

    query_span(reversed_span).map do |event|
      event.with_span(
        new_whole: event.whole&.reverse_within(cycle_start),
        new_part: event.part.reverse_within(cycle_start)
      )
    end
  end
end

#slow(amount) ⇒ Object

Raises:

  • (ArgumentError)


19
20
21
22
23
24
# File 'lib/cyclotone/transforms/time.rb', line 19

def slow(amount)
  factor = Pattern.to_rational(amount)
  raise ArgumentError, "slow amount must be positive" unless factor.positive?

  fast(Rational(1, 1) / factor)
end

#swing(amount, div = 4) ⇒ Object

Raises:

  • (ArgumentError)


76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/cyclotone/transforms/time.rb', line 76

def swing(amount, div = 4)
  normalized_div = Pattern.to_rational(div)
  raise ArgumentError, "swing division must be positive" unless normalized_div.positive?

  step_shift = Pattern.to_rational(amount) / normalized_div

  map_events do |event|
    next event unless event.onset

    cycle_start = Rational(event.onset.floor)
    step_index = ((event.onset - cycle_start) * div).floor.to_i
    next event if step_index.even?

    shifted = Pattern.shift_event(event, step_shift)
    cycle_span = TimeSpan.new(cycle_start, cycle_start + 1)
    clipped_part = shifted.part.intersection(cycle_span)
    next nil unless clipped_part

    clipped_whole = shifted.whole&.intersection(cycle_span) || shifted.whole
    shifted.with_span(part: clipped_part, whole: clipped_whole)
  end
end