Module: Cyclotone::Transition

Included in:
Stream
Defined in:
lib/cyclotone/transition.rb

Constant Summary collapse

DEFAULT_DURATION =
2

Instance Method Summary collapse

Instance Method Details

#anticipate(id, pattern) ⇒ Object



102
103
104
# File 'lib/cyclotone/transition.rb', line 102

def anticipate(id, pattern)
  jump_in(id, 8, pattern)
end

#clutch(id, pattern) ⇒ Object



30
31
32
# File 'lib/cyclotone/transition.rb', line 30

def clutch(id, pattern)
  clutch_in(id, DEFAULT_DURATION, pattern)
end

#clutch_in(id, cycles, pattern) ⇒ Object



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/cyclotone/transition.rb', line 34

def clutch_in(id, cycles, pattern)
  slot_id = normalize_slot_reference(id)
  current = pattern_for_transition(slot_id)
  replacement = Pattern.ensure_pattern(pattern)
  duration = Pattern.to_rational(cycles)
  return assign(slot_id, replacement) if duration <= 0

  start_cycle = transition_start_cycle
  swapped = Pattern.new do |span|
    source_events = current.query_span(span).map { |event| [event, :current] }
    target_events = replacement.query_span(span).map { |event| [event, :replacement] }

    (source_events + target_events).filter_map do |event, source|
      time = event.onset || event.part.start
      desired_source = clutch_source(slot_id, time, event.value, start_cycle, duration)
      event if source == desired_source
    end.then { |events| Pattern.sort_events(events) }
  end

  assign_transition(slot_id, swapped, replacement: replacement, finish_cycle: start_cycle + duration)
end

#fade_in(cycles) ⇒ Object



106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/cyclotone/transition.rb', line 106

def fade_in(cycles)
  start_cycle = transition_start_cycle
  duration = Pattern.to_rational(cycles)
  return self if duration <= 0

  @slots.each_key do |slot_id|
    replacement = @slots.fetch(slot_id)
    assign_transition(
      slot_id,
      apply_gain_envelope(replacement, start_cycle: start_cycle, duration: duration, direction: :in),
      replacement: replacement,
      finish_cycle: start_cycle + duration
    )
  end

  self
end

#fade_out(cycles) ⇒ Object



124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/cyclotone/transition.rb', line 124

def fade_out(cycles)
  start_cycle = transition_start_cycle
  duration = Pattern.to_rational(cycles)
  return self if duration <= 0

  @slots.each_key do |slot_id|
    assign_transition(
      slot_id,
      apply_gain_envelope(@slots.fetch(slot_id), start_cycle: start_cycle, duration: duration, direction: :out),
      replacement: Pattern.silence,
      finish_cycle: start_cycle + duration
    )
  end

  self
end

#interpolate(id, pattern) ⇒ Object



56
57
58
# File 'lib/cyclotone/transition.rb', line 56

def interpolate(id, pattern)
  interpolate_in(id, 4, pattern)
end

#interpolate_in(id, cycles, pattern) ⇒ Object



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/cyclotone/transition.rb', line 60

def interpolate_in(id, cycles, pattern)
  slot_id = normalize_slot_reference(id)
  current = pattern_for_transition(slot_id)
  replacement = Pattern.ensure_pattern(pattern)
  duration = Pattern.to_rational(cycles)
  return assign(slot_id, replacement) if duration <= 0

  start_cycle = transition_start_cycle
  morphed = Pattern.new do |span|
    source_events = current.query_span(span)
    target_events = replacement.query_span(span)

    transition_anchor_times(source_events, target_events).filter_map do |time|
      source_event = event_at_time(source_events, time)
      target_event = event_at_time(target_events, time)
      base_event = target_event || source_event
      next unless base_event

      progress = transition_progress(time, start_cycle, duration)
      base_event.with_value(interpolate_value(source_event&.value, target_event&.value, progress))
    end.then { |events| Pattern.sort_events(events) }
  end

  assign_transition(slot_id, morphed, replacement: replacement, finish_cycle: start_cycle + duration)
end

#jump(id, pattern) ⇒ Object



86
87
88
# File 'lib/cyclotone/transition.rb', line 86

def jump(id, pattern)
  assign(normalize_slot_reference(id), pattern)
end

#jump_in(id, cycles, pattern) ⇒ Object



90
91
92
93
94
95
96
97
98
99
100
# File 'lib/cyclotone/transition.rb', line 90

def jump_in(id, cycles, pattern)
  slot_id = normalize_slot_reference(id)
  current = pattern_for_transition(slot_id)
  replacement = Pattern.ensure_pattern(pattern)
  switch_cycle = transition_start_cycle + Pattern.to_rational(cycles)
  return assign(slot_id, replacement) if switch_cycle <= transition_start_cycle

  delayed = Pattern.new { |span| split_query(span, switch_cycle, current, replacement) }

  assign_transition(slot_id, delayed, replacement: replacement, finish_cycle: switch_cycle)
end

#xfade(id, pattern) ⇒ Object



7
8
9
# File 'lib/cyclotone/transition.rb', line 7

def xfade(id, pattern)
  xfade_in(id, DEFAULT_DURATION, pattern)
end

#xfade_in(id, cycles, pattern) ⇒ Object



11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# File 'lib/cyclotone/transition.rb', line 11

def xfade_in(id, cycles, pattern)
  slot_id = normalize_slot_reference(id)
  current = pattern_for_transition(slot_id)
  replacement = Pattern.ensure_pattern(pattern)
  duration = Pattern.to_rational(cycles)
  return assign(slot_id, replacement) if duration <= 0

  start_cycle = transition_start_cycle

  mixed = Pattern.stack(
    [
      apply_gain_envelope(current, start_cycle: start_cycle, duration: duration, direction: :out),
      apply_gain_envelope(replacement, start_cycle: start_cycle, duration: duration, direction: :in)
    ]
  )

  assign_transition(slot_id, mixed, replacement: replacement, finish_cycle: start_cycle + duration)
end