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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -6
- data/README.md +5 -0
- data/Rakefile +50 -1
- data/lib/deftones/analysis/meter.rb +22 -2
- data/lib/deftones/component/channel.rb +1 -1
- data/lib/deftones/component/compressor.rb +127 -22
- data/lib/deftones/component/filter.rb +29 -19
- data/lib/deftones/component/merge.rb +14 -0
- data/lib/deftones/component/multiband_compressor.rb +1 -1
- data/lib/deftones/component/one_pole_filter.rb +10 -3
- data/lib/deftones/component/panner.rb +25 -2
- data/lib/deftones/component/panner3d.rb +0 -10
- data/lib/deftones/component/split.rb +14 -0
- data/lib/deftones/context.rb +90 -9
- data/lib/deftones/core/audio_block.rb +64 -5
- data/lib/deftones/core/audio_node.rb +98 -8
- data/lib/deftones/core/gain.rb +0 -8
- data/lib/deftones/core/instrument.rb +52 -10
- data/lib/deftones/core/param.rb +51 -1
- data/lib/deftones/core/signal.rb +79 -28
- data/lib/deftones/core/source.rb +71 -11
- data/lib/deftones/destination.rb +41 -17
- data/lib/deftones/draw.rb +6 -10
- data/lib/deftones/dsp/biquad.rb +9 -4
- data/lib/deftones/dsp/delay_line.rb +2 -2
- data/lib/deftones/dsp/helpers.rb +7 -0
- data/lib/deftones/effect/bit_crusher.rb +10 -2
- data/lib/deftones/effect/chebyshev.rb +7 -3
- data/lib/deftones/effect/distortion.rb +5 -3
- data/lib/deftones/effect/feedback_delay.rb +2 -1
- data/lib/deftones/effect/oversampling.rb +43 -0
- data/lib/deftones/effect/phaser.rb +2 -1
- data/lib/deftones/effect/pitch_shift.rb +1 -2
- data/lib/deftones/effect/reverb.rb +73 -5
- data/lib/deftones/event/callback_behavior.rb +7 -3
- data/lib/deftones/event/loop.rb +7 -2
- data/lib/deftones/event/part.rb +18 -3
- data/lib/deftones/event/pattern.rb +51 -6
- data/lib/deftones/event/sequence.rb +19 -5
- data/lib/deftones/event/tone_event.rb +7 -2
- data/lib/deftones/event/transport.rb +243 -21
- data/lib/deftones/instrument/poly_synth.rb +81 -15
- data/lib/deftones/instrument/sampler.rb +53 -10
- data/lib/deftones/io/buffer.rb +376 -55
- data/lib/deftones/io/buffers.rb +28 -4
- data/lib/deftones/io/recorder.rb +2 -1
- data/lib/deftones/music/frequency.rb +13 -8
- data/lib/deftones/music/midi.rb +132 -9
- data/lib/deftones/music/note.rb +13 -3
- data/lib/deftones/music/time.rb +42 -4
- data/lib/deftones/offline_context.rb +194 -17
- data/lib/deftones/portaudio_support.rb +68 -9
- data/lib/deftones/source/fat_oscillator.rb +28 -9
- data/lib/deftones/source/grain_player.rb +49 -2
- data/lib/deftones/source/noise.rb +42 -10
- data/lib/deftones/source/omni_oscillator.rb +1 -2
- data/lib/deftones/source/oscillator.rb +83 -19
- data/lib/deftones/source/player.rb +24 -6
- data/lib/deftones/source/players.rb +39 -6
- data/lib/deftones/source/tone_buffer_source.rb +12 -6
- data/lib/deftones/source/tone_oscillator_node.rb +4 -3
- data/lib/deftones/source/user_media.rb +83 -10
- data/lib/deftones/version.rb +1 -1
- data/lib/deftones.rb +108 -31
- metadata +3 -44
data/lib/deftones/context.rb
CHANGED
|
@@ -6,31 +6,45 @@ module Deftones
|
|
|
6
6
|
DEFAULT_BUFFER_SIZE = 256
|
|
7
7
|
DEFAULT_CHANNELS = 2
|
|
8
8
|
|
|
9
|
-
attr_reader :
|
|
9
|
+
attr_reader :buffer_size, :channels, :draw, :latency_hint, :look_ahead, :output_device_id,
|
|
10
|
+
:output_device_label, :sample_rate, :stream_error, :stream_status_flags, :transport
|
|
11
|
+
attr_accessor :on_stream_error
|
|
10
12
|
|
|
11
13
|
def initialize(sample_rate: DEFAULT_SAMPLE_RATE, buffer_size: DEFAULT_BUFFER_SIZE, channels: DEFAULT_CHANNELS,
|
|
12
|
-
realtime_backend: nil, autostart: true, latency_hint: "interactive", look_ahead: nil
|
|
14
|
+
realtime_backend: nil, autostart: true, latency_hint: "interactive", look_ahead: nil,
|
|
15
|
+
transport: nil, draw: nil, on_stream_error: nil, stream_error_mode: :abort,
|
|
16
|
+
output_device_id: nil, output_device_label: nil)
|
|
13
17
|
@sample_rate = sample_rate
|
|
14
18
|
@buffer_size = buffer_size
|
|
15
19
|
@channels = channels
|
|
20
|
+
@transport = transport || Event::Transport.new(clock: self)
|
|
21
|
+
@draw = draw || Draw.new
|
|
16
22
|
@realtime_backend = realtime_backend
|
|
17
23
|
@autostart = autostart
|
|
18
24
|
@latency_hint = latency_hint
|
|
19
25
|
@look_ahead = look_ahead || (buffer_size.to_f / sample_rate)
|
|
26
|
+
@output_device_id = output_device_id
|
|
27
|
+
@output_device_label = output_device_label
|
|
28
|
+
@on_stream_error = on_stream_error
|
|
29
|
+
@stream_error_mode = normalize_stream_error_mode(stream_error_mode)
|
|
20
30
|
@output = Core::Gain.new(context: self, gain: 1.0)
|
|
21
31
|
@running = false
|
|
22
32
|
@closed = false
|
|
23
33
|
@started_at = monotonic_time
|
|
24
34
|
@stream = nil
|
|
25
35
|
@rendered_frames = 0
|
|
36
|
+
@scheduler_position = 0.0
|
|
26
37
|
@stream_error = nil
|
|
38
|
+
@stream_status_flags = []
|
|
27
39
|
end
|
|
28
40
|
|
|
29
41
|
def start(use_realtime: true)
|
|
30
42
|
@closed = false
|
|
31
43
|
@started_at = monotonic_time
|
|
32
44
|
@rendered_frames = 0
|
|
45
|
+
@scheduler_position = 0.0
|
|
33
46
|
@stream_error = nil
|
|
47
|
+
@stream_status_flags.clear
|
|
34
48
|
@running = true
|
|
35
49
|
start_realtime_stream if use_realtime
|
|
36
50
|
self
|
|
@@ -101,11 +115,38 @@ module Deftones
|
|
|
101
115
|
buffer_size.to_f / sample_rate
|
|
102
116
|
end
|
|
103
117
|
|
|
118
|
+
def reset!
|
|
119
|
+
stop
|
|
120
|
+
@transport = Event::Transport.new(clock: self)
|
|
121
|
+
@draw = Draw.new
|
|
122
|
+
@stream_error = nil
|
|
123
|
+
@stream_status_flags.clear
|
|
124
|
+
@rendered_frames = 0
|
|
125
|
+
@scheduler_position = 0.0
|
|
126
|
+
self
|
|
127
|
+
end
|
|
128
|
+
|
|
104
129
|
alias rawContext raw_context
|
|
105
130
|
alias sampleTime sample_time
|
|
106
131
|
alias blockTime block_time
|
|
107
132
|
alias latencyHint latency_hint
|
|
108
133
|
alias lookAhead look_ahead
|
|
134
|
+
alias outputDeviceId output_device_id
|
|
135
|
+
alias outputDeviceLabel output_device_label
|
|
136
|
+
alias onStreamError on_stream_error
|
|
137
|
+
alias onStreamError= on_stream_error=
|
|
138
|
+
|
|
139
|
+
def stream_error_mode
|
|
140
|
+
@stream_error_mode
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def stream_error_mode=(value)
|
|
144
|
+
@stream_error_mode = normalize_stream_error_mode(value)
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
alias streamErrorMode stream_error_mode
|
|
148
|
+
alias streamErrorMode= stream_error_mode=
|
|
149
|
+
alias streamStatusFlags stream_status_flags
|
|
109
150
|
|
|
110
151
|
private
|
|
111
152
|
|
|
@@ -138,12 +179,42 @@ module Deftones
|
|
|
138
179
|
|
|
139
180
|
def pull_realtime_samples(frames)
|
|
140
181
|
start_frame = @rendered_frames
|
|
182
|
+
next_frame = start_frame + frames
|
|
183
|
+
window_start = start_frame.to_f / sample_rate
|
|
184
|
+
window_end = next_frame.to_f / sample_rate
|
|
185
|
+
scheduler_end = window_end + look_ahead
|
|
186
|
+
scheduler_start = [@scheduler_position, window_start].max
|
|
187
|
+
@transport.prepare_render_window(scheduler_start, scheduler_end)
|
|
188
|
+
Deftones.transport.prepare_render_window(scheduler_start, scheduler_end) unless Deftones.transport.equal?(@transport)
|
|
141
189
|
chunk = render_block_frames(frames, start_frame).fit_channels(@channels)
|
|
142
|
-
@rendered_frames
|
|
143
|
-
|
|
190
|
+
@rendered_frames = next_frame
|
|
191
|
+
@scheduler_position = scheduler_end
|
|
192
|
+
@draw.advance_to(scheduler_end)
|
|
193
|
+
Deftones.draw.advance_to(scheduler_end) unless Deftones.draw.equal?(@draw)
|
|
144
194
|
chunk.interleaved
|
|
145
195
|
end
|
|
146
196
|
|
|
197
|
+
def handle_stream_error(error)
|
|
198
|
+
@stream_error ||= error
|
|
199
|
+
@on_stream_error&.call(error)
|
|
200
|
+
@stream_error_mode == :continue ? :continue : :abort
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def record_stream_status_flags(status_flags)
|
|
204
|
+
return self if status_flags.nil?
|
|
205
|
+
return self if status_flags.respond_to?(:zero?) && status_flags.zero?
|
|
206
|
+
|
|
207
|
+
@stream_status_flags << status_flags
|
|
208
|
+
self
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def normalize_stream_error_mode(value)
|
|
212
|
+
normalized = value.to_sym
|
|
213
|
+
return normalized if %i[abort continue].include?(normalized)
|
|
214
|
+
|
|
215
|
+
raise ArgumentError, "Unsupported stream error mode: #{value}"
|
|
216
|
+
end
|
|
217
|
+
|
|
147
218
|
def monotonic_time
|
|
148
219
|
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
149
220
|
end
|
|
@@ -152,6 +223,7 @@ module Deftones
|
|
|
152
223
|
def initialize(context:)
|
|
153
224
|
@context = context
|
|
154
225
|
@stream = nil
|
|
226
|
+
@silence_cache = {}
|
|
155
227
|
end
|
|
156
228
|
|
|
157
229
|
def start
|
|
@@ -190,7 +262,12 @@ module Deftones
|
|
|
190
262
|
def open_stream
|
|
191
263
|
Deftones::PortAudioSupport.acquire!
|
|
192
264
|
@stream = PortAudio::Stream.new(
|
|
193
|
-
output: Deftones::PortAudioSupport.output_parameters(
|
|
265
|
+
output: Deftones::PortAudioSupport.output_parameters(
|
|
266
|
+
@context.channels,
|
|
267
|
+
device_id: @context.output_device_id,
|
|
268
|
+
label: @context.output_device_label,
|
|
269
|
+
sample_rate: @context.sample_rate
|
|
270
|
+
),
|
|
194
271
|
sample_rate: @context.sample_rate.to_f,
|
|
195
272
|
frames_per_buffer: @context.buffer_size,
|
|
196
273
|
&method(:process)
|
|
@@ -200,13 +277,17 @@ module Deftones
|
|
|
200
277
|
raise
|
|
201
278
|
end
|
|
202
279
|
|
|
203
|
-
def process(_input, output, frame_count, _time_info,
|
|
280
|
+
def process(_input, output, frame_count, _time_info, status_flags, _user_data)
|
|
281
|
+
@context.send(:record_stream_status_flags, status_flags)
|
|
204
282
|
output.write_array_of_float(@context.send(:pull_realtime_samples, frame_count))
|
|
205
283
|
:continue
|
|
206
284
|
rescue StandardError => error
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
285
|
+
output.write_array_of_float(silence_for(frame_count)) unless output.null?
|
|
286
|
+
@context.send(:handle_stream_error, error)
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
def silence_for(frame_count)
|
|
290
|
+
@silence_cache[frame_count] ||= Array.new(frame_count * @context.channels, 0.0).freeze
|
|
210
291
|
end
|
|
211
292
|
end
|
|
212
293
|
end
|
|
@@ -19,6 +19,23 @@ module Deftones
|
|
|
19
19
|
from_channel_data(Array.new([channels.to_i, 1].max) { normalized.dup })
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
+
def self.from_interleaved(samples, channels:)
|
|
23
|
+
channel_count = [channels.to_i, 1].max
|
|
24
|
+
normalized = Array(samples).map(&:to_f)
|
|
25
|
+
frame_count = (normalized.length.to_f / channel_count).ceil
|
|
26
|
+
from_channel_data(
|
|
27
|
+
Array.new(channel_count) do |channel_index|
|
|
28
|
+
Array.new(frame_count) do |frame_index|
|
|
29
|
+
normalized[(frame_index * channel_count) + channel_index] || 0.0
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def self.from_packed_float32(payload, channels:)
|
|
36
|
+
from_interleaved(payload.unpack("e*"), channels: channels)
|
|
37
|
+
end
|
|
38
|
+
|
|
22
39
|
def initialize(channel_data)
|
|
23
40
|
@channel_data = channel_data
|
|
24
41
|
end
|
|
@@ -52,31 +69,73 @@ module Deftones
|
|
|
52
69
|
end
|
|
53
70
|
end
|
|
54
71
|
|
|
72
|
+
def packed_float32
|
|
73
|
+
interleaved.pack("e*")
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def packed_float64
|
|
77
|
+
interleaved.pack("E*")
|
|
78
|
+
end
|
|
79
|
+
|
|
55
80
|
def channel(index)
|
|
56
81
|
@channel_data[index] || Array.new(num_frames, 0.0)
|
|
57
82
|
end
|
|
58
83
|
|
|
59
|
-
def fit_channels(target_channels)
|
|
84
|
+
def fit_channels(target_channels, downmix: :average, upmix: :wrap)
|
|
60
85
|
target = [target_channels.to_i, 1].max
|
|
61
86
|
return dup if target == channels
|
|
62
|
-
return self.class.from_channel_data([
|
|
87
|
+
return self.class.from_channel_data([downmixed_channel(downmix)]) if target == 1
|
|
63
88
|
|
|
64
89
|
if channels == 1
|
|
65
90
|
return self.class.from_channel_data(Array.new(target) { @channel_data.first.dup })
|
|
66
91
|
end
|
|
67
92
|
|
|
68
|
-
self.class.from_channel_data(Array.new(target) { |index|
|
|
93
|
+
self.class.from_channel_data(Array.new(target) { |index| upmixed_channel(index, upmix) })
|
|
69
94
|
end
|
|
70
95
|
|
|
71
|
-
def mix!(other)
|
|
96
|
+
def mix!(other, headroom: :sum, gain: 1.0)
|
|
72
97
|
incoming = other.fit_channels(channels)
|
|
73
98
|
channels.times do |channel_index|
|
|
74
99
|
num_frames.times do |frame_index|
|
|
75
|
-
@channel_data[channel_index][frame_index]
|
|
100
|
+
mixed = @channel_data[channel_index][frame_index] + (incoming.channel_data[channel_index][frame_index] * gain)
|
|
101
|
+
@channel_data[channel_index][frame_index] = apply_headroom(mixed, headroom)
|
|
76
102
|
end
|
|
77
103
|
end
|
|
78
104
|
self
|
|
79
105
|
end
|
|
106
|
+
|
|
107
|
+
private
|
|
108
|
+
|
|
109
|
+
def downmixed_channel(policy)
|
|
110
|
+
case policy
|
|
111
|
+
when :average then mono
|
|
112
|
+
when :sum
|
|
113
|
+
Array.new(num_frames) { |frame_index| @channel_data.sum { |channel| channel[frame_index] } }
|
|
114
|
+
when :first
|
|
115
|
+
channel(0).dup
|
|
116
|
+
else
|
|
117
|
+
raise ArgumentError, "Unsupported downmix policy: #{policy}"
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def upmixed_channel(index, policy)
|
|
122
|
+
case policy
|
|
123
|
+
when :wrap then channel(index % channels).dup
|
|
124
|
+
when :silence then index < channels ? channel(index).dup : Array.new(num_frames, 0.0)
|
|
125
|
+
when :duplicate then channel([index, channels - 1].min).dup
|
|
126
|
+
else
|
|
127
|
+
raise ArgumentError, "Unsupported upmix policy: #{policy}"
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def apply_headroom(sample, policy)
|
|
132
|
+
case policy
|
|
133
|
+
when :sum then sample
|
|
134
|
+
when :clamp then sample.clamp(-1.0, 1.0)
|
|
135
|
+
else
|
|
136
|
+
raise ArgumentError, "Unsupported headroom policy: #{policy}"
|
|
137
|
+
end
|
|
138
|
+
end
|
|
80
139
|
end
|
|
81
140
|
end
|
|
82
141
|
end
|
|
@@ -18,12 +18,14 @@ module Deftones
|
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
def connect(destination, output_index: 0, input_index: 0)
|
|
21
|
-
_ = output_index
|
|
22
|
-
_ = input_index
|
|
23
21
|
raise ArgumentError, "destination is required" if destination.nil?
|
|
24
22
|
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
source_node = output_for_connection(output_index)
|
|
24
|
+
destination_node = destination_for_connection(destination, input_index)
|
|
25
|
+
validate_connectable!(source_node, destination_node)
|
|
26
|
+
raise ArgumentError, "connection would create a cycle" if destination_node.send(:reaches_node?, source_node)
|
|
27
|
+
|
|
28
|
+
source_node.attach_destination(destination_node)
|
|
27
29
|
self
|
|
28
30
|
end
|
|
29
31
|
|
|
@@ -52,6 +54,21 @@ module Deftones
|
|
|
52
54
|
self
|
|
53
55
|
end
|
|
54
56
|
|
|
57
|
+
def inputs
|
|
58
|
+
@sources.dup
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def outputs
|
|
62
|
+
@destinations.dup
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def connected?(destination = nil)
|
|
66
|
+
return @destinations.any? if destination.nil?
|
|
67
|
+
|
|
68
|
+
destination_node = destination.respond_to?(:input) ? destination.input : destination
|
|
69
|
+
@destinations.include?(destination_node)
|
|
70
|
+
end
|
|
71
|
+
|
|
55
72
|
def to_output
|
|
56
73
|
connect(context.output)
|
|
57
74
|
self
|
|
@@ -121,7 +138,10 @@ module Deftones
|
|
|
121
138
|
1
|
|
122
139
|
end
|
|
123
140
|
|
|
124
|
-
def set(**params)
|
|
141
|
+
def set(strict: false, **params)
|
|
142
|
+
unknown = params.keys.reject { |key| respond_to?(:"#{key}=") }
|
|
143
|
+
raise ArgumentError, "Unknown parameter(s): #{unknown.join(', ')}" if strict && unknown.any?
|
|
144
|
+
|
|
125
145
|
params.each do |key, value|
|
|
126
146
|
writer = :"#{key}="
|
|
127
147
|
public_send(writer, value) if respond_to?(writer)
|
|
@@ -129,11 +149,19 @@ module Deftones
|
|
|
129
149
|
self
|
|
130
150
|
end
|
|
131
151
|
|
|
132
|
-
def get(*keys)
|
|
133
|
-
|
|
152
|
+
def get(*keys, strict: false)
|
|
153
|
+
unknown = []
|
|
154
|
+
values = keys.flatten.each_with_object({}) do |key, collected|
|
|
134
155
|
reader = key.to_sym
|
|
135
|
-
|
|
156
|
+
if respond_to?(reader)
|
|
157
|
+
collected[reader] = public_send(reader)
|
|
158
|
+
else
|
|
159
|
+
unknown << reader
|
|
160
|
+
end
|
|
136
161
|
end
|
|
162
|
+
raise ArgumentError, "Unknown parameter(s): #{unknown.join(', ')}" if strict && unknown.any?
|
|
163
|
+
|
|
164
|
+
values
|
|
137
165
|
end
|
|
138
166
|
|
|
139
167
|
def name
|
|
@@ -157,6 +185,7 @@ module Deftones
|
|
|
157
185
|
alias channelInterpretation channel_interpretation
|
|
158
186
|
alias numberOfInputs number_of_inputs
|
|
159
187
|
alias numberOfOutputs number_of_outputs
|
|
188
|
+
alias connected connected?
|
|
160
189
|
alias toString to_s
|
|
161
190
|
|
|
162
191
|
def dispose
|
|
@@ -177,7 +206,21 @@ module Deftones
|
|
|
177
206
|
|
|
178
207
|
protected
|
|
179
208
|
|
|
209
|
+
def output_for_index(index)
|
|
210
|
+
raise_connection_index_error!(:output_index, index, number_of_outputs) unless index.zero?
|
|
211
|
+
|
|
212
|
+
output
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
def input_for_index(index)
|
|
216
|
+
raise_connection_index_error!(:input_index, index, number_of_inputs) unless index.zero?
|
|
217
|
+
|
|
218
|
+
input
|
|
219
|
+
end
|
|
220
|
+
|
|
180
221
|
def render_block(num_frames, start_frame = 0, cache = {})
|
|
222
|
+
raise Deftones::Error, "cannot render disposed node: #{name}" if disposed?
|
|
223
|
+
|
|
181
224
|
cache_key = [object_id, :block, start_frame, num_frames]
|
|
182
225
|
return cache.fetch(cache_key).dup if cache.key?(cache_key)
|
|
183
226
|
|
|
@@ -257,6 +300,53 @@ module Deftones
|
|
|
257
300
|
def default_output_channels
|
|
258
301
|
default_input_channels
|
|
259
302
|
end
|
|
303
|
+
|
|
304
|
+
def reaches_node?(target, visited = {})
|
|
305
|
+
return true if equal?(target)
|
|
306
|
+
return false if visited[object_id]
|
|
307
|
+
|
|
308
|
+
visited[object_id] = true
|
|
309
|
+
@destinations.any? { |destination| destination.send(:reaches_node?, target, visited) }
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
def raise_connection_index_error!(name, index, count)
|
|
313
|
+
raise ArgumentError, "#{name} #{index} is out of range for #{count} #{count == 1 ? 'port' : 'ports'}"
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
private
|
|
317
|
+
|
|
318
|
+
def output_for_connection(index)
|
|
319
|
+
normalized_index = normalize_connection_index(index, :output_index)
|
|
320
|
+
output_for_index(normalized_index)
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
def destination_for_connection(destination, index)
|
|
324
|
+
normalized_index = normalize_connection_index(index, :input_index)
|
|
325
|
+
if destination.respond_to?(:input_for_index)
|
|
326
|
+
destination.input_for_index(normalized_index)
|
|
327
|
+
else
|
|
328
|
+
destination_node = destination.respond_to?(:input) ? destination.input : destination
|
|
329
|
+
validate_connection_index!(normalized_index, destination_node.number_of_inputs, :input_index)
|
|
330
|
+
destination_node
|
|
331
|
+
end
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
def normalize_connection_index(index, name)
|
|
335
|
+
Integer(index).tap do |normalized|
|
|
336
|
+
raise ArgumentError, "#{name} must be greater than or equal to 0" if normalized.negative?
|
|
337
|
+
end
|
|
338
|
+
rescue ArgumentError, TypeError
|
|
339
|
+
raise ArgumentError, "#{name} must be an integer"
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
def validate_connection_index!(index, count, name)
|
|
343
|
+
raise_connection_index_error!(name, index, count) if index >= count
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
def validate_connectable!(source_node, destination_node)
|
|
347
|
+
raise Deftones::Error, "cannot connect disposed source node" if source_node.disposed?
|
|
348
|
+
raise Deftones::Error, "cannot connect disposed destination node" if destination_node.disposed?
|
|
349
|
+
end
|
|
260
350
|
end
|
|
261
351
|
end
|
|
262
352
|
end
|
data/lib/deftones/core/gain.rb
CHANGED
|
@@ -14,14 +14,6 @@ module Deftones
|
|
|
14
14
|
@gain.value = value
|
|
15
15
|
end
|
|
16
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
17
|
def multichannel_process?
|
|
26
18
|
true
|
|
27
19
|
end
|
|
@@ -5,7 +5,7 @@ module Deftones
|
|
|
5
5
|
class Instrument < AudioNode
|
|
6
6
|
class VolumeProxy
|
|
7
7
|
attr_reader :instrument
|
|
8
|
-
|
|
8
|
+
attr_reader :value
|
|
9
9
|
|
|
10
10
|
def initialize(instrument, value: 0.0)
|
|
11
11
|
@instrument = instrument
|
|
@@ -17,17 +17,48 @@ module Deftones
|
|
|
17
17
|
instrument.apply_volume!
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
-
def ramp_to(target_value,
|
|
21
|
-
|
|
20
|
+
def ramp_to(target_value, duration = nil)
|
|
21
|
+
return assign_immediately(target_value) if duration.nil?
|
|
22
|
+
|
|
23
|
+
resolved_duration = Deftones::Music::Time.parse(duration)
|
|
24
|
+
return assign_immediately(target_value) if resolved_duration <= 0.0
|
|
25
|
+
|
|
26
|
+
@value = target_value.to_f
|
|
27
|
+
instrument.output.gain.linear_ramp_to_value_at_time(
|
|
28
|
+
instrument.mute? ? 0.0 : Deftones.db_to_gain(@value),
|
|
29
|
+
instrument.context.current_time + resolved_duration
|
|
30
|
+
)
|
|
22
31
|
self
|
|
23
32
|
end
|
|
24
33
|
|
|
25
|
-
|
|
26
|
-
|
|
34
|
+
def set_value_at_time(target_value, time)
|
|
35
|
+
@value = target_value.to_f
|
|
36
|
+
instrument.output.gain.set_value_at_time(instrument.mute? ? 0.0 : Deftones.db_to_gain(@value), time)
|
|
37
|
+
self
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def linear_ramp_to(target_value, duration = nil)
|
|
41
|
+
ramp_to(target_value, duration)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def exponential_ramp_to(target_value, duration = nil)
|
|
45
|
+
ramp_to(target_value, duration)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
alias setValueAtTime set_value_at_time
|
|
49
|
+
alias linearRampTo linear_ramp_to
|
|
50
|
+
alias exponentialRampTo exponential_ramp_to
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
def assign_immediately(target_value)
|
|
55
|
+
self.value = target_value
|
|
56
|
+
self
|
|
57
|
+
end
|
|
27
58
|
end
|
|
28
59
|
|
|
29
60
|
attr_reader :output, :volume
|
|
30
|
-
|
|
61
|
+
attr_reader :mute
|
|
31
62
|
|
|
32
63
|
def initialize(context: Deftones.context)
|
|
33
64
|
super(context: context)
|
|
@@ -58,7 +89,10 @@ module Deftones
|
|
|
58
89
|
@mute
|
|
59
90
|
end
|
|
60
91
|
|
|
61
|
-
def set(**params)
|
|
92
|
+
def set(strict: false, **params)
|
|
93
|
+
unknown = params.keys.reject { |key| respond_to?(:"#{key}=") }
|
|
94
|
+
raise ArgumentError, "Unknown parameter(s): #{unknown.join(', ')}" if strict && unknown.any?
|
|
95
|
+
|
|
62
96
|
params.each do |key, value|
|
|
63
97
|
writer = :"#{key}="
|
|
64
98
|
public_send(writer, value) if respond_to?(writer)
|
|
@@ -66,12 +100,20 @@ module Deftones
|
|
|
66
100
|
self
|
|
67
101
|
end
|
|
68
102
|
|
|
69
|
-
def get(*keys)
|
|
103
|
+
def get(*keys, strict: false)
|
|
70
104
|
requested = keys.flatten
|
|
71
|
-
|
|
105
|
+
unknown = []
|
|
106
|
+
values = requested.each_with_object({}) do |key, collected|
|
|
72
107
|
reader = key.to_sym
|
|
73
|
-
|
|
108
|
+
if respond_to?(reader)
|
|
109
|
+
collected[reader] = public_send(reader)
|
|
110
|
+
else
|
|
111
|
+
unknown << reader
|
|
112
|
+
end
|
|
74
113
|
end
|
|
114
|
+
raise ArgumentError, "Unknown parameter(s): #{unknown.join(', ')}" if strict && unknown.any?
|
|
115
|
+
|
|
116
|
+
values
|
|
75
117
|
end
|
|
76
118
|
|
|
77
119
|
def release_all(time = nil)
|
data/lib/deftones/core/param.rb
CHANGED
|
@@ -3,11 +3,13 @@
|
|
|
3
3
|
module Deftones
|
|
4
4
|
module Core
|
|
5
5
|
class Param < Signal
|
|
6
|
-
attr_reader :lfo
|
|
6
|
+
attr_reader :lfo, :audio_source, :modulation_amount
|
|
7
7
|
|
|
8
8
|
def initialize(**options)
|
|
9
9
|
super
|
|
10
10
|
@lfo = nil
|
|
11
|
+
@audio_source = nil
|
|
12
|
+
@modulation_amount = 1.0
|
|
11
13
|
end
|
|
12
14
|
|
|
13
15
|
def set_param(param)
|
|
@@ -25,7 +27,55 @@ module Deftones
|
|
|
25
27
|
@lfo = source
|
|
26
28
|
end
|
|
27
29
|
|
|
30
|
+
def connect_audio(source, amount: 1.0)
|
|
31
|
+
raise ArgumentError, "audio source is required" if source.nil?
|
|
32
|
+
raise ArgumentError, "audio source must render or process samples" unless modulation_source?(source)
|
|
33
|
+
|
|
34
|
+
@audio_source = source
|
|
35
|
+
@modulation_amount = amount.to_f
|
|
36
|
+
self
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def disconnect_audio(source = nil)
|
|
40
|
+
return self if source && source != @audio_source
|
|
41
|
+
|
|
42
|
+
@audio_source = nil
|
|
43
|
+
@modulation_amount = 1.0
|
|
44
|
+
self
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def audio_rate?
|
|
48
|
+
!@audio_source.nil?
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def process(num_frames, start_frame = 0)
|
|
52
|
+
values = super
|
|
53
|
+
return values unless @audio_source
|
|
54
|
+
|
|
55
|
+
modulation = modulation_samples(num_frames, start_frame)
|
|
56
|
+
values.zip(modulation).map { |base, sample| base + (sample.to_f * @modulation_amount) }
|
|
57
|
+
end
|
|
58
|
+
|
|
28
59
|
alias setParam set_param
|
|
60
|
+
alias connectAudio connect_audio
|
|
61
|
+
alias disconnectAudio disconnect_audio
|
|
62
|
+
alias audioRate audio_rate?
|
|
63
|
+
|
|
64
|
+
private
|
|
65
|
+
|
|
66
|
+
def modulation_source?(source)
|
|
67
|
+
source.respond_to?(:values) || source.respond_to?(:process) || source.respond_to?(:render)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def modulation_samples(num_frames, start_frame)
|
|
71
|
+
if @audio_source.respond_to?(:values)
|
|
72
|
+
@audio_source.values(num_frames, start_frame)
|
|
73
|
+
elsif @audio_source.respond_to?(:render)
|
|
74
|
+
@audio_source.render(num_frames, start_frame)
|
|
75
|
+
else
|
|
76
|
+
@audio_source.process(num_frames, start_frame)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
29
79
|
end
|
|
30
80
|
end
|
|
31
81
|
end
|