Class: Deftones::Effects::PitchShift
- Inherits:
-
Core::Effect
- Object
- Core::Effect
- Deftones::Effects::PitchShift
- Defined in:
- lib/deftones/effect/pitch_shift.rb
Instance Attribute Summary collapse
-
#delay_time ⇒ Object
(also: #delayTime)
readonly
Returns the value of attribute delay_time.
-
#feedback ⇒ Object
readonly
Returns the value of attribute feedback.
-
#pitch ⇒ Object
Returns the value of attribute pitch.
-
#window_size ⇒ Object
(also: #windowSize)
Returns the value of attribute window_size.
Attributes inherited from Core::Effect
Instance Method Summary collapse
- #advance_phase ⇒ Object private
- #default_delay_time ⇒ Object private
- #ensure_delay_line_capacity!(channels, delay_seconds, ratio) ⇒ Object private
- #head_delay_samples(phase, delay_seconds, ratio) ⇒ Object private
- #head_gain(phase) ⇒ Object private
-
#initialize(pitch: nil, semitones: 0.0, window: 0.1, window_size: nil, delay_time: nil, feedback: 0.0, context: Deftones.context, **options) ⇒ PitchShift
constructor
A new instance of PitchShift.
- #phase_step ⇒ Object private
- #pitch_ratio ⇒ Object private
- #process_effect_block(input_block, num_frames, start_frame, _cache) ⇒ Object private
- #required_delay_samples(delay_seconds, ratio) ⇒ Object private
- #resolve_window_size(value) ⇒ Object private
- #semitones ⇒ Object
- #semitones=(value) ⇒ Object
- #shifted_head_sample(delay_line, phase, delay_seconds, ratio) ⇒ Object private
- #shifted_sample(delay_line, delay_seconds, ratio, phase) ⇒ Object private
- #sweep_span_samples(ratio) ⇒ Object private
- #windowSize=(value) ⇒ Object
Methods inherited from Core::Effect
#multichannel_process?, #normalize_channel_output, #process, #process_effect
Constructor Details
#initialize(pitch: nil, semitones: 0.0, window: 0.1, window_size: nil, delay_time: nil, feedback: 0.0, context: Deftones.context, **options) ⇒ PitchShift
Returns a new instance of PitchShift.
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
# File 'lib/deftones/effect/pitch_shift.rb', line 9 def initialize( pitch: nil, semitones: 0.0, window: 0.1, window_size: nil, delay_time: nil, feedback: 0.0, context: Deftones.context, ** ) super(context: context, **) @pitch = pitch.nil? ? semitones.to_f : pitch.to_f @window_size = resolve_window_size(window_size || window) @delay_time = Core::Signal.new( value: delay_time.nil? ? default_delay_time : delay_time, units: :time, context: context ) @feedback = Core::Signal.new(value: feedback, units: :number, context: context) @phase = 0.0 @delay_lines = [] @max_delay_samples = 0 ensure_delay_line_capacity!(1, @delay_time.value.to_f, pitch_ratio) end |
Instance Attribute Details
#delay_time ⇒ Object (readonly) Also known as: delayTime
Returns the value of attribute delay_time.
6 7 8 |
# File 'lib/deftones/effect/pitch_shift.rb', line 6 def delay_time @delay_time end |
#feedback ⇒ Object (readonly)
Returns the value of attribute feedback.
6 7 8 |
# File 'lib/deftones/effect/pitch_shift.rb', line 6 def feedback @feedback end |
#pitch ⇒ Object
Returns the value of attribute pitch.
6 7 8 |
# File 'lib/deftones/effect/pitch_shift.rb', line 6 def pitch @pitch end |
#window_size ⇒ Object Also known as: windowSize
Returns the value of attribute window_size.
7 8 9 |
# File 'lib/deftones/effect/pitch_shift.rb', line 7 def window_size @window_size end |
Instance Method Details
#advance_phase ⇒ Object (private)
112 113 114 |
# File 'lib/deftones/effect/pitch_shift.rb', line 112 def advance_phase @phase = (@phase + phase_step) % 1.0 end |
#default_delay_time ⇒ Object (private)
150 151 152 |
# File 'lib/deftones/effect/pitch_shift.rb', line 150 def default_delay_time @window_size * 0.5 end |
#ensure_delay_line_capacity!(channels, delay_seconds, ratio) ⇒ Object (private)
136 137 138 139 140 141 142 143 144 |
# File 'lib/deftones/effect/pitch_shift.rb', line 136 def ensure_delay_line_capacity!(channels, delay_seconds, ratio) required = [required_delay_samples(delay_seconds, ratio), 2].max required_channels = [channels.to_i, 1].max needs_resize = required > @max_delay_samples || @delay_lines.length < required_channels return unless needs_resize @max_delay_samples = required @delay_lines = Array.new(required_channels) { DSP::DelayLine.new(@max_delay_samples) } end |
#head_delay_samples(phase, delay_seconds, ratio) ⇒ Object (private)
96 97 98 99 100 101 102 103 104 105 106 |
# File 'lib/deftones/effect/pitch_shift.rb', line 96 def head_delay_samples(phase, delay_seconds, ratio) base_delay = [delay_seconds, context.sample_time].max * context.sample_rate sweep = sweep_span_samples(ratio) offset = if ratio >= 1.0 (1.0 - phase) * sweep else phase * sweep end [base_delay + offset, 1.0].max end |
#head_gain(phase) ⇒ Object (private)
108 109 110 |
# File 'lib/deftones/effect/pitch_shift.rb', line 108 def head_gain(phase) Math.sin(Math::PI * phase)**2 end |
#phase_step ⇒ Object (private)
116 117 118 |
# File 'lib/deftones/effect/pitch_shift.rb', line 116 def phase_step 1.0 / (@window_size * context.sample_rate) end |
#pitch_ratio ⇒ Object (private)
120 121 122 |
# File 'lib/deftones/effect/pitch_shift.rb', line 120 def pitch_ratio 2.0**(@pitch.to_f / 12.0) end |
#process_effect_block(input_block, num_frames, start_frame, _cache) ⇒ Object (private)
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
# File 'lib/deftones/effect/pitch_shift.rb', line 61 def process_effect_block(input_block, num_frames, start_frame, _cache) delays = @delay_time.process(num_frames, start_frame) feedbacks = @feedback.process(num_frames, start_frame) ratio = pitch_ratio ensure_delay_line_capacity!(input_block.channels, delays.max.to_f, ratio) output = Array.new(input_block.channels) { Array.new(num_frames, 0.0) } num_frames.times do |index| delay_seconds = delays[index].to_f feedback = feedbacks[index].to_f.clamp(-0.999, 0.999) phase = @phase input_block.channel_data.each_with_index do |channel, channel_index| sample = shifted_sample(@delay_lines[channel_index], delay_seconds, ratio, phase) @delay_lines[channel_index].write(channel[index] + (sample * feedback)) output[channel_index][index] = sample end advance_phase end Core::AudioBlock.from_channel_data(output) end |
#required_delay_samples(delay_seconds, ratio) ⇒ Object (private)
128 129 130 131 132 133 134 |
# File 'lib/deftones/effect/pitch_shift.rb', line 128 def required_delay_samples(delay_seconds, ratio) ( ([delay_seconds, context.sample_time].max * context.sample_rate) + sweep_span_samples(ratio) + (@window_size * context.sample_rate) ).ceil + 2 end |
#resolve_window_size(value) ⇒ Object (private)
146 147 148 |
# File 'lib/deftones/effect/pitch_shift.rb', line 146 def resolve_window_size(value) [value.to_f, context.sample_time * 2.0].max end |
#semitones ⇒ Object
34 35 36 |
# File 'lib/deftones/effect/pitch_shift.rb', line 34 def semitones @pitch end |
#semitones=(value) ⇒ Object
38 39 40 |
# File 'lib/deftones/effect/pitch_shift.rb', line 38 def semitones=(value) self.pitch = value end |
#shifted_head_sample(delay_line, phase, delay_seconds, ratio) ⇒ Object (private)
91 92 93 94 |
# File 'lib/deftones/effect/pitch_shift.rb', line 91 def shifted_head_sample(delay_line, phase, delay_seconds, ratio) delay_samples = head_delay_samples(phase, delay_seconds, ratio) delay_line.read(delay_samples) end |
#shifted_sample(delay_line, delay_seconds, ratio, phase) ⇒ Object (private)
83 84 85 86 87 88 89 |
# File 'lib/deftones/effect/pitch_shift.rb', line 83 def shifted_sample(delay_line, delay_seconds, ratio, phase) primary_phase = phase secondary_phase = (phase + 0.5) % 1.0 primary = shifted_head_sample(delay_line, primary_phase, delay_seconds, ratio) secondary = shifted_head_sample(delay_line, secondary_phase, delay_seconds, ratio) (primary * head_gain(primary_phase)) + (secondary * head_gain(secondary_phase)) end |
#sweep_span_samples(ratio) ⇒ Object (private)
124 125 126 |
# File 'lib/deftones/effect/pitch_shift.rb', line 124 def sweep_span_samples(ratio) @window_size * context.sample_rate * (1.0 - ratio).abs end |
#windowSize=(value) ⇒ Object
55 56 57 |
# File 'lib/deftones/effect/pitch_shift.rb', line 55 def windowSize=(value) self.window_size = value end |