Class: Deftones::Component::Compressor
- Inherits:
-
Deftones::Core::AudioNode
- Object
- Deftones::Core::AudioNode
- Deftones::Component::Compressor
- Defined in:
- lib/deftones/component/compressor.rb
Constant Summary collapse
- DETECTORS =
%i[peak rms].freeze
Instance Attribute Summary collapse
-
#attack ⇒ Object
Returns the value of attribute attack.
-
#detector ⇒ Object
Returns the value of attribute detector.
-
#knee ⇒ Object
Returns the value of attribute knee.
-
#lookahead ⇒ Object
Returns the value of attribute lookahead.
-
#lookahead_samples ⇒ Object
readonly
Returns the value of attribute lookahead_samples.
-
#ratio ⇒ Object
Returns the value of attribute ratio.
-
#release ⇒ Object
Returns the value of attribute release.
-
#rms_window ⇒ Object
Returns the value of attribute rms_window.
-
#threshold ⇒ Object
Returns the value of attribute threshold.
-
#true_peak ⇒ Object
Returns the value of attribute true_peak.
Attributes inherited from Deftones::Core::AudioNode
Instance Method Summary collapse
- #compress(sample, channel_index) ⇒ Object private
- #detector_level(sample, channel_index) ⇒ Object private
- #ensure_channel_state(channels) ⇒ Object private
- #gain_reduction_db(level_db) ⇒ Object private
- #hard_knee_gain_reduction_db(level_db, ratio) ⇒ Object private
-
#initialize(threshold: -18.0,, ratio: 4.0, attack: 0.01, release: 0.1, detector: :peak, knee: 0.0, lookahead: 0.0, rms_window: 0.01, true_peak: false, context: Deftones.context) ⇒ Compressor
constructor
A new instance of Compressor.
- #lookahead_sample(sample, channel_index) ⇒ Object private
- #multichannel_process? ⇒ Boolean
- #normalize_detector(value) ⇒ Object private
- #process(input_block, num_frames, _start_frame, _cache) ⇒ Object
- #rms_level(sample, channel_index) ⇒ Object private
- #smoothing_for(seconds) ⇒ Object private
Methods inherited from Deftones::Core::AudioNode
#>>, #attach_destination, #attach_source, #block_time, #chain, #channel_count, #channel_count_mode, #channel_interpretation, #connect, #connected?, #default_input_channels, #default_output_channels, #destination_for_connection, #detach_all_destinations, #detach_destination, #detach_source, #disconnect, #dispose, #disposed?, #fan, #get, #immediate, #input_for_index, #inputs, #mix_source_blocks, #name, #normalize_connection_index, #normalize_output_block, #now, #number_of_inputs, #number_of_outputs, #output, #output_for_connection, #output_for_index, #outputs, #raise_connection_index_error!, #reaches_node?, #render, #render_block, #sample_time, #set, #to_destination, #to_frequency, #to_master, #to_midi, #to_output, #to_s, #to_seconds, #to_ticks, #uses_legacy_render_for_block?, #validate_connectable!, #validate_connection_index!
Constructor Details
#initialize(threshold: -18.0,, ratio: 4.0, attack: 0.01, release: 0.1, detector: :peak, knee: 0.0, lookahead: 0.0, rms_window: 0.01, true_peak: false, context: Deftones.context) ⇒ Compressor
Returns a new instance of Compressor.
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
# File 'lib/deftones/component/compressor.rb', line 11 def initialize(threshold: -18.0, ratio: 4.0, attack: 0.01, release: 0.1, detector: :peak, knee: 0.0, lookahead: 0.0, rms_window: 0.01, true_peak: false, context: Deftones.context) super(context: context) @gain_db = [] @rms_energy = [] @lookahead_buffers = [] @previous_detector_samples = [] self.threshold = threshold self.ratio = ratio self.attack = attack self.release = release self.detector = detector self.knee = knee self.lookahead = lookahead self.rms_window = rms_window self.true_peak = true_peak end |
Instance Attribute Details
#attack ⇒ Object
Returns the value of attribute attack.
8 9 10 |
# File 'lib/deftones/component/compressor.rb', line 8 def attack @attack end |
#detector ⇒ Object
Returns the value of attribute detector.
8 9 10 |
# File 'lib/deftones/component/compressor.rb', line 8 def detector @detector end |
#knee ⇒ Object
Returns the value of attribute knee.
8 9 10 |
# File 'lib/deftones/component/compressor.rb', line 8 def knee @knee end |
#lookahead ⇒ Object
Returns the value of attribute lookahead.
8 9 10 |
# File 'lib/deftones/component/compressor.rb', line 8 def lookahead @lookahead end |
#lookahead_samples ⇒ Object (readonly)
Returns the value of attribute lookahead_samples.
8 9 10 |
# File 'lib/deftones/component/compressor.rb', line 8 def lookahead_samples @lookahead_samples end |
#ratio ⇒ Object
Returns the value of attribute ratio.
8 9 10 |
# File 'lib/deftones/component/compressor.rb', line 8 def ratio @ratio end |
#release ⇒ Object
Returns the value of attribute release.
8 9 10 |
# File 'lib/deftones/component/compressor.rb', line 8 def release @release end |
#rms_window ⇒ Object
Returns the value of attribute rms_window.
8 9 10 |
# File 'lib/deftones/component/compressor.rb', line 8 def rms_window @rms_window end |
#threshold ⇒ Object
Returns the value of attribute threshold.
8 9 10 |
# File 'lib/deftones/component/compressor.rb', line 8 def threshold @threshold end |
#true_peak ⇒ Object
Returns the value of attribute true_peak.
8 9 10 |
# File 'lib/deftones/component/compressor.rb', line 8 def true_peak @true_peak end |
Instance Method Details
#compress(sample, channel_index) ⇒ Object (private)
86 87 88 89 90 91 92 93 94 95 96 |
# File 'lib/deftones/component/compressor.rb', line 86 def compress(sample, channel_index) level = [detector_level(sample, channel_index), 1.0e-9].max level_db = 20.0 * Math.log10(level) target_gain_db = gain_reduction_db(level_db) current_gain_db = @gain_db[channel_index] smoothing = target_gain_db < current_gain_db ? @attack_smoothing : @release_smoothing current_gain_db += (target_gain_db - current_gain_db) * smoothing @gain_db[channel_index] = current_gain_db lookahead_sample(sample, channel_index) * (10.0**(current_gain_db / 20.0)) end |
#detector_level(sample, channel_index) ⇒ Object (private)
98 99 100 101 102 103 104 105 106 107 108 109 |
# File 'lib/deftones/component/compressor.rb', line 98 def detector_level(sample, channel_index) level = case @detector when :rms then rms_level(sample, channel_index) else sample.abs end return level unless @true_peak previous_sample = @previous_detector_samples[channel_index] || sample @previous_detector_samples[channel_index] = sample [level, sample.abs, previous_sample.abs, ((previous_sample + sample) * 0.5).abs].max end |
#ensure_channel_state(channels) ⇒ Object (private)
149 150 151 152 153 154 155 156 157 158 159 |
# File 'lib/deftones/component/compressor.rb', line 149 def ensure_channel_state(channels) required = [channels.to_i, 1].max @gain_db.fill(0.0, @gain_db.length...required) @rms_energy.fill(0.0, @rms_energy.length...required) @previous_detector_samples.fill(0.0, @previous_detector_samples.length...required) required.times do |channel_index| next if @lookahead_buffers[channel_index]&.length == @lookahead_samples @lookahead_buffers[channel_index] = Array.new(@lookahead_samples, 0.0) end end |
#gain_reduction_db(level_db) ⇒ Object (private)
117 118 119 120 121 122 123 124 125 126 127 128 |
# File 'lib/deftones/component/compressor.rb', line 117 def gain_reduction_db(level_db) ratio = [@ratio, 1.0].max return 0.0 if ratio <= 1.0 return hard_knee_gain_reduction_db(level_db, ratio) if @knee.zero? over_threshold = level_db - @threshold half_knee = @knee * 0.5 return 0.0 if over_threshold <= -half_knee return hard_knee_gain_reduction_db(level_db, ratio) if over_threshold >= half_knee ((1.0 / ratio) - 1.0) * ((over_threshold + half_knee)**2.0) / (2.0 * @knee) end |
#hard_knee_gain_reduction_db(level_db, ratio) ⇒ Object (private)
130 131 132 133 134 135 |
# File 'lib/deftones/component/compressor.rb', line 130 def hard_knee_gain_reduction_db(level_db, ratio) return 0.0 unless level_db > @threshold compressed_db = @threshold + ((level_db - @threshold) / ratio) compressed_db - level_db end |
#lookahead_sample(sample, channel_index) ⇒ Object (private)
137 138 139 140 141 142 143 |
# File 'lib/deftones/component/compressor.rb', line 137 def lookahead_sample(sample, channel_index) return sample if @lookahead_samples.zero? buffer = @lookahead_buffers[channel_index] buffer << sample buffer.shift || 0.0 end |
#multichannel_process? ⇒ Boolean
71 72 73 |
# File 'lib/deftones/component/compressor.rb', line 71 def multichannel_process? true end |
#normalize_detector(value) ⇒ Object (private)
161 162 163 164 165 166 |
# File 'lib/deftones/component/compressor.rb', line 161 def normalize_detector(value) normalized = value.to_sym return normalized if DETECTORS.include?(normalized) raise ArgumentError, "Unsupported compressor detector: #{value}" end |
#process(input_block, num_frames, _start_frame, _cache) ⇒ Object
75 76 77 78 79 80 81 82 |
# File 'lib/deftones/component/compressor.rb', line 75 def process(input_block, num_frames, _start_frame, _cache) ensure_channel_state(input_block.channels) Core::AudioBlock.from_channel_data( input_block.channel_data.each_with_index.map do |channel, channel_index| Array.new(num_frames) { |index| compress(channel[index], channel_index) } end ) end |
#rms_level(sample, channel_index) ⇒ Object (private)
111 112 113 114 115 |
# File 'lib/deftones/component/compressor.rb', line 111 def rms_level(sample, channel_index) energy = @rms_energy[channel_index] @rms_energy[channel_index] = ((1.0 - @rms_smoothing) * energy) + (@rms_smoothing * sample * sample) Math.sqrt(@rms_energy[channel_index]) end |
#smoothing_for(seconds) ⇒ Object (private)
145 146 147 |
# File 'lib/deftones/component/compressor.rb', line 145 def smoothing_for(seconds) 1.0 / [(seconds.to_f * context.sample_rate), 1.0].max end |