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,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Deftones
4
+ module Effects
5
+ class Distortion < Core::Effect
6
+ attr_accessor :amount
7
+
8
+ def initialize(amount: 0.5, **options)
9
+ super(**options)
10
+ @amount = amount.to_f
11
+ end
12
+
13
+ private
14
+
15
+ def process_effect(input_buffer, _num_frames, _start_frame, _cache, channel_index: 0)
16
+ _ = channel_index
17
+ drive = 1.0 + (@amount * 20.0)
18
+ input_buffer.map { |sample| DSP::Helpers.soft_clip(sample, drive) }
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Deftones
4
+ module Effects
5
+ class FeedbackDelay < Core::Effect
6
+ attr_reader :delay_time, :feedback
7
+
8
+ def initialize(delay_time: "8n", feedback: 0.3, max_delay: 2.0, context: Deftones.context, **options)
9
+ super(context: context, **options)
10
+ @delay_time = Core::Signal.new(value: delay_time, units: :time, context: context)
11
+ @feedback = Core::Signal.new(value: feedback, units: :number, context: context)
12
+ @max_delay_samples = (max_delay * context.sample_rate).ceil
13
+ @delay_lines = []
14
+ end
15
+
16
+ private
17
+
18
+ def process_effect(input_buffer, num_frames, start_frame, _cache, channel_index: 0)
19
+ delays = @delay_time.process(num_frames, start_frame)
20
+ feedbacks = @feedback.process(num_frames, start_frame)
21
+ delay_line = ensure_delay_line(channel_index)
22
+
23
+ Array.new(num_frames) do |index|
24
+ delay_samples = delays[index] * context.sample_rate
25
+ delay_line.tap(delay_samples, input_sample: input_buffer[index], feedback: feedbacks[index])
26
+ end
27
+ end
28
+
29
+ def ensure_delay_line(channel_index)
30
+ required = [channel_index.to_i, 0].max
31
+ while @delay_lines.length <= required
32
+ @delay_lines << DSP::DelayLine.new(@max_delay_samples)
33
+ end
34
+ @delay_lines[required]
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Deftones
4
+ module Effects
5
+ class Freeverb < Reverb
6
+ def initialize(decay: 0.82, pre_delay: 0.005, **options)
7
+ super(decay: decay, pre_delay: pre_delay, **options)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Deftones
4
+ module Effects
5
+ class FrequencyShifter < Core::Effect
6
+ DEFAULT_KERNEL_SIZE = 31
7
+
8
+ attr_accessor :frequency
9
+
10
+ def initialize(frequency: 30.0, context: Deftones.context, **options)
11
+ super(context: context, **options)
12
+ @frequency = frequency.to_f
13
+ @phase = 0.0
14
+ @kernel = self.class.send(:hilbert_kernel, DEFAULT_KERNEL_SIZE)
15
+ @delay = (@kernel.length / 2.0).floor
16
+ @histories = []
17
+ end
18
+
19
+ private
20
+
21
+ def process_effect_block(input_block, num_frames, _start_frame, _cache)
22
+ ensure_histories(input_block.channels)
23
+ output = Array.new(input_block.channels) { Array.new(num_frames, 0.0) }
24
+
25
+ num_frames.times do |index|
26
+ phase = @phase
27
+ input_block.channel_data.each_with_index do |channel, channel_index|
28
+ analytic = analytic_components(channel[index], channel_index)
29
+ output[channel_index][index] = shift_analytic_signal(*analytic, phase)
30
+ end
31
+ advance_phase
32
+ end
33
+
34
+ Core::AudioBlock.from_channel_data(output)
35
+ end
36
+
37
+ def analytic_components(sample, channel_index)
38
+ history = @histories[channel_index]
39
+ history.unshift(sample)
40
+ history.pop
41
+ delayed = history[@delay] || 0.0
42
+ quadrature = @kernel.each_with_index.sum(0.0) do |coefficient, index|
43
+ coefficient * (history[index] || 0.0)
44
+ end
45
+ [delayed, quadrature]
46
+ end
47
+
48
+ def shift_analytic_signal(in_phase, quadrature, phase)
49
+ radians = 2.0 * Math::PI * phase
50
+ cosine = Math.cos(radians)
51
+ sine = Math.sin(radians)
52
+ if @frequency.negative?
53
+ (in_phase * cosine) + (quadrature * sine)
54
+ else
55
+ (in_phase * cosine) - (quadrature * sine)
56
+ end
57
+ end
58
+
59
+ def advance_phase
60
+ @phase = (@phase + (shift_frequency / context.sample_rate)) % 1.0
61
+ end
62
+
63
+ def shift_frequency
64
+ @frequency.abs
65
+ end
66
+
67
+ def ensure_histories(channels)
68
+ required = [channels.to_i, 1].max
69
+ while @histories.length < required
70
+ @histories << Array.new(@kernel.length, 0.0)
71
+ end
72
+ end
73
+
74
+ def self.hilbert_kernel(size)
75
+ raise ArgumentError, "kernel size must be odd" if size.even?
76
+
77
+ center = size / 2
78
+ Array.new(size) do |index|
79
+ offset = index - center
80
+ next 0.0 if offset.zero? || offset.even?
81
+
82
+ coefficient = 2.0 / (Math::PI * offset)
83
+ window = 0.54 - (0.46 * Math.cos((2.0 * Math::PI * index) / (size - 1)))
84
+ coefficient * window
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Deftones
4
+ module Effects
5
+ class JCReverb < Reverb
6
+ def initialize(decay: 0.6, pre_delay: 0.003, **options)
7
+ super(decay: decay, pre_delay: pre_delay, **options)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,159 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Deftones
4
+ module Effects
5
+ module ModulationControl
6
+ def start(time = nil)
7
+ return schedule_modulation_event(:start, time) if synced?
8
+
9
+ @modulation_start_time = resolve_modulation_time(time)
10
+ @modulation_stop_time = nil if @modulation_stop_time && @modulation_stop_time <= @modulation_start_time
11
+ self
12
+ end
13
+
14
+ def stop(time = nil)
15
+ return schedule_modulation_event(:stop, time) if synced?
16
+
17
+ @modulation_stop_time = resolve_modulation_time(time)
18
+ self
19
+ end
20
+
21
+ def restart(time = nil)
22
+ stop(time)
23
+ start(time)
24
+ end
25
+
26
+ def cancel_stop
27
+ clear_modulation_event(:stop)
28
+ @modulation_stop_time = nil
29
+ self
30
+ end
31
+
32
+ def state(time = context.current_time)
33
+ modulation_active_at?(resolve_modulation_time(time)) ? :started : :stopped
34
+ end
35
+
36
+ def sync
37
+ @modulation_synced = true
38
+ self
39
+ end
40
+
41
+ def unsync
42
+ @modulation_synced = false
43
+ clear_modulation_event(:start)
44
+ clear_modulation_event(:stop)
45
+ self
46
+ end
47
+
48
+ def synced?
49
+ !!@modulation_synced
50
+ end
51
+
52
+ def dispose
53
+ unsync
54
+ super
55
+ end
56
+
57
+ alias cancelStop cancel_stop
58
+
59
+ private
60
+
61
+ def initialize_modulation_control
62
+ @modulation_start_time = Float::INFINITY
63
+ @modulation_stop_time = nil
64
+ @modulation_synced = false
65
+ @modulation_transport_event_ids = {}
66
+ end
67
+
68
+ def modulation_phase_for(current_time)
69
+ return nil unless modulation_active_at?(current_time)
70
+
71
+ current_phase = @phase
72
+ @phase = (@phase + (modulation_frequency / context.sample_rate)) % 1.0
73
+ current_phase
74
+ end
75
+
76
+ def modulation_frequency
77
+ @frequency.to_f
78
+ end
79
+
80
+ def modulation_sample_for(phase, type = modulation_type)
81
+ normalized_phase = phase.to_f % 1.0
82
+
83
+ case normalize_modulation_type(type)
84
+ when :square
85
+ normalized_phase < 0.5 ? 1.0 : -1.0
86
+ when :triangle
87
+ normalized_phase < 0.5 ? ((4.0 * normalized_phase) - 1.0) : (3.0 - (4.0 * normalized_phase))
88
+ when :sawtooth
89
+ (2.0 * normalized_phase) - 1.0
90
+ else
91
+ Math.sin(2.0 * Math::PI * normalized_phase)
92
+ end
93
+ end
94
+
95
+ def unipolar_modulation_value(phase, default: 0.0)
96
+ return default if phase.nil?
97
+
98
+ (modulation_sample_for(phase) + 1.0) * 0.5
99
+ end
100
+
101
+ def bipolar_modulation_value(phase, default: 0.0)
102
+ return default if phase.nil?
103
+
104
+ modulation_sample_for(phase)
105
+ end
106
+
107
+ def modulation_active_at?(time)
108
+ time >= @modulation_start_time && (@modulation_stop_time.nil? || time < @modulation_stop_time)
109
+ end
110
+
111
+ def resolve_modulation_time(time)
112
+ return context.current_time if time.nil?
113
+
114
+ Deftones::Music::Time.parse(time)
115
+ end
116
+
117
+ def resolve_modulation_transport_time(time)
118
+ return Deftones.transport.seconds if time.nil?
119
+
120
+ time
121
+ end
122
+
123
+ def schedule_modulation_event(kind, time)
124
+ clear_modulation_event(kind)
125
+ @modulation_transport_event_ids[kind] = Deftones.transport.schedule(resolve_modulation_transport_time(time)) do |scheduled_time|
126
+ if kind == :start
127
+ @modulation_start_time = scheduled_time
128
+ @modulation_stop_time = nil if @modulation_stop_time && @modulation_stop_time <= @modulation_start_time
129
+ else
130
+ @modulation_stop_time = scheduled_time
131
+ end
132
+ end
133
+ self
134
+ end
135
+
136
+ def clear_modulation_event(kind)
137
+ event_id = @modulation_transport_event_ids.delete(kind)
138
+ return self unless event_id
139
+
140
+ Deftones.transport.clear(event_id)
141
+ self
142
+ end
143
+
144
+ def modulation_type
145
+ return :sine unless respond_to?(:type)
146
+
147
+ normalize_modulation_type(type)
148
+ end
149
+
150
+ def normalize_modulation_type(type)
151
+ normalized = type.to_sym
152
+ normalized = :sawtooth if normalized == :ramp
153
+ return normalized if %i[sine square triangle sawtooth].include?(normalized)
154
+
155
+ raise ArgumentError, "Unsupported modulation type: #{type}"
156
+ end
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Deftones
4
+ module Effects
5
+ class Phaser < Core::Effect
6
+ include ModulationControl
7
+
8
+ attr_accessor :frequency, :octaves, :q, :base_frequency, :stages, :type
9
+
10
+ def initialize(
11
+ frequency: 0.5,
12
+ octaves: 3.0,
13
+ q: 0.8,
14
+ base_frequency: 300.0,
15
+ stages: 4,
16
+ type: :sine,
17
+ context: Deftones.context,
18
+ **options
19
+ )
20
+ super(context: context, **options)
21
+ @frequency = frequency.to_f
22
+ @octaves = octaves.to_f
23
+ @q = q.to_f
24
+ @base_frequency = base_frequency.to_f
25
+ @stages = stages.to_i
26
+ @type = normalize_modulation_type(type)
27
+ @phase = 0.0
28
+ @filters = []
29
+ initialize_modulation_control
30
+ end
31
+
32
+ def stages=(value)
33
+ @stages = [value.to_i, 1].max
34
+ end
35
+
36
+ private
37
+
38
+ def process_effect_block(input_block, num_frames, start_frame, _cache)
39
+ filter_banks = stage_filters(input_block.channels)
40
+ output = Array.new(input_block.channels) { Array.new(num_frames, 0.0) }
41
+
42
+ num_frames.times do |index|
43
+ current_time = (start_frame + index).to_f / context.sample_rate
44
+ phase = modulation_phase_for(current_time)
45
+ modulation = unipolar_modulation_value(phase, default: 0.5)
46
+ input_block.channel_data.each_with_index do |channel, channel_index|
47
+ output[channel_index][index] = filter_banks[channel_index].each_with_index.reduce(channel[index]) do |sample, (filter, stage_index)|
48
+ stage_position = @stages == 1 ? 0.0 : (stage_index.to_f / (@stages - 1)) - 0.5
49
+ cutoff = @base_frequency * (2.0**((modulation * @octaves) + (stage_position * 0.5)))
50
+ filter.update(type: :notch, frequency: cutoff, q: @q, gain_db: 0.0, sample_rate: context.sample_rate)
51
+ filter.process_sample(sample)
52
+ end
53
+ end
54
+ end
55
+
56
+ Core::AudioBlock.from_channel_data(output)
57
+ end
58
+
59
+ def stage_filters(channels)
60
+ required_channels = [channels.to_i, 1].max
61
+ while @filters.length < required_channels
62
+ @filters << Array.new(@stages) { DSP::Biquad.new }
63
+ end
64
+
65
+ @filters.map! do |bank|
66
+ bank.length == @stages ? bank : Array.new(@stages) { DSP::Biquad.new }
67
+ end
68
+ @filters
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Deftones
4
+ module Effects
5
+ class PingPongDelay < FeedbackDelay
6
+ def initialize(max_delay: 2.0, **options)
7
+ super(max_delay: max_delay, **options)
8
+ @left_delay_line = DSP::DelayLine.new(@max_delay_samples)
9
+ @right_delay_line = DSP::DelayLine.new(@max_delay_samples)
10
+ end
11
+
12
+ private
13
+
14
+ def process_effect_block(input_block, num_frames, start_frame, _cache)
15
+ delays = @delay_time.process(num_frames, start_frame)
16
+ feedbacks = @feedback.process(num_frames, start_frame)
17
+ source = input_block.fit_channels(2)
18
+ left = Array.new(num_frames, 0.0)
19
+ right = Array.new(num_frames, 0.0)
20
+
21
+ num_frames.times do |index|
22
+ delay_samples = delays[index] * context.sample_rate
23
+ feedback = feedbacks[index].to_f.clamp(-0.999, 0.999)
24
+ left_tap = @left_delay_line.read(delay_samples)
25
+ right_tap = @right_delay_line.read(delay_samples)
26
+ input_left = input_block.channels == 1 ? input_block.mono[index] : source.channel_data[0][index]
27
+ input_right = input_block.channels == 1 ? 0.0 : source.channel_data[1][index]
28
+
29
+ @left_delay_line.write(input_left + (right_tap * feedback))
30
+ @right_delay_line.write(input_right + (left_tap * feedback))
31
+
32
+ left[index] = left_tap
33
+ right[index] = right_tap
34
+ end
35
+
36
+ Core::AudioBlock.from_channel_data([left, right])
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,156 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Deftones
4
+ module Effects
5
+ class PitchShift < Core::Effect
6
+ attr_reader :delay_time, :feedback
7
+ attr_accessor :pitch
8
+ attr_reader :window_size
9
+
10
+ def initialize(
11
+ pitch: nil,
12
+ semitones: 0.0,
13
+ window: 0.1,
14
+ window_size: nil,
15
+ delay_time: nil,
16
+ feedback: 0.0,
17
+ context: Deftones.context,
18
+ **options
19
+ )
20
+ super(context: context, **options)
21
+ @pitch = pitch.nil? ? semitones.to_f : pitch.to_f
22
+ @window_size = resolve_window_size(window_size || window)
23
+ @delay_time = Core::Signal.new(
24
+ value: delay_time.nil? ? default_delay_time : delay_time,
25
+ units: :time,
26
+ context: context
27
+ )
28
+ @feedback = Core::Signal.new(value: feedback, units: :number, context: context)
29
+ @phase = 0.0
30
+ @delay_lines = []
31
+ @max_delay_samples = 0
32
+ ensure_delay_line_capacity!(1, @delay_time.value.to_f, pitch_ratio)
33
+ end
34
+
35
+ def semitones
36
+ @pitch
37
+ end
38
+
39
+ def semitones=(value)
40
+ self.pitch = value
41
+ end
42
+
43
+ def pitch=(value)
44
+ @pitch = value.to_f
45
+ ensure_delay_line_capacity!([@delay_lines.length, 1].max, @delay_time.value.to_f, pitch_ratio)
46
+ end
47
+
48
+ def window_size=(value)
49
+ @window_size = resolve_window_size(value)
50
+ ensure_delay_line_capacity!([@delay_lines.length, 1].max, @delay_time.value.to_f, pitch_ratio)
51
+ end
52
+
53
+ alias delayTime delay_time
54
+ alias windowSize window_size
55
+
56
+ def windowSize=(value)
57
+ self.window_size = value
58
+ end
59
+
60
+ private
61
+
62
+ def process_effect_block(input_block, num_frames, start_frame, _cache)
63
+ delays = @delay_time.process(num_frames, start_frame)
64
+ feedbacks = @feedback.process(num_frames, start_frame)
65
+ ratio = pitch_ratio
66
+ ensure_delay_line_capacity!(input_block.channels, delays.max.to_f, ratio)
67
+ output = Array.new(input_block.channels) { Array.new(num_frames, 0.0) }
68
+
69
+ num_frames.times do |index|
70
+ delay_seconds = delays[index].to_f
71
+ feedback = feedbacks[index].to_f.clamp(-0.999, 0.999)
72
+ phase = @phase
73
+ input_block.channel_data.each_with_index do |channel, channel_index|
74
+ sample = shifted_sample(@delay_lines[channel_index], delay_seconds, ratio, phase)
75
+ @delay_lines[channel_index].write(channel[index] + (sample * feedback))
76
+ output[channel_index][index] = sample
77
+ end
78
+ advance_phase
79
+ end
80
+
81
+ Core::AudioBlock.from_channel_data(output)
82
+ end
83
+
84
+ def shifted_sample(delay_line, delay_seconds, ratio, phase)
85
+ primary_phase = phase
86
+ secondary_phase = (phase + 0.5) % 1.0
87
+ primary = shifted_head_sample(delay_line, primary_phase, delay_seconds, ratio)
88
+ secondary = shifted_head_sample(delay_line, secondary_phase, delay_seconds, ratio)
89
+ (primary * head_gain(primary_phase)) + (secondary * head_gain(secondary_phase))
90
+ end
91
+
92
+ def shifted_head_sample(delay_line, phase, delay_seconds, ratio)
93
+ delay_samples = head_delay_samples(phase, delay_seconds, ratio)
94
+ delay_line.read(delay_samples)
95
+ end
96
+
97
+ def head_delay_samples(phase, delay_seconds, ratio)
98
+ base_delay = [delay_seconds, context.sample_time].max * context.sample_rate
99
+ sweep = sweep_span_samples(ratio)
100
+ offset = if ratio >= 1.0
101
+ (1.0 - phase) * sweep
102
+ else
103
+ phase * sweep
104
+ end
105
+
106
+ [base_delay + offset, 1.0].max
107
+ end
108
+
109
+ def head_gain(phase)
110
+ Math.sin(Math::PI * phase)**2
111
+ end
112
+
113
+ def advance_phase
114
+ @phase = (@phase + phase_step) % 1.0
115
+ end
116
+
117
+ def phase_step
118
+ 1.0 / (@window_size * context.sample_rate)
119
+ end
120
+
121
+ def pitch_ratio
122
+ 2.0**(@pitch.to_f / 12.0)
123
+ end
124
+
125
+ def sweep_span_samples(ratio)
126
+ @window_size * context.sample_rate * (1.0 - ratio).abs
127
+ end
128
+
129
+ def required_delay_samples(delay_seconds, ratio)
130
+ (
131
+ ([delay_seconds, context.sample_time].max * context.sample_rate) +
132
+ sweep_span_samples(ratio) +
133
+ (@window_size * context.sample_rate)
134
+ ).ceil + 2
135
+ end
136
+
137
+ def ensure_delay_line_capacity!(channels, delay_seconds, ratio)
138
+ required = [required_delay_samples(delay_seconds, ratio), 2].max
139
+ required_channels = [channels.to_i, 1].max
140
+ needs_resize = required > @max_delay_samples || @delay_lines.length < required_channels
141
+ return unless needs_resize
142
+
143
+ @max_delay_samples = required
144
+ @delay_lines = Array.new(required_channels) { DSP::DelayLine.new(@max_delay_samples) }
145
+ end
146
+
147
+ def resolve_window_size(value)
148
+ [value.to_f, context.sample_time * 2.0].max
149
+ end
150
+
151
+ def default_delay_time
152
+ @window_size * 0.5
153
+ end
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Deftones
4
+ module Effects
5
+ class Reverb < Core::Effect
6
+ attr_accessor :decay, :pre_delay
7
+
8
+ def initialize(decay: 0.7, pre_delay: 0.01, context: Deftones.context, **options)
9
+ super(context: context, **options)
10
+ @decay = decay.to_f
11
+ @pre_delay = pre_delay.to_f
12
+ @comb_times = [0.0297, 0.0371, 0.0411, 0.0437]
13
+ @allpass_times = [0.005, 0.0017]
14
+ @comb_lines = []
15
+ @allpass_lines = []
16
+ @pre_delay_lines = []
17
+ end
18
+
19
+ private
20
+
21
+ def process_effect_block(input_block, num_frames, start_frame, cache)
22
+ output_channels = [input_block.channels, 2].max
23
+ source = input_block.fit_channels(output_channels)
24
+
25
+ Core::AudioBlock.from_channel_data(
26
+ source.channel_data.each_with_index.map do |channel, channel_index|
27
+ process_effect(channel, num_frames, start_frame, cache, channel_index: channel_index)
28
+ end
29
+ )
30
+ end
31
+
32
+ def process_effect(input_buffer, num_frames, _start_frame, _cache, channel_index: 0)
33
+ ensure_delay_network(channel_index)
34
+ pre_delay_line = @pre_delay_lines[channel_index]
35
+ comb_lines = @comb_lines[channel_index]
36
+ allpass_lines = @allpass_lines[channel_index]
37
+ comb_times = channel_times(@comb_times, channel_index, 0.00037)
38
+ allpass_times = channel_times(@allpass_times, channel_index, 0.00011)
39
+
40
+ Array.new(num_frames) do |index|
41
+ dry = input_buffer[index]
42
+ delayed = pre_delay_line.tap(@pre_delay * context.sample_rate, input_sample: dry)
43
+ comb_sum = comb_lines.each_with_index.sum do |line, comb_index|
44
+ line.tap(comb_times[comb_index] * context.sample_rate, input_sample: delayed, feedback: @decay)
45
+ end / comb_lines.length.to_f
46
+
47
+ allpass_lines.each_with_index.reduce(comb_sum) do |sample, (line, allpass_index)|
48
+ tap = line.read(allpass_times[allpass_index] * context.sample_rate)
49
+ line.write(sample + (tap * 0.5))
50
+ tap - (sample * 0.5)
51
+ end
52
+ end
53
+ end
54
+
55
+ def ensure_delay_network(channel_index)
56
+ required = [channel_index.to_i, 0].max
57
+ while @pre_delay_lines.length <= required
58
+ @pre_delay_lines << DSP::DelayLine.new((0.1 * context.sample_rate).ceil)
59
+ @comb_lines << @comb_times.map { |seconds| DSP::DelayLine.new((seconds * context.sample_rate).ceil + 2) }
60
+ @allpass_lines << @allpass_times.map { |seconds| DSP::DelayLine.new((seconds * context.sample_rate).ceil + 2) }
61
+ end
62
+ end
63
+
64
+ def channel_times(times, channel_index, offset)
65
+ return times if channel_index.zero?
66
+
67
+ times.map { |time| time + (offset * channel_index) }
68
+ end
69
+ end
70
+ end
71
+ end