Class: Deftones::Effects::Reverb

Inherits:
Core::Effect show all
Defined in:
lib/deftones/effect/reverb.rb

Direct Known Subclasses

Freeverb, JCReverb

Instance Attribute Summary collapse

Attributes inherited from Core::Effect

#wet

Instance Method Summary collapse

Methods inherited from Core::Effect

#multichannel_process?, #normalize_channel_output, #process

Constructor Details

#initialize(decay: 0.7, pre_delay: 0.01, damping: 0.0, freeze: false, wet_normalization: false, width: 1.0, context: Deftones.context, **options) ⇒ Reverb

Returns a new instance of Reverb.



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# File 'lib/deftones/effect/reverb.rb', line 9

def initialize(decay: 0.7, pre_delay: 0.01, damping: 0.0, freeze: false, wet_normalization: false,
               width: 1.0, context: Deftones.context, **options)
  super(context: context, **options)
  @decay = decay.to_f
  @pre_delay = pre_delay.to_f
  self.damping = damping
  self.freeze = freeze
  self.wet_normalization = wet_normalization
  self.width = width
  @comb_times = [0.0297, 0.0371, 0.0411, 0.0437]
  @allpass_times = [0.005, 0.0017]
  @comb_lines = []
  @comb_damping_state = []
  @allpass_lines = []
  @pre_delay_lines = []
end

Instance Attribute Details

#dampingObject Also known as: dampening

Returns the value of attribute damping.



7
8
9
# File 'lib/deftones/effect/reverb.rb', line 7

def damping
  @damping
end

#decayObject

Returns the value of attribute decay.



6
7
8
# File 'lib/deftones/effect/reverb.rb', line 6

def decay
  @decay
end

#freezeObject

Returns the value of attribute freeze.



7
8
9
# File 'lib/deftones/effect/reverb.rb', line 7

def freeze
  @freeze
end

#pre_delayObject

Returns the value of attribute pre_delay.



6
7
8
# File 'lib/deftones/effect/reverb.rb', line 6

def pre_delay
  @pre_delay
end

#wet_normalizationObject Also known as: wetNormalization

Returns the value of attribute wet_normalization.



7
8
9
# File 'lib/deftones/effect/reverb.rb', line 7

def wet_normalization
  @wet_normalization
end

#widthObject

Returns the value of attribute width.



7
8
9
# File 'lib/deftones/effect/reverb.rb', line 7

def width
  @width
end

Instance Method Details

#apply_stereo_width(channel_data) ⇒ Object (private)



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

def apply_stereo_width(channel_data)
  return channel_data if channel_data.length < 2 || @width >= 1.0

  left = channel_data[0]
  right = channel_data[1]
  widened_left = []
  widened_right = []
  left.each_index do |index|
    mid = (left[index] + right[index]) * 0.5
    side = (left[index] - right[index]) * 0.5 * @width
    widened_left << (mid + side)
    widened_right << (mid - side)
  end
  [widened_left, widened_right] + channel_data.drop(2)
end

#channel_times(times, channel_index, offset) ⇒ Object (private)



106
107
108
109
110
# File 'lib/deftones/effect/reverb.rb', line 106

def channel_times(times, channel_index, offset)
  return times if channel_index.zero?

  times.map { |time| time + (offset * channel_index) }
end

#effective_decayObject (private)



128
129
130
# File 'lib/deftones/effect/reverb.rb', line 128

def effective_decay
  @freeze ? 0.995 : @decay.to_f.clamp(0.0, 0.995)
end

#ensure_delay_network(channel_index) ⇒ Object (private)



96
97
98
99
100
101
102
103
104
# File 'lib/deftones/effect/reverb.rb', line 96

def ensure_delay_network(channel_index)
  required = [channel_index.to_i, 0].max
  while @pre_delay_lines.length <= required
    @pre_delay_lines << DSP::DelayLine.new((0.1 * context.sample_rate).ceil)
    @comb_lines << @comb_times.map { |seconds| DSP::DelayLine.new((seconds * context.sample_rate).ceil + 2) }
    @comb_damping_state << Array.new(@comb_times.length, 0.0)
    @allpass_lines << @allpass_times.map { |seconds| DSP::DelayLine.new((seconds * context.sample_rate).ceil + 2) }
  end
end

#normalize_wet_sample(sample, feedback) ⇒ Object (private)



132
133
134
135
136
# File 'lib/deftones/effect/reverb.rb', line 132

def normalize_wet_sample(sample, feedback)
  return sample unless @wet_normalization

  sample * (1.0 - ([feedback.abs, 0.95].min * 0.35))
end

#process_comb(line, delay_samples, input_sample, feedback, channel_index, comb_index) ⇒ Object (private)



87
88
89
90
91
92
93
94
# File 'lib/deftones/effect/reverb.rb', line 87

def process_comb(line, delay_samples, input_sample, feedback, channel_index, comb_index)
  delayed_sample = line.read(delay_samples)
  previous = @comb_damping_state[channel_index][comb_index]
  filtered = DSP::Helpers.lerp(delayed_sample, previous, @damping)
  @comb_damping_state[channel_index][comb_index] = filtered
  line.write(input_sample + (filtered * feedback))
  filtered
end

#process_effect(input_buffer, num_frames, _start_frame, _cache, channel_index: 0) ⇒ Object (private)



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
85
# File 'lib/deftones/effect/reverb.rb', line 60

def process_effect(input_buffer, num_frames, _start_frame, _cache, channel_index: 0)
  ensure_delay_network(channel_index)
  pre_delay_line = @pre_delay_lines[channel_index]
  comb_lines = @comb_lines[channel_index]
  allpass_lines = @allpass_lines[channel_index]
  comb_times = channel_times(@comb_times, channel_index, 0.00037)
  allpass_times = channel_times(@allpass_times, channel_index, 0.00011)

  Array.new(num_frames) do |index|
    dry = input_buffer[index]
    delayed = pre_delay_line.tap(@pre_delay * context.sample_rate, input_sample: dry)
    feedback = effective_decay
    comb_input = @freeze ? 0.0 : delayed
    comb_sum = comb_lines.each_with_index.sum do |line, comb_index|
      process_comb(line, comb_times[comb_index] * context.sample_rate, comb_input, feedback, channel_index,
                   comb_index)
    end / comb_lines.length.to_f

    wet_sample = allpass_lines.each_with_index.reduce(comb_sum) do |sample, (line, allpass_index)|
      tap = line.read(allpass_times[allpass_index] * context.sample_rate)
      line.write(sample + (tap * 0.5))
      tap - (sample * 0.5)
    end
    normalize_wet_sample(wet_sample, feedback)
  end
end

#process_effect_block(input_block, num_frames, start_frame, cache) ⇒ Object (private)



49
50
51
52
53
54
55
56
57
58
# File 'lib/deftones/effect/reverb.rb', line 49

def process_effect_block(input_block, num_frames, start_frame, cache)
  output_channels = [input_block.channels, 2].max
  source = input_block.fit_channels(output_channels)

  Core::AudioBlock.from_channel_data(
    apply_stereo_width(source.channel_data.each_with_index.map do |channel, channel_index|
      process_effect(channel, num_frames, start_frame, cache, channel_index: channel_index)
    end)
  )
end