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,100 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deftones
|
|
4
|
+
class Draw
|
|
5
|
+
class << self
|
|
6
|
+
def instance
|
|
7
|
+
@instance ||= new
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def reset!
|
|
11
|
+
@instance = nil
|
|
12
|
+
self
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def method_missing(method_name, *arguments, &block)
|
|
16
|
+
return super unless instance.respond_to?(method_name)
|
|
17
|
+
|
|
18
|
+
instance.public_send(method_name, *arguments, &block)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def respond_to_missing?(method_name, include_private = false)
|
|
22
|
+
instance.respond_to?(method_name, include_private) || super
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def initialize
|
|
27
|
+
@timeline = {}
|
|
28
|
+
@next_id = 0
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def schedule(callback_or_time = nil, maybe_time = nil, &block)
|
|
32
|
+
callback, time = resolve_schedule_arguments(callback_or_time, maybe_time, block)
|
|
33
|
+
event_id = @next_id
|
|
34
|
+
@timeline[event_id] = { time: resolve_time(time), callback: callback }
|
|
35
|
+
@next_id += 1
|
|
36
|
+
event_id
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def cancel(after_time = 0, event_id: nil)
|
|
40
|
+
if event_id.nil? && after_time.is_a?(Integer) && @timeline.key?(after_time)
|
|
41
|
+
return @timeline.delete(after_time)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
return @timeline.delete(event_id) if event_id
|
|
45
|
+
|
|
46
|
+
threshold = resolve_time(after_time)
|
|
47
|
+
@timeline.delete_if { |_id, event| event[:time] >= threshold }
|
|
48
|
+
self
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def dispose
|
|
52
|
+
@timeline.clear
|
|
53
|
+
@next_id = 0
|
|
54
|
+
self
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def prepare_render(duration)
|
|
58
|
+
flush_until(duration)
|
|
59
|
+
self
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def advance_to(time)
|
|
63
|
+
flush_until(time)
|
|
64
|
+
self
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
private
|
|
68
|
+
|
|
69
|
+
def resolve_schedule_arguments(callback_or_time, maybe_time, block)
|
|
70
|
+
if callback_or_time.respond_to?(:call)
|
|
71
|
+
[callback_or_time, maybe_time]
|
|
72
|
+
else
|
|
73
|
+
[block, callback_or_time]
|
|
74
|
+
end.tap do |callback, _time|
|
|
75
|
+
raise ArgumentError, "callback is required" unless callback
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def resolve_time(value)
|
|
80
|
+
Deftones::Music::Time.parse(value || 0)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def due_events(limit)
|
|
84
|
+
events = @timeline.values.select { |event| event[:time] <= limit }
|
|
85
|
+
events.sort_by { |event| event[:time] }
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def flush_until(time)
|
|
89
|
+
limit = resolve_time(time)
|
|
90
|
+
due_events(limit).each do |event|
|
|
91
|
+
invoke(event[:callback], event[:time])
|
|
92
|
+
end
|
|
93
|
+
@timeline.delete_if { |_id, event| event[:time] <= limit }
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def invoke(callback, time)
|
|
97
|
+
callback.arity.zero? ? callback.call : callback.call(time)
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deftones
|
|
4
|
+
module DSP
|
|
5
|
+
class Biquad
|
|
6
|
+
TYPES = %i[lowpass highpass bandpass notch allpass peaking lowshelf highshelf].freeze
|
|
7
|
+
|
|
8
|
+
def initialize
|
|
9
|
+
reset!
|
|
10
|
+
@coefficients = [1.0, 0.0, 0.0, 0.0, 0.0]
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def update(type:, frequency:, q:, gain_db:, sample_rate:)
|
|
14
|
+
raise ArgumentError, "Unsupported filter type: #{type}" unless TYPES.include?(type)
|
|
15
|
+
|
|
16
|
+
normalized_frequency = Helpers.clamp(frequency.to_f, 10.0, (sample_rate / 2.0) - 10.0)
|
|
17
|
+
omega = (2.0 * Math::PI * normalized_frequency) / sample_rate
|
|
18
|
+
sin_omega = Math.sin(omega)
|
|
19
|
+
cos_omega = Math.cos(omega)
|
|
20
|
+
alpha = sin_omega / (2.0 * [q.to_f, 0.001].max)
|
|
21
|
+
a = 10.0**(gain_db.to_f / 40.0)
|
|
22
|
+
|
|
23
|
+
b0, b1, b2, a0, a1, a2 =
|
|
24
|
+
case type
|
|
25
|
+
when :lowpass
|
|
26
|
+
lowpass_coefficients(cos_omega, alpha)
|
|
27
|
+
when :highpass
|
|
28
|
+
highpass_coefficients(cos_omega, alpha)
|
|
29
|
+
when :bandpass
|
|
30
|
+
[alpha, 0.0, -alpha, 1.0 + alpha, -2.0 * cos_omega, 1.0 - alpha]
|
|
31
|
+
when :notch
|
|
32
|
+
[1.0, -2.0 * cos_omega, 1.0, 1.0 + alpha, -2.0 * cos_omega, 1.0 - alpha]
|
|
33
|
+
when :allpass
|
|
34
|
+
[1.0 - alpha, -2.0 * cos_omega, 1.0 + alpha, 1.0 + alpha, -2.0 * cos_omega, 1.0 - alpha]
|
|
35
|
+
when :peaking
|
|
36
|
+
peaking_coefficients(cos_omega, alpha, a)
|
|
37
|
+
when :lowshelf
|
|
38
|
+
shelf_coefficients(:low, cos_omega, sin_omega, a)
|
|
39
|
+
when :highshelf
|
|
40
|
+
shelf_coefficients(:high, cos_omega, sin_omega, a)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
@coefficients = normalize(b0, b1, b2, a0, a1, a2)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def process_sample(sample)
|
|
47
|
+
b0, b1, b2, a1, a2 = @coefficients
|
|
48
|
+
output = (b0 * sample) + (b1 * @x1) + (b2 * @x2) - (a1 * @y1) - (a2 * @y2)
|
|
49
|
+
@x2 = @x1
|
|
50
|
+
@x1 = sample
|
|
51
|
+
@y2 = @y1
|
|
52
|
+
@y1 = output
|
|
53
|
+
output
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def reset!
|
|
57
|
+
@x1 = 0.0
|
|
58
|
+
@x2 = 0.0
|
|
59
|
+
@y1 = 0.0
|
|
60
|
+
@y2 = 0.0
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
private
|
|
64
|
+
|
|
65
|
+
def lowpass_coefficients(cos_omega, alpha)
|
|
66
|
+
[
|
|
67
|
+
(1.0 - cos_omega) / 2.0,
|
|
68
|
+
1.0 - cos_omega,
|
|
69
|
+
(1.0 - cos_omega) / 2.0,
|
|
70
|
+
1.0 + alpha,
|
|
71
|
+
-2.0 * cos_omega,
|
|
72
|
+
1.0 - alpha
|
|
73
|
+
]
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def highpass_coefficients(cos_omega, alpha)
|
|
77
|
+
[
|
|
78
|
+
(1.0 + cos_omega) / 2.0,
|
|
79
|
+
-(1.0 + cos_omega),
|
|
80
|
+
(1.0 + cos_omega) / 2.0,
|
|
81
|
+
1.0 + alpha,
|
|
82
|
+
-2.0 * cos_omega,
|
|
83
|
+
1.0 - alpha
|
|
84
|
+
]
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def peaking_coefficients(cos_omega, alpha, amplitude)
|
|
88
|
+
[
|
|
89
|
+
1.0 + (alpha * amplitude),
|
|
90
|
+
-2.0 * cos_omega,
|
|
91
|
+
1.0 - (alpha * amplitude),
|
|
92
|
+
1.0 + (alpha / amplitude),
|
|
93
|
+
-2.0 * cos_omega,
|
|
94
|
+
1.0 - (alpha / amplitude)
|
|
95
|
+
]
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def shelf_coefficients(kind, cos_omega, sin_omega, amplitude)
|
|
99
|
+
sqrt_a = Math.sqrt(amplitude)
|
|
100
|
+
alpha = sin_omega / Math.sqrt(2.0)
|
|
101
|
+
common = 2.0 * sqrt_a * alpha
|
|
102
|
+
|
|
103
|
+
if kind == :low
|
|
104
|
+
[
|
|
105
|
+
amplitude * ((amplitude + 1.0) - ((amplitude - 1.0) * cos_omega) + common),
|
|
106
|
+
2.0 * amplitude * ((amplitude - 1.0) - ((amplitude + 1.0) * cos_omega)),
|
|
107
|
+
amplitude * ((amplitude + 1.0) - ((amplitude - 1.0) * cos_omega) - common),
|
|
108
|
+
(amplitude + 1.0) + ((amplitude - 1.0) * cos_omega) + common,
|
|
109
|
+
-2.0 * ((amplitude - 1.0) + ((amplitude + 1.0) * cos_omega)),
|
|
110
|
+
(amplitude + 1.0) + ((amplitude - 1.0) * cos_omega) - common
|
|
111
|
+
]
|
|
112
|
+
else
|
|
113
|
+
[
|
|
114
|
+
amplitude * ((amplitude + 1.0) + ((amplitude - 1.0) * cos_omega) + common),
|
|
115
|
+
-2.0 * amplitude * ((amplitude - 1.0) + ((amplitude + 1.0) * cos_omega)),
|
|
116
|
+
amplitude * ((amplitude + 1.0) + ((amplitude - 1.0) * cos_omega) - common),
|
|
117
|
+
(amplitude + 1.0) - ((amplitude - 1.0) * cos_omega) + common,
|
|
118
|
+
2.0 * ((amplitude - 1.0) - ((amplitude + 1.0) * cos_omega)),
|
|
119
|
+
(amplitude + 1.0) - ((amplitude - 1.0) * cos_omega) - common
|
|
120
|
+
]
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def normalize(b0, b1, b2, a0, a1, a2)
|
|
125
|
+
[b0 / a0, b1 / a0, b2 / a0, a1 / a0, a2 / a0]
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deftones
|
|
4
|
+
module DSP
|
|
5
|
+
class DelayLine
|
|
6
|
+
def initialize(max_delay_samples)
|
|
7
|
+
@buffer = Array.new([max_delay_samples.to_i + 2, 2].max, 0.0)
|
|
8
|
+
@write_index = 0
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def read(delay_samples)
|
|
12
|
+
fractional_read(delay_samples.to_f)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def write(sample)
|
|
16
|
+
@buffer[@write_index] = sample
|
|
17
|
+
@write_index = (@write_index + 1) % @buffer.length
|
|
18
|
+
sample
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def tap(delay_samples, input_sample: 0.0, feedback: 0.0)
|
|
22
|
+
delayed_sample = read(delay_samples)
|
|
23
|
+
write(input_sample + (delayed_sample * feedback))
|
|
24
|
+
delayed_sample
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def fractional_read(delay_samples)
|
|
30
|
+
read_position = @write_index - delay_samples
|
|
31
|
+
read_position += @buffer.length while read_position.negative?
|
|
32
|
+
|
|
33
|
+
base_index = read_position.floor % @buffer.length
|
|
34
|
+
next_index = (base_index + 1) % @buffer.length
|
|
35
|
+
fraction = read_position - read_position.floor
|
|
36
|
+
|
|
37
|
+
Helpers.lerp(@buffer[base_index], @buffer[next_index], fraction)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deftones
|
|
4
|
+
module DSP
|
|
5
|
+
module Helpers
|
|
6
|
+
module_function
|
|
7
|
+
|
|
8
|
+
def clamp(value, min_value, max_value)
|
|
9
|
+
[[value, min_value].max, max_value].min
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def lerp(from, to, progress)
|
|
13
|
+
from + ((to - from) * progress)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def mix(dry, wet, wet_amount)
|
|
17
|
+
(dry * (1.0 - wet_amount)) + (wet * wet_amount)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def soft_clip(value, drive = 1.0)
|
|
21
|
+
Math.tanh(value * drive)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deftones
|
|
4
|
+
module Effects
|
|
5
|
+
class AutoFilter < Core::Effect
|
|
6
|
+
include ModulationControl
|
|
7
|
+
|
|
8
|
+
FilterProxy = Struct.new(:type, :q, :rolloff, keyword_init: true)
|
|
9
|
+
|
|
10
|
+
attr_accessor :frequency, :octaves, :depth, :base_frequency, :type
|
|
11
|
+
attr_reader :filter
|
|
12
|
+
|
|
13
|
+
def initialize(
|
|
14
|
+
frequency: 1.0,
|
|
15
|
+
base_frequency: 200.0,
|
|
16
|
+
octaves: 2.5,
|
|
17
|
+
depth: 1.0,
|
|
18
|
+
type: :sine,
|
|
19
|
+
filter_type: :lowpass,
|
|
20
|
+
q: 0.8,
|
|
21
|
+
filter: nil,
|
|
22
|
+
context: Deftones.context,
|
|
23
|
+
**options
|
|
24
|
+
)
|
|
25
|
+
super(context: context, wet: 1.0, **options)
|
|
26
|
+
@frequency = frequency.to_f
|
|
27
|
+
@base_frequency = base_frequency.to_f
|
|
28
|
+
@octaves = octaves.to_f
|
|
29
|
+
@depth = depth.to_f
|
|
30
|
+
@type = normalize_modulation_type(type)
|
|
31
|
+
@filter = resolve_filter(filter, filter_type: filter_type, q: q)
|
|
32
|
+
@phase = 0.0
|
|
33
|
+
@biquads = []
|
|
34
|
+
initialize_modulation_control
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def q
|
|
38
|
+
@filter.q
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def q=(value)
|
|
42
|
+
@filter.q = value.to_f
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
alias baseFrequency base_frequency
|
|
46
|
+
|
|
47
|
+
def baseFrequency=(value)
|
|
48
|
+
self.base_frequency = value
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
def process_effect_block(input_block, num_frames, start_frame, _cache)
|
|
54
|
+
ensure_biquads(input_block.channels)
|
|
55
|
+
output = Array.new(input_block.channels) { Array.new(num_frames, 0.0) }
|
|
56
|
+
|
|
57
|
+
num_frames.times do |index|
|
|
58
|
+
current_time = (start_frame + index).to_f / context.sample_rate
|
|
59
|
+
phase = modulation_phase_for(current_time)
|
|
60
|
+
modulation = unipolar_modulation_value(phase, default: 0.5) * @depth.clamp(0.0, 1.0)
|
|
61
|
+
cutoff = @base_frequency * (2.0**(modulation * @octaves))
|
|
62
|
+
input_block.channel_data.each_with_index do |channel, channel_index|
|
|
63
|
+
biquad = @biquads[channel_index]
|
|
64
|
+
biquad.update(
|
|
65
|
+
type: @filter.type,
|
|
66
|
+
frequency: cutoff,
|
|
67
|
+
q: @filter.q,
|
|
68
|
+
gain_db: 0.0,
|
|
69
|
+
sample_rate: context.sample_rate
|
|
70
|
+
)
|
|
71
|
+
output[channel_index][index] = biquad.process_sample(channel[index])
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
Core::AudioBlock.from_channel_data(output)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def resolve_filter(filter, filter_type:, q:)
|
|
79
|
+
return filter if filter.respond_to?(:type) && filter.respond_to?(:q)
|
|
80
|
+
|
|
81
|
+
FilterProxy.new(type: filter_type.to_sym, q: q.to_f, rolloff: -12)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def ensure_biquads(channels)
|
|
85
|
+
required = [channels.to_i, 1].max
|
|
86
|
+
while @biquads.length < required
|
|
87
|
+
@biquads << DSP::Biquad.new
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deftones
|
|
4
|
+
module Effects
|
|
5
|
+
class AutoPanner < Core::Effect
|
|
6
|
+
include ModulationControl
|
|
7
|
+
|
|
8
|
+
attr_accessor :frequency, :depth, :type
|
|
9
|
+
|
|
10
|
+
def initialize(frequency: 2.0, depth: 0.5, type: :sine, context: Deftones.context, **options)
|
|
11
|
+
super(context: context, wet: 1.0, **options)
|
|
12
|
+
@frequency = frequency.to_f
|
|
13
|
+
@depth = depth.to_f
|
|
14
|
+
@type = normalize_modulation_type(type)
|
|
15
|
+
@phase = 0.0
|
|
16
|
+
initialize_modulation_control
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def process_effect_block(input_block, num_frames, start_frame, _cache)
|
|
22
|
+
output = Array.new(2) { Array.new(num_frames, 0.0) }
|
|
23
|
+
stereo_input = input_block.fit_channels(2)
|
|
24
|
+
mono_input = input_block.mono
|
|
25
|
+
|
|
26
|
+
num_frames.times do |index|
|
|
27
|
+
current_time = (start_frame + index).to_f / context.sample_rate
|
|
28
|
+
phase = modulation_phase_for(current_time)
|
|
29
|
+
modulation = bipolar_modulation_value(phase, default: 0.0) * @depth.clamp(0.0, 1.0)
|
|
30
|
+
if input_block.channels == 1
|
|
31
|
+
sample = mono_input[index]
|
|
32
|
+
output[0][index] = sample * left_gain(modulation)
|
|
33
|
+
output[1][index] = sample * right_gain(modulation)
|
|
34
|
+
next
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
output[0][index] = stereo_input.channel_data[0][index] * left_gain(modulation)
|
|
38
|
+
output[1][index] = stereo_input.channel_data[1][index] * right_gain(modulation)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
Core::AudioBlock.from_channel_data(output)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def left_gain(pan)
|
|
45
|
+
Math.cos(angle_for(pan))
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def right_gain(pan)
|
|
49
|
+
Math.sin(angle_for(pan))
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def angle_for(pan)
|
|
53
|
+
((pan.to_f.clamp(-1.0, 1.0) + 1.0) * Math::PI) * 0.25
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deftones
|
|
4
|
+
module Effects
|
|
5
|
+
class AutoWah < Core::Effect
|
|
6
|
+
FollowerSettings = Struct.new(:attack, :release, keyword_init: true)
|
|
7
|
+
|
|
8
|
+
attr_accessor :base_frequency, :octaves, :q, :sensitivity, :gain
|
|
9
|
+
attr_reader :follower
|
|
10
|
+
|
|
11
|
+
def initialize(
|
|
12
|
+
base_frequency: 200.0,
|
|
13
|
+
octaves: 4.0,
|
|
14
|
+
sensitivity: 0.0,
|
|
15
|
+
q: 2.0,
|
|
16
|
+
gain: 2.0,
|
|
17
|
+
follower: {},
|
|
18
|
+
context: Deftones.context,
|
|
19
|
+
**options
|
|
20
|
+
)
|
|
21
|
+
super(context: context, wet: 1.0, **options)
|
|
22
|
+
@base_frequency = base_frequency.to_f
|
|
23
|
+
@octaves = octaves.to_f
|
|
24
|
+
@sensitivity = sensitivity.to_f
|
|
25
|
+
@q = q.to_f
|
|
26
|
+
@gain = gain.to_f
|
|
27
|
+
@envelopes = []
|
|
28
|
+
@filters = []
|
|
29
|
+
@follower = resolve_follower(follower)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def process_effect(input_buffer, num_frames, _start_frame, _cache, channel_index: 0)
|
|
35
|
+
ensure_tracking_state(channel_index)
|
|
36
|
+
envelope = @envelopes[channel_index]
|
|
37
|
+
filter = @filters[channel_index]
|
|
38
|
+
|
|
39
|
+
Array.new(num_frames) do |index|
|
|
40
|
+
sample = input_buffer[index]
|
|
41
|
+
envelope = track_envelope(sample.abs, envelope)
|
|
42
|
+
openness = openness_for(envelope)
|
|
43
|
+
cutoff = @base_frequency * (2.0**(@octaves * openness))
|
|
44
|
+
filter.update(type: :bandpass, frequency: cutoff, q: @q, gain_db: 0.0, sample_rate: context.sample_rate)
|
|
45
|
+
filter.process_sample(sample) * output_gain(openness)
|
|
46
|
+
end
|
|
47
|
+
ensure
|
|
48
|
+
@envelopes[channel_index] = envelope
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def track_envelope(level, current_envelope)
|
|
52
|
+
smoothing =
|
|
53
|
+
if level >= current_envelope
|
|
54
|
+
follower_coefficient(@follower.attack)
|
|
55
|
+
else
|
|
56
|
+
follower_coefficient(@follower.release)
|
|
57
|
+
end
|
|
58
|
+
(smoothing * current_envelope) + ((1.0 - smoothing) * level)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def follower_coefficient(duration)
|
|
62
|
+
return 0.0 if duration.to_f <= 0.0
|
|
63
|
+
|
|
64
|
+
Math.exp(-1.0 / (duration.to_f * context.sample_rate))
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def openness_for(level)
|
|
68
|
+
level_db = level.positive? ? Deftones.gain_to_db(level) : -100.0
|
|
69
|
+
threshold = [@sensitivity.to_f, -99.0].max
|
|
70
|
+
return level.clamp(0.0, 1.0) if threshold >= 0.0
|
|
71
|
+
|
|
72
|
+
((level_db - threshold) / -threshold).clamp(0.0, 1.0)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def output_gain(openness)
|
|
76
|
+
1.0 + ((@gain - 1.0) * openness)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def resolve_follower(follower)
|
|
80
|
+
return follower if follower.is_a?(FollowerSettings)
|
|
81
|
+
|
|
82
|
+
settings = follower.respond_to?(:to_h) ? follower.to_h : {}
|
|
83
|
+
FollowerSettings.new(
|
|
84
|
+
attack: settings.fetch(:attack, 0.3).to_f,
|
|
85
|
+
release: settings.fetch(:release, 0.5).to_f
|
|
86
|
+
)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def ensure_tracking_state(channel_index)
|
|
90
|
+
required = [channel_index.to_i, 0].max
|
|
91
|
+
@envelopes.fill(0.0, @envelopes.length..required)
|
|
92
|
+
while @filters.length <= required
|
|
93
|
+
@filters << DSP::Biquad.new
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deftones
|
|
4
|
+
module Effects
|
|
5
|
+
class BitCrusher < Core::Effect
|
|
6
|
+
attr_accessor :bits, :downsample
|
|
7
|
+
|
|
8
|
+
def initialize(bits: 8, downsample: 2, **options)
|
|
9
|
+
super(**options)
|
|
10
|
+
@bits = bits.to_i
|
|
11
|
+
@downsample = [downsample.to_i, 1].max
|
|
12
|
+
@hold_counters = []
|
|
13
|
+
@held_samples = []
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def process_effect(input_buffer, _num_frames, _start_frame, _cache, channel_index: 0)
|
|
19
|
+
step = 2.0 / (2**@bits)
|
|
20
|
+
ensure_state(channel_index)
|
|
21
|
+
|
|
22
|
+
input_buffer.map do |sample|
|
|
23
|
+
if (@hold_counters[channel_index] % @downsample).zero?
|
|
24
|
+
@held_samples[channel_index] = ((sample / step).round * step).clamp(-1.0, 1.0)
|
|
25
|
+
end
|
|
26
|
+
@hold_counters[channel_index] += 1
|
|
27
|
+
@held_samples[channel_index]
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def ensure_state(channel_index)
|
|
32
|
+
required = [channel_index.to_i, 0].max
|
|
33
|
+
@hold_counters.fill(0, @hold_counters.length..required)
|
|
34
|
+
@held_samples.fill(0.0, @held_samples.length..required)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deftones
|
|
4
|
+
module Effects
|
|
5
|
+
class Chebyshev < Core::Effect
|
|
6
|
+
attr_accessor :order
|
|
7
|
+
|
|
8
|
+
def initialize(order: 3, **options)
|
|
9
|
+
super(**options)
|
|
10
|
+
@order = [order.to_i, 1].max
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def process_effect(input_buffer, _num_frames, _start_frame, _cache, channel_index: 0)
|
|
16
|
+
_ = channel_index
|
|
17
|
+
input_buffer.map { |sample| chebyshev(sample.clamp(-1.0, 1.0), @order) }
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def chebyshev(value, order)
|
|
21
|
+
return value if order == 1
|
|
22
|
+
|
|
23
|
+
previous = value
|
|
24
|
+
current = (2.0 * value * value) - 1.0
|
|
25
|
+
return current if order == 2
|
|
26
|
+
|
|
27
|
+
3.upto(order) do
|
|
28
|
+
next_value = (2.0 * value * current) - previous
|
|
29
|
+
previous = current
|
|
30
|
+
current = next_value
|
|
31
|
+
end
|
|
32
|
+
current
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deftones
|
|
4
|
+
module Effects
|
|
5
|
+
class Chorus < Core::Effect
|
|
6
|
+
include ModulationControl
|
|
7
|
+
|
|
8
|
+
attr_accessor :frequency, :depth, :delay_time, :feedback, :spread, :type
|
|
9
|
+
|
|
10
|
+
def initialize(
|
|
11
|
+
frequency: 1.5,
|
|
12
|
+
depth: 0.003,
|
|
13
|
+
delay_time: 0.015,
|
|
14
|
+
feedback: 0.0,
|
|
15
|
+
spread: 180.0,
|
|
16
|
+
type: :sine,
|
|
17
|
+
context: Deftones.context,
|
|
18
|
+
**options
|
|
19
|
+
)
|
|
20
|
+
super(context: context, **options)
|
|
21
|
+
@frequency = frequency.to_f
|
|
22
|
+
@depth = depth.to_f
|
|
23
|
+
@delay_time = delay_time.to_f
|
|
24
|
+
@feedback = feedback.to_f
|
|
25
|
+
@spread = spread.to_f
|
|
26
|
+
@type = normalize_modulation_type(type)
|
|
27
|
+
@phase = 0.0
|
|
28
|
+
@delay_lines = []
|
|
29
|
+
initialize_modulation_control
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def process_effect_block(input_block, num_frames, start_frame, _cache)
|
|
35
|
+
output_channels = [input_block.channels, 2].max
|
|
36
|
+
source = input_block.fit_channels(output_channels)
|
|
37
|
+
ensure_delay_lines(output_channels)
|
|
38
|
+
output = Array.new(output_channels) { Array.new(num_frames, 0.0) }
|
|
39
|
+
|
|
40
|
+
num_frames.times do |index|
|
|
41
|
+
current_time = (start_frame + index).to_f / context.sample_rate
|
|
42
|
+
base_phase = modulation_phase_for(current_time)
|
|
43
|
+
|
|
44
|
+
output_channels.times do |channel_index|
|
|
45
|
+
phase = base_phase.nil? ? nil : base_phase + channel_phase_offset(channel_index, output_channels)
|
|
46
|
+
modulation = unipolar_modulation_value(phase, default: 0.5)
|
|
47
|
+
delay = (@delay_time + (@depth * modulation)) * context.sample_rate
|
|
48
|
+
output[channel_index][index] = @delay_lines[channel_index].tap(
|
|
49
|
+
delay,
|
|
50
|
+
input_sample: source.channel_data[channel_index][index],
|
|
51
|
+
feedback: @feedback
|
|
52
|
+
)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
Core::AudioBlock.from_channel_data(output)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def ensure_delay_lines(channels)
|
|
60
|
+
required = [channels.to_i, 1].max
|
|
61
|
+
while @delay_lines.length < required
|
|
62
|
+
@delay_lines << DSP::DelayLine.new((0.1 * context.sample_rate).ceil)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def channel_phase_offset(channel_index, channels)
|
|
67
|
+
return 0.0 if channels <= 1 || channel_index.zero?
|
|
68
|
+
|
|
69
|
+
(@spread / 360.0) * (channel_index.to_f / [channels - 1, 1].max)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|