Class: Wavify::DSP::Automation

Inherits:
Object
  • Object
show all
Defined in:
lib/wavify/dsp/automation.rb,
sig/dsp.rbs

Overview

Linear automation curve for time-varying parameters.

Defined Under Namespace

Classes: Point

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(points) ⇒ Automation

Returns a new instance of Automation.

Parameters:

  • points (Array[untyped])

Raises:



11
12
13
14
15
16
# File 'lib/wavify/dsp/automation.rb', line 11

def initialize(points)
  normalized = Array(points).map { |point| coerce_point(point) }.sort_by(&:time)
  raise InvalidParameterError, "automation points must not be empty" if normalized.empty?

  @points = normalized.freeze
end

Instance Attribute Details

#pointsArray[[Float, Float]] (readonly)

Returns the value of attribute points.

Returns:

  • (Array[[Float, Float]])


9
10
11
# File 'lib/wavify/dsp/automation.rb', line 9

def points
  @points
end

Instance Method Details

#apply(buffer) {|arg0, arg1, arg2| ... } ⇒ Core::SampleBuffer

Parameters:

Yields:

Yield Parameters:

  • arg0 (Numeric)
  • arg1 (Float)
  • arg2 (Integer)

Yield Returns:

  • (Numeric)

Returns:

Raises:



33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/wavify/dsp/automation.rb', line 33

def apply(buffer)
  raise InvalidParameterError, "buffer must be Core::SampleBuffer" unless buffer.is_a?(Core::SampleBuffer)
  raise InvalidParameterError, "automation apply requires a block" unless block_given?

  format = buffer.format
  point_index = 1
  samples = []
  buffer.samples.each_slice(format.channels).with_index do |frame, frame_index|
    time = frame_index.to_f / format.sample_rate
    point_index += 1 while point_index < @points.length && @points.fetch(point_index).time < time
    value = value_between_points(time, point_index)
    frame.each_with_index do |sample, channel|
      samples << yield(sample, value, time, channel)
    end
  end
  Core::SampleBuffer.new(samples, format)
end

#apply_gain(buffer, unit: :db) ⇒ Core::SampleBuffer

Parameters:

Returns:



51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/wavify/dsp/automation.rb', line 51

def apply_gain(buffer, unit: :db)
  mode = unit.to_sym
  raise InvalidParameterError, "unit must be :db or :linear" unless %i[db linear].include?(mode)

  float_format = buffer.format.with(sample_format: :float, bit_depth: 32)
  processed = apply(buffer.convert(float_format)) do |sample, value, _time, _channel|
    factor = mode == :db ? (10.0**(value / 20.0)) : value
    sample * factor
  end
  processed.convert(buffer.format)
rescue NoMethodError
  raise InvalidParameterError, "unit must be Symbol/String"
end

#value_at(time) ⇒ Float

Parameters:

  • time (Numeric)

Returns:

  • (Float)


18
19
20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/wavify/dsp/automation.rb', line 18

def value_at(time)
  seconds = validate_time!(time)
  return @points.first.value if seconds <= @points.first.time
  return @points.last.value if seconds >= @points.last.time

  right_index = @points.bsearch_index { |point| point.time >= seconds }
  left = @points.fetch(right_index - 1)
  right = @points.fetch(right_index)
  span = right.time - left.time
  return right.value if span.zero?

  ratio = (seconds - left.time) / span
  left.value + ((right.value - left.value) * ratio)
end