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,83 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deftones
|
|
4
|
+
module Analysis
|
|
5
|
+
class Meter < Core::AudioNode
|
|
6
|
+
attr_accessor :normal_range
|
|
7
|
+
|
|
8
|
+
def initialize(smoothing: 0.8, normal_range: false, channels: 1, context: Deftones.context)
|
|
9
|
+
super(context: context)
|
|
10
|
+
@channels = [channels.to_i, 1].max
|
|
11
|
+
@peak_values = Array.new(@channels, 0.0)
|
|
12
|
+
@rms_values = Array.new(@channels, 0.0)
|
|
13
|
+
@normal_range = !!normal_range
|
|
14
|
+
self.smoothing = smoothing
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def channels
|
|
18
|
+
@channels
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def peak
|
|
22
|
+
@peak_values.length == 1 ? @peak_values.first : @peak_values.dup
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def rms
|
|
26
|
+
@rms_values.length == 1 ? @rms_values.first : @rms_values.dup
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def smoothing
|
|
30
|
+
@smoothing
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def smoothing=(value)
|
|
34
|
+
@smoothing = Deftones::DSP::Helpers.clamp(value.to_f, 0.0, 1.0)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def get_value
|
|
38
|
+
values = @rms_values.map do |value|
|
|
39
|
+
if @normal_range
|
|
40
|
+
Deftones::DSP::Helpers.clamp(value, 0.0, 1.0)
|
|
41
|
+
else
|
|
42
|
+
Deftones.gain_to_db([value.abs, 1.0e-12].max)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
values.length == 1 ? values.first : values
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def multichannel_process?
|
|
50
|
+
true
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def process(input_block, num_frames, _start_frame, _cache)
|
|
54
|
+
analysis_block = input_block.fit_channels(@channels)
|
|
55
|
+
|
|
56
|
+
@channels.times do |channel_index|
|
|
57
|
+
segment = analysis_block.channel_data[channel_index].first(num_frames)
|
|
58
|
+
instantaneous_peak = segment.map(&:abs).max || 0.0
|
|
59
|
+
instantaneous_rms = Math.sqrt(segment.sum { |sample| sample * sample } / [segment.length, 1].max)
|
|
60
|
+
@peak_values[channel_index] = smooth(@peak_values[channel_index], instantaneous_peak)
|
|
61
|
+
@rms_values[channel_index] = smooth(@rms_values[channel_index], instantaneous_rms)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
input_block
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
alias getValue get_value
|
|
68
|
+
alias normalRange normal_range
|
|
69
|
+
|
|
70
|
+
def normalRange=(value)
|
|
71
|
+
self.normal_range = value
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
private
|
|
75
|
+
|
|
76
|
+
def smooth(previous, current)
|
|
77
|
+
return current if @smoothing.zero?
|
|
78
|
+
|
|
79
|
+
(previous.to_f * @smoothing) + (current.to_f * (1.0 - @smoothing))
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deftones
|
|
4
|
+
module Analysis
|
|
5
|
+
class Waveform < Core::AudioNode
|
|
6
|
+
class Snapshot
|
|
7
|
+
attr_reader :samples
|
|
8
|
+
|
|
9
|
+
def initialize(samples)
|
|
10
|
+
@samples = samples
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def peak
|
|
14
|
+
@samples.map(&:abs).max || 0.0
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def rms
|
|
18
|
+
return 0.0 if @samples.empty?
|
|
19
|
+
|
|
20
|
+
Math.sqrt(@samples.sum { |sample| sample * sample } / @samples.length)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def initialize(size: 1024, smoothing: 0.8, return_type: :float, normal_range: false, context: Deftones.context)
|
|
25
|
+
super(context: context)
|
|
26
|
+
@delegate = Analysis::Analyser.new(
|
|
27
|
+
size: size,
|
|
28
|
+
type: :waveform,
|
|
29
|
+
smoothing: smoothing,
|
|
30
|
+
return_type: return_type,
|
|
31
|
+
normal_range: normal_range,
|
|
32
|
+
context: context
|
|
33
|
+
)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def size
|
|
37
|
+
@delegate.size
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def size=(value)
|
|
41
|
+
@delegate.size = value
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def smoothing
|
|
45
|
+
@delegate.smoothing
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def smoothing=(value)
|
|
49
|
+
@delegate.smoothing = value
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def return_type
|
|
53
|
+
@delegate.return_type
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def return_type=(value)
|
|
57
|
+
@delegate.return_type = value
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def normal_range
|
|
61
|
+
@delegate.normal_range
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def normal_range=(value)
|
|
65
|
+
@delegate.normal_range = value
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def get_value
|
|
69
|
+
@delegate.get_value
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
alias getValue get_value
|
|
73
|
+
alias returnType return_type
|
|
74
|
+
alias normalRange normal_range
|
|
75
|
+
|
|
76
|
+
def returnType=(value)
|
|
77
|
+
self.return_type = value
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def normalRange=(value)
|
|
81
|
+
self.normal_range = value
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def process(input_buffer, num_frames, start_frame, cache)
|
|
85
|
+
@delegate.process(input_buffer, num_frames, start_frame, cache)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def multichannel_process?
|
|
89
|
+
true
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deftones
|
|
4
|
+
module Component
|
|
5
|
+
class Channel < Core::AudioNode
|
|
6
|
+
@buses = Hash.new { |hash, key| hash[key] = {} }
|
|
7
|
+
|
|
8
|
+
class << self
|
|
9
|
+
attr_reader :buses
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
attr_reader :input, :output, :pan_vol, :solo
|
|
13
|
+
|
|
14
|
+
def initialize(pan: 0.0, volume: 0.0, solo: false, muted: false, mute: nil, context: Deftones.context)
|
|
15
|
+
super(context: context)
|
|
16
|
+
muted = mute unless mute.nil?
|
|
17
|
+
@pan_vol = PanVol.new(pan: pan, volume: volume, context: context)
|
|
18
|
+
@solo = Solo.new(solo: solo, muted: muted, context: context)
|
|
19
|
+
@input = @pan_vol.input
|
|
20
|
+
@output = @solo
|
|
21
|
+
@sends = []
|
|
22
|
+
@pan_vol.output >> @solo
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def render(num_frames, start_frame = 0, cache = {})
|
|
26
|
+
@output.render(num_frames, start_frame, cache)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def render_block(num_frames, start_frame = 0, cache = {})
|
|
30
|
+
@output.send(:render_block, num_frames, start_frame, cache)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def pan
|
|
34
|
+
@pan_vol.panner.pan
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def pan=(value)
|
|
38
|
+
@pan_vol.panner.pan = value
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def volume
|
|
42
|
+
@pan_vol.volume.volume
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def volume=(value)
|
|
46
|
+
@pan_vol.volume.volume = value
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def mute
|
|
50
|
+
@solo.mute
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def mute=(value)
|
|
54
|
+
@solo.mute = value
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def mute?
|
|
58
|
+
@solo.mute?
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def muted
|
|
62
|
+
mute
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def muted=(value)
|
|
66
|
+
self.mute = value
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def solo?
|
|
70
|
+
@solo.solo?
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def solo=(value)
|
|
74
|
+
@solo.solo = value
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def send(name, volume = 0.0)
|
|
78
|
+
send_gain = Core::Gain.new(gain: Deftones.db_to_gain(volume), context: context)
|
|
79
|
+
@output >> send_gain >> bus(name)
|
|
80
|
+
@sends << send_gain
|
|
81
|
+
send_gain
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def receive(name)
|
|
85
|
+
bus(name) >> @input
|
|
86
|
+
self
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def dispose
|
|
90
|
+
@sends.each(&:dispose)
|
|
91
|
+
@sends.clear
|
|
92
|
+
@pan_vol.dispose
|
|
93
|
+
@solo.dispose
|
|
94
|
+
super
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
alias panVol pan_vol
|
|
98
|
+
alias solo solo?
|
|
99
|
+
alias muted? muted
|
|
100
|
+
|
|
101
|
+
private
|
|
102
|
+
|
|
103
|
+
def bus(name)
|
|
104
|
+
registry = self.class.buses[context.object_id]
|
|
105
|
+
registry[name.to_sym] ||= Core::Gain.new(gain: 1.0, context: context)
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deftones
|
|
4
|
+
module Component
|
|
5
|
+
class Compressor < Core::AudioNode
|
|
6
|
+
attr_accessor :threshold, :ratio, :attack, :release
|
|
7
|
+
|
|
8
|
+
def initialize(threshold: -18.0, ratio: 4.0, attack: 0.01, release: 0.1, context: Deftones.context)
|
|
9
|
+
super(context: context)
|
|
10
|
+
@threshold = threshold.to_f
|
|
11
|
+
@ratio = ratio.to_f
|
|
12
|
+
@attack = attack.to_f
|
|
13
|
+
@release = release.to_f
|
|
14
|
+
@gain_db = []
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def multichannel_process?
|
|
18
|
+
true
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def process(input_block, num_frames, _start_frame, _cache)
|
|
22
|
+
ensure_gain_state(input_block.channels)
|
|
23
|
+
Core::AudioBlock.from_channel_data(
|
|
24
|
+
input_block.channel_data.each_with_index.map do |channel, channel_index|
|
|
25
|
+
Array.new(num_frames) { |index| compress(channel[index], channel_index) }
|
|
26
|
+
end
|
|
27
|
+
)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def compress(sample, channel_index)
|
|
33
|
+
level = [sample.abs, 1.0e-9].max
|
|
34
|
+
level_db = 20.0 * Math.log10(level)
|
|
35
|
+
target_gain_db =
|
|
36
|
+
if level_db > @threshold
|
|
37
|
+
compressed_db = @threshold + ((level_db - @threshold) / [@ratio, 1.0].max)
|
|
38
|
+
compressed_db - level_db
|
|
39
|
+
else
|
|
40
|
+
0.0
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
current_gain_db = @gain_db[channel_index]
|
|
44
|
+
smoothing = target_gain_db < current_gain_db ? attack_smoothing : release_smoothing
|
|
45
|
+
current_gain_db += (target_gain_db - current_gain_db) * smoothing
|
|
46
|
+
@gain_db[channel_index] = current_gain_db
|
|
47
|
+
sample * (10.0**(current_gain_db / 20.0))
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def attack_smoothing
|
|
51
|
+
1.0 / [(@attack * context.sample_rate), 1.0].max
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def release_smoothing
|
|
55
|
+
1.0 / [(@release * context.sample_rate), 1.0].max
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def ensure_gain_state(channels)
|
|
59
|
+
required = [channels.to_i, 1].max
|
|
60
|
+
@gain_db.fill(0.0, @gain_db.length...required)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deftones
|
|
4
|
+
module Component
|
|
5
|
+
class Convolver < Core::Effect
|
|
6
|
+
attr_reader :buffer
|
|
7
|
+
attr_accessor :normalize
|
|
8
|
+
|
|
9
|
+
def initialize(source = nil, wet: 1.0, normalize: false, context: Deftones.context, &onload)
|
|
10
|
+
super(wet: wet, context: context)
|
|
11
|
+
@normalize = normalize
|
|
12
|
+
@buffer = nil
|
|
13
|
+
@kernels = [[1.0]]
|
|
14
|
+
@histories = []
|
|
15
|
+
load(source, &onload) if source
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def buffer=(value)
|
|
19
|
+
load(value)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def load(source)
|
|
23
|
+
@buffer = coerce_buffer(source)
|
|
24
|
+
@kernels = normalize_kernels(@buffer.to_array)
|
|
25
|
+
@histories = []
|
|
26
|
+
yield self if block_given?
|
|
27
|
+
self
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def process_effect_block(input_block, num_frames, _start_frame, _cache)
|
|
33
|
+
return input_block if passthrough?
|
|
34
|
+
|
|
35
|
+
output_channels = [input_block.channels, @kernels.length].max
|
|
36
|
+
source = input_block.fit_channels([input_block.channels, 1].max)
|
|
37
|
+
output = Array.new(output_channels) { Array.new(num_frames, 0.0) }
|
|
38
|
+
|
|
39
|
+
output_channels.times do |channel_index|
|
|
40
|
+
kernel = @kernels[channel_index % @kernels.length]
|
|
41
|
+
history = ensure_history(channel_index, kernel.length).dup
|
|
42
|
+
input_channel = source.channel_data[[channel_index, source.channels - 1].min]
|
|
43
|
+
|
|
44
|
+
output[channel_index] = Array.new(num_frames) do |frame_index|
|
|
45
|
+
history << input_channel[frame_index]
|
|
46
|
+
sample = convolve(history, kernel)
|
|
47
|
+
history.shift while history.length > kernel.length
|
|
48
|
+
sample
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
@histories[channel_index] = history
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
Core::AudioBlock.from_channel_data(output)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def passthrough?
|
|
58
|
+
@buffer.nil? || (@kernels.length == 1 && @kernels.first.length == 1 && @kernels.first.first == 1.0)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def convolve(history, kernel)
|
|
62
|
+
kernel.each_with_index.sum do |coefficient, offset|
|
|
63
|
+
history_index = history.length - 1 - offset
|
|
64
|
+
next 0.0 if history_index.negative?
|
|
65
|
+
|
|
66
|
+
coefficient * history[history_index]
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def coerce_buffer(source)
|
|
71
|
+
return source if source.is_a?(IO::Buffer)
|
|
72
|
+
|
|
73
|
+
IO::Buffer.load(source)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def normalize_kernels(channel_arrays)
|
|
77
|
+
kernels = channel_arrays.map { |channel| channel.map(&:to_f) }.reject(&:empty?)
|
|
78
|
+
return [[1.0]] if kernels.empty?
|
|
79
|
+
return kernels unless @normalize
|
|
80
|
+
|
|
81
|
+
peak = kernels.flatten.map(&:abs).max || 0.0
|
|
82
|
+
return kernels if peak.zero?
|
|
83
|
+
|
|
84
|
+
kernels.map { |kernel| kernel.map { |sample| sample / peak } }
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def ensure_history(channel_index, kernel_length)
|
|
88
|
+
required = [channel_index.to_i, 0].max
|
|
89
|
+
while @histories.length <= required
|
|
90
|
+
@histories << Array.new([kernel_length - 1, 0].max, 0.0)
|
|
91
|
+
end
|
|
92
|
+
if @histories[required].length != [kernel_length - 1, 0].max
|
|
93
|
+
@histories[required] = Array.new([kernel_length - 1, 0].max, 0.0)
|
|
94
|
+
end
|
|
95
|
+
@histories[required]
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deftones
|
|
4
|
+
module Component
|
|
5
|
+
class CrossFade < Core::AudioNode
|
|
6
|
+
attr_reader :input, :output, :a, :b, :fade
|
|
7
|
+
|
|
8
|
+
def initialize(fade: 0.5, context: Deftones.context)
|
|
9
|
+
super(context: context)
|
|
10
|
+
@a = Core::Gain.new(context: context)
|
|
11
|
+
@b = Core::Gain.new(context: context)
|
|
12
|
+
@fade = Core::Signal.new(value: fade, units: :number, context: context)
|
|
13
|
+
@input = @a
|
|
14
|
+
@output = self
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def fade=(value)
|
|
18
|
+
@fade.value = value
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def left
|
|
22
|
+
@a
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def right
|
|
26
|
+
@b
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def render(num_frames, start_frame = 0, cache = {})
|
|
30
|
+
cache_key = [object_id, start_frame, num_frames]
|
|
31
|
+
return cache.fetch(cache_key).dup if cache.key?(cache_key)
|
|
32
|
+
|
|
33
|
+
fades = @fade.process(num_frames, start_frame)
|
|
34
|
+
a_buffer = @a.render(num_frames, start_frame, cache)
|
|
35
|
+
b_buffer = @b.render(num_frames, start_frame, cache)
|
|
36
|
+
output_buffer = Array.new(num_frames) do |index|
|
|
37
|
+
DSP::Helpers.mix(a_buffer[index], b_buffer[index], fades[index].clamp(0.0, 1.0))
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
cache[cache_key] = output_buffer
|
|
41
|
+
output_buffer.dup
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def render_block(num_frames, start_frame = 0, cache = {})
|
|
45
|
+
cache_key = [object_id, :block, start_frame, num_frames]
|
|
46
|
+
return cache.fetch(cache_key).dup if cache.key?(cache_key)
|
|
47
|
+
|
|
48
|
+
fades = @fade.process(num_frames, start_frame)
|
|
49
|
+
a_block = @a.send(:render_block, num_frames, start_frame, cache)
|
|
50
|
+
b_block = @b.send(:render_block, num_frames, start_frame, cache)
|
|
51
|
+
channels = [a_block.channels, b_block.channels].max
|
|
52
|
+
mixed = Core::AudioBlock.from_channel_data(
|
|
53
|
+
Array.new(channels) do |channel_index|
|
|
54
|
+
a_channel = a_block.fit_channels(channels).channel(channel_index)
|
|
55
|
+
b_channel = b_block.fit_channels(channels).channel(channel_index)
|
|
56
|
+
Array.new(num_frames) do |index|
|
|
57
|
+
DSP::Helpers.mix(a_channel[index], b_channel[index], fades[index].clamp(0.0, 1.0))
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
cache[cache_key] = mixed
|
|
63
|
+
mixed.dup
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deftones
|
|
4
|
+
module Component
|
|
5
|
+
class Envelope < Core::AudioNode
|
|
6
|
+
STATES = %i[idle attack decay sustain release].freeze
|
|
7
|
+
|
|
8
|
+
attr_accessor :attack, :decay, :sustain, :release
|
|
9
|
+
attr_reader :state
|
|
10
|
+
|
|
11
|
+
def initialize(attack: 0.01, decay: 0.1, sustain: 0.5, release: 1.0, context: Deftones.context)
|
|
12
|
+
super(context: context)
|
|
13
|
+
@attack = attack.to_f
|
|
14
|
+
@decay = decay.to_f
|
|
15
|
+
@sustain = sustain.to_f
|
|
16
|
+
@release = release.to_f
|
|
17
|
+
@state = :idle
|
|
18
|
+
@events = []
|
|
19
|
+
@current_value = 0.0
|
|
20
|
+
@velocity = 1.0
|
|
21
|
+
@stage_started_at = 0.0
|
|
22
|
+
@stage_from_value = 0.0
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def trigger_attack(time = nil, velocity = 1.0)
|
|
26
|
+
schedule_event(:attack, resolve_time(time), velocity.to_f)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def trigger_release(time = nil)
|
|
30
|
+
schedule_event(:release, resolve_time(time), nil)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def trigger_attack_release(duration, time = nil, velocity = 1.0)
|
|
34
|
+
attack_time = resolve_time(time)
|
|
35
|
+
trigger_attack(attack_time, velocity)
|
|
36
|
+
trigger_release(attack_time + Deftones::Music::Time.parse(duration))
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def active?
|
|
40
|
+
@state != :idle || !@events.empty?
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def idle?
|
|
44
|
+
!active?
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def multichannel_process?
|
|
48
|
+
true
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def process(input_block, num_frames, start_frame, _cache)
|
|
52
|
+
values = Array.new(num_frames) do |index|
|
|
53
|
+
time = sample_time(start_frame + index)
|
|
54
|
+
consume_events(time)
|
|
55
|
+
@current_value = envelope_value_at(time)
|
|
56
|
+
@current_value
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
Core::AudioBlock.from_channel_data(
|
|
60
|
+
input_block.channel_data.map do |channel|
|
|
61
|
+
Array.new(num_frames) { |index| channel[index] * values[index] }
|
|
62
|
+
end
|
|
63
|
+
)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
private
|
|
67
|
+
|
|
68
|
+
def schedule_event(type, time, velocity)
|
|
69
|
+
@events << { type: type, time: time, velocity: velocity }
|
|
70
|
+
@events.sort_by! { |event| event[:time] }
|
|
71
|
+
self
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def consume_events(time)
|
|
75
|
+
while @events.any? && @events.first[:time] <= time
|
|
76
|
+
event = @events.shift
|
|
77
|
+
|
|
78
|
+
case event[:type]
|
|
79
|
+
when :attack
|
|
80
|
+
@state = :attack
|
|
81
|
+
@velocity = event[:velocity] || 1.0
|
|
82
|
+
@stage_started_at = event[:time]
|
|
83
|
+
@stage_from_value = @current_value
|
|
84
|
+
when :release
|
|
85
|
+
@state = :release
|
|
86
|
+
@stage_started_at = event[:time]
|
|
87
|
+
@stage_from_value = @current_value
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def envelope_value_at(time)
|
|
93
|
+
case @state
|
|
94
|
+
when :idle
|
|
95
|
+
0.0
|
|
96
|
+
when :attack
|
|
97
|
+
attack_value(time)
|
|
98
|
+
when :decay
|
|
99
|
+
decay_value(time)
|
|
100
|
+
when :sustain
|
|
101
|
+
@velocity * @sustain
|
|
102
|
+
when :release
|
|
103
|
+
release_value(time)
|
|
104
|
+
else
|
|
105
|
+
0.0
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def attack_value(time)
|
|
110
|
+
return transition_to(:decay, @velocity, time) if @attack <= 0.0
|
|
111
|
+
|
|
112
|
+
progress = (time - @stage_started_at) / @attack
|
|
113
|
+
return transition_to(:decay, @velocity, @stage_started_at + @attack) if progress >= 1.0
|
|
114
|
+
|
|
115
|
+
lerp(@stage_from_value, @velocity, progress)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def decay_value(time)
|
|
119
|
+
sustain_level = @velocity * @sustain
|
|
120
|
+
return transition_to(:sustain, sustain_level, time) if @decay <= 0.0
|
|
121
|
+
|
|
122
|
+
progress = (time - @stage_started_at) / @decay
|
|
123
|
+
return transition_to(:sustain, sustain_level, @stage_started_at + @decay) if progress >= 1.0
|
|
124
|
+
|
|
125
|
+
lerp(@velocity, sustain_level, progress)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def release_value(time)
|
|
129
|
+
return transition_to(:idle, 0.0, time) if @release <= 0.0
|
|
130
|
+
|
|
131
|
+
progress = (time - @stage_started_at) / @release
|
|
132
|
+
return transition_to(:idle, 0.0, @stage_started_at + @release) if progress >= 1.0
|
|
133
|
+
|
|
134
|
+
lerp(@stage_from_value, 0.0, progress)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def transition_to(next_state, value, time)
|
|
138
|
+
@state = next_state
|
|
139
|
+
@stage_started_at = time
|
|
140
|
+
@stage_from_value = value
|
|
141
|
+
@current_value = value
|
|
142
|
+
value
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def lerp(from, to, progress)
|
|
146
|
+
from + ((to - from) * progress.clamp(0.0, 1.0))
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def resolve_time(time)
|
|
150
|
+
return context.current_time if time.nil?
|
|
151
|
+
|
|
152
|
+
Deftones::Music::Time.parse(time)
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def sample_time(frame_index)
|
|
156
|
+
frame_index.to_f / context.sample_rate
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
end
|