deftones 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.yardopts +5 -0
- data/CHANGELOG.md +12 -0
- data/LICENSE.txt +21 -0
- data/README.md +197 -0
- data/Rakefile +8 -0
- data/examples/poly_chord.rb +14 -0
- data/examples/render_sampler.rb +16 -0
- data/examples/render_sequence.rb +13 -0
- data/examples/render_synth.rb +18 -0
- data/lib/deftones/analysis/analyser.rb +162 -0
- data/lib/deftones/analysis/dc_meter.rb +56 -0
- data/lib/deftones/analysis/fft.rb +128 -0
- data/lib/deftones/analysis/meter.rb +83 -0
- data/lib/deftones/analysis/waveform.rb +93 -0
- data/lib/deftones/component/amplitude_envelope.rb +8 -0
- data/lib/deftones/component/biquad_filter.rb +7 -0
- data/lib/deftones/component/channel.rb +109 -0
- data/lib/deftones/component/compressor.rb +64 -0
- data/lib/deftones/component/convolver.rb +99 -0
- data/lib/deftones/component/cross_fade.rb +67 -0
- data/lib/deftones/component/envelope.rb +160 -0
- data/lib/deftones/component/eq3.rb +73 -0
- data/lib/deftones/component/feedback_comb_filter.rb +75 -0
- data/lib/deftones/component/filter.rb +75 -0
- data/lib/deftones/component/follower.rb +55 -0
- data/lib/deftones/component/frequency_envelope.rb +24 -0
- data/lib/deftones/component/gate.rb +46 -0
- data/lib/deftones/component/lfo.rb +88 -0
- data/lib/deftones/component/limiter.rb +11 -0
- data/lib/deftones/component/lowpass_comb_filter.rb +43 -0
- data/lib/deftones/component/merge.rb +45 -0
- data/lib/deftones/component/mid_side_compressor.rb +43 -0
- data/lib/deftones/component/mid_side_merge.rb +53 -0
- data/lib/deftones/component/mid_side_split.rb +54 -0
- data/lib/deftones/component/mono.rb +8 -0
- data/lib/deftones/component/multiband_compressor.rb +70 -0
- data/lib/deftones/component/multiband_split.rb +133 -0
- data/lib/deftones/component/one_pole_filter.rb +71 -0
- data/lib/deftones/component/pan_vol.rb +26 -0
- data/lib/deftones/component/panner.rb +75 -0
- data/lib/deftones/component/panner3d.rb +322 -0
- data/lib/deftones/component/solo.rb +56 -0
- data/lib/deftones/component/split.rb +47 -0
- data/lib/deftones/component/volume.rb +31 -0
- data/lib/deftones/context.rb +213 -0
- data/lib/deftones/core/audio_block.rb +82 -0
- data/lib/deftones/core/audio_node.rb +262 -0
- data/lib/deftones/core/clock.rb +91 -0
- data/lib/deftones/core/computed_signal.rb +69 -0
- data/lib/deftones/core/delay.rb +44 -0
- data/lib/deftones/core/effect.rb +66 -0
- data/lib/deftones/core/emitter.rb +51 -0
- data/lib/deftones/core/gain.rb +39 -0
- data/lib/deftones/core/instrument.rb +109 -0
- data/lib/deftones/core/param.rb +31 -0
- data/lib/deftones/core/signal.rb +452 -0
- data/lib/deftones/core/signal_operator_methods.rb +73 -0
- data/lib/deftones/core/signal_operators.rb +138 -0
- data/lib/deftones/core/signal_shapers.rb +83 -0
- data/lib/deftones/core/source.rb +213 -0
- data/lib/deftones/core/synced_signal.rb +88 -0
- data/lib/deftones/destination.rb +132 -0
- data/lib/deftones/draw.rb +100 -0
- data/lib/deftones/dsp/biquad.rb +129 -0
- data/lib/deftones/dsp/delay_line.rb +41 -0
- data/lib/deftones/dsp/helpers.rb +25 -0
- data/lib/deftones/effect/auto_filter.rb +92 -0
- data/lib/deftones/effect/auto_panner.rb +57 -0
- data/lib/deftones/effect/auto_wah.rb +98 -0
- data/lib/deftones/effect/bit_crusher.rb +38 -0
- data/lib/deftones/effect/chebyshev.rb +36 -0
- data/lib/deftones/effect/chorus.rb +73 -0
- data/lib/deftones/effect/distortion.rb +22 -0
- data/lib/deftones/effect/feedback_delay.rb +38 -0
- data/lib/deftones/effect/freeverb.rb +11 -0
- data/lib/deftones/effect/frequency_shifter.rb +89 -0
- data/lib/deftones/effect/jc_reverb.rb +11 -0
- data/lib/deftones/effect/modulation_control.rb +159 -0
- data/lib/deftones/effect/phaser.rb +72 -0
- data/lib/deftones/effect/ping_pong_delay.rb +40 -0
- data/lib/deftones/effect/pitch_shift.rb +156 -0
- data/lib/deftones/effect/reverb.rb +71 -0
- data/lib/deftones/effect/stereo_widener.rb +34 -0
- data/lib/deftones/effect/tremolo.rb +52 -0
- data/lib/deftones/effect/vibrato.rb +47 -0
- data/lib/deftones/event/callback_behavior.rb +61 -0
- data/lib/deftones/event/loop.rb +53 -0
- data/lib/deftones/event/part.rb +51 -0
- data/lib/deftones/event/pattern.rb +94 -0
- data/lib/deftones/event/sequence.rb +87 -0
- data/lib/deftones/event/tone_event.rb +77 -0
- data/lib/deftones/event/transport.rb +276 -0
- data/lib/deftones/instrument/am_synth.rb +56 -0
- data/lib/deftones/instrument/duo_synth.rb +68 -0
- data/lib/deftones/instrument/fm_synth.rb +60 -0
- data/lib/deftones/instrument/membrane_synth.rb +60 -0
- data/lib/deftones/instrument/metal_synth.rb +61 -0
- data/lib/deftones/instrument/mono_synth.rb +88 -0
- data/lib/deftones/instrument/noise_synth.rb +56 -0
- data/lib/deftones/instrument/pluck_synth.rb +41 -0
- data/lib/deftones/instrument/poly_synth.rb +96 -0
- data/lib/deftones/instrument/sampler.rb +97 -0
- data/lib/deftones/instrument/synth.rb +60 -0
- data/lib/deftones/io/buffer.rb +352 -0
- data/lib/deftones/io/buffers.rb +77 -0
- data/lib/deftones/io/recorder.rb +89 -0
- data/lib/deftones/listener.rb +120 -0
- data/lib/deftones/music/frequency.rb +128 -0
- data/lib/deftones/music/midi.rb +206 -0
- data/lib/deftones/music/note.rb +58 -0
- data/lib/deftones/music/ticks.rb +106 -0
- data/lib/deftones/music/time.rb +209 -0
- data/lib/deftones/music/transport_time.rb +94 -0
- data/lib/deftones/music/unit_helpers.rb +30 -0
- data/lib/deftones/offline_context.rb +46 -0
- data/lib/deftones/portaudio_support.rb +112 -0
- data/lib/deftones/source/am_oscillator.rb +42 -0
- data/lib/deftones/source/fat_oscillator.rb +49 -0
- data/lib/deftones/source/fm_oscillator.rb +47 -0
- data/lib/deftones/source/grain_player.rb +198 -0
- data/lib/deftones/source/karplus_strong.rb +51 -0
- data/lib/deftones/source/noise.rb +99 -0
- data/lib/deftones/source/omni_oscillator.rb +175 -0
- data/lib/deftones/source/oscillator.rb +74 -0
- data/lib/deftones/source/player.rb +228 -0
- data/lib/deftones/source/players.rb +133 -0
- data/lib/deftones/source/pulse_oscillator.rb +38 -0
- data/lib/deftones/source/pwm_oscillator.rb +49 -0
- data/lib/deftones/source/tone_buffer_source.rb +136 -0
- data/lib/deftones/source/tone_oscillator_node.rb +65 -0
- data/lib/deftones/source/user_media.rb +519 -0
- data/lib/deftones/version.rb +5 -0
- data/lib/deftones.rb +542 -0
- metadata +221 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deftones
|
|
4
|
+
module Effects
|
|
5
|
+
class Distortion < Core::Effect
|
|
6
|
+
attr_accessor :amount
|
|
7
|
+
|
|
8
|
+
def initialize(amount: 0.5, **options)
|
|
9
|
+
super(**options)
|
|
10
|
+
@amount = amount.to_f
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def process_effect(input_buffer, _num_frames, _start_frame, _cache, channel_index: 0)
|
|
16
|
+
_ = channel_index
|
|
17
|
+
drive = 1.0 + (@amount * 20.0)
|
|
18
|
+
input_buffer.map { |sample| DSP::Helpers.soft_clip(sample, drive) }
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deftones
|
|
4
|
+
module Effects
|
|
5
|
+
class FeedbackDelay < Core::Effect
|
|
6
|
+
attr_reader :delay_time, :feedback
|
|
7
|
+
|
|
8
|
+
def initialize(delay_time: "8n", feedback: 0.3, max_delay: 2.0, context: Deftones.context, **options)
|
|
9
|
+
super(context: context, **options)
|
|
10
|
+
@delay_time = Core::Signal.new(value: delay_time, units: :time, context: context)
|
|
11
|
+
@feedback = Core::Signal.new(value: feedback, units: :number, context: context)
|
|
12
|
+
@max_delay_samples = (max_delay * context.sample_rate).ceil
|
|
13
|
+
@delay_lines = []
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def process_effect(input_buffer, num_frames, start_frame, _cache, channel_index: 0)
|
|
19
|
+
delays = @delay_time.process(num_frames, start_frame)
|
|
20
|
+
feedbacks = @feedback.process(num_frames, start_frame)
|
|
21
|
+
delay_line = ensure_delay_line(channel_index)
|
|
22
|
+
|
|
23
|
+
Array.new(num_frames) do |index|
|
|
24
|
+
delay_samples = delays[index] * context.sample_rate
|
|
25
|
+
delay_line.tap(delay_samples, input_sample: input_buffer[index], feedback: feedbacks[index])
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def ensure_delay_line(channel_index)
|
|
30
|
+
required = [channel_index.to_i, 0].max
|
|
31
|
+
while @delay_lines.length <= required
|
|
32
|
+
@delay_lines << DSP::DelayLine.new(@max_delay_samples)
|
|
33
|
+
end
|
|
34
|
+
@delay_lines[required]
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deftones
|
|
4
|
+
module Effects
|
|
5
|
+
class FrequencyShifter < Core::Effect
|
|
6
|
+
DEFAULT_KERNEL_SIZE = 31
|
|
7
|
+
|
|
8
|
+
attr_accessor :frequency
|
|
9
|
+
|
|
10
|
+
def initialize(frequency: 30.0, context: Deftones.context, **options)
|
|
11
|
+
super(context: context, **options)
|
|
12
|
+
@frequency = frequency.to_f
|
|
13
|
+
@phase = 0.0
|
|
14
|
+
@kernel = self.class.send(:hilbert_kernel, DEFAULT_KERNEL_SIZE)
|
|
15
|
+
@delay = (@kernel.length / 2.0).floor
|
|
16
|
+
@histories = []
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def process_effect_block(input_block, num_frames, _start_frame, _cache)
|
|
22
|
+
ensure_histories(input_block.channels)
|
|
23
|
+
output = Array.new(input_block.channels) { Array.new(num_frames, 0.0) }
|
|
24
|
+
|
|
25
|
+
num_frames.times do |index|
|
|
26
|
+
phase = @phase
|
|
27
|
+
input_block.channel_data.each_with_index do |channel, channel_index|
|
|
28
|
+
analytic = analytic_components(channel[index], channel_index)
|
|
29
|
+
output[channel_index][index] = shift_analytic_signal(*analytic, phase)
|
|
30
|
+
end
|
|
31
|
+
advance_phase
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
Core::AudioBlock.from_channel_data(output)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def analytic_components(sample, channel_index)
|
|
38
|
+
history = @histories[channel_index]
|
|
39
|
+
history.unshift(sample)
|
|
40
|
+
history.pop
|
|
41
|
+
delayed = history[@delay] || 0.0
|
|
42
|
+
quadrature = @kernel.each_with_index.sum(0.0) do |coefficient, index|
|
|
43
|
+
coefficient * (history[index] || 0.0)
|
|
44
|
+
end
|
|
45
|
+
[delayed, quadrature]
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def shift_analytic_signal(in_phase, quadrature, phase)
|
|
49
|
+
radians = 2.0 * Math::PI * phase
|
|
50
|
+
cosine = Math.cos(radians)
|
|
51
|
+
sine = Math.sin(radians)
|
|
52
|
+
if @frequency.negative?
|
|
53
|
+
(in_phase * cosine) + (quadrature * sine)
|
|
54
|
+
else
|
|
55
|
+
(in_phase * cosine) - (quadrature * sine)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def advance_phase
|
|
60
|
+
@phase = (@phase + (shift_frequency / context.sample_rate)) % 1.0
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def shift_frequency
|
|
64
|
+
@frequency.abs
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def ensure_histories(channels)
|
|
68
|
+
required = [channels.to_i, 1].max
|
|
69
|
+
while @histories.length < required
|
|
70
|
+
@histories << Array.new(@kernel.length, 0.0)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def self.hilbert_kernel(size)
|
|
75
|
+
raise ArgumentError, "kernel size must be odd" if size.even?
|
|
76
|
+
|
|
77
|
+
center = size / 2
|
|
78
|
+
Array.new(size) do |index|
|
|
79
|
+
offset = index - center
|
|
80
|
+
next 0.0 if offset.zero? || offset.even?
|
|
81
|
+
|
|
82
|
+
coefficient = 2.0 / (Math::PI * offset)
|
|
83
|
+
window = 0.54 - (0.46 * Math.cos((2.0 * Math::PI * index) / (size - 1)))
|
|
84
|
+
coefficient * window
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deftones
|
|
4
|
+
module Effects
|
|
5
|
+
module ModulationControl
|
|
6
|
+
def start(time = nil)
|
|
7
|
+
return schedule_modulation_event(:start, time) if synced?
|
|
8
|
+
|
|
9
|
+
@modulation_start_time = resolve_modulation_time(time)
|
|
10
|
+
@modulation_stop_time = nil if @modulation_stop_time && @modulation_stop_time <= @modulation_start_time
|
|
11
|
+
self
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def stop(time = nil)
|
|
15
|
+
return schedule_modulation_event(:stop, time) if synced?
|
|
16
|
+
|
|
17
|
+
@modulation_stop_time = resolve_modulation_time(time)
|
|
18
|
+
self
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def restart(time = nil)
|
|
22
|
+
stop(time)
|
|
23
|
+
start(time)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def cancel_stop
|
|
27
|
+
clear_modulation_event(:stop)
|
|
28
|
+
@modulation_stop_time = nil
|
|
29
|
+
self
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def state(time = context.current_time)
|
|
33
|
+
modulation_active_at?(resolve_modulation_time(time)) ? :started : :stopped
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def sync
|
|
37
|
+
@modulation_synced = true
|
|
38
|
+
self
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def unsync
|
|
42
|
+
@modulation_synced = false
|
|
43
|
+
clear_modulation_event(:start)
|
|
44
|
+
clear_modulation_event(:stop)
|
|
45
|
+
self
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def synced?
|
|
49
|
+
!!@modulation_synced
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def dispose
|
|
53
|
+
unsync
|
|
54
|
+
super
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
alias cancelStop cancel_stop
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
|
|
61
|
+
def initialize_modulation_control
|
|
62
|
+
@modulation_start_time = Float::INFINITY
|
|
63
|
+
@modulation_stop_time = nil
|
|
64
|
+
@modulation_synced = false
|
|
65
|
+
@modulation_transport_event_ids = {}
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def modulation_phase_for(current_time)
|
|
69
|
+
return nil unless modulation_active_at?(current_time)
|
|
70
|
+
|
|
71
|
+
current_phase = @phase
|
|
72
|
+
@phase = (@phase + (modulation_frequency / context.sample_rate)) % 1.0
|
|
73
|
+
current_phase
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def modulation_frequency
|
|
77
|
+
@frequency.to_f
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def modulation_sample_for(phase, type = modulation_type)
|
|
81
|
+
normalized_phase = phase.to_f % 1.0
|
|
82
|
+
|
|
83
|
+
case normalize_modulation_type(type)
|
|
84
|
+
when :square
|
|
85
|
+
normalized_phase < 0.5 ? 1.0 : -1.0
|
|
86
|
+
when :triangle
|
|
87
|
+
normalized_phase < 0.5 ? ((4.0 * normalized_phase) - 1.0) : (3.0 - (4.0 * normalized_phase))
|
|
88
|
+
when :sawtooth
|
|
89
|
+
(2.0 * normalized_phase) - 1.0
|
|
90
|
+
else
|
|
91
|
+
Math.sin(2.0 * Math::PI * normalized_phase)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def unipolar_modulation_value(phase, default: 0.0)
|
|
96
|
+
return default if phase.nil?
|
|
97
|
+
|
|
98
|
+
(modulation_sample_for(phase) + 1.0) * 0.5
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def bipolar_modulation_value(phase, default: 0.0)
|
|
102
|
+
return default if phase.nil?
|
|
103
|
+
|
|
104
|
+
modulation_sample_for(phase)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def modulation_active_at?(time)
|
|
108
|
+
time >= @modulation_start_time && (@modulation_stop_time.nil? || time < @modulation_stop_time)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def resolve_modulation_time(time)
|
|
112
|
+
return context.current_time if time.nil?
|
|
113
|
+
|
|
114
|
+
Deftones::Music::Time.parse(time)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def resolve_modulation_transport_time(time)
|
|
118
|
+
return Deftones.transport.seconds if time.nil?
|
|
119
|
+
|
|
120
|
+
time
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def schedule_modulation_event(kind, time)
|
|
124
|
+
clear_modulation_event(kind)
|
|
125
|
+
@modulation_transport_event_ids[kind] = Deftones.transport.schedule(resolve_modulation_transport_time(time)) do |scheduled_time|
|
|
126
|
+
if kind == :start
|
|
127
|
+
@modulation_start_time = scheduled_time
|
|
128
|
+
@modulation_stop_time = nil if @modulation_stop_time && @modulation_stop_time <= @modulation_start_time
|
|
129
|
+
else
|
|
130
|
+
@modulation_stop_time = scheduled_time
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
self
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def clear_modulation_event(kind)
|
|
137
|
+
event_id = @modulation_transport_event_ids.delete(kind)
|
|
138
|
+
return self unless event_id
|
|
139
|
+
|
|
140
|
+
Deftones.transport.clear(event_id)
|
|
141
|
+
self
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def modulation_type
|
|
145
|
+
return :sine unless respond_to?(:type)
|
|
146
|
+
|
|
147
|
+
normalize_modulation_type(type)
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def normalize_modulation_type(type)
|
|
151
|
+
normalized = type.to_sym
|
|
152
|
+
normalized = :sawtooth if normalized == :ramp
|
|
153
|
+
return normalized if %i[sine square triangle sawtooth].include?(normalized)
|
|
154
|
+
|
|
155
|
+
raise ArgumentError, "Unsupported modulation type: #{type}"
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deftones
|
|
4
|
+
module Effects
|
|
5
|
+
class Phaser < Core::Effect
|
|
6
|
+
include ModulationControl
|
|
7
|
+
|
|
8
|
+
attr_accessor :frequency, :octaves, :q, :base_frequency, :stages, :type
|
|
9
|
+
|
|
10
|
+
def initialize(
|
|
11
|
+
frequency: 0.5,
|
|
12
|
+
octaves: 3.0,
|
|
13
|
+
q: 0.8,
|
|
14
|
+
base_frequency: 300.0,
|
|
15
|
+
stages: 4,
|
|
16
|
+
type: :sine,
|
|
17
|
+
context: Deftones.context,
|
|
18
|
+
**options
|
|
19
|
+
)
|
|
20
|
+
super(context: context, **options)
|
|
21
|
+
@frequency = frequency.to_f
|
|
22
|
+
@octaves = octaves.to_f
|
|
23
|
+
@q = q.to_f
|
|
24
|
+
@base_frequency = base_frequency.to_f
|
|
25
|
+
@stages = stages.to_i
|
|
26
|
+
@type = normalize_modulation_type(type)
|
|
27
|
+
@phase = 0.0
|
|
28
|
+
@filters = []
|
|
29
|
+
initialize_modulation_control
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def stages=(value)
|
|
33
|
+
@stages = [value.to_i, 1].max
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def process_effect_block(input_block, num_frames, start_frame, _cache)
|
|
39
|
+
filter_banks = stage_filters(input_block.channels)
|
|
40
|
+
output = Array.new(input_block.channels) { Array.new(num_frames, 0.0) }
|
|
41
|
+
|
|
42
|
+
num_frames.times do |index|
|
|
43
|
+
current_time = (start_frame + index).to_f / context.sample_rate
|
|
44
|
+
phase = modulation_phase_for(current_time)
|
|
45
|
+
modulation = unipolar_modulation_value(phase, default: 0.5)
|
|
46
|
+
input_block.channel_data.each_with_index do |channel, channel_index|
|
|
47
|
+
output[channel_index][index] = filter_banks[channel_index].each_with_index.reduce(channel[index]) do |sample, (filter, stage_index)|
|
|
48
|
+
stage_position = @stages == 1 ? 0.0 : (stage_index.to_f / (@stages - 1)) - 0.5
|
|
49
|
+
cutoff = @base_frequency * (2.0**((modulation * @octaves) + (stage_position * 0.5)))
|
|
50
|
+
filter.update(type: :notch, frequency: cutoff, q: @q, gain_db: 0.0, sample_rate: context.sample_rate)
|
|
51
|
+
filter.process_sample(sample)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
Core::AudioBlock.from_channel_data(output)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def stage_filters(channels)
|
|
60
|
+
required_channels = [channels.to_i, 1].max
|
|
61
|
+
while @filters.length < required_channels
|
|
62
|
+
@filters << Array.new(@stages) { DSP::Biquad.new }
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
@filters.map! do |bank|
|
|
66
|
+
bank.length == @stages ? bank : Array.new(@stages) { DSP::Biquad.new }
|
|
67
|
+
end
|
|
68
|
+
@filters
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deftones
|
|
4
|
+
module Effects
|
|
5
|
+
class PingPongDelay < FeedbackDelay
|
|
6
|
+
def initialize(max_delay: 2.0, **options)
|
|
7
|
+
super(max_delay: max_delay, **options)
|
|
8
|
+
@left_delay_line = DSP::DelayLine.new(@max_delay_samples)
|
|
9
|
+
@right_delay_line = DSP::DelayLine.new(@max_delay_samples)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
def process_effect_block(input_block, num_frames, start_frame, _cache)
|
|
15
|
+
delays = @delay_time.process(num_frames, start_frame)
|
|
16
|
+
feedbacks = @feedback.process(num_frames, start_frame)
|
|
17
|
+
source = input_block.fit_channels(2)
|
|
18
|
+
left = Array.new(num_frames, 0.0)
|
|
19
|
+
right = Array.new(num_frames, 0.0)
|
|
20
|
+
|
|
21
|
+
num_frames.times do |index|
|
|
22
|
+
delay_samples = delays[index] * context.sample_rate
|
|
23
|
+
feedback = feedbacks[index].to_f.clamp(-0.999, 0.999)
|
|
24
|
+
left_tap = @left_delay_line.read(delay_samples)
|
|
25
|
+
right_tap = @right_delay_line.read(delay_samples)
|
|
26
|
+
input_left = input_block.channels == 1 ? input_block.mono[index] : source.channel_data[0][index]
|
|
27
|
+
input_right = input_block.channels == 1 ? 0.0 : source.channel_data[1][index]
|
|
28
|
+
|
|
29
|
+
@left_delay_line.write(input_left + (right_tap * feedback))
|
|
30
|
+
@right_delay_line.write(input_right + (left_tap * feedback))
|
|
31
|
+
|
|
32
|
+
left[index] = left_tap
|
|
33
|
+
right[index] = right_tap
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
Core::AudioBlock.from_channel_data([left, right])
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deftones
|
|
4
|
+
module Effects
|
|
5
|
+
class PitchShift < Core::Effect
|
|
6
|
+
attr_reader :delay_time, :feedback
|
|
7
|
+
attr_accessor :pitch
|
|
8
|
+
attr_reader :window_size
|
|
9
|
+
|
|
10
|
+
def initialize(
|
|
11
|
+
pitch: nil,
|
|
12
|
+
semitones: 0.0,
|
|
13
|
+
window: 0.1,
|
|
14
|
+
window_size: nil,
|
|
15
|
+
delay_time: nil,
|
|
16
|
+
feedback: 0.0,
|
|
17
|
+
context: Deftones.context,
|
|
18
|
+
**options
|
|
19
|
+
)
|
|
20
|
+
super(context: context, **options)
|
|
21
|
+
@pitch = pitch.nil? ? semitones.to_f : pitch.to_f
|
|
22
|
+
@window_size = resolve_window_size(window_size || window)
|
|
23
|
+
@delay_time = Core::Signal.new(
|
|
24
|
+
value: delay_time.nil? ? default_delay_time : delay_time,
|
|
25
|
+
units: :time,
|
|
26
|
+
context: context
|
|
27
|
+
)
|
|
28
|
+
@feedback = Core::Signal.new(value: feedback, units: :number, context: context)
|
|
29
|
+
@phase = 0.0
|
|
30
|
+
@delay_lines = []
|
|
31
|
+
@max_delay_samples = 0
|
|
32
|
+
ensure_delay_line_capacity!(1, @delay_time.value.to_f, pitch_ratio)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def semitones
|
|
36
|
+
@pitch
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def semitones=(value)
|
|
40
|
+
self.pitch = value
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def pitch=(value)
|
|
44
|
+
@pitch = value.to_f
|
|
45
|
+
ensure_delay_line_capacity!([@delay_lines.length, 1].max, @delay_time.value.to_f, pitch_ratio)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def window_size=(value)
|
|
49
|
+
@window_size = resolve_window_size(value)
|
|
50
|
+
ensure_delay_line_capacity!([@delay_lines.length, 1].max, @delay_time.value.to_f, pitch_ratio)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
alias delayTime delay_time
|
|
54
|
+
alias windowSize window_size
|
|
55
|
+
|
|
56
|
+
def windowSize=(value)
|
|
57
|
+
self.window_size = value
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
private
|
|
61
|
+
|
|
62
|
+
def process_effect_block(input_block, num_frames, start_frame, _cache)
|
|
63
|
+
delays = @delay_time.process(num_frames, start_frame)
|
|
64
|
+
feedbacks = @feedback.process(num_frames, start_frame)
|
|
65
|
+
ratio = pitch_ratio
|
|
66
|
+
ensure_delay_line_capacity!(input_block.channels, delays.max.to_f, ratio)
|
|
67
|
+
output = Array.new(input_block.channels) { Array.new(num_frames, 0.0) }
|
|
68
|
+
|
|
69
|
+
num_frames.times do |index|
|
|
70
|
+
delay_seconds = delays[index].to_f
|
|
71
|
+
feedback = feedbacks[index].to_f.clamp(-0.999, 0.999)
|
|
72
|
+
phase = @phase
|
|
73
|
+
input_block.channel_data.each_with_index do |channel, channel_index|
|
|
74
|
+
sample = shifted_sample(@delay_lines[channel_index], delay_seconds, ratio, phase)
|
|
75
|
+
@delay_lines[channel_index].write(channel[index] + (sample * feedback))
|
|
76
|
+
output[channel_index][index] = sample
|
|
77
|
+
end
|
|
78
|
+
advance_phase
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
Core::AudioBlock.from_channel_data(output)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def shifted_sample(delay_line, delay_seconds, ratio, phase)
|
|
85
|
+
primary_phase = phase
|
|
86
|
+
secondary_phase = (phase + 0.5) % 1.0
|
|
87
|
+
primary = shifted_head_sample(delay_line, primary_phase, delay_seconds, ratio)
|
|
88
|
+
secondary = shifted_head_sample(delay_line, secondary_phase, delay_seconds, ratio)
|
|
89
|
+
(primary * head_gain(primary_phase)) + (secondary * head_gain(secondary_phase))
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def shifted_head_sample(delay_line, phase, delay_seconds, ratio)
|
|
93
|
+
delay_samples = head_delay_samples(phase, delay_seconds, ratio)
|
|
94
|
+
delay_line.read(delay_samples)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def head_delay_samples(phase, delay_seconds, ratio)
|
|
98
|
+
base_delay = [delay_seconds, context.sample_time].max * context.sample_rate
|
|
99
|
+
sweep = sweep_span_samples(ratio)
|
|
100
|
+
offset = if ratio >= 1.0
|
|
101
|
+
(1.0 - phase) * sweep
|
|
102
|
+
else
|
|
103
|
+
phase * sweep
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
[base_delay + offset, 1.0].max
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def head_gain(phase)
|
|
110
|
+
Math.sin(Math::PI * phase)**2
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def advance_phase
|
|
114
|
+
@phase = (@phase + phase_step) % 1.0
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def phase_step
|
|
118
|
+
1.0 / (@window_size * context.sample_rate)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def pitch_ratio
|
|
122
|
+
2.0**(@pitch.to_f / 12.0)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def sweep_span_samples(ratio)
|
|
126
|
+
@window_size * context.sample_rate * (1.0 - ratio).abs
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def required_delay_samples(delay_seconds, ratio)
|
|
130
|
+
(
|
|
131
|
+
([delay_seconds, context.sample_time].max * context.sample_rate) +
|
|
132
|
+
sweep_span_samples(ratio) +
|
|
133
|
+
(@window_size * context.sample_rate)
|
|
134
|
+
).ceil + 2
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def ensure_delay_line_capacity!(channels, delay_seconds, ratio)
|
|
138
|
+
required = [required_delay_samples(delay_seconds, ratio), 2].max
|
|
139
|
+
required_channels = [channels.to_i, 1].max
|
|
140
|
+
needs_resize = required > @max_delay_samples || @delay_lines.length < required_channels
|
|
141
|
+
return unless needs_resize
|
|
142
|
+
|
|
143
|
+
@max_delay_samples = required
|
|
144
|
+
@delay_lines = Array.new(required_channels) { DSP::DelayLine.new(@max_delay_samples) }
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def resolve_window_size(value)
|
|
148
|
+
[value.to_f, context.sample_time * 2.0].max
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def default_delay_time
|
|
152
|
+
@window_size * 0.5
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deftones
|
|
4
|
+
module Effects
|
|
5
|
+
class Reverb < Core::Effect
|
|
6
|
+
attr_accessor :decay, :pre_delay
|
|
7
|
+
|
|
8
|
+
def initialize(decay: 0.7, pre_delay: 0.01, context: Deftones.context, **options)
|
|
9
|
+
super(context: context, **options)
|
|
10
|
+
@decay = decay.to_f
|
|
11
|
+
@pre_delay = pre_delay.to_f
|
|
12
|
+
@comb_times = [0.0297, 0.0371, 0.0411, 0.0437]
|
|
13
|
+
@allpass_times = [0.005, 0.0017]
|
|
14
|
+
@comb_lines = []
|
|
15
|
+
@allpass_lines = []
|
|
16
|
+
@pre_delay_lines = []
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def process_effect_block(input_block, num_frames, start_frame, cache)
|
|
22
|
+
output_channels = [input_block.channels, 2].max
|
|
23
|
+
source = input_block.fit_channels(output_channels)
|
|
24
|
+
|
|
25
|
+
Core::AudioBlock.from_channel_data(
|
|
26
|
+
source.channel_data.each_with_index.map do |channel, channel_index|
|
|
27
|
+
process_effect(channel, num_frames, start_frame, cache, channel_index: channel_index)
|
|
28
|
+
end
|
|
29
|
+
)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def process_effect(input_buffer, num_frames, _start_frame, _cache, channel_index: 0)
|
|
33
|
+
ensure_delay_network(channel_index)
|
|
34
|
+
pre_delay_line = @pre_delay_lines[channel_index]
|
|
35
|
+
comb_lines = @comb_lines[channel_index]
|
|
36
|
+
allpass_lines = @allpass_lines[channel_index]
|
|
37
|
+
comb_times = channel_times(@comb_times, channel_index, 0.00037)
|
|
38
|
+
allpass_times = channel_times(@allpass_times, channel_index, 0.00011)
|
|
39
|
+
|
|
40
|
+
Array.new(num_frames) do |index|
|
|
41
|
+
dry = input_buffer[index]
|
|
42
|
+
delayed = pre_delay_line.tap(@pre_delay * context.sample_rate, input_sample: dry)
|
|
43
|
+
comb_sum = comb_lines.each_with_index.sum do |line, comb_index|
|
|
44
|
+
line.tap(comb_times[comb_index] * context.sample_rate, input_sample: delayed, feedback: @decay)
|
|
45
|
+
end / comb_lines.length.to_f
|
|
46
|
+
|
|
47
|
+
allpass_lines.each_with_index.reduce(comb_sum) do |sample, (line, allpass_index)|
|
|
48
|
+
tap = line.read(allpass_times[allpass_index] * context.sample_rate)
|
|
49
|
+
line.write(sample + (tap * 0.5))
|
|
50
|
+
tap - (sample * 0.5)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def ensure_delay_network(channel_index)
|
|
56
|
+
required = [channel_index.to_i, 0].max
|
|
57
|
+
while @pre_delay_lines.length <= required
|
|
58
|
+
@pre_delay_lines << DSP::DelayLine.new((0.1 * context.sample_rate).ceil)
|
|
59
|
+
@comb_lines << @comb_times.map { |seconds| DSP::DelayLine.new((seconds * context.sample_rate).ceil + 2) }
|
|
60
|
+
@allpass_lines << @allpass_times.map { |seconds| DSP::DelayLine.new((seconds * context.sample_rate).ceil + 2) }
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def channel_times(times, channel_index, offset)
|
|
65
|
+
return times if channel_index.zero?
|
|
66
|
+
|
|
67
|
+
times.map { |time| time + (offset * channel_index) }
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|