Class: Wavify::DSP::Effects::EffectBase

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

Overview

Base class for sample-by-sample effects with runtime channel state.

Constant Summary collapse

TAIL_CHUNK_FRAMES =
DSP::Processor::TAIL_CHUNK_FRAMES

Instance Method Summary collapse

Instance Method Details

#apply(buffer) ⇒ Wavify::Core::SampleBuffer

Applies the effect to a sample buffer.

Parameters:

Returns:



14
15
16
# File 'lib/wavify/dsp/effects/effect_base.rb', line 14

def apply(buffer)
  DSP::Processor.render(self, buffer)
end

#build_runtimeEffectBase

Builds an independent runtime instance for offline rendering.

Returns:



103
104
105
# File 'lib/wavify/dsp/effects/effect_base.rb', line 103

def build_runtime
  dup.reset
end

#flush(format: nil) ⇒ Enumerator<Wavify::Core::SampleBuffer>?

Emits effect tail after input has ended.

Parameters:

Returns:



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/wavify/dsp/effects/effect_base.rb', line 42

def flush(format: nil)
  return nil unless runtime_prepared?

  frames = tail_frame_count
  return nil if frames.zero?

  target_format = format || Core::Format.new(
    channels: @runtime_channels,
    sample_rate: @runtime_sample_rate,
    bit_depth: 32,
    sample_format: :float
  )
  runtime_format = Core::Format.new(
    channels: @runtime_channels,
    sample_rate: @runtime_sample_rate,
    bit_depth: 32,
    sample_format: :float
  )
  Enumerator.new do |yielder|
    remaining = frames
    while remaining.positive?
      chunk_frames = [remaining, TAIL_CHUNK_FRAMES].min
      silence = Core::SampleBuffer.new(Array.new(chunk_frames * @runtime_channels, 0.0), runtime_format)
      yielder << process(silence).convert(target_format)
      remaining -= chunk_frames
    end
  ensure
    reset
  end
end

#latencyFloat

Returns processing latency in seconds.

Returns:

  • (Float)

    processing latency in seconds



79
80
81
# File 'lib/wavify/dsp/effects/effect_base.rb', line 79

def latency
  0.0
end

#lookaheadFloat

Returns lookahead duration in seconds.

Returns:

  • (Float)

    lookahead duration in seconds



84
85
86
# File 'lib/wavify/dsp/effects/effect_base.rb', line 84

def lookahead
  0.0
end

#process(buffer) ⇒ Core::SampleBuffer

Parameters:

Returns:

Raises:



18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/wavify/dsp/effects/effect_base.rb', line 18

def process(buffer)
  raise InvalidParameterError, "buffer must be Core::SampleBuffer" unless buffer.is_a?(Core::SampleBuffer)

  float_format = buffer.format.with(sample_format: :float, bit_depth: 32)
  float_buffer = buffer.convert(float_format)

  prepare_runtime_if_needed!(
    sample_rate: float_format.sample_rate,
    channels: float_buffer.format.channels
  )

  output = Array.new(float_buffer.samples.length)
  float_buffer.samples.each_with_index do |sample, sample_index|
    channel = sample_index % @runtime_channels
    output[sample_index] = process_sample(sample, channel: channel, sample_rate: @runtime_sample_rate)
  end

  Core::SampleBuffer.new(output, float_format).convert(buffer.format)
end

#process_sample(_sample, channel:, sample_rate:) ⇒ Numeric

Parameters:

  • sample (Numeric)
  • channel: (Integer)
  • sample_rate: (Integer)

Returns:

  • (Numeric)

Raises:

  • (NotImplementedError)


88
89
90
# File 'lib/wavify/dsp/effects/effect_base.rb', line 88

def process_sample(_sample, channel:, sample_rate:)
  raise NotImplementedError
end

#resetEffectBase

Resets runtime state and cached channel/sample-rate information.

Returns:



95
96
97
98
99
100
# File 'lib/wavify/dsp/effects/effect_base.rb', line 95

def reset
  @runtime_sample_rate = nil
  @runtime_channels = nil
  reset_runtime_state
  self
end

#tail_durationFloat

Returns tail duration in seconds emitted by #flush.

Returns:

  • (Float)

    tail duration in seconds emitted by #flush



74
75
76
# File 'lib/wavify/dsp/effects/effect_base.rb', line 74

def tail_duration
  0.0
end