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.
- checksums.yaml +7 -0
- data/.yardopts +5 -0
- data/CHANGELOG.md +12 -0
- data/LICENSE.txt +21 -0
- data/README.md +197 -0
- data/Rakefile +8 -0
- data/examples/poly_chord.rb +14 -0
- data/examples/render_sampler.rb +16 -0
- data/examples/render_sequence.rb +13 -0
- data/examples/render_synth.rb +18 -0
- data/lib/deftones/analysis/analyser.rb +162 -0
- data/lib/deftones/analysis/dc_meter.rb +56 -0
- data/lib/deftones/analysis/fft.rb +128 -0
- data/lib/deftones/analysis/meter.rb +83 -0
- data/lib/deftones/analysis/waveform.rb +93 -0
- data/lib/deftones/component/amplitude_envelope.rb +8 -0
- data/lib/deftones/component/biquad_filter.rb +7 -0
- data/lib/deftones/component/channel.rb +109 -0
- data/lib/deftones/component/compressor.rb +64 -0
- data/lib/deftones/component/convolver.rb +99 -0
- data/lib/deftones/component/cross_fade.rb +67 -0
- data/lib/deftones/component/envelope.rb +160 -0
- data/lib/deftones/component/eq3.rb +73 -0
- data/lib/deftones/component/feedback_comb_filter.rb +75 -0
- data/lib/deftones/component/filter.rb +75 -0
- data/lib/deftones/component/follower.rb +55 -0
- data/lib/deftones/component/frequency_envelope.rb +24 -0
- data/lib/deftones/component/gate.rb +46 -0
- data/lib/deftones/component/lfo.rb +88 -0
- data/lib/deftones/component/limiter.rb +11 -0
- data/lib/deftones/component/lowpass_comb_filter.rb +43 -0
- data/lib/deftones/component/merge.rb +45 -0
- data/lib/deftones/component/mid_side_compressor.rb +43 -0
- data/lib/deftones/component/mid_side_merge.rb +53 -0
- data/lib/deftones/component/mid_side_split.rb +54 -0
- data/lib/deftones/component/mono.rb +8 -0
- data/lib/deftones/component/multiband_compressor.rb +70 -0
- data/lib/deftones/component/multiband_split.rb +133 -0
- data/lib/deftones/component/one_pole_filter.rb +71 -0
- data/lib/deftones/component/pan_vol.rb +26 -0
- data/lib/deftones/component/panner.rb +75 -0
- data/lib/deftones/component/panner3d.rb +322 -0
- data/lib/deftones/component/solo.rb +56 -0
- data/lib/deftones/component/split.rb +47 -0
- data/lib/deftones/component/volume.rb +31 -0
- data/lib/deftones/context.rb +213 -0
- data/lib/deftones/core/audio_block.rb +82 -0
- data/lib/deftones/core/audio_node.rb +262 -0
- data/lib/deftones/core/clock.rb +91 -0
- data/lib/deftones/core/computed_signal.rb +69 -0
- data/lib/deftones/core/delay.rb +44 -0
- data/lib/deftones/core/effect.rb +66 -0
- data/lib/deftones/core/emitter.rb +51 -0
- data/lib/deftones/core/gain.rb +39 -0
- data/lib/deftones/core/instrument.rb +109 -0
- data/lib/deftones/core/param.rb +31 -0
- data/lib/deftones/core/signal.rb +452 -0
- data/lib/deftones/core/signal_operator_methods.rb +73 -0
- data/lib/deftones/core/signal_operators.rb +138 -0
- data/lib/deftones/core/signal_shapers.rb +83 -0
- data/lib/deftones/core/source.rb +213 -0
- data/lib/deftones/core/synced_signal.rb +88 -0
- data/lib/deftones/destination.rb +132 -0
- data/lib/deftones/draw.rb +100 -0
- data/lib/deftones/dsp/biquad.rb +129 -0
- data/lib/deftones/dsp/delay_line.rb +41 -0
- data/lib/deftones/dsp/helpers.rb +25 -0
- data/lib/deftones/effect/auto_filter.rb +92 -0
- data/lib/deftones/effect/auto_panner.rb +57 -0
- data/lib/deftones/effect/auto_wah.rb +98 -0
- data/lib/deftones/effect/bit_crusher.rb +38 -0
- data/lib/deftones/effect/chebyshev.rb +36 -0
- data/lib/deftones/effect/chorus.rb +73 -0
- data/lib/deftones/effect/distortion.rb +22 -0
- data/lib/deftones/effect/feedback_delay.rb +38 -0
- data/lib/deftones/effect/freeverb.rb +11 -0
- data/lib/deftones/effect/frequency_shifter.rb +89 -0
- data/lib/deftones/effect/jc_reverb.rb +11 -0
- data/lib/deftones/effect/modulation_control.rb +159 -0
- data/lib/deftones/effect/phaser.rb +72 -0
- data/lib/deftones/effect/ping_pong_delay.rb +40 -0
- data/lib/deftones/effect/pitch_shift.rb +156 -0
- data/lib/deftones/effect/reverb.rb +71 -0
- data/lib/deftones/effect/stereo_widener.rb +34 -0
- data/lib/deftones/effect/tremolo.rb +52 -0
- data/lib/deftones/effect/vibrato.rb +47 -0
- data/lib/deftones/event/callback_behavior.rb +61 -0
- data/lib/deftones/event/loop.rb +53 -0
- data/lib/deftones/event/part.rb +51 -0
- data/lib/deftones/event/pattern.rb +94 -0
- data/lib/deftones/event/sequence.rb +87 -0
- data/lib/deftones/event/tone_event.rb +77 -0
- data/lib/deftones/event/transport.rb +276 -0
- data/lib/deftones/instrument/am_synth.rb +56 -0
- data/lib/deftones/instrument/duo_synth.rb +68 -0
- data/lib/deftones/instrument/fm_synth.rb +60 -0
- data/lib/deftones/instrument/membrane_synth.rb +60 -0
- data/lib/deftones/instrument/metal_synth.rb +61 -0
- data/lib/deftones/instrument/mono_synth.rb +88 -0
- data/lib/deftones/instrument/noise_synth.rb +56 -0
- data/lib/deftones/instrument/pluck_synth.rb +41 -0
- data/lib/deftones/instrument/poly_synth.rb +96 -0
- data/lib/deftones/instrument/sampler.rb +97 -0
- data/lib/deftones/instrument/synth.rb +60 -0
- data/lib/deftones/io/buffer.rb +352 -0
- data/lib/deftones/io/buffers.rb +77 -0
- data/lib/deftones/io/recorder.rb +89 -0
- data/lib/deftones/listener.rb +120 -0
- data/lib/deftones/music/frequency.rb +128 -0
- data/lib/deftones/music/midi.rb +206 -0
- data/lib/deftones/music/note.rb +58 -0
- data/lib/deftones/music/ticks.rb +106 -0
- data/lib/deftones/music/time.rb +209 -0
- data/lib/deftones/music/transport_time.rb +94 -0
- data/lib/deftones/music/unit_helpers.rb +30 -0
- data/lib/deftones/offline_context.rb +46 -0
- data/lib/deftones/portaudio_support.rb +112 -0
- data/lib/deftones/source/am_oscillator.rb +42 -0
- data/lib/deftones/source/fat_oscillator.rb +49 -0
- data/lib/deftones/source/fm_oscillator.rb +47 -0
- data/lib/deftones/source/grain_player.rb +198 -0
- data/lib/deftones/source/karplus_strong.rb +51 -0
- data/lib/deftones/source/noise.rb +99 -0
- data/lib/deftones/source/omni_oscillator.rb +175 -0
- data/lib/deftones/source/oscillator.rb +74 -0
- data/lib/deftones/source/player.rb +228 -0
- data/lib/deftones/source/players.rb +133 -0
- data/lib/deftones/source/pulse_oscillator.rb +38 -0
- data/lib/deftones/source/pwm_oscillator.rb +49 -0
- data/lib/deftones/source/tone_buffer_source.rb +136 -0
- data/lib/deftones/source/tone_oscillator_node.rb +65 -0
- data/lib/deftones/source/user_media.rb +519 -0
- data/lib/deftones/version.rb +5 -0
- data/lib/deftones.rb +542 -0
- metadata +221 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deftones
|
|
4
|
+
module Core
|
|
5
|
+
class AudioBlock
|
|
6
|
+
attr_reader :channel_data
|
|
7
|
+
|
|
8
|
+
def self.silent(num_frames, channels = 1)
|
|
9
|
+
from_channel_data(Array.new([channels.to_i, 1].max) { Array.new(num_frames.to_i, 0.0) })
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def self.from_channel_data(channel_data)
|
|
13
|
+
normalized = channel_data.map { |channel| channel.map(&:to_f) }
|
|
14
|
+
new(normalized)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.from_mono(samples, channels: 1)
|
|
18
|
+
normalized = samples.map(&:to_f)
|
|
19
|
+
from_channel_data(Array.new([channels.to_i, 1].max) { normalized.dup })
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def initialize(channel_data)
|
|
23
|
+
@channel_data = channel_data
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def channels
|
|
27
|
+
@channel_data.length
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def num_frames
|
|
31
|
+
@channel_data.first&.length || 0
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def dup
|
|
35
|
+
self.class.from_channel_data(@channel_data)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def mono
|
|
39
|
+
return [] if @channel_data.empty?
|
|
40
|
+
return @channel_data.first.dup if channels == 1
|
|
41
|
+
|
|
42
|
+
Array.new(num_frames) do |frame_index|
|
|
43
|
+
@channel_data.sum { |channel| channel[frame_index] } / channels.to_f
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def interleaved
|
|
48
|
+
Array.new(num_frames * channels) do |index|
|
|
49
|
+
frame_index = index / channels
|
|
50
|
+
channel_index = index % channels
|
|
51
|
+
@channel_data[channel_index][frame_index]
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def channel(index)
|
|
56
|
+
@channel_data[index] || Array.new(num_frames, 0.0)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def fit_channels(target_channels)
|
|
60
|
+
target = [target_channels.to_i, 1].max
|
|
61
|
+
return dup if target == channels
|
|
62
|
+
return self.class.from_channel_data([mono]) if target == 1
|
|
63
|
+
|
|
64
|
+
if channels == 1
|
|
65
|
+
return self.class.from_channel_data(Array.new(target) { @channel_data.first.dup })
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
self.class.from_channel_data(Array.new(target) { |index| channel(index % channels).dup })
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def mix!(other)
|
|
72
|
+
incoming = other.fit_channels(channels)
|
|
73
|
+
channels.times do |channel_index|
|
|
74
|
+
num_frames.times do |frame_index|
|
|
75
|
+
@channel_data[channel_index][frame_index] += incoming.channel_data[channel_index][frame_index]
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
self
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deftones
|
|
4
|
+
module Core
|
|
5
|
+
class AudioNode
|
|
6
|
+
attr_reader :context, :input
|
|
7
|
+
|
|
8
|
+
def initialize(context: Deftones.context)
|
|
9
|
+
@context = context
|
|
10
|
+
@input = self
|
|
11
|
+
@sources = []
|
|
12
|
+
@destinations = []
|
|
13
|
+
@disposed = false
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def output
|
|
17
|
+
self
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def connect(destination, output_index: 0, input_index: 0)
|
|
21
|
+
_ = output_index
|
|
22
|
+
_ = input_index
|
|
23
|
+
raise ArgumentError, "destination is required" if destination.nil?
|
|
24
|
+
|
|
25
|
+
destination_node = destination.respond_to?(:input) ? destination.input : destination
|
|
26
|
+
output.attach_destination(destination_node)
|
|
27
|
+
self
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def disconnect(destination = nil)
|
|
31
|
+
if destination
|
|
32
|
+
destination_node = destination.respond_to?(:input) ? destination.input : destination
|
|
33
|
+
output.detach_destination(destination_node)
|
|
34
|
+
else
|
|
35
|
+
output.detach_all_destinations
|
|
36
|
+
end
|
|
37
|
+
self
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def >>(other)
|
|
41
|
+
connect(other)
|
|
42
|
+
other
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def chain(*nodes)
|
|
46
|
+
[self, *nodes].each_cons(2) { |source, destination| source.connect(destination) }
|
|
47
|
+
nodes.last || self
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def fan(*nodes)
|
|
51
|
+
nodes.each { |node| connect(node) }
|
|
52
|
+
self
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def to_output
|
|
56
|
+
connect(context.output)
|
|
57
|
+
self
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def to_destination
|
|
61
|
+
to_output
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def to_master
|
|
65
|
+
to_destination
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def now
|
|
69
|
+
context.current_time
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def immediate
|
|
73
|
+
now
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def to_seconds(time = nil)
|
|
77
|
+
return context.current_time if time.nil?
|
|
78
|
+
|
|
79
|
+
Deftones::Music::Time.parse(time)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def to_ticks(time = nil)
|
|
83
|
+
return Deftones.transport.ticks if time.nil?
|
|
84
|
+
|
|
85
|
+
Deftones.transport.seconds_to_ticks(to_seconds(time))
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def to_frequency(value)
|
|
89
|
+
Deftones::Music::Frequency.parse(value)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def to_midi(value)
|
|
93
|
+
Deftones::Music::Midi.parse(value)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def sample_time
|
|
97
|
+
1.0 / context.sample_rate
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def block_time
|
|
101
|
+
context.buffer_size.to_f / context.sample_rate
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def channel_count
|
|
105
|
+
context.channels
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def channel_count_mode
|
|
109
|
+
"max"
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def channel_interpretation
|
|
113
|
+
"speakers"
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def number_of_inputs
|
|
117
|
+
1
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def number_of_outputs
|
|
121
|
+
1
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def set(**params)
|
|
125
|
+
params.each do |key, value|
|
|
126
|
+
writer = :"#{key}="
|
|
127
|
+
public_send(writer, value) if respond_to?(writer)
|
|
128
|
+
end
|
|
129
|
+
self
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def get(*keys)
|
|
133
|
+
keys.flatten.each_with_object({}) do |key, values|
|
|
134
|
+
reader = key.to_sym
|
|
135
|
+
values[reader] = public_send(reader) if respond_to?(reader)
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def name
|
|
140
|
+
self.class.name.split("::").last
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def to_s
|
|
144
|
+
name
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
alias toDestination to_destination
|
|
148
|
+
alias toMaster to_master
|
|
149
|
+
alias toSeconds to_seconds
|
|
150
|
+
alias toTicks to_ticks
|
|
151
|
+
alias toFrequency to_frequency
|
|
152
|
+
alias toMidi to_midi
|
|
153
|
+
alias sampleTime sample_time
|
|
154
|
+
alias blockTime block_time
|
|
155
|
+
alias channelCount channel_count
|
|
156
|
+
alias channelCountMode channel_count_mode
|
|
157
|
+
alias channelInterpretation channel_interpretation
|
|
158
|
+
alias numberOfInputs number_of_inputs
|
|
159
|
+
alias numberOfOutputs number_of_outputs
|
|
160
|
+
alias toString to_s
|
|
161
|
+
|
|
162
|
+
def dispose
|
|
163
|
+
disconnect
|
|
164
|
+
@sources.dup.each { |source| source.detach_destination(self) }
|
|
165
|
+
@sources.clear
|
|
166
|
+
@disposed = true
|
|
167
|
+
self
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def disposed?
|
|
171
|
+
@disposed
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def render(num_frames, start_frame = 0, cache = {})
|
|
175
|
+
render_block(num_frames, start_frame, cache).mono
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
protected
|
|
179
|
+
|
|
180
|
+
def render_block(num_frames, start_frame = 0, cache = {})
|
|
181
|
+
cache_key = [object_id, :block, start_frame, num_frames]
|
|
182
|
+
return cache.fetch(cache_key).dup if cache.key?(cache_key)
|
|
183
|
+
|
|
184
|
+
output_block = if uses_legacy_render_for_block?
|
|
185
|
+
normalize_output_block(render(num_frames, start_frame, cache), num_frames, 1)
|
|
186
|
+
else
|
|
187
|
+
input_block = mix_source_blocks(num_frames, start_frame, cache)
|
|
188
|
+
if multichannel_process?
|
|
189
|
+
normalize_output_block(process(input_block, num_frames, start_frame, cache), num_frames, input_block.channels)
|
|
190
|
+
else
|
|
191
|
+
mono_output = process(input_block.mono, num_frames, start_frame, cache)
|
|
192
|
+
normalize_output_block(mono_output, num_frames, [input_block.channels, default_output_channels].max)
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
cache[cache_key] = output_block
|
|
197
|
+
output_block.dup
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def attach_source(source)
|
|
201
|
+
return if @sources.include?(source)
|
|
202
|
+
|
|
203
|
+
@sources << source
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def detach_source(source)
|
|
207
|
+
@sources.delete(source)
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
def attach_destination(destination)
|
|
211
|
+
return if @destinations.include?(destination)
|
|
212
|
+
|
|
213
|
+
@destinations << destination
|
|
214
|
+
destination.attach_source(self)
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def detach_destination(destination)
|
|
218
|
+
return unless @destinations.delete(destination)
|
|
219
|
+
|
|
220
|
+
destination.detach_source(self)
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def detach_all_destinations
|
|
224
|
+
@destinations.dup.each { |destination| detach_destination(destination) }
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
def mix_source_blocks(num_frames, start_frame, cache)
|
|
228
|
+
blocks = @sources.map { |source| source.send(:render_block, num_frames, start_frame, cache) }
|
|
229
|
+
output_channels = blocks.map(&:channels).max || default_input_channels
|
|
230
|
+
mixed = AudioBlock.silent(num_frames, output_channels)
|
|
231
|
+
blocks.each { |block| mixed.mix!(block) }
|
|
232
|
+
mixed
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
def normalize_output_block(output, num_frames, channels)
|
|
236
|
+
return output.dup if output.is_a?(AudioBlock)
|
|
237
|
+
|
|
238
|
+
AudioBlock.from_mono(output, channels: channels)
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def process(input_buffer, _num_frames, _start_frame, _cache)
|
|
242
|
+
input_buffer
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
def uses_legacy_render_for_block?
|
|
246
|
+
self.class.instance_method(:render).owner != AudioNode
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
def multichannel_process?
|
|
250
|
+
false
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
def default_input_channels
|
|
254
|
+
1
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
def default_output_channels
|
|
258
|
+
default_input_channels
|
|
259
|
+
end
|
|
260
|
+
end
|
|
261
|
+
end
|
|
262
|
+
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deftones
|
|
4
|
+
module Core
|
|
5
|
+
class Clock < Emitter
|
|
6
|
+
attr_reader :context, :frequency, :state
|
|
7
|
+
|
|
8
|
+
def initialize(frequency: 1.0, context: Deftones.context, &block)
|
|
9
|
+
super()
|
|
10
|
+
@context = context
|
|
11
|
+
@frequency = Signal.new(value: frequency, units: :frequency, context: context)
|
|
12
|
+
@state = :stopped
|
|
13
|
+
@start_time = 0.0
|
|
14
|
+
@offset_ticks = 0.0
|
|
15
|
+
on(:tick, &block) if block
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def start(time = nil, offset: nil)
|
|
19
|
+
@start_time = resolve_time(time)
|
|
20
|
+
@offset_ticks = offset.to_f if offset
|
|
21
|
+
@state = :started
|
|
22
|
+
self
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def stop(time = nil)
|
|
26
|
+
@offset_ticks = ticks_at(resolve_time(time))
|
|
27
|
+
@start_time = resolve_time(time)
|
|
28
|
+
@state = :stopped
|
|
29
|
+
self
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def pause(time = nil)
|
|
33
|
+
@offset_ticks = ticks_at(resolve_time(time))
|
|
34
|
+
@start_time = resolve_time(time)
|
|
35
|
+
@state = :paused
|
|
36
|
+
self
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def ticks(time = context.current_time)
|
|
40
|
+
ticks_at(resolve_time(time))
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def seconds(time = context.current_time)
|
|
44
|
+
ticks(time) / current_frequency
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def next_tick_time(time = context.current_time)
|
|
48
|
+
tick_duration = 1.0 / current_frequency
|
|
49
|
+
current_ticks = ticks(time)
|
|
50
|
+
next_tick = current_ticks.floor + 1
|
|
51
|
+
resolve_time(time) + ((next_tick - current_ticks) * tick_duration)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def get_ticks_at_time(time)
|
|
55
|
+
ticks(time)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def emit_ticks_until(time)
|
|
59
|
+
return self unless state == :started
|
|
60
|
+
|
|
61
|
+
current_time = resolve_time(time)
|
|
62
|
+
emitted_ticks = (@last_emitted_tick || @offset_ticks.floor) + 1
|
|
63
|
+
while emitted_ticks <= ticks_at(current_time).floor
|
|
64
|
+
emit(:tick, emitted_ticks)
|
|
65
|
+
emitted_ticks += 1
|
|
66
|
+
end
|
|
67
|
+
@last_emitted_tick = emitted_ticks - 1
|
|
68
|
+
self
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
alias getTicksAtTime get_ticks_at_time
|
|
72
|
+
alias nextTickTime next_tick_time
|
|
73
|
+
|
|
74
|
+
private
|
|
75
|
+
|
|
76
|
+
def resolve_time(value)
|
|
77
|
+
value.nil? ? context.current_time : Deftones::Music::Time.parse(value)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def ticks_at(time)
|
|
81
|
+
return @offset_ticks unless state == :started
|
|
82
|
+
|
|
83
|
+
@offset_ticks + ([time - @start_time, 0.0].max * current_frequency)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def current_frequency
|
|
87
|
+
[frequency.value.to_f, 1.0e-6].max
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deftones
|
|
4
|
+
module Core
|
|
5
|
+
class ComputedSignal < Signal
|
|
6
|
+
attr_reader :input
|
|
7
|
+
|
|
8
|
+
def initialize(input:, units: nil, context: nil)
|
|
9
|
+
resolved_context = resolve_context(input, fallback: context)
|
|
10
|
+
resolved_units = units || default_units(input)
|
|
11
|
+
|
|
12
|
+
super(value: 0.0, units: resolved_units, context: resolved_context)
|
|
13
|
+
@input = coerce_signal(input || 0.0, units: resolved_units)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def value
|
|
17
|
+
value_at(context.current_time)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def value=(_new_value)
|
|
21
|
+
raise Deftones::Error, "#{self.class.name} is derived and cannot be assigned"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def ramp_to(*)
|
|
25
|
+
raise Deftones::Error, "#{self.class.name} is derived and cannot be automated directly"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
alias linear_ramp_to ramp_to
|
|
29
|
+
alias exponential_ramp_to ramp_to
|
|
30
|
+
alias set_value_at_time ramp_to
|
|
31
|
+
alias cancel_scheduled_values ramp_to
|
|
32
|
+
|
|
33
|
+
def process(num_frames, start_frame = 0)
|
|
34
|
+
Array.new(num_frames) do |offset|
|
|
35
|
+
value_at(sample_time(start_frame + offset))
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def default_units(candidate)
|
|
42
|
+
candidate.respond_to?(:units) ? candidate.units : :number
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def resolve_context(candidate, fallback:)
|
|
46
|
+
return candidate.context if candidate.respond_to?(:context)
|
|
47
|
+
return fallback if fallback
|
|
48
|
+
|
|
49
|
+
Deftones.context
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def coerce_signal(value, units:)
|
|
53
|
+
return value if value.respond_to?(:value_at)
|
|
54
|
+
|
|
55
|
+
Signal.new(value: value, units: units, context: context)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def exponentiate(value, exponent)
|
|
59
|
+
return value**exponent if value >= 0.0 || integer_like?(exponent)
|
|
60
|
+
|
|
61
|
+
-((-value)**exponent)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def integer_like?(value)
|
|
65
|
+
value.finite? && value.round == value
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deftones
|
|
4
|
+
module Core
|
|
5
|
+
class Delay < AudioNode
|
|
6
|
+
attr_reader :delay_time, :max_delay
|
|
7
|
+
|
|
8
|
+
def initialize(delay_time: 0.0, max_delay: 1.0, context: Deftones.context)
|
|
9
|
+
super(context: context)
|
|
10
|
+
@delay_time = Signal.new(value: delay_time, units: :time, context: context)
|
|
11
|
+
@max_delay = [max_delay.to_f, @delay_time.value].max
|
|
12
|
+
@delay_lines = []
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def multichannel_process?
|
|
16
|
+
true
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def process(input_block, num_frames, start_frame, _cache)
|
|
20
|
+
delay_values = @delay_time.process(num_frames, start_frame)
|
|
21
|
+
AudioBlock.from_channel_data(
|
|
22
|
+
input_block.channel_data.each_with_index.map do |channel, channel_index|
|
|
23
|
+
delay_line = ensure_delay_line(channel_index)
|
|
24
|
+
Array.new(num_frames) do |index|
|
|
25
|
+
delay_seconds = [delay_values[index], 0.0].max
|
|
26
|
+
delay_samples = [delay_seconds * context.sample_rate, @max_delay * context.sample_rate].min
|
|
27
|
+
delay_line.tap(delay_samples, input_sample: channel[index], feedback: 0.0)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def ensure_delay_line(channel_index)
|
|
36
|
+
required = [channel_index.to_i, 0].max
|
|
37
|
+
while @delay_lines.length <= required
|
|
38
|
+
@delay_lines << DSP::DelayLine.new((@max_delay * context.sample_rate).ceil)
|
|
39
|
+
end
|
|
40
|
+
@delay_lines[required]
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deftones
|
|
4
|
+
module Core
|
|
5
|
+
class Effect < AudioNode
|
|
6
|
+
attr_reader :wet
|
|
7
|
+
|
|
8
|
+
def initialize(wet: 1.0, context: Deftones.context)
|
|
9
|
+
super(context: context)
|
|
10
|
+
@wet = Signal.new(value: wet, units: :number, context: context)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def wet=(value)
|
|
14
|
+
@wet.value = value
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def multichannel_process?
|
|
18
|
+
true
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def process(input_block, num_frames, start_frame, cache)
|
|
22
|
+
wet_values = @wet.process(num_frames, start_frame)
|
|
23
|
+
wet_block = process_effect_block(input_block.dup, num_frames, start_frame, cache)
|
|
24
|
+
output_channels = [input_block.channels, wet_block.channels].max
|
|
25
|
+
dry_block = input_block.fit_channels(output_channels)
|
|
26
|
+
wet_block = wet_block.fit_channels(output_channels)
|
|
27
|
+
|
|
28
|
+
AudioBlock.from_channel_data(
|
|
29
|
+
Array.new(output_channels) do |channel_index|
|
|
30
|
+
Array.new(num_frames) do |frame_index|
|
|
31
|
+
DSP::Helpers.mix(
|
|
32
|
+
dry_block.channel_data[channel_index][frame_index],
|
|
33
|
+
wet_block.channel_data[channel_index][frame_index],
|
|
34
|
+
wet_values[frame_index].clamp(0.0, 1.0)
|
|
35
|
+
)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
def process_effect_block(input_block, num_frames, start_frame, cache)
|
|
44
|
+
AudioBlock.from_channel_data(
|
|
45
|
+
input_block.channel_data.each_with_index.map do |channel, channel_index|
|
|
46
|
+
normalize_channel_output(
|
|
47
|
+
process_effect(channel, num_frames, start_frame, cache, channel_index: channel_index),
|
|
48
|
+
num_frames
|
|
49
|
+
)
|
|
50
|
+
end
|
|
51
|
+
)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def process_effect(input_buffer, _num_frames, _start_frame, _cache, channel_index: 0)
|
|
55
|
+
input_buffer
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def normalize_channel_output(output, num_frames)
|
|
59
|
+
normalized = Array(output).map(&:to_f)
|
|
60
|
+
return normalized.first(num_frames) if normalized.length >= num_frames
|
|
61
|
+
|
|
62
|
+
normalized + Array.new(num_frames - normalized.length, 0.0)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deftones
|
|
4
|
+
module Core
|
|
5
|
+
class Emitter
|
|
6
|
+
def initialize
|
|
7
|
+
@listeners = Hash.new { |hash, key| hash[key] = [] }
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def on(event_name, &block)
|
|
11
|
+
raise ArgumentError, "block is required" unless block
|
|
12
|
+
|
|
13
|
+
@listeners[event_name.to_sym] << block
|
|
14
|
+
self
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def once(event_name, &block)
|
|
18
|
+
raise ArgumentError, "block is required" unless block
|
|
19
|
+
|
|
20
|
+
wrapper = nil
|
|
21
|
+
wrapper = proc do |*arguments|
|
|
22
|
+
off(event_name, wrapper)
|
|
23
|
+
block.call(*arguments)
|
|
24
|
+
end
|
|
25
|
+
on(event_name, &wrapper)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def off(event_name, listener = nil)
|
|
29
|
+
key = event_name.to_sym
|
|
30
|
+
return @listeners.delete(key) unless listener
|
|
31
|
+
|
|
32
|
+
@listeners[key].delete(listener)
|
|
33
|
+
self
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def emit(event_name, *arguments)
|
|
37
|
+
@listeners[event_name.to_sym].dup.each { |listener| listener.call(*arguments) }
|
|
38
|
+
self
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def listeners(event_name)
|
|
42
|
+
@listeners[event_name.to_sym].dup
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def dispose
|
|
46
|
+
@listeners.clear
|
|
47
|
+
self
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deftones
|
|
4
|
+
module Core
|
|
5
|
+
class Gain < AudioNode
|
|
6
|
+
attr_reader :gain
|
|
7
|
+
|
|
8
|
+
def initialize(gain: 1.0, context: Deftones.context)
|
|
9
|
+
super(context: context)
|
|
10
|
+
@gain = Signal.new(value: gain, units: :number, context: context)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def gain=(value)
|
|
14
|
+
@gain.value = value
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def process(input_buffer, num_frames, start_frame, _cache)
|
|
18
|
+
gain_values = @gain.process(num_frames, start_frame)
|
|
19
|
+
|
|
20
|
+
Array.new(num_frames) do |index|
|
|
21
|
+
input_buffer[index] * gain_values[index]
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def multichannel_process?
|
|
26
|
+
true
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def process(input_block, num_frames, start_frame, _cache)
|
|
30
|
+
gain_values = @gain.process(num_frames, start_frame)
|
|
31
|
+
AudioBlock.from_channel_data(
|
|
32
|
+
input_block.channel_data.map do |channel|
|
|
33
|
+
Array.new(num_frames) { |index| channel[index] * gain_values[index] }
|
|
34
|
+
end
|
|
35
|
+
)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|