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,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