deftones 0.1.0 → 1.0.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 (66) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +11 -6
  3. data/README.md +5 -0
  4. data/Rakefile +50 -1
  5. data/lib/deftones/analysis/meter.rb +22 -2
  6. data/lib/deftones/component/channel.rb +1 -1
  7. data/lib/deftones/component/compressor.rb +127 -22
  8. data/lib/deftones/component/filter.rb +29 -19
  9. data/lib/deftones/component/merge.rb +14 -0
  10. data/lib/deftones/component/multiband_compressor.rb +1 -1
  11. data/lib/deftones/component/one_pole_filter.rb +10 -3
  12. data/lib/deftones/component/panner.rb +25 -2
  13. data/lib/deftones/component/panner3d.rb +0 -10
  14. data/lib/deftones/component/split.rb +14 -0
  15. data/lib/deftones/context.rb +90 -9
  16. data/lib/deftones/core/audio_block.rb +64 -5
  17. data/lib/deftones/core/audio_node.rb +98 -8
  18. data/lib/deftones/core/gain.rb +0 -8
  19. data/lib/deftones/core/instrument.rb +52 -10
  20. data/lib/deftones/core/param.rb +51 -1
  21. data/lib/deftones/core/signal.rb +79 -28
  22. data/lib/deftones/core/source.rb +71 -11
  23. data/lib/deftones/destination.rb +41 -17
  24. data/lib/deftones/draw.rb +6 -10
  25. data/lib/deftones/dsp/biquad.rb +9 -4
  26. data/lib/deftones/dsp/delay_line.rb +2 -2
  27. data/lib/deftones/dsp/helpers.rb +7 -0
  28. data/lib/deftones/effect/bit_crusher.rb +10 -2
  29. data/lib/deftones/effect/chebyshev.rb +7 -3
  30. data/lib/deftones/effect/distortion.rb +5 -3
  31. data/lib/deftones/effect/feedback_delay.rb +2 -1
  32. data/lib/deftones/effect/oversampling.rb +43 -0
  33. data/lib/deftones/effect/phaser.rb +2 -1
  34. data/lib/deftones/effect/pitch_shift.rb +1 -2
  35. data/lib/deftones/effect/reverb.rb +73 -5
  36. data/lib/deftones/event/callback_behavior.rb +7 -3
  37. data/lib/deftones/event/loop.rb +7 -2
  38. data/lib/deftones/event/part.rb +18 -3
  39. data/lib/deftones/event/pattern.rb +51 -6
  40. data/lib/deftones/event/sequence.rb +19 -5
  41. data/lib/deftones/event/tone_event.rb +7 -2
  42. data/lib/deftones/event/transport.rb +243 -21
  43. data/lib/deftones/instrument/poly_synth.rb +81 -15
  44. data/lib/deftones/instrument/sampler.rb +53 -10
  45. data/lib/deftones/io/buffer.rb +376 -55
  46. data/lib/deftones/io/buffers.rb +28 -4
  47. data/lib/deftones/io/recorder.rb +2 -1
  48. data/lib/deftones/music/frequency.rb +13 -8
  49. data/lib/deftones/music/midi.rb +132 -9
  50. data/lib/deftones/music/note.rb +13 -3
  51. data/lib/deftones/music/time.rb +42 -4
  52. data/lib/deftones/offline_context.rb +194 -17
  53. data/lib/deftones/portaudio_support.rb +68 -9
  54. data/lib/deftones/source/fat_oscillator.rb +28 -9
  55. data/lib/deftones/source/grain_player.rb +49 -2
  56. data/lib/deftones/source/noise.rb +42 -10
  57. data/lib/deftones/source/omni_oscillator.rb +1 -2
  58. data/lib/deftones/source/oscillator.rb +83 -19
  59. data/lib/deftones/source/player.rb +24 -6
  60. data/lib/deftones/source/players.rb +39 -6
  61. data/lib/deftones/source/tone_buffer_source.rb +12 -6
  62. data/lib/deftones/source/tone_oscillator_node.rb +4 -3
  63. data/lib/deftones/source/user_media.rb +83 -10
  64. data/lib/deftones/version.rb +1 -1
  65. data/lib/deftones.rb +108 -31
  66. metadata +3 -44
@@ -9,6 +9,7 @@ module Deftones
9
9
 
10
10
  attr_reader :context, :units, :input, :output, :default_value
11
11
  attr_accessor :min_value, :max_value, :convert_values
12
+ attr_reader :clamp_values
12
13
 
13
14
  def initialize(value: 0.0, units: :number, context: Deftones.context)
14
15
  @context = context
@@ -21,6 +22,8 @@ module Deftones
21
22
  @min_value = -Float::INFINITY
22
23
  @max_value = Float::INFINITY
23
24
  @events = []
25
+ @next_event_order = 0
26
+ @clamp_values = false
24
27
  @disposed = false
25
28
  end
26
29
 
@@ -29,7 +32,7 @@ module Deftones
29
32
  end
30
33
 
31
34
  def value=(new_value)
32
- @base_value = coerce_value(new_value)
35
+ @base_value = bounded_value(coerce_value(new_value))
33
36
  @events.clear
34
37
  end
35
38
 
@@ -50,7 +53,7 @@ module Deftones
50
53
  resolved_end = context.current_time + Deftones::Music::Time.parse(duration)
51
54
  schedule_automation(
52
55
  :linear,
53
- coerce_value(target_value),
56
+ bounded_value(coerce_value(target_value)),
54
57
  start_time: resolve_automation_start_time(resolved_end),
55
58
  end_time: resolved_end
56
59
  )
@@ -60,7 +63,7 @@ module Deftones
60
63
  resolved_end = resolve_time(end_time)
61
64
  schedule_automation(
62
65
  :linear,
63
- coerce_value(target_value),
66
+ bounded_value(coerce_value(target_value)),
64
67
  start_time: resolve_automation_start_time(resolved_end),
65
68
  end_time: resolved_end
66
69
  )
@@ -70,7 +73,7 @@ module Deftones
70
73
  resolved_end = context.current_time + Deftones::Music::Time.parse(duration)
71
74
  schedule_automation(
72
75
  :exponential,
73
- coerce_value(target_value),
76
+ bounded_value(coerce_value(target_value)),
74
77
  start_time: resolve_automation_start_time(resolved_end),
75
78
  end_time: resolved_end
76
79
  )
@@ -80,45 +83,42 @@ module Deftones
80
83
  resolved_end = resolve_time(end_time)
81
84
  schedule_automation(
82
85
  :exponential,
83
- coerce_value(target_value),
86
+ bounded_value(coerce_value(target_value)),
84
87
  start_time: resolve_automation_start_time(resolved_end),
85
88
  end_time: resolved_end
86
89
  )
87
90
  end
88
91
 
89
92
  def set_value_at_time(target_value, time)
90
- @events << { type: :set, time: resolve_time(time), value: coerce_value(target_value) }
91
- sort_events!
93
+ add_event(type: :set, time: resolve_time(time), value: bounded_value(coerce_value(target_value)))
92
94
  self
93
95
  end
94
96
 
95
97
  def set_value_curve_at_time(values, start_time, duration)
96
- curve = Array(values).map { |value| coerce_value(value) }
98
+ curve = Array(values).map { |value| bounded_value(coerce_value(value)) }
97
99
  resolved_start = resolve_time(start_time)
98
100
  resolved_duration = Deftones::Music::Time.parse(duration)
99
- @events << {
101
+ add_event(
100
102
  type: :curve,
101
103
  time: resolved_start,
102
104
  start_time: resolved_start,
103
105
  end_time: resolved_start + resolved_duration,
104
106
  duration: resolved_duration,
105
107
  values: curve
106
- }
107
- sort_events!
108
+ )
108
109
  self
109
110
  end
110
111
 
111
112
  def set_target_at_time(target_value, start_time, time_constant)
112
113
  resolved_start = resolve_time(start_time)
113
- @events << {
114
+ add_event(
114
115
  type: :target,
115
116
  time: resolved_start,
116
117
  start_time: resolved_start,
117
118
  time_constant: [Deftones::Music::Time.parse(time_constant), 1.0e-6].max,
118
119
  from: value_at(resolved_start),
119
- to: coerce_value(target_value)
120
- }
121
- sort_events!
120
+ to: bounded_value(coerce_value(target_value))
121
+ )
122
122
  self
123
123
  end
124
124
 
@@ -152,6 +152,11 @@ module Deftones
152
152
  value_at(resolve_time(time))
153
153
  end
154
154
 
155
+ def clamp_values=(value)
156
+ @clamp_values = !!value
157
+ @base_value = bounded_value(@base_value)
158
+ end
159
+
155
160
  def dispose
156
161
  @events.clear
157
162
  @disposed = true
@@ -174,9 +179,9 @@ module Deftones
174
179
  alias getValueAtTime get_value_at_time
175
180
 
176
181
  def connect(destination, output_index: 0, input_index: 0)
177
- _ = output_index
178
- _ = input_index
179
- return self if destination.nil?
182
+ raise ArgumentError, "destination is required" if destination.nil?
183
+ raise ArgumentError, "output_index must be 0 for Signal connections" unless Integer(output_index).zero?
184
+ raise ArgumentError, "input_index must be 0 for Signal connections" unless Integer(input_index).zero?
180
185
 
181
186
  if destination.respond_to?(:value=)
182
187
  destination.value = value
@@ -190,7 +195,10 @@ module Deftones
190
195
  self
191
196
  end
192
197
 
193
- def set(**params)
198
+ def set(strict: false, **params)
199
+ unknown = params.keys.reject { |key| respond_to?(:"#{key}=") }
200
+ raise ArgumentError, "Unknown parameter(s): #{unknown.join(', ')}" if strict && unknown.any?
201
+
194
202
  params.each do |key, entry|
195
203
  writer = :"#{key}="
196
204
  public_send(writer, entry) if respond_to?(writer)
@@ -198,11 +206,19 @@ module Deftones
198
206
  self
199
207
  end
200
208
 
201
- def get(*keys)
202
- keys.flatten.each_with_object({}) do |key, values|
209
+ def get(*keys, strict: false)
210
+ unknown = []
211
+ values = keys.flatten.each_with_object({}) do |key, collected|
203
212
  reader = key.to_sym
204
- values[reader] = public_send(reader) if respond_to?(reader)
213
+ if respond_to?(reader)
214
+ collected[reader] = public_send(reader)
215
+ else
216
+ unknown << reader
217
+ end
205
218
  end
219
+ raise ArgumentError, "Unknown parameter(s): #{unknown.join(', ')}" if strict && unknown.any?
220
+
221
+ values
206
222
  end
207
223
 
208
224
  def get_defaults
@@ -252,6 +268,8 @@ module Deftones
252
268
  alias minValue= min_value=
253
269
  alias maxValue max_value
254
270
  alias maxValue= max_value=
271
+ alias clampValues clamp_values
272
+ alias clampValues= clamp_values=
255
273
  alias defaultValue default_value
256
274
  alias getDefaults get_defaults
257
275
  alias toSeconds to_seconds
@@ -261,11 +279,24 @@ module Deftones
261
279
  alias exponentialApproachValueAtTime exponential_approach_value_at_time
262
280
 
263
281
  def process(num_frames, start_frame = 0)
282
+ return Array.new(num_frames, bounded_value(@base_value)) if @events.empty?
283
+
264
284
  Array.new(num_frames) do |offset|
265
285
  value_at(sample_time(start_frame + offset))
266
286
  end
267
287
  end
268
288
 
289
+ def automation_event_count
290
+ @events.length
291
+ end
292
+
293
+ def automation_events?
294
+ @events.any?
295
+ end
296
+
297
+ alias automationEventCount automation_event_count
298
+ alias automationEvents? automation_events?
299
+
269
300
  def value_at(time)
270
301
  current_value = @base_value
271
302
 
@@ -300,14 +331,14 @@ module Deftones
300
331
  end
301
332
  end
302
333
 
303
- current_value
334
+ bounded_value(current_value)
304
335
  end
305
336
 
306
337
  private
307
338
 
308
339
  def schedule_automation(type, target_value, start_time:, end_time:)
309
340
  duration_in_seconds = [end_time.to_f - start_time.to_f, 0.0].max
310
- @events << {
341
+ add_event(
311
342
  type: type,
312
343
  start_time: start_time,
313
344
  end_time: end_time,
@@ -315,13 +346,26 @@ module Deftones
315
346
  duration: duration_in_seconds,
316
347
  from: value_at(start_time),
317
348
  to: target_value
318
- }
319
- sort_events!
349
+ )
320
350
  self
321
351
  end
322
352
 
323
- def sort_events!
324
- @events.sort_by! { |event| event.fetch(:time, event[:start_time]) }
353
+ def add_event(event)
354
+ event[:order] = @next_event_order
355
+ @next_event_order += 1
356
+ insert_event(event)
357
+ end
358
+
359
+ def insert_event(event)
360
+ key = event_sort_key(event)
361
+ index = @events.bsearch_index { |existing| (event_sort_key(existing) <=> key).positive? }
362
+ return @events << event unless index
363
+
364
+ @events.insert(index, event)
365
+ end
366
+
367
+ def event_sort_key(event)
368
+ [event.fetch(:time, event[:start_time]), event.fetch(:order, 0)]
325
369
  end
326
370
 
327
371
  def resolve_automation_start_time(end_time)
@@ -447,6 +491,13 @@ module Deftones
447
491
  def db_to_gain(value)
448
492
  10.0**(value / 20.0)
449
493
  end
494
+
495
+ def bounded_value(value)
496
+ return value unless @clamp_values
497
+ return value unless value.is_a?(Numeric)
498
+
499
+ value.clamp(@min_value, @max_value)
500
+ end
450
501
  end
451
502
  end
452
503
  end
@@ -5,30 +5,91 @@ module Deftones
5
5
  class Source < AudioNode
6
6
  class VolumeProxy
7
7
  attr_reader :source
8
- attr_accessor :value
9
8
 
10
9
  def initialize(source, value: 0.0)
11
10
  @source = source
12
11
  @value = value.to_f
12
+ @automation = Signal.new(value: @value, units: :number, context: source.context)
13
+ end
14
+
15
+ def value
16
+ @value
13
17
  end
14
18
 
15
19
  def value=(new_value)
16
20
  @value = new_value.to_f
21
+ @automation.value = @value
17
22
  source.send(:apply_volume!)
18
23
  end
19
24
 
20
- def ramp_to(target_value, _duration = nil)
21
- self.value = target_value
25
+ def ramp_to(target_value, duration = nil)
26
+ return assign_immediately(target_value) if duration.nil?
27
+
28
+ resolved_duration = Deftones::Music::Time.parse(duration)
29
+ return assign_immediately(target_value) if resolved_duration <= 0.0
30
+
31
+ @value = target_value.to_f
32
+ @automation.linear_ramp_to_value_at_time(@value, source.context.current_time + resolved_duration)
33
+ source.send(:apply_volume!)
34
+ self
35
+ end
36
+
37
+ def set_value_at_time(target_value, time)
38
+ @value = target_value.to_f
39
+ @automation.set_value_at_time(@value, time)
40
+ source.send(:apply_volume!)
41
+ self
42
+ end
43
+
44
+ def gains(num_frames, start_frame)
45
+ return Array.new(num_frames, 0.0) if source.mute?
46
+
47
+ @automation.process(num_frames, start_frame).map { |db| Deftones.db_to_gain(db) }
48
+ end
49
+
50
+ def current_gain
51
+ return 0.0 if source.mute?
52
+
53
+ Deftones.db_to_gain(@automation.get_value_at_time(source.context.current_time))
54
+ end
55
+
56
+ def cancel_scheduled_values(after_time = 0)
57
+ @automation.cancel_scheduled_values(after_time)
22
58
  self
23
59
  end
24
60
 
25
- alias linear_ramp_to ramp_to
26
- alias exponential_ramp_to ramp_to
61
+ def cancel_and_hold_at_time(time)
62
+ @automation.cancel_and_hold_at_time(time)
63
+ @value = @automation.get_value_at_time(time)
64
+ source.send(:apply_volume!)
65
+ self
66
+ end
67
+
68
+ def linear_ramp_to(target_value, duration = nil)
69
+ ramp_to(target_value, duration)
70
+ end
71
+
72
+ def exponential_ramp_to(target_value, duration = nil)
73
+ ramp_to(target_value, duration)
74
+ end
75
+
76
+ alias setValueAtTime set_value_at_time
77
+ alias cancelScheduledValues cancel_scheduled_values
78
+ alias cancelAndHoldAtTime cancel_and_hold_at_time
79
+ alias linearRampTo linear_ramp_to
80
+ alias exponentialRampTo exponential_ramp_to
81
+
82
+ private
83
+
84
+ def assign_immediately(target_value)
85
+ self.value = target_value
86
+ self
87
+ end
27
88
  end
28
89
 
29
90
  attr_reader :volume
30
91
  attr_accessor :onstop
31
- attr_accessor :mute
92
+ attr_reader :mute
32
93
 
33
94
  def initialize(context: Deftones.context)
34
95
  super(context: context)
@@ -127,16 +188,15 @@ module Deftones
127
188
  end
128
189
 
129
190
  def render(num_frames, start_frame = 0, cache = {})
130
- output_buffer = super.map { |sample| sample * @output_gain }
131
- notify_stop_in_window(start_frame, num_frames)
132
- output_buffer
191
+ super
133
192
  end
134
193
 
135
194
  def render_block(num_frames, start_frame = 0, cache = {})
136
195
  output_block = super
196
+ volume_gains = @volume.gains(num_frames, start_frame)
137
197
  scaled = AudioBlock.from_channel_data(
138
198
  output_block.channel_data.map do |channel|
139
- channel.map { |sample| sample * @output_gain }
199
+ channel.each_with_index.map { |sample, index| sample * volume_gains[index] }
140
200
  end
141
201
  )
142
202
  notify_stop_in_window(start_frame, num_frames)
@@ -159,7 +219,7 @@ module Deftones
159
219
  end
160
220
 
161
221
  def apply_volume!
162
- @output_gain = mute ? 0.0 : Deftones.db_to_gain(@volume.value)
222
+ @output_gain = @volume.current_gain
163
223
  self
164
224
  end
165
225
 
@@ -1,10 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "forwardable"
4
+
3
5
  module Deftones
4
6
  class Destination
5
7
  class VolumeProxy
6
8
  attr_reader :destination
7
- attr_accessor :value
9
+ attr_reader :value
8
10
 
9
11
  def initialize(destination, value: 0.0)
10
12
  @destination = destination
@@ -16,24 +18,56 @@ module Deftones
16
18
  destination.apply_volume!
17
19
  end
18
20
 
19
- def ramp_to(target_value, _duration = nil)
20
- self.value = target_value
21
+ def ramp_to(target_value, duration = nil)
22
+ return assign_immediately(target_value) if duration.nil?
23
+
24
+ resolved_duration = Deftones::Music::Time.parse(duration)
25
+ return assign_immediately(target_value) if resolved_duration <= 0.0
26
+
27
+ @value = target_value.to_f
28
+ destination.node.gain.linear_ramp_to_value_at_time(
29
+ destination.mute? ? 0.0 : Deftones.db_to_gain(@value),
30
+ destination.context.current_time + resolved_duration
31
+ )
21
32
  self
22
33
  end
23
34
 
24
- alias linear_ramp_to ramp_to
25
- alias exponential_ramp_to ramp_to
35
+ def linear_ramp_to(target_value, duration = nil)
36
+ ramp_to(target_value, duration)
37
+ end
38
+
39
+ def exponential_ramp_to(target_value, duration = nil)
40
+ ramp_to(target_value, duration)
41
+ end
26
42
 
27
43
  def set_value_at_time(target_value, _time)
44
+ @value = target_value.to_f
45
+ destination.node.gain.set_value_at_time(destination.mute? ? 0.0 : Deftones.db_to_gain(@value), _time)
46
+ self
47
+ end
48
+
49
+ alias linearRampTo linear_ramp_to
50
+ alias exponentialRampTo exponential_ramp_to
51
+ alias setValueAtTime set_value_at_time
52
+
53
+ private
54
+
55
+ def assign_immediately(target_value)
28
56
  self.value = target_value
29
57
  self
30
58
  end
31
59
  end
32
60
 
33
- attr_reader :context, :volume
34
- attr_accessor :mute
61
+ attr_reader :context, :mute, :volume
35
62
 
36
63
  class << self
64
+ extend Forwardable
65
+
66
+ def_delegators :node, :input, :output, :volume, :mute, :mute=, :mute?
67
+ def_delegators :node, :sample_time, :block_time, :max_channel_count
68
+ def_delegators :node, :connect, :disconnect, :chain, :fan, :apply_volume!
69
+ def_delegators :node, :sampleTime, :blockTime, :maxChannelCount
70
+
37
71
  def node(context: Deftones.context)
38
72
  registry[context.object_id] ||= new(context: context)
39
73
  end
@@ -43,16 +77,6 @@ module Deftones
43
77
  self
44
78
  end
45
79
 
46
- def method_missing(method_name, *arguments, &block)
47
- return super unless node.respond_to?(method_name)
48
-
49
- node.public_send(method_name, *arguments, &block)
50
- end
51
-
52
- def respond_to_missing?(method_name, include_private = false)
53
- node.respond_to?(method_name, include_private) || super
54
- end
55
-
56
80
  private
57
81
 
58
82
  def registry
data/lib/deftones/draw.rb CHANGED
@@ -1,8 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "forwardable"
4
+
3
5
  module Deftones
4
6
  class Draw
5
7
  class << self
8
+ extend Forwardable
9
+
10
+ def_delegators :instance, :schedule, :cancel, :dispose, :prepare_render, :advance_to
11
+
6
12
  def instance
7
13
  @instance ||= new
8
14
  end
@@ -11,16 +17,6 @@ module Deftones
11
17
  @instance = nil
12
18
  self
13
19
  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
20
  end
25
21
 
26
22
  def initialize
@@ -13,8 +13,12 @@ module Deftones
13
13
  def update(type:, frequency:, q:, gain_db:, sample_rate:)
14
14
  raise ArgumentError, "Unsupported filter type: #{type}" unless TYPES.include?(type)
15
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
16
+ normalized_sample_rate = [sample_rate.to_f, 1.0].max
17
+ nyquist = normalized_sample_rate / 2.0
18
+ lower_bound = [10.0, nyquist * 0.5].min
19
+ upper_bound = [nyquist - 1.0e-6, lower_bound].max
20
+ normalized_frequency = Helpers.clamp(frequency.to_f, lower_bound, upper_bound)
21
+ omega = (2.0 * Math::PI * normalized_frequency) / normalized_sample_rate
18
22
  sin_omega = Math.sin(omega)
19
23
  cos_omega = Math.cos(omega)
20
24
  alpha = sin_omega / (2.0 * [q.to_f, 0.001].max)
@@ -45,9 +49,10 @@ module Deftones
45
49
 
46
50
  def process_sample(sample)
47
51
  b0, b1, b2, a1, a2 = @coefficients
48
- output = (b0 * sample) + (b1 * @x1) + (b2 * @x2) - (a1 * @y1) - (a2 * @y2)
52
+ input = Helpers.flush_denormal(sample)
53
+ output = Helpers.flush_denormal((b0 * input) + (b1 * @x1) + (b2 * @x2) - (a1 * @y1) - (a2 * @y2))
49
54
  @x2 = @x1
50
- @x1 = sample
55
+ @x1 = input
51
56
  @y2 = @y1
52
57
  @y1 = output
53
58
  output
@@ -13,7 +13,7 @@ module Deftones
13
13
  end
14
14
 
15
15
  def write(sample)
16
- @buffer[@write_index] = sample
16
+ @buffer[@write_index] = Helpers.flush_denormal(sample)
17
17
  @write_index = (@write_index + 1) % @buffer.length
18
18
  sample
19
19
  end
@@ -34,7 +34,7 @@ module Deftones
34
34
  next_index = (base_index + 1) % @buffer.length
35
35
  fraction = read_position - read_position.floor
36
36
 
37
- Helpers.lerp(@buffer[base_index], @buffer[next_index], fraction)
37
+ Helpers.flush_denormal(Helpers.lerp(@buffer[base_index], @buffer[next_index], fraction))
38
38
  end
39
39
  end
40
40
  end
@@ -3,6 +3,8 @@
3
3
  module Deftones
4
4
  module DSP
5
5
  module Helpers
6
+ DENORMAL_THRESHOLD = 1.0e-300
7
+
6
8
  module_function
7
9
 
8
10
  def clamp(value, min_value, max_value)
@@ -20,6 +22,11 @@ module Deftones
20
22
  def soft_clip(value, drive = 1.0)
21
23
  Math.tanh(value * drive)
22
24
  end
25
+
26
+ def flush_denormal(value)
27
+ sample = value.to_f
28
+ sample.abs < DENORMAL_THRESHOLD ? 0.0 : sample
29
+ end
23
30
  end
24
31
  end
25
32
  end
@@ -3,12 +3,15 @@
3
3
  module Deftones
4
4
  module Effects
5
5
  class BitCrusher < Core::Effect
6
+ include Oversampling
7
+
6
8
  attr_accessor :bits, :downsample
7
9
 
8
- def initialize(bits: 8, downsample: 2, **options)
10
+ def initialize(bits: 8, downsample: 2, oversample: 1, **options)
9
11
  super(**options)
10
12
  @bits = bits.to_i
11
13
  @downsample = [downsample.to_i, 1].max
14
+ self.oversample = oversample
12
15
  @hold_counters = []
13
16
  @held_samples = []
14
17
  end
@@ -21,13 +24,18 @@ module Deftones
21
24
 
22
25
  input_buffer.map do |sample|
23
26
  if (@hold_counters[channel_index] % @downsample).zero?
24
- @held_samples[channel_index] = ((sample / step).round * step).clamp(-1.0, 1.0)
27
+ @held_samples[channel_index] =
28
+ process_oversampled([sample], channel_index) { |candidate| quantize(candidate, step) }.first
25
29
  end
26
30
  @hold_counters[channel_index] += 1
27
31
  @held_samples[channel_index]
28
32
  end
29
33
  end
30
34
 
35
+ def quantize(sample, step)
36
+ ((sample / step).round * step).clamp(-1.0, 1.0)
37
+ end
38
+
31
39
  def ensure_state(channel_index)
32
40
  required = [channel_index.to_i, 0].max
33
41
  @hold_counters.fill(0, @hold_counters.length..required)
@@ -3,18 +3,22 @@
3
3
  module Deftones
4
4
  module Effects
5
5
  class Chebyshev < Core::Effect
6
+ include Oversampling
7
+
6
8
  attr_accessor :order
7
9
 
8
- def initialize(order: 3, **options)
10
+ def initialize(order: 3, oversample: 1, **options)
9
11
  super(**options)
10
12
  @order = [order.to_i, 1].max
13
+ self.oversample = oversample
11
14
  end
12
15
 
13
16
  private
14
17
 
15
18
  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) }
19
+ process_oversampled(input_buffer, channel_index) do |sample|
20
+ chebyshev(sample.clamp(-1.0, 1.0), @order)
21
+ end
18
22
  end
19
23
 
20
24
  def chebyshev(value, order)
@@ -3,19 +3,21 @@
3
3
  module Deftones
4
4
  module Effects
5
5
  class Distortion < Core::Effect
6
+ include Oversampling
7
+
6
8
  attr_accessor :amount
7
9
 
8
- def initialize(amount: 0.5, **options)
10
+ def initialize(amount: 0.5, oversample: 1, **options)
9
11
  super(**options)
10
12
  @amount = amount.to_f
13
+ self.oversample = oversample
11
14
  end
12
15
 
13
16
  private
14
17
 
15
18
  def process_effect(input_buffer, _num_frames, _start_frame, _cache, channel_index: 0)
16
- _ = channel_index
17
19
  drive = 1.0 + (@amount * 20.0)
18
- input_buffer.map { |sample| DSP::Helpers.soft_clip(sample, drive) }
20
+ process_oversampled(input_buffer, channel_index) { |sample| DSP::Helpers.soft_clip(sample, drive) }
19
21
  end
20
22
  end
21
23
  end
@@ -22,7 +22,8 @@ module Deftones
22
22
 
23
23
  Array.new(num_frames) do |index|
24
24
  delay_samples = delays[index] * context.sample_rate
25
- delay_line.tap(delay_samples, input_sample: input_buffer[index], feedback: feedbacks[index])
25
+ feedback = feedbacks[index].to_f.clamp(-0.999, 0.999)
26
+ delay_line.tap(delay_samples, input_sample: input_buffer[index], feedback: feedback)
26
27
  end
27
28
  end
28
29