Class: RoadToRubykaigi::Audio::RoundedSquareOscillator

Inherits:
Object
  • Object
show all
Includes:
Phasor
Defined in:
lib/road_to_rubykaigi/audio/oscillator.rb

Constant Summary collapse

DUTY_CYCLE =
{
  d0: 0.125,
  d1: 0.25,
  d2: 0.5,
}
SMOOTH_WIDTH =
0.05

Instance Method Summary collapse

Methods included from Phasor

#gain, #sample_rate

Instance Method Details

#duty_cycle=(duty_cycle) ⇒ Object



143
144
145
# File 'lib/road_to_rubykaigi/audio/oscillator.rb', line 143

def duty_cycle=(duty_cycle)
  @duty_cycle = (DUTY_CYCLE.key?(duty_cycle) ? duty_cycle : :d0)
end

#generate(frequencies:) ⇒ Object



116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/road_to_rubykaigi/audio/oscillator.rb', line 116

def generate(frequencies:)
  samples = frequencies.map do |frequency|
    phase = tick(frequency: frequency)
    off_to_on_end = SMOOTH_WIDTH / 2.0
    on_to_off_start = duty_cycle - SMOOTH_WIDTH / 2.0
    on_to_off_end = duty_cycle + SMOOTH_WIDTH / 2.0

    case phase
    when 0..off_to_on_end
      t = phase / off_to_on_end
      smoothstep_weight = t ** 2 * (3 - 2 * t)
      -1.0 + smoothstep_weight * 2
    when off_to_on_end..on_to_off_start
      1.0
    when on_to_off_start..on_to_off_end
      t = (phase - on_to_off_start) / SMOOTH_WIDTH
      cos_weight = 1 - Math.cos(Math::PI * t)
      1.0 - cos_weight
    else
      # We don't need interpolate off_to_on_start..1
      # because 1 is essentially contiguous with 0.
      -1.0
    end
  end
  samples.sum / samples.size
end