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.
Files changed (135) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +5 -0
  3. data/CHANGELOG.md +12 -0
  4. data/LICENSE.txt +21 -0
  5. data/README.md +197 -0
  6. data/Rakefile +8 -0
  7. data/examples/poly_chord.rb +14 -0
  8. data/examples/render_sampler.rb +16 -0
  9. data/examples/render_sequence.rb +13 -0
  10. data/examples/render_synth.rb +18 -0
  11. data/lib/deftones/analysis/analyser.rb +162 -0
  12. data/lib/deftones/analysis/dc_meter.rb +56 -0
  13. data/lib/deftones/analysis/fft.rb +128 -0
  14. data/lib/deftones/analysis/meter.rb +83 -0
  15. data/lib/deftones/analysis/waveform.rb +93 -0
  16. data/lib/deftones/component/amplitude_envelope.rb +8 -0
  17. data/lib/deftones/component/biquad_filter.rb +7 -0
  18. data/lib/deftones/component/channel.rb +109 -0
  19. data/lib/deftones/component/compressor.rb +64 -0
  20. data/lib/deftones/component/convolver.rb +99 -0
  21. data/lib/deftones/component/cross_fade.rb +67 -0
  22. data/lib/deftones/component/envelope.rb +160 -0
  23. data/lib/deftones/component/eq3.rb +73 -0
  24. data/lib/deftones/component/feedback_comb_filter.rb +75 -0
  25. data/lib/deftones/component/filter.rb +75 -0
  26. data/lib/deftones/component/follower.rb +55 -0
  27. data/lib/deftones/component/frequency_envelope.rb +24 -0
  28. data/lib/deftones/component/gate.rb +46 -0
  29. data/lib/deftones/component/lfo.rb +88 -0
  30. data/lib/deftones/component/limiter.rb +11 -0
  31. data/lib/deftones/component/lowpass_comb_filter.rb +43 -0
  32. data/lib/deftones/component/merge.rb +45 -0
  33. data/lib/deftones/component/mid_side_compressor.rb +43 -0
  34. data/lib/deftones/component/mid_side_merge.rb +53 -0
  35. data/lib/deftones/component/mid_side_split.rb +54 -0
  36. data/lib/deftones/component/mono.rb +8 -0
  37. data/lib/deftones/component/multiband_compressor.rb +70 -0
  38. data/lib/deftones/component/multiband_split.rb +133 -0
  39. data/lib/deftones/component/one_pole_filter.rb +71 -0
  40. data/lib/deftones/component/pan_vol.rb +26 -0
  41. data/lib/deftones/component/panner.rb +75 -0
  42. data/lib/deftones/component/panner3d.rb +322 -0
  43. data/lib/deftones/component/solo.rb +56 -0
  44. data/lib/deftones/component/split.rb +47 -0
  45. data/lib/deftones/component/volume.rb +31 -0
  46. data/lib/deftones/context.rb +213 -0
  47. data/lib/deftones/core/audio_block.rb +82 -0
  48. data/lib/deftones/core/audio_node.rb +262 -0
  49. data/lib/deftones/core/clock.rb +91 -0
  50. data/lib/deftones/core/computed_signal.rb +69 -0
  51. data/lib/deftones/core/delay.rb +44 -0
  52. data/lib/deftones/core/effect.rb +66 -0
  53. data/lib/deftones/core/emitter.rb +51 -0
  54. data/lib/deftones/core/gain.rb +39 -0
  55. data/lib/deftones/core/instrument.rb +109 -0
  56. data/lib/deftones/core/param.rb +31 -0
  57. data/lib/deftones/core/signal.rb +452 -0
  58. data/lib/deftones/core/signal_operator_methods.rb +73 -0
  59. data/lib/deftones/core/signal_operators.rb +138 -0
  60. data/lib/deftones/core/signal_shapers.rb +83 -0
  61. data/lib/deftones/core/source.rb +213 -0
  62. data/lib/deftones/core/synced_signal.rb +88 -0
  63. data/lib/deftones/destination.rb +132 -0
  64. data/lib/deftones/draw.rb +100 -0
  65. data/lib/deftones/dsp/biquad.rb +129 -0
  66. data/lib/deftones/dsp/delay_line.rb +41 -0
  67. data/lib/deftones/dsp/helpers.rb +25 -0
  68. data/lib/deftones/effect/auto_filter.rb +92 -0
  69. data/lib/deftones/effect/auto_panner.rb +57 -0
  70. data/lib/deftones/effect/auto_wah.rb +98 -0
  71. data/lib/deftones/effect/bit_crusher.rb +38 -0
  72. data/lib/deftones/effect/chebyshev.rb +36 -0
  73. data/lib/deftones/effect/chorus.rb +73 -0
  74. data/lib/deftones/effect/distortion.rb +22 -0
  75. data/lib/deftones/effect/feedback_delay.rb +38 -0
  76. data/lib/deftones/effect/freeverb.rb +11 -0
  77. data/lib/deftones/effect/frequency_shifter.rb +89 -0
  78. data/lib/deftones/effect/jc_reverb.rb +11 -0
  79. data/lib/deftones/effect/modulation_control.rb +159 -0
  80. data/lib/deftones/effect/phaser.rb +72 -0
  81. data/lib/deftones/effect/ping_pong_delay.rb +40 -0
  82. data/lib/deftones/effect/pitch_shift.rb +156 -0
  83. data/lib/deftones/effect/reverb.rb +71 -0
  84. data/lib/deftones/effect/stereo_widener.rb +34 -0
  85. data/lib/deftones/effect/tremolo.rb +52 -0
  86. data/lib/deftones/effect/vibrato.rb +47 -0
  87. data/lib/deftones/event/callback_behavior.rb +61 -0
  88. data/lib/deftones/event/loop.rb +53 -0
  89. data/lib/deftones/event/part.rb +51 -0
  90. data/lib/deftones/event/pattern.rb +94 -0
  91. data/lib/deftones/event/sequence.rb +87 -0
  92. data/lib/deftones/event/tone_event.rb +77 -0
  93. data/lib/deftones/event/transport.rb +276 -0
  94. data/lib/deftones/instrument/am_synth.rb +56 -0
  95. data/lib/deftones/instrument/duo_synth.rb +68 -0
  96. data/lib/deftones/instrument/fm_synth.rb +60 -0
  97. data/lib/deftones/instrument/membrane_synth.rb +60 -0
  98. data/lib/deftones/instrument/metal_synth.rb +61 -0
  99. data/lib/deftones/instrument/mono_synth.rb +88 -0
  100. data/lib/deftones/instrument/noise_synth.rb +56 -0
  101. data/lib/deftones/instrument/pluck_synth.rb +41 -0
  102. data/lib/deftones/instrument/poly_synth.rb +96 -0
  103. data/lib/deftones/instrument/sampler.rb +97 -0
  104. data/lib/deftones/instrument/synth.rb +60 -0
  105. data/lib/deftones/io/buffer.rb +352 -0
  106. data/lib/deftones/io/buffers.rb +77 -0
  107. data/lib/deftones/io/recorder.rb +89 -0
  108. data/lib/deftones/listener.rb +120 -0
  109. data/lib/deftones/music/frequency.rb +128 -0
  110. data/lib/deftones/music/midi.rb +206 -0
  111. data/lib/deftones/music/note.rb +58 -0
  112. data/lib/deftones/music/ticks.rb +106 -0
  113. data/lib/deftones/music/time.rb +209 -0
  114. data/lib/deftones/music/transport_time.rb +94 -0
  115. data/lib/deftones/music/unit_helpers.rb +30 -0
  116. data/lib/deftones/offline_context.rb +46 -0
  117. data/lib/deftones/portaudio_support.rb +112 -0
  118. data/lib/deftones/source/am_oscillator.rb +42 -0
  119. data/lib/deftones/source/fat_oscillator.rb +49 -0
  120. data/lib/deftones/source/fm_oscillator.rb +47 -0
  121. data/lib/deftones/source/grain_player.rb +198 -0
  122. data/lib/deftones/source/karplus_strong.rb +51 -0
  123. data/lib/deftones/source/noise.rb +99 -0
  124. data/lib/deftones/source/omni_oscillator.rb +175 -0
  125. data/lib/deftones/source/oscillator.rb +74 -0
  126. data/lib/deftones/source/player.rb +228 -0
  127. data/lib/deftones/source/players.rb +133 -0
  128. data/lib/deftones/source/pulse_oscillator.rb +38 -0
  129. data/lib/deftones/source/pwm_oscillator.rb +49 -0
  130. data/lib/deftones/source/tone_buffer_source.rb +136 -0
  131. data/lib/deftones/source/tone_oscillator_node.rb +65 -0
  132. data/lib/deftones/source/user_media.rb +519 -0
  133. data/lib/deftones/version.rb +5 -0
  134. data/lib/deftones.rb +542 -0
  135. 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