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,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deftones
|
|
4
|
+
module Component
|
|
5
|
+
class EQ3 < Core::AudioNode
|
|
6
|
+
attr_accessor :low, :mid, :high
|
|
7
|
+
attr_reader :low_frequency, :high_frequency
|
|
8
|
+
|
|
9
|
+
def initialize(low: 0.0, mid: 0.0, high: 0.0, low_frequency: 400.0,
|
|
10
|
+
high_frequency: 2_500.0, context: Deftones.context)
|
|
11
|
+
super(context: context)
|
|
12
|
+
@low = low.to_f
|
|
13
|
+
@mid = mid.to_f
|
|
14
|
+
@high = high.to_f
|
|
15
|
+
@low_frequency = Core::Signal.new(value: low_frequency, units: :frequency, context: context)
|
|
16
|
+
@high_frequency = Core::Signal.new(value: high_frequency, units: :frequency, context: context)
|
|
17
|
+
@low_shelves = []
|
|
18
|
+
@mid_peaks = []
|
|
19
|
+
@high_shelves = []
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def multichannel_process?
|
|
23
|
+
true
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def process(input_block, num_frames, start_frame, _cache)
|
|
27
|
+
update_filters(input_block.channels, start_frame)
|
|
28
|
+
|
|
29
|
+
Core::AudioBlock.from_channel_data(
|
|
30
|
+
input_block.channel_data.each_with_index.map do |channel, channel_index|
|
|
31
|
+
low_shelf = @low_shelves[channel_index]
|
|
32
|
+
mid_peak = @mid_peaks[channel_index]
|
|
33
|
+
high_shelf = @high_shelves[channel_index]
|
|
34
|
+
|
|
35
|
+
Array.new(num_frames) do |index|
|
|
36
|
+
sample = channel[index]
|
|
37
|
+
sample = low_shelf.process_sample(sample)
|
|
38
|
+
sample = mid_peak.process_sample(sample)
|
|
39
|
+
high_shelf.process_sample(sample)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
def update_filters(channels, start_frame)
|
|
48
|
+
ensure_filter_banks(channels)
|
|
49
|
+
low_frequency = @low_frequency.process(1, start_frame).first
|
|
50
|
+
high_frequency = @high_frequency.process(1, start_frame).first
|
|
51
|
+
|
|
52
|
+
@low_shelves.each do |filter|
|
|
53
|
+
filter.update(type: :lowshelf, frequency: low_frequency, q: 0.707, gain_db: @low, sample_rate: context.sample_rate)
|
|
54
|
+
end
|
|
55
|
+
@mid_peaks.each do |filter|
|
|
56
|
+
filter.update(type: :peaking, frequency: Math.sqrt(low_frequency * high_frequency), q: 0.8, gain_db: @mid, sample_rate: context.sample_rate)
|
|
57
|
+
end
|
|
58
|
+
@high_shelves.each do |filter|
|
|
59
|
+
filter.update(type: :highshelf, frequency: high_frequency, q: 0.707, gain_db: @high, sample_rate: context.sample_rate)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def ensure_filter_banks(channels)
|
|
64
|
+
required = [channels.to_i, 1].max
|
|
65
|
+
while @low_shelves.length < required
|
|
66
|
+
@low_shelves << DSP::Biquad.new
|
|
67
|
+
@mid_peaks << DSP::Biquad.new
|
|
68
|
+
@high_shelves << DSP::Biquad.new
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deftones
|
|
4
|
+
module Component
|
|
5
|
+
class FeedbackCombFilter < Core::AudioNode
|
|
6
|
+
attr_reader :delay_time, :resonance
|
|
7
|
+
|
|
8
|
+
def initialize(delay_time: 0.1, resonance: 0.5, max_delay: 1.0, context: Deftones.context)
|
|
9
|
+
super(context: context)
|
|
10
|
+
@delay_time = Core::Signal.new(value: delay_time, units: :time, context: context)
|
|
11
|
+
@resonance = Core::Signal.new(value: resonance, units: :number, context: context)
|
|
12
|
+
@max_delay_samples = [(max_delay.to_f * context.sample_rate).ceil, 2].max
|
|
13
|
+
@delay_lines = []
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def delay_time=(value)
|
|
17
|
+
@delay_time.value = value
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def resonance=(value)
|
|
21
|
+
@resonance.value = value
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def multichannel_process?
|
|
25
|
+
true
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def process(input_block, num_frames, start_frame, _cache)
|
|
29
|
+
delay_times = @delay_time.process(num_frames, start_frame)
|
|
30
|
+
resonances = @resonance.process(num_frames, start_frame)
|
|
31
|
+
ensure_delay_lines(input_block.channels)
|
|
32
|
+
|
|
33
|
+
Core::AudioBlock.from_channel_data(
|
|
34
|
+
input_block.channel_data.each_with_index.map do |channel, channel_index|
|
|
35
|
+
delay_line = @delay_lines[channel_index]
|
|
36
|
+
|
|
37
|
+
Array.new(num_frames) do |index|
|
|
38
|
+
delayed = delay_line.read(delay_samples(delay_times[index]))
|
|
39
|
+
feedback = filtered_feedback(delayed, index, start_frame, channel_index)
|
|
40
|
+
delay_line.write(channel[index] + (feedback * clamp_resonance(resonances[index])))
|
|
41
|
+
delayed
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def reset!
|
|
48
|
+
@delay_lines = []
|
|
49
|
+
self
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
def delay_samples(duration)
|
|
55
|
+
samples = duration.to_f * context.sample_rate
|
|
56
|
+
[[samples, 1.0].max, @max_delay_samples].min
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def clamp_resonance(value)
|
|
60
|
+
value.to_f.clamp(-0.999, 0.999)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def filtered_feedback(sample, _index, _start_frame, _channel_index = 0)
|
|
64
|
+
sample
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def ensure_delay_lines(channels)
|
|
68
|
+
required = [channels.to_i, 1].max
|
|
69
|
+
while @delay_lines.length < required
|
|
70
|
+
@delay_lines << DSP::DelayLine.new(@max_delay_samples)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deftones
|
|
4
|
+
module Component
|
|
5
|
+
class Filter < Core::AudioNode
|
|
6
|
+
TYPES = DSP::Biquad::TYPES
|
|
7
|
+
|
|
8
|
+
attr_reader :detune, :frequency, :q, :gain
|
|
9
|
+
attr_accessor :type
|
|
10
|
+
|
|
11
|
+
def initialize(type: :lowpass, frequency: 350.0, q: 1.0, gain: 0.0, detune: 0.0, context: Deftones.context)
|
|
12
|
+
super(context: context)
|
|
13
|
+
@type = normalize_type(type)
|
|
14
|
+
@frequency = Core::Signal.new(value: frequency, units: :frequency, context: context)
|
|
15
|
+
@q = Core::Signal.new(value: q, units: :number, context: context)
|
|
16
|
+
@gain = Core::Signal.new(value: gain, units: :number, context: context)
|
|
17
|
+
@detune = Core::Signal.new(value: detune, units: :number, context: context)
|
|
18
|
+
@biquads = []
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def detune=(value)
|
|
22
|
+
@detune.value = value
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def multichannel_process?
|
|
26
|
+
true
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def process(input_block, num_frames, start_frame, _cache)
|
|
30
|
+
update_filters(input_block.channels, start_frame)
|
|
31
|
+
Core::AudioBlock.from_channel_data(
|
|
32
|
+
input_block.channel_data.each_with_index.map do |channel, channel_index|
|
|
33
|
+
biquad = @biquads[channel_index]
|
|
34
|
+
Array.new(num_frames) { |index| biquad.process_sample(channel[index]) }
|
|
35
|
+
end
|
|
36
|
+
)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def reset!
|
|
40
|
+
@biquads.each(&:reset!)
|
|
41
|
+
self
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
def update_filters(channels, start_frame)
|
|
47
|
+
ensure_biquads(channels)
|
|
48
|
+
frequency = @frequency.process(1, start_frame).first
|
|
49
|
+
detune = @detune.process(1, start_frame).first
|
|
50
|
+
@biquads.each do |biquad|
|
|
51
|
+
biquad.update(
|
|
52
|
+
type: normalize_type(@type),
|
|
53
|
+
frequency: frequency * (2.0**(detune / 1200.0)),
|
|
54
|
+
q: @q.process(1, start_frame).first,
|
|
55
|
+
gain_db: @gain.process(1, start_frame).first * 24.0,
|
|
56
|
+
sample_rate: context.sample_rate
|
|
57
|
+
)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def ensure_biquads(channels)
|
|
62
|
+
required = [channels.to_i, 1].max
|
|
63
|
+
missing = required - @biquads.length
|
|
64
|
+
missing.times { @biquads << DSP::Biquad.new } if missing.positive?
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def normalize_type(type)
|
|
68
|
+
normalized = type.to_sym
|
|
69
|
+
return normalized if TYPES.include?(normalized)
|
|
70
|
+
|
|
71
|
+
raise ArgumentError, "Unsupported filter type: #{type}"
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deftones
|
|
4
|
+
module Component
|
|
5
|
+
class Follower < Core::AudioNode
|
|
6
|
+
attr_reader :smoothing
|
|
7
|
+
|
|
8
|
+
def initialize(smoothing: 0.05, context: Deftones.context)
|
|
9
|
+
super(context: context)
|
|
10
|
+
@smoothing = Core::Signal.new(value: smoothing, units: :time, context: context)
|
|
11
|
+
@state = []
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def smoothing=(value)
|
|
15
|
+
@smoothing.value = value
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def multichannel_process?
|
|
19
|
+
true
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def process(input_block, num_frames, start_frame, _cache)
|
|
23
|
+
smoothing_values = @smoothing.process(num_frames, start_frame)
|
|
24
|
+
ensure_state(input_block.channels)
|
|
25
|
+
output_channels = input_block.channel_data.each_with_index.map do |channel, channel_index|
|
|
26
|
+
Array.new(num_frames) do |index|
|
|
27
|
+
coefficient = smoothing_coefficient(smoothing_values[index])
|
|
28
|
+
magnitude = channel[index].abs
|
|
29
|
+
@state[channel_index] += (1.0 - coefficient) * (magnitude - @state[channel_index])
|
|
30
|
+
@state[channel_index]
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
Core::AudioBlock.from_channel_data(output_channels)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def reset!
|
|
38
|
+
@state = []
|
|
39
|
+
self
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def smoothing_coefficient(duration)
|
|
45
|
+
seconds = [duration.to_f, 1.0 / context.sample_rate].max
|
|
46
|
+
Math.exp(-1.0 / (seconds * context.sample_rate))
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def ensure_state(channels)
|
|
50
|
+
required = [channels.to_i, 1].max
|
|
51
|
+
@state.fill(0.0, @state.length...required)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deftones
|
|
4
|
+
module Component
|
|
5
|
+
class FrequencyEnvelope < Envelope
|
|
6
|
+
attr_accessor :base_frequency, :octaves
|
|
7
|
+
|
|
8
|
+
def initialize(base_frequency: 440.0, octaves: 2.0, **options)
|
|
9
|
+
super(**options)
|
|
10
|
+
@base_frequency = base_frequency.to_f
|
|
11
|
+
@octaves = octaves.to_f
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def values(num_frames, start_frame = 0)
|
|
15
|
+
Array.new(num_frames) do |index|
|
|
16
|
+
time = sample_time(start_frame + index)
|
|
17
|
+
consume_events(time)
|
|
18
|
+
@current_value = envelope_value_at(time)
|
|
19
|
+
@base_frequency * (2.0**(@current_value * @octaves))
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deftones
|
|
4
|
+
module Component
|
|
5
|
+
class Gate < Core::AudioNode
|
|
6
|
+
attr_accessor :threshold, :release
|
|
7
|
+
|
|
8
|
+
def initialize(threshold: -40.0, release: 0.05, context: Deftones.context)
|
|
9
|
+
super(context: context)
|
|
10
|
+
@threshold = threshold.to_f
|
|
11
|
+
@release = release.to_f
|
|
12
|
+
@gain = []
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def multichannel_process?
|
|
16
|
+
true
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def process(input_block, num_frames, _start_frame, _cache)
|
|
20
|
+
ensure_gain_state(input_block.channels)
|
|
21
|
+
Core::AudioBlock.from_channel_data(
|
|
22
|
+
input_block.channel_data.each_with_index.map do |channel, channel_index|
|
|
23
|
+
Array.new(num_frames) { |index| gate(channel[index], channel_index) }
|
|
24
|
+
end
|
|
25
|
+
)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def gate(sample, channel_index)
|
|
31
|
+
level_db = 20.0 * Math.log10([sample.abs, 1.0e-9].max)
|
|
32
|
+
target = level_db >= @threshold ? 1.0 : 0.0
|
|
33
|
+
smoothing = 1.0 / [(@release * context.sample_rate), 1.0].max
|
|
34
|
+
gain = @gain[channel_index]
|
|
35
|
+
gain += (target - gain) * smoothing
|
|
36
|
+
@gain[channel_index] = gain
|
|
37
|
+
sample * gain
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def ensure_gain_state(channels)
|
|
41
|
+
required = [channels.to_i, 1].max
|
|
42
|
+
@gain.fill(0.0, @gain.length...required)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deftones
|
|
4
|
+
module Component
|
|
5
|
+
class LFO < Source::Oscillator
|
|
6
|
+
attr_reader :amplitude, :units
|
|
7
|
+
attr_accessor :convert
|
|
8
|
+
|
|
9
|
+
def initialize(
|
|
10
|
+
frequency: 1.0,
|
|
11
|
+
min: 0.0,
|
|
12
|
+
max: 1.0,
|
|
13
|
+
amplitude: 1.0,
|
|
14
|
+
units: :number,
|
|
15
|
+
convert: true,
|
|
16
|
+
type: :sine,
|
|
17
|
+
context: Deftones.context
|
|
18
|
+
)
|
|
19
|
+
super(type: type, frequency: frequency, context: context)
|
|
20
|
+
@units = units.to_sym
|
|
21
|
+
@convert = !!convert
|
|
22
|
+
@amplitude = Core::Param.new(value: amplitude, units: :number, context: context)
|
|
23
|
+
self.min = min
|
|
24
|
+
self.max = max
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def min
|
|
28
|
+
@min
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def min=(value)
|
|
32
|
+
@min = coerce_range_value(value)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def max
|
|
36
|
+
@max
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def max=(value)
|
|
40
|
+
@max = coerce_range_value(value)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def units=(value)
|
|
44
|
+
@units = value.to_sym
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def get_defaults
|
|
48
|
+
{
|
|
49
|
+
frequency: 1.0,
|
|
50
|
+
min: 0.0,
|
|
51
|
+
max: 1.0,
|
|
52
|
+
amplitude: 1.0,
|
|
53
|
+
units: :number,
|
|
54
|
+
convert: true,
|
|
55
|
+
type: :sine
|
|
56
|
+
}
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def process(_input_buffer, num_frames, start_frame, cache)
|
|
60
|
+
waveform = super
|
|
61
|
+
amplitudes = @amplitude.process(num_frames, start_frame)
|
|
62
|
+
midpoint = (@min + @max) * 0.5
|
|
63
|
+
half_range = (@max - @min) * 0.5
|
|
64
|
+
|
|
65
|
+
Array.new(num_frames) do |index|
|
|
66
|
+
depth = amplitudes[index].clamp(0.0, 1.0)
|
|
67
|
+
midpoint + (waveform[index] * half_range * depth)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def values(num_frames, start_frame = 0, cache = {})
|
|
72
|
+
render(num_frames, start_frame, cache)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
alias getDefaults get_defaults
|
|
76
|
+
|
|
77
|
+
private
|
|
78
|
+
|
|
79
|
+
def coerce_range_value(value)
|
|
80
|
+
return value.value_of if value.respond_to?(:value_of) && !convert
|
|
81
|
+
|
|
82
|
+
signal = Core::Signal.new(value: 0.0, units: units, context: context)
|
|
83
|
+
signal.convert = convert
|
|
84
|
+
signal.send(:coerce_value, value)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deftones
|
|
4
|
+
module Component
|
|
5
|
+
class Limiter < Compressor
|
|
6
|
+
def initialize(threshold: -1.0, ratio: 20.0, attack: 0.001, release: 0.05, **options)
|
|
7
|
+
super(threshold: threshold, ratio: ratio, attack: attack, release: release, **options)
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deftones
|
|
4
|
+
module Component
|
|
5
|
+
class LowpassCombFilter < FeedbackCombFilter
|
|
6
|
+
attr_reader :dampening
|
|
7
|
+
|
|
8
|
+
def initialize(dampening: 3_000.0, **options)
|
|
9
|
+
super(**options)
|
|
10
|
+
@dampening = Core::Signal.new(value: dampening, units: :frequency, context: context)
|
|
11
|
+
@filter_state = []
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def dampening=(value)
|
|
15
|
+
@dampening.value = value
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def reset!
|
|
19
|
+
@filter_state = []
|
|
20
|
+
super
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def filtered_feedback(sample, index, start_frame, channel_index = 0)
|
|
26
|
+
ensure_filter_state(channel_index + 1)
|
|
27
|
+
coefficient = feedback_coefficient(@dampening.process(1, start_frame + index).first)
|
|
28
|
+
@filter_state[channel_index] += (1.0 - coefficient) * (sample - @filter_state[channel_index])
|
|
29
|
+
@filter_state[channel_index]
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def feedback_coefficient(frequency)
|
|
33
|
+
normalized = [[frequency.to_f, 1.0].max, (context.sample_rate * 0.49)].min
|
|
34
|
+
Math.exp((-2.0 * Math::PI * normalized) / context.sample_rate)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def ensure_filter_state(channels)
|
|
38
|
+
required = [channels.to_i, 1].max
|
|
39
|
+
@filter_state.fill(0.0, @filter_state.length...required)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deftones
|
|
4
|
+
module Component
|
|
5
|
+
class Merge < Core::AudioNode
|
|
6
|
+
attr_reader :input, :output, :left, :right
|
|
7
|
+
|
|
8
|
+
def initialize(context: Deftones.context)
|
|
9
|
+
super(context: context)
|
|
10
|
+
@left = Core::Gain.new(context: context)
|
|
11
|
+
@right = Core::Gain.new(context: context)
|
|
12
|
+
@input = @left
|
|
13
|
+
@output = self
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def render(num_frames, start_frame = 0, cache = {})
|
|
17
|
+
cache_key = [object_id, start_frame, num_frames]
|
|
18
|
+
return cache.fetch(cache_key).dup if cache.key?(cache_key)
|
|
19
|
+
|
|
20
|
+
left_buffer = @left.render(num_frames, start_frame, cache)
|
|
21
|
+
right_buffer = @right.render(num_frames, start_frame, cache)
|
|
22
|
+
output_buffer = Array.new(num_frames) do |index|
|
|
23
|
+
(left_buffer[index] + right_buffer[index]) * 0.5
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
cache[cache_key] = output_buffer
|
|
27
|
+
output_buffer.dup
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def render_block(num_frames, start_frame = 0, cache = {})
|
|
31
|
+
cache_key = [object_id, :block, start_frame, num_frames]
|
|
32
|
+
return cache.fetch(cache_key).dup if cache.key?(cache_key)
|
|
33
|
+
|
|
34
|
+
left_block = @left.send(:render_block, num_frames, start_frame, cache)
|
|
35
|
+
right_block = @right.send(:render_block, num_frames, start_frame, cache)
|
|
36
|
+
output_block = Core::AudioBlock.from_channel_data([
|
|
37
|
+
left_block.mono,
|
|
38
|
+
right_block.mono
|
|
39
|
+
])
|
|
40
|
+
cache[cache_key] = output_block
|
|
41
|
+
output_block.dup
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deftones
|
|
4
|
+
module Component
|
|
5
|
+
class MidSideCompressor < Core::AudioNode
|
|
6
|
+
attr_reader :input, :merge, :mid, :output, :side, :split
|
|
7
|
+
|
|
8
|
+
def initialize(mid: {}, side: {}, context: Deftones.context)
|
|
9
|
+
super(context: context)
|
|
10
|
+
@split = MidSideSplit.new(context: context)
|
|
11
|
+
@merge = MidSideMerge.new(context: context)
|
|
12
|
+
@input = @split
|
|
13
|
+
@output = @merge
|
|
14
|
+
@mid = build_compressor(mid, context)
|
|
15
|
+
@side = build_compressor(side, context)
|
|
16
|
+
@split.mid >> @mid >> @merge.mid
|
|
17
|
+
@split.side >> @side >> @merge.side
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def render(num_frames, start_frame = 0, cache = {})
|
|
21
|
+
@output.render(num_frames, start_frame, cache)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def render_block(num_frames, start_frame = 0, cache = {})
|
|
25
|
+
@output.send(:render_block, num_frames, start_frame, cache)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def build_compressor(definition, context)
|
|
31
|
+
return definition if definition.is_a?(Compressor)
|
|
32
|
+
|
|
33
|
+
Compressor.new(
|
|
34
|
+
threshold: definition.fetch(:threshold, -24.0),
|
|
35
|
+
ratio: definition.fetch(:ratio, 3.0),
|
|
36
|
+
attack: definition.fetch(:attack, 0.01),
|
|
37
|
+
release: definition.fetch(:release, 0.1),
|
|
38
|
+
context: context
|
|
39
|
+
)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deftones
|
|
4
|
+
module Component
|
|
5
|
+
class MidSideMerge < Core::AudioNode
|
|
6
|
+
SQRT_ONE_HALF = Math.sqrt(0.5)
|
|
7
|
+
|
|
8
|
+
attr_reader :input, :output, :mid, :side
|
|
9
|
+
|
|
10
|
+
def initialize(context: Deftones.context)
|
|
11
|
+
super(context: context)
|
|
12
|
+
@mid = Core::Gain.new(context: context)
|
|
13
|
+
@side = Core::Gain.new(context: context)
|
|
14
|
+
@input = @mid
|
|
15
|
+
@output = self
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def render(num_frames, start_frame = 0, cache = {})
|
|
19
|
+
cache_key = [object_id, start_frame, num_frames]
|
|
20
|
+
return cache.fetch(cache_key).dup if cache.key?(cache_key)
|
|
21
|
+
|
|
22
|
+
mid_buffer = @mid.render(num_frames, start_frame, cache)
|
|
23
|
+
side_buffer = @side.render(num_frames, start_frame, cache)
|
|
24
|
+
|
|
25
|
+
output_buffer = Array.new(num_frames) do |index|
|
|
26
|
+
left = (mid_buffer[index] + side_buffer[index]) * SQRT_ONE_HALF
|
|
27
|
+
right = (mid_buffer[index] - side_buffer[index]) * SQRT_ONE_HALF
|
|
28
|
+
(left + right) * 0.5
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
cache[cache_key] = output_buffer
|
|
32
|
+
output_buffer.dup
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def render_block(num_frames, start_frame = 0, cache = {})
|
|
36
|
+
cache_key = [object_id, :block, start_frame, num_frames]
|
|
37
|
+
return cache.fetch(cache_key).dup if cache.key?(cache_key)
|
|
38
|
+
|
|
39
|
+
mid_block = @mid.send(:render_block, num_frames, start_frame, cache)
|
|
40
|
+
side_block = @side.send(:render_block, num_frames, start_frame, cache)
|
|
41
|
+
mid = mid_block.mono
|
|
42
|
+
side = side_block.mono
|
|
43
|
+
output = Core::AudioBlock.from_channel_data([
|
|
44
|
+
Array.new(num_frames) { |index| (mid[index] + side[index]) * SQRT_ONE_HALF },
|
|
45
|
+
Array.new(num_frames) { |index| (mid[index] - side[index]) * SQRT_ONE_HALF }
|
|
46
|
+
])
|
|
47
|
+
|
|
48
|
+
cache[cache_key] = output
|
|
49
|
+
output.dup
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deftones
|
|
4
|
+
module Component
|
|
5
|
+
class MidSideSplit < Core::AudioNode
|
|
6
|
+
SQRT_TWO = Math.sqrt(2.0)
|
|
7
|
+
|
|
8
|
+
attr_reader :mid, :side
|
|
9
|
+
|
|
10
|
+
def initialize(context: Deftones.context)
|
|
11
|
+
super(context: context)
|
|
12
|
+
@mid = OutputTap.new(parent: self, mode: :mid, context: context)
|
|
13
|
+
@side = OutputTap.new(parent: self, mode: :side, context: context)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def render_output(mode, num_frames, start_frame = 0, cache = {})
|
|
17
|
+
render_output_block(mode, num_frames, start_frame, cache).mono
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def render_output_block(mode, num_frames, start_frame = 0, cache = {})
|
|
21
|
+
input_block = send(:mix_source_blocks, num_frames, start_frame, cache).fit_channels(2)
|
|
22
|
+
left = input_block.channel(0)
|
|
23
|
+
right = input_block.channel(1)
|
|
24
|
+
|
|
25
|
+
output = case mode
|
|
26
|
+
when :mid
|
|
27
|
+
Array.new(num_frames) { |index| (left[index] + right[index]) / SQRT_TWO }
|
|
28
|
+
when :side
|
|
29
|
+
Array.new(num_frames) { |index| (left[index] - right[index]) / SQRT_TWO }
|
|
30
|
+
else
|
|
31
|
+
raise ArgumentError, "Unsupported mid/side output: #{mode}"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
Core::AudioBlock.from_channel_data([output])
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
class OutputTap < Core::AudioNode
|
|
38
|
+
def initialize(parent:, mode:, context: Deftones.context)
|
|
39
|
+
super(context: context)
|
|
40
|
+
@parent = parent
|
|
41
|
+
@mode = mode
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def render_block(num_frames, start_frame = 0, cache = {})
|
|
45
|
+
@parent.render_output_block(@mode, num_frames, start_frame, cache)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def render(num_frames, start_frame = 0, cache = {})
|
|
49
|
+
@parent.render_output(@mode, num_frames, start_frame, cache)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|