Class: Deftones::Effects::FrequencyShifter

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

Constant Summary collapse

DEFAULT_KERNEL_SIZE =
31

Instance Attribute Summary collapse

Attributes inherited from Core::Effect

#wet

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Core::Effect

#multichannel_process?, #normalize_channel_output, #process, #process_effect

Constructor Details

#initialize(frequency: 30.0, context: Deftones.context, **options) ⇒ FrequencyShifter

Returns a new instance of FrequencyShifter.



10
11
12
13
14
15
16
17
# File 'lib/deftones/effect/frequency_shifter.rb', line 10

def initialize(frequency: 30.0, context: Deftones.context, **options)
  super(context: context, **options)
  @frequency = frequency.to_f
  @phase = 0.0
  @kernel = self.class.send(:hilbert_kernel, DEFAULT_KERNEL_SIZE)
  @delay = (@kernel.length / 2.0).floor
  @histories = []
end

Instance Attribute Details

#frequencyObject

Returns the value of attribute frequency.



8
9
10
# File 'lib/deftones/effect/frequency_shifter.rb', line 8

def frequency
  @frequency
end

Class Method Details

.hilbert_kernel(size) ⇒ Object

Raises:

  • (ArgumentError)


74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/deftones/effect/frequency_shifter.rb', line 74

def self.hilbert_kernel(size)
  raise ArgumentError, "kernel size must be odd" if size.even?

  center = size / 2
  Array.new(size) do |index|
    offset = index - center
    next 0.0 if offset.zero? || offset.even?

    coefficient = 2.0 / (Math::PI * offset)
    window = 0.54 - (0.46 * Math.cos((2.0 * Math::PI * index) / (size - 1)))
    coefficient * window
  end
end

Instance Method Details

#advance_phaseObject (private)



59
60
61
# File 'lib/deftones/effect/frequency_shifter.rb', line 59

def advance_phase
  @phase = (@phase + (shift_frequency / context.sample_rate)) % 1.0
end

#analytic_components(sample, channel_index) ⇒ Object (private)



37
38
39
40
41
42
43
44
45
46
# File 'lib/deftones/effect/frequency_shifter.rb', line 37

def analytic_components(sample, channel_index)
  history = @histories[channel_index]
  history.unshift(sample)
  history.pop
  delayed = history[@delay] || 0.0
  quadrature = @kernel.each_with_index.sum(0.0) do |coefficient, index|
    coefficient * (history[index] || 0.0)
  end
  [delayed, quadrature]
end

#ensure_histories(channels) ⇒ Object (private)



67
68
69
70
71
72
# File 'lib/deftones/effect/frequency_shifter.rb', line 67

def ensure_histories(channels)
  required = [channels.to_i, 1].max
  while @histories.length < required
    @histories << Array.new(@kernel.length, 0.0)
  end
end

#process_effect_block(input_block, num_frames, _start_frame, _cache) ⇒ Object (private)



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/deftones/effect/frequency_shifter.rb', line 21

def process_effect_block(input_block, num_frames, _start_frame, _cache)
  ensure_histories(input_block.channels)
  output = Array.new(input_block.channels) { Array.new(num_frames, 0.0) }

  num_frames.times do |index|
    phase = @phase
    input_block.channel_data.each_with_index do |channel, channel_index|
      analytic = analytic_components(channel[index], channel_index)
      output[channel_index][index] = shift_analytic_signal(*analytic, phase)
    end
    advance_phase
  end

  Core::AudioBlock.from_channel_data(output)
end

#shift_analytic_signal(in_phase, quadrature, phase) ⇒ Object (private)



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

def shift_analytic_signal(in_phase, quadrature, phase)
  radians = 2.0 * Math::PI * phase
  cosine = Math.cos(radians)
  sine = Math.sin(radians)
  if @frequency.negative?
    (in_phase * cosine) + (quadrature * sine)
  else
    (in_phase * cosine) - (quadrature * sine)
  end
end

#shift_frequencyObject (private)



63
64
65
# File 'lib/deftones/effect/frequency_shifter.rb', line 63

def shift_frequency
  @frequency.abs
end