Module: Cyclotone::Oscillators

Defined in:
lib/cyclotone/oscillators.rb

Class Method Summary collapse

Class Method Details

.bipolar(pattern) ⇒ Object



74
75
76
# File 'lib/cyclotone/oscillators.rb', line 74

def bipolar(pattern)
  Pattern.ensure_pattern(pattern).fmap { |value| (value.to_f * 2.0) - 1.0 }
end

.brownian(step: 0.1) ⇒ Object

Raises:

  • (ArgumentError)


92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/cyclotone/oscillators.rb', line 92

def brownian(step: 0.1)
  normalized_step = step.to_f
  raise ArgumentError, "brownian step must be positive" unless normalized_step.positive?

  cache = { -1 => 0.5 }
  highest_cycle = -1

  Pattern.continuous do |time|
    cycle = time.floor

    while highest_cycle < cycle
      next_cycle = highest_cycle + 1
      cache[next_cycle] = brownian_step(cache.fetch(highest_cycle), next_cycle, normalized_step)
      highest_cycle = next_cycle
    end

    cache.fetch(cycle, 0.5)
  end
end

.cosine(freq: 1, phase: 0, bipolar: false) ⇒ Object



13
14
15
16
17
# File 'lib/cyclotone/oscillators.rb', line 13

def cosine(freq: 1, phase: 0, bipolar: false)
  oscillator(freq: freq, phase_offset: phase, bipolar: bipolar) do |time|
    (Math.cos(phase(time)) + 1.0) / 2.0
  end
end

.irand(maximum) ⇒ Object



48
49
50
51
52
# File 'lib/cyclotone/oscillators.rb', line 48

def irand(maximum)
  normalized_maximum = positive_integer(maximum, "irand maximum")

  rand.fmap { |value| (value * normalized_maximum).floor }
end

.isaw(freq: 1, phase: 0, bipolar: false) ⇒ Object



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

def isaw(freq: 1, phase: 0, bipolar: false)
  oscillator(freq: freq, phase_offset: phase, bipolar: bipolar) { |time| 1.0 - cycle_position(time) }
end

.noise(steps: 128) ⇒ Object



78
79
80
# File 'lib/cyclotone/oscillators.rb', line 78

def noise(steps: 128)
  rand(steps: steps)
end

.perlinObject



54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/cyclotone/oscillators.rb', line 54

def perlin
  Pattern.continuous do |time|
    left = time.floor
    right = left + 1
    amount = cycle_position(time)
    smooth = amount * amount * (3.0 - (2.0 * amount))
    left_value = Support::Deterministic.float(:perlin, left)
    right_value = Support::Deterministic.float(:perlin, right)

    left_value + ((right_value - left_value) * smooth)
  end
end

.positive_integer(value, label) ⇒ Object



167
168
169
170
171
172
173
174
# File 'lib/cyclotone/oscillators.rb', line 167

def positive_integer(value, label)
  normalized = Integer(value)
  raise ArgumentError, "#{label} must be positive" unless normalized.positive?

  normalized
rescue ArgumentError, TypeError => error
  raise ArgumentError, "invalid #{label}: #{error.message}"
end

.rand(steps: 128) ⇒ Object



38
39
40
41
42
43
44
45
46
# File 'lib/cyclotone/oscillators.rb', line 38

def rand(steps: 128)
  normalized_steps = positive_integer(steps, "rand steps")

  Pattern.continuous do |time|
    cycle = time.floor
    step = (cycle_position(time) * normalized_steps).floor.to_i
    Support::Deterministic.float(:rand, cycle, step)
  end
end

.range(low, high, pattern, mode: :raw) ⇒ Object



67
68
69
70
71
72
# File 'lib/cyclotone/oscillators.rb', line 67

def range(low, high, pattern, mode: :raw)
  Pattern.ensure_pattern(pattern).fmap do |value|
    normalized_value = normalize_range_value(value.to_f, mode)
    low.to_f + ((high.to_f - low.to_f) * normalized_value)
  end
end

.sample_and_hold(pattern, steps: 8) ⇒ Object



82
83
84
85
86
87
88
89
90
# File 'lib/cyclotone/oscillators.rb', line 82

def sample_and_hold(pattern, steps: 8)
  normalized_steps = positive_integer(steps, "sample_and_hold steps")
  source = Pattern.ensure_pattern(pattern)

  Pattern.continuous do |time|
    sampled_time = Rational((time * normalized_steps).floor, normalized_steps)
    source.query_point(sampled_time)
  end
end

.saw(freq: 1, phase: 0, bipolar: false) ⇒ Object



26
27
28
# File 'lib/cyclotone/oscillators.rb', line 26

def saw(freq: 1, phase: 0, bipolar: false)
  oscillator(freq: freq, phase_offset: phase, bipolar: bipolar) { |time| cycle_position(time) }
end

.sine(freq: 1, phase: 0, bipolar: false) ⇒ Object



7
8
9
10
11
# File 'lib/cyclotone/oscillators.rb', line 7

def sine(freq: 1, phase: 0, bipolar: false)
  oscillator(freq: freq, phase_offset: phase, bipolar: bipolar) do |time|
    (Math.sin(phase(time)) + 1.0) / 2.0
  end
end

.smooth(pattern, interpolator: nil, &block) ⇒ Object



112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/cyclotone/oscillators.rb', line 112

def smooth(pattern, interpolator: nil, &block)
  source = Pattern.ensure_pattern(pattern)
  return source if source.continuous?

  interpolation = block || interpolator
  cache = {}
  Pattern.continuous do |time|
    rational_time = Pattern.to_rational(time)
    cache.fetch(rational_time) do
      cache[rational_time] = interpolate(source, rational_time, interpolation)
      cache.shift if cache.length > 256
      cache[rational_time]
    end
  end
end

.square(freq: 1, phase: 0, bipolar: false) ⇒ Object



34
35
36
# File 'lib/cyclotone/oscillators.rb', line 34

def square(freq: 1, phase: 0, bipolar: false)
  oscillator(freq: freq, phase_offset: phase, bipolar: bipolar) { |time| cycle_position(time) < 0.5 ? 0.0 : 1.0 }
end

.tri(freq: 1, phase: 0, bipolar: false) ⇒ Object



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

def tri(freq: 1, phase: 0, bipolar: false)
  oscillator(freq: freq, phase_offset: phase, bipolar: bipolar) do |time|
    position = cycle_position(time)
    position < 0.5 ? position * 2.0 : (1.0 - position) * 2.0
  end
end