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