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
|
@@ -6,13 +6,18 @@ module Deftones
|
|
|
6
6
|
module PortAudioSupport
|
|
7
7
|
class << self
|
|
8
8
|
def available?
|
|
9
|
+
load_backend!
|
|
9
10
|
!!defined?(PortAudio)
|
|
10
11
|
end
|
|
11
12
|
|
|
12
13
|
def acquire!
|
|
13
14
|
raise Deftones::MissingRealtimeBackendError, "PortAudio backend is unavailable" unless available?
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
mutex.synchronize do
|
|
17
|
+
PortAudio.init if ref_count.zero?
|
|
18
|
+
@ref_count = ref_count + 1
|
|
19
|
+
end
|
|
20
|
+
self
|
|
16
21
|
rescue StandardError => error
|
|
17
22
|
raise Deftones::MissingRealtimeBackendError, error.message
|
|
18
23
|
end
|
|
@@ -20,17 +25,35 @@ module Deftones
|
|
|
20
25
|
def release
|
|
21
26
|
return unless available?
|
|
22
27
|
|
|
23
|
-
|
|
28
|
+
mutex.synchronize do
|
|
29
|
+
return self if ref_count.zero?
|
|
30
|
+
|
|
31
|
+
@ref_count = ref_count - 1
|
|
32
|
+
PortAudio.terminate if ref_count.zero?
|
|
33
|
+
end
|
|
34
|
+
self
|
|
24
35
|
rescue StandardError
|
|
25
36
|
nil
|
|
26
37
|
end
|
|
27
38
|
|
|
28
|
-
def output_parameters(channels)
|
|
29
|
-
build_stream_parameters(
|
|
39
|
+
def output_parameters(channels, device_id: nil, label: nil, sample_rate: nil)
|
|
40
|
+
build_stream_parameters(
|
|
41
|
+
direction: :output,
|
|
42
|
+
channels: channels,
|
|
43
|
+
device_id: device_id,
|
|
44
|
+
label: label,
|
|
45
|
+
sample_rate: sample_rate
|
|
46
|
+
)
|
|
30
47
|
end
|
|
31
48
|
|
|
32
|
-
def input_parameters(channels, device_id: nil, label: nil)
|
|
33
|
-
build_stream_parameters(
|
|
49
|
+
def input_parameters(channels, device_id: nil, label: nil, sample_rate: nil)
|
|
50
|
+
build_stream_parameters(
|
|
51
|
+
direction: :input,
|
|
52
|
+
channels: channels,
|
|
53
|
+
device_id: device_id,
|
|
54
|
+
label: label,
|
|
55
|
+
sample_rate: sample_rate
|
|
56
|
+
)
|
|
34
57
|
end
|
|
35
58
|
|
|
36
59
|
def check_error!(result, fallback: nil)
|
|
@@ -41,11 +64,29 @@ module Deftones
|
|
|
41
64
|
|
|
42
65
|
private
|
|
43
66
|
|
|
44
|
-
def
|
|
67
|
+
def load_backend!
|
|
68
|
+
return true if defined?(PortAudio)
|
|
69
|
+
|
|
70
|
+
require "portaudio"
|
|
71
|
+
true
|
|
72
|
+
rescue LoadError
|
|
73
|
+
false
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def mutex
|
|
77
|
+
@mutex ||= Mutex.new
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def ref_count
|
|
81
|
+
@ref_count ||= 0
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def build_stream_parameters(direction:, channels:, device_id: nil, label: nil, sample_rate: nil)
|
|
45
85
|
device =
|
|
46
86
|
resolve_device(direction: direction, device_id: device_id, label: label)
|
|
47
87
|
|
|
48
88
|
raise Deftones::MissingRealtimeBackendError, "No default #{direction} device available" unless device
|
|
89
|
+
detect_sample_rate_mismatch!(device, sample_rate) if sample_rate
|
|
49
90
|
|
|
50
91
|
{
|
|
51
92
|
device: device,
|
|
@@ -101,11 +142,29 @@ module Deftones
|
|
|
101
142
|
candidates = []
|
|
102
143
|
candidates << device.label if device.respond_to?(:label)
|
|
103
144
|
candidates << device.name if device.respond_to?(:name)
|
|
104
|
-
|
|
145
|
+
matcher = label.is_a?(Regexp) ? label : Regexp.new(Regexp.escape(label.to_s), Regexp::IGNORECASE)
|
|
146
|
+
candidates.compact.any? { |candidate| candidate.to_s.match?(matcher) }
|
|
105
147
|
end
|
|
106
148
|
|
|
107
149
|
def suggested_latency(device, direction)
|
|
108
|
-
direction == :input ?
|
|
150
|
+
method_name = direction == :input ? :default_low_input_latency : :default_low_output_latency
|
|
151
|
+
return device.public_send(method_name) if device.respond_to?(method_name)
|
|
152
|
+
|
|
153
|
+
0.05
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def detect_sample_rate_mismatch!(device, requested_sample_rate)
|
|
157
|
+
device_sample_rate =
|
|
158
|
+
if device.respond_to?(:default_sample_rate)
|
|
159
|
+
device.default_sample_rate
|
|
160
|
+
elsif device.respond_to?(:sample_rate)
|
|
161
|
+
device.sample_rate
|
|
162
|
+
end
|
|
163
|
+
return unless device_sample_rate
|
|
164
|
+
return if device_sample_rate.to_f == requested_sample_rate.to_f
|
|
165
|
+
|
|
166
|
+
raise Deftones::MissingRealtimeBackendError,
|
|
167
|
+
"PortAudio device sample rate #{device_sample_rate} does not match requested #{requested_sample_rate}"
|
|
109
168
|
end
|
|
110
169
|
end
|
|
111
170
|
end
|
|
@@ -3,21 +3,32 @@
|
|
|
3
3
|
module Deftones
|
|
4
4
|
module Source
|
|
5
5
|
class FatOscillator < Core::Source
|
|
6
|
-
attr_reader :frequency
|
|
7
|
-
attr_accessor :count, :spread, :type
|
|
6
|
+
attr_reader :count, :frequency, :spread, :type
|
|
8
7
|
|
|
9
8
|
def initialize(type: :sawtooth, frequency: 440.0, count: 3, spread: 20.0, context: Deftones.context)
|
|
10
9
|
super(context: context)
|
|
11
|
-
@type = type.to_sym
|
|
12
10
|
@frequency = Core::Signal.new(value: frequency, units: :frequency, context: context)
|
|
13
|
-
@
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
@phases = []
|
|
12
|
+
self.type = type
|
|
13
|
+
self.count = count
|
|
14
|
+
self.spread = spread
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def count=(value)
|
|
18
|
+
@count = [value.to_i, 1].max
|
|
19
|
+
@phases = Array.new(@count, 0.0) if @phases.length != @count
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def spread=(value)
|
|
23
|
+
@spread = value.to_f
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def type=(value)
|
|
27
|
+
@type = normalize_type(value)
|
|
16
28
|
end
|
|
17
29
|
|
|
18
30
|
def process(_input_buffer, num_frames, start_frame, _cache)
|
|
19
31
|
frequencies = @frequency.process(num_frames, start_frame)
|
|
20
|
-
generator = Oscillator::GENERATORS.fetch(@type) { Oscillator::GENERATORS[:sawtooth] }
|
|
21
32
|
|
|
22
33
|
Array.new(num_frames) do |index|
|
|
23
34
|
current_time = (start_frame + index).to_f / context.sample_rate
|
|
@@ -25,8 +36,9 @@ module Deftones
|
|
|
25
36
|
|
|
26
37
|
detuned = detune_frequencies(frequencies[index])
|
|
27
38
|
samples = @phases.each_with_index.map do |phase, voice_index|
|
|
28
|
-
|
|
29
|
-
|
|
39
|
+
phase_increment = detuned[voice_index] / context.sample_rate
|
|
40
|
+
sample = Oscillator.sample(@type, phase, phase_increment)
|
|
41
|
+
@phases[voice_index] = (phase + phase_increment) % 1.0
|
|
30
42
|
sample
|
|
31
43
|
end
|
|
32
44
|
samples.sum / samples.length.to_f
|
|
@@ -44,6 +56,13 @@ module Deftones
|
|
|
44
56
|
@phases = Array.new(@count, 0.0) if @phases.length != @count
|
|
45
57
|
offsets
|
|
46
58
|
end
|
|
59
|
+
|
|
60
|
+
def normalize_type(type)
|
|
61
|
+
normalized = type.to_sym
|
|
62
|
+
return normalized if Oscillator::TYPES.include?(normalized)
|
|
63
|
+
|
|
64
|
+
raise ArgumentError, "Unsupported oscillator type: #{type}"
|
|
65
|
+
end
|
|
47
66
|
end
|
|
48
67
|
end
|
|
49
68
|
end
|
|
@@ -3,21 +3,33 @@
|
|
|
3
3
|
module Deftones
|
|
4
4
|
module Source
|
|
5
5
|
class GrainPlayer < Player
|
|
6
|
+
WINDOWS = %i[linear hann hamming blackman].freeze
|
|
7
|
+
|
|
6
8
|
attr_reader :detune
|
|
7
9
|
attr_accessor :grain_size, :overlap, :jitter
|
|
10
|
+
attr_reader :window
|
|
8
11
|
|
|
9
|
-
def initialize(grain_size: 0.05, overlap: 0.5, jitter: 0.002, detune: 0.0,
|
|
12
|
+
def initialize(grain_size: 0.05, overlap: 0.5, jitter: 0.002, detune: 0.0,
|
|
13
|
+
window: :linear, jitter_seed: nil, rng: nil, **options)
|
|
10
14
|
super(**options)
|
|
11
15
|
@grain_size = grain_size.to_f
|
|
12
16
|
@overlap = overlap.to_f
|
|
13
17
|
@jitter = jitter.to_f
|
|
14
18
|
@detune = Core::Signal.new(value: detune, units: :number, context: context)
|
|
19
|
+
@window = normalize_window(window)
|
|
20
|
+
@jitter_seed = jitter_seed
|
|
21
|
+
@rng = rng
|
|
22
|
+
@grain_random_cache = {}
|
|
15
23
|
end
|
|
16
24
|
|
|
17
25
|
def detune=(value)
|
|
18
26
|
@detune.value = value
|
|
19
27
|
end
|
|
20
28
|
|
|
29
|
+
def window=(value)
|
|
30
|
+
@window = normalize_window(value)
|
|
31
|
+
end
|
|
32
|
+
|
|
21
33
|
def process(_input_buffer, num_frames, start_frame, _cache)
|
|
22
34
|
rates = @playback_rate.process(num_frames, start_frame)
|
|
23
35
|
detunes = @detune.process(num_frames, start_frame)
|
|
@@ -156,10 +168,35 @@ module Deftones
|
|
|
156
168
|
end
|
|
157
169
|
|
|
158
170
|
def grain_random(grain_index)
|
|
159
|
-
|
|
171
|
+
return @grain_random_cache[grain_index] if @grain_random_cache.key?(grain_index)
|
|
172
|
+
|
|
173
|
+
@grain_random_cache[grain_index] =
|
|
174
|
+
if @rng
|
|
175
|
+
@rng.rand
|
|
176
|
+
elsif @jitter_seed
|
|
177
|
+
Random.new(@jitter_seed.to_i + grain_index).rand
|
|
178
|
+
else
|
|
179
|
+
Math.sin((grain_index + 1) * 12_989.0).abs % 1.0
|
|
180
|
+
end
|
|
160
181
|
end
|
|
161
182
|
|
|
162
183
|
def grain_window_gain(grain_elapsed, grain_duration, overlap_duration)
|
|
184
|
+
return overlapped_linear_gain(grain_elapsed, grain_duration, overlap_duration) if @window == :linear
|
|
185
|
+
|
|
186
|
+
progress = (grain_elapsed / grain_duration).clamp(0.0, 1.0)
|
|
187
|
+
case @window
|
|
188
|
+
when :hann
|
|
189
|
+
0.5 - (0.5 * Math.cos(2.0 * Math::PI * progress))
|
|
190
|
+
when :hamming
|
|
191
|
+
0.54 - (0.46 * Math.cos(2.0 * Math::PI * progress))
|
|
192
|
+
when :blackman
|
|
193
|
+
0.42 - (0.5 * Math.cos(2.0 * Math::PI * progress)) + (0.08 * Math.cos(4.0 * Math::PI * progress))
|
|
194
|
+
else
|
|
195
|
+
overlapped_linear_gain(grain_elapsed, grain_duration, overlap_duration)
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def overlapped_linear_gain(grain_elapsed, grain_duration, overlap_duration)
|
|
163
200
|
return 1.0 if overlap_duration <= 0.0
|
|
164
201
|
|
|
165
202
|
fade_in = (grain_elapsed / overlap_duration).clamp(0.0, 1.0)
|
|
@@ -167,6 +204,16 @@ module Deftones
|
|
|
167
204
|
[fade_in, fade_out].min
|
|
168
205
|
end
|
|
169
206
|
|
|
207
|
+
def normalize_window(window)
|
|
208
|
+
normalized = window.to_sym
|
|
209
|
+
return normalized if WINDOWS.include?(normalized)
|
|
210
|
+
|
|
211
|
+
raise ArgumentError, "Unsupported grain window: #{window}"
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
alias grainWindow window
|
|
215
|
+
alias grainWindow= window=
|
|
216
|
+
|
|
170
217
|
def naturally_finished?(current_time, playback_rate, grain_duration)
|
|
171
218
|
return false if @loop
|
|
172
219
|
return false unless playback_rate.positive?
|
|
@@ -5,17 +5,18 @@ module Deftones
|
|
|
5
5
|
class Noise < Core::Source
|
|
6
6
|
TYPES = %i[white pink brown].freeze
|
|
7
7
|
|
|
8
|
-
attr_accessor :
|
|
9
|
-
attr_reader :playback_rate
|
|
8
|
+
attr_accessor :fade_in, :fade_out
|
|
9
|
+
attr_reader :playback_rate, :type
|
|
10
10
|
|
|
11
|
-
def initialize(type: :white, playback_rate: 1.0, fade_in: 0.0, fade_out: 0.0,
|
|
11
|
+
def initialize(type: :white, playback_rate: 1.0, fade_in: 0.0, fade_out: 0.0, seed: nil, rng: nil,
|
|
12
|
+
context: Deftones.context)
|
|
12
13
|
super(context: context)
|
|
13
|
-
@type = normalize_type(type)
|
|
14
14
|
@playback_rate = playback_rate.to_f
|
|
15
15
|
@fade_in = fade_in.to_f
|
|
16
16
|
@fade_out = fade_out.to_f
|
|
17
|
-
@
|
|
18
|
-
|
|
17
|
+
@rng = rng || (seed.nil? ? Random : Random.new(seed))
|
|
18
|
+
reset_colored_state
|
|
19
|
+
self.type = type
|
|
19
20
|
@held_sample = next_noise_sample
|
|
20
21
|
@playback_phase = 0.0
|
|
21
22
|
end
|
|
@@ -24,6 +25,15 @@ module Deftones
|
|
|
24
25
|
@playback_rate = value.to_f
|
|
25
26
|
end
|
|
26
27
|
|
|
28
|
+
def type=(value)
|
|
29
|
+
normalized = normalize_type(value)
|
|
30
|
+
return @type = normalized if @type == normalized
|
|
31
|
+
|
|
32
|
+
@type = normalized
|
|
33
|
+
reset_colored_state
|
|
34
|
+
@held_sample = next_noise_sample if defined?(@held_sample)
|
|
35
|
+
end
|
|
36
|
+
|
|
27
37
|
def process(_input_buffer, num_frames, start_frame, _cache)
|
|
28
38
|
Array.new(num_frames) do |index|
|
|
29
39
|
current_time = (start_frame + index).to_f / context.sample_rate
|
|
@@ -72,19 +82,41 @@ module Deftones
|
|
|
72
82
|
end
|
|
73
83
|
|
|
74
84
|
def next_noise_sample
|
|
75
|
-
white = (rand * 2.0) - 1.0
|
|
85
|
+
white = (@rng.rand * 2.0) - 1.0
|
|
76
86
|
|
|
77
87
|
case normalize_type(@type)
|
|
78
88
|
when :white
|
|
79
89
|
white
|
|
80
90
|
when :pink
|
|
81
|
-
|
|
82
|
-
@pink_state * 3.5
|
|
91
|
+
pink_noise_sample(white)
|
|
83
92
|
when :brown
|
|
84
|
-
|
|
93
|
+
brown_noise_sample(white)
|
|
85
94
|
end
|
|
86
95
|
end
|
|
87
96
|
|
|
97
|
+
def pink_noise_sample(white)
|
|
98
|
+
@pink_state[0] = (0.99886 * @pink_state[0]) + (white * 0.0555179)
|
|
99
|
+
@pink_state[1] = (0.99332 * @pink_state[1]) + (white * 0.0750759)
|
|
100
|
+
@pink_state[2] = (0.96900 * @pink_state[2]) + (white * 0.1538520)
|
|
101
|
+
@pink_state[3] = (0.86650 * @pink_state[3]) + (white * 0.3104856)
|
|
102
|
+
@pink_state[4] = (0.55000 * @pink_state[4]) + (white * 0.5329522)
|
|
103
|
+
@pink_state[5] = (-0.7616 * @pink_state[5]) - (white * 0.0168980)
|
|
104
|
+
|
|
105
|
+
sample = @pink_state[0..6].sum + (white * 0.5362)
|
|
106
|
+
@pink_state[6] = white * 0.115926
|
|
107
|
+
Deftones::DSP::Helpers.clamp(sample * 0.11, -1.0, 1.0)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def brown_noise_sample(white)
|
|
111
|
+
@brown_state = (@brown_state + (0.02 * white)) / 1.02
|
|
112
|
+
Deftones::DSP::Helpers.clamp(@brown_state * 3.5, -1.0, 1.0)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def reset_colored_state
|
|
116
|
+
@pink_state = Array.new(7, 0.0)
|
|
117
|
+
@brown_state = 0.0
|
|
118
|
+
end
|
|
119
|
+
|
|
88
120
|
def normalize_type(type)
|
|
89
121
|
normalized = type.to_sym
|
|
90
122
|
return normalized if TYPES.include?(normalized)
|
|
@@ -3,27 +3,82 @@
|
|
|
3
3
|
module Deftones
|
|
4
4
|
module Source
|
|
5
5
|
class Oscillator < Core::Source
|
|
6
|
+
TYPES = %i[sine square sawtooth triangle].freeze
|
|
6
7
|
GENERATORS = {
|
|
7
|
-
sine:
|
|
8
|
-
|
|
9
|
-
},
|
|
10
|
-
|
|
11
|
-
phase < 0.5 ? 1.0 : -1.0
|
|
12
|
-
},
|
|
13
|
-
sawtooth: lambda { |phase|
|
|
14
|
-
(2.0 * phase) - 1.0
|
|
15
|
-
},
|
|
16
|
-
triangle: lambda { |phase|
|
|
17
|
-
(4.0 * (phase < 0.5 ? phase : 1.0 - phase)) - 1.0
|
|
18
|
-
}
|
|
8
|
+
sine: ->(phase) { Math.sin(2.0 * Math::PI * phase) },
|
|
9
|
+
square: ->(phase) { phase < 0.5 ? 1.0 : -1.0 },
|
|
10
|
+
sawtooth: ->(phase) { (2.0 * phase) - 1.0 },
|
|
11
|
+
triangle: ->(phase) { (4.0 * (phase < 0.5 ? phase : 1.0 - phase)) - 1.0 }
|
|
19
12
|
}.freeze
|
|
20
13
|
|
|
21
|
-
|
|
22
|
-
|
|
14
|
+
class << self
|
|
15
|
+
def sample(type, phase, phase_increment = 0.0)
|
|
16
|
+
case type
|
|
17
|
+
when :sine
|
|
18
|
+
Math.sin(2.0 * Math::PI * phase)
|
|
19
|
+
when :square
|
|
20
|
+
bandlimited_square(phase, phase_increment)
|
|
21
|
+
when :sawtooth
|
|
22
|
+
bandlimited_sawtooth(phase, phase_increment)
|
|
23
|
+
when :triangle
|
|
24
|
+
bandlimited_triangle(phase, phase_increment)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def bandlimited_square(phase, phase_increment)
|
|
31
|
+
sample = phase < 0.5 ? 1.0 : -1.0
|
|
32
|
+
sample += poly_blep(phase, phase_increment)
|
|
33
|
+
sample -= poly_blep((phase + 0.5) % 1.0, phase_increment)
|
|
34
|
+
sample
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def bandlimited_sawtooth(phase, phase_increment)
|
|
38
|
+
((2.0 * phase) - 1.0) - poly_blep(phase, phase_increment)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def bandlimited_triangle(phase, phase_increment)
|
|
42
|
+
return naive_triangle(phase) unless phase_increment.positive?
|
|
43
|
+
|
|
44
|
+
max_harmonic = (0.5 / phase_increment.abs).floor
|
|
45
|
+
return naive_triangle(phase) if max_harmonic < 1
|
|
46
|
+
|
|
47
|
+
sum = 0.0
|
|
48
|
+
harmonic = 1
|
|
49
|
+
while harmonic <= max_harmonic
|
|
50
|
+
sum += Math.cos(2.0 * Math::PI * harmonic * phase) / (harmonic * harmonic)
|
|
51
|
+
harmonic += 2
|
|
52
|
+
end
|
|
53
|
+
-(8.0 / (Math::PI * Math::PI)) * sum
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def naive_triangle(phase)
|
|
57
|
+
(4.0 * (phase < 0.5 ? phase : 1.0 - phase)) - 1.0
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def poly_blep(phase, phase_increment)
|
|
61
|
+
increment = [phase_increment.abs, 1.0e-9].max
|
|
62
|
+
return poly_blep_start(phase / increment) if phase < increment
|
|
63
|
+
return poly_blep_end((phase - 1.0) / increment) if phase > 1.0 - increment
|
|
64
|
+
|
|
65
|
+
0.0
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def poly_blep_start(t)
|
|
69
|
+
(t + t) - (t * t) - 1.0
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def poly_blep_end(t)
|
|
73
|
+
(t * t) + (t + t) + 1.0
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
attr_reader :detune, :frequency, :type
|
|
23
78
|
|
|
24
79
|
def initialize(type: :sine, frequency: 440.0, detune: 0.0, phase: 0.0, context: Deftones.context)
|
|
25
80
|
super(context: context)
|
|
26
|
-
|
|
81
|
+
self.type = type
|
|
27
82
|
@frequency = Core::Signal.new(value: frequency, units: :frequency, context: context)
|
|
28
83
|
@detune = Core::Signal.new(value: detune, units: :number, context: context)
|
|
29
84
|
self.phase = phase
|
|
@@ -41,8 +96,12 @@ module Deftones
|
|
|
41
96
|
@phase = value.to_f % 1.0
|
|
42
97
|
end
|
|
43
98
|
|
|
99
|
+
def type=(value)
|
|
100
|
+
@type = normalize_type(value)
|
|
101
|
+
end
|
|
102
|
+
|
|
44
103
|
def process(_input_buffer, num_frames, start_frame, _cache)
|
|
45
|
-
|
|
104
|
+
oscillator_type = normalize_type(@type)
|
|
46
105
|
frequencies = @frequency.process(num_frames, start_frame)
|
|
47
106
|
detunes = @detune.process(num_frames, start_frame)
|
|
48
107
|
|
|
@@ -50,22 +109,27 @@ module Deftones
|
|
|
50
109
|
current_time = (start_frame + index).to_f / context.sample_rate
|
|
51
110
|
next 0.0 unless active_at?(current_time)
|
|
52
111
|
|
|
53
|
-
sample = generator.call(@phase)
|
|
54
112
|
frequency = frequencies[index] * detune_ratio(detunes[index])
|
|
55
|
-
|
|
113
|
+
phase_increment = frequency / context.sample_rate
|
|
114
|
+
sample = sample_for(oscillator_type, @phase, phase_increment)
|
|
115
|
+
@phase = (@phase + phase_increment) % 1.0
|
|
56
116
|
sample
|
|
57
117
|
end
|
|
58
118
|
end
|
|
59
119
|
|
|
60
120
|
private
|
|
61
121
|
|
|
122
|
+
def sample_for(type, phase, phase_increment)
|
|
123
|
+
Oscillator.sample(type, phase, phase_increment)
|
|
124
|
+
end
|
|
125
|
+
|
|
62
126
|
def detune_ratio(cents)
|
|
63
127
|
2.0**(cents.to_f / 1200.0)
|
|
64
128
|
end
|
|
65
129
|
|
|
66
130
|
def normalize_type(type)
|
|
67
131
|
normalized = type.to_sym
|
|
68
|
-
return normalized if
|
|
132
|
+
return normalized if TYPES.include?(normalized)
|
|
69
133
|
|
|
70
134
|
raise ArgumentError, "Unsupported oscillator type: #{type}"
|
|
71
135
|
end
|
|
@@ -79,6 +79,7 @@ module Deftones
|
|
|
79
79
|
|
|
80
80
|
def process(_input_buffer, num_frames, start_frame, _cache)
|
|
81
81
|
rates = @playback_rate.process(num_frames, start_frame)
|
|
82
|
+
sample_positions = sample_positions_for(num_frames, start_frame, rates)
|
|
82
83
|
return process_multichannel_buffer(num_frames, start_frame, rates) if multichannel_process?
|
|
83
84
|
|
|
84
85
|
Array.new(num_frames) do |index|
|
|
@@ -86,7 +87,7 @@ module Deftones
|
|
|
86
87
|
notify_stop(current_time) if @stop_time && current_time >= @stop_time
|
|
87
88
|
next 0.0 unless active_at?(current_time)
|
|
88
89
|
|
|
89
|
-
sample_position =
|
|
90
|
+
sample_position = sample_positions[index]
|
|
90
91
|
if sample_position.negative?
|
|
91
92
|
@stop_time ||= current_time
|
|
92
93
|
notify_stop(current_time)
|
|
@@ -161,13 +162,14 @@ module Deftones
|
|
|
161
162
|
|
|
162
163
|
def process_multichannel_buffer(num_frames, start_frame, rates)
|
|
163
164
|
output = Array.new(@buffer.channels) { Array.new(num_frames, 0.0) }
|
|
165
|
+
sample_positions = sample_positions_for(num_frames, start_frame, rates)
|
|
164
166
|
|
|
165
167
|
num_frames.times do |index|
|
|
166
168
|
current_time = (start_frame + index).to_f / context.sample_rate
|
|
167
169
|
notify_stop(current_time) if @stop_time && current_time >= @stop_time
|
|
168
170
|
next unless active_at?(current_time)
|
|
169
171
|
|
|
170
|
-
sample_position =
|
|
172
|
+
sample_position = sample_positions[index]
|
|
171
173
|
if sample_position.negative?
|
|
172
174
|
@stop_time ||= current_time
|
|
173
175
|
notify_stop(current_time)
|
|
@@ -183,10 +185,26 @@ module Deftones
|
|
|
183
185
|
Core::AudioBlock.from_channel_data(output)
|
|
184
186
|
end
|
|
185
187
|
|
|
186
|
-
def
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
188
|
+
def sample_positions_for(num_frames, start_frame, rates)
|
|
189
|
+
return Array.new(num_frames, -1.0) if @start_time.infinite?
|
|
190
|
+
|
|
191
|
+
ratio = @buffer.sample_rate.to_f / context.sample_rate
|
|
192
|
+
base_position = integrated_position_at_frame(start_frame)
|
|
193
|
+
|
|
194
|
+
Array.new(num_frames) do |index|
|
|
195
|
+
position = resolve_buffer_position(base_position)
|
|
196
|
+
base_position += rates[index].to_f * ratio
|
|
197
|
+
position
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def integrated_position_at_frame(frame_index)
|
|
202
|
+
start_frame = (@start_time * context.sample_rate).floor
|
|
203
|
+
frames_to_integrate = [frame_index - start_frame, 0].max
|
|
204
|
+
return @seek_position if frames_to_integrate.zero?
|
|
205
|
+
|
|
206
|
+
rates = @playback_rate.process(frames_to_integrate, start_frame)
|
|
207
|
+
@seek_position + (rates.sum * (@buffer.sample_rate.to_f / context.sample_rate))
|
|
190
208
|
end
|
|
191
209
|
|
|
192
210
|
def resolve_buffer_position(base_position)
|
|
@@ -7,7 +7,7 @@ module Deftones
|
|
|
7
7
|
|
|
8
8
|
class VolumeProxy
|
|
9
9
|
attr_reader :players
|
|
10
|
-
|
|
10
|
+
attr_reader :value
|
|
11
11
|
|
|
12
12
|
def initialize(players, value: 0.0)
|
|
13
13
|
@players = players
|
|
@@ -19,13 +19,29 @@ module Deftones
|
|
|
19
19
|
players.send(:apply_controls!)
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
-
def ramp_to(target_value,
|
|
23
|
-
|
|
22
|
+
def ramp_to(target_value, duration = nil)
|
|
23
|
+
@value = target_value.to_f
|
|
24
|
+
players.each { |player| player.volume.ramp_to(@value, duration) }
|
|
24
25
|
self
|
|
25
26
|
end
|
|
26
27
|
|
|
27
|
-
|
|
28
|
-
|
|
28
|
+
def set_value_at_time(target_value, time)
|
|
29
|
+
@value = target_value.to_f
|
|
30
|
+
players.each { |player| player.volume.set_value_at_time(@value, time) }
|
|
31
|
+
self
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def linear_ramp_to(target_value, duration = nil)
|
|
35
|
+
ramp_to(target_value, duration)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def exponential_ramp_to(target_value, duration = nil)
|
|
39
|
+
ramp_to(target_value, duration)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
alias setValueAtTime set_value_at_time
|
|
43
|
+
alias linearRampTo linear_ramp_to
|
|
44
|
+
alias exponentialRampTo exponential_ramp_to
|
|
29
45
|
end
|
|
30
46
|
|
|
31
47
|
attr_reader :volume
|
|
@@ -41,6 +57,8 @@ module Deftones
|
|
|
41
57
|
end
|
|
42
58
|
|
|
43
59
|
def add(name, buffer)
|
|
60
|
+
raise Deftones::Error, "cannot add player to disposed Players" if @disposed
|
|
61
|
+
|
|
44
62
|
player = Player.new(buffer: buffer, context: @context)
|
|
45
63
|
player.volume.value = @volume.value
|
|
46
64
|
player.mute = @mute
|
|
@@ -93,11 +111,25 @@ module Deftones
|
|
|
93
111
|
@volume.value = value
|
|
94
112
|
end
|
|
95
113
|
|
|
96
|
-
def stop_all(time = nil)
|
|
114
|
+
def stop_all(time = nil, dispose: false)
|
|
97
115
|
@players.each_value { |player| player.stop(time) }
|
|
116
|
+
self.dispose if dispose
|
|
98
117
|
self
|
|
99
118
|
end
|
|
100
119
|
|
|
120
|
+
def stop_all_and_dispose(time = nil)
|
|
121
|
+
stop_all(time)
|
|
122
|
+
dispose
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def stopped?(time = @context.current_time)
|
|
126
|
+
state(time: time).values.all? { |entry| entry == :stopped }
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def disposed?
|
|
130
|
+
@disposed
|
|
131
|
+
end
|
|
132
|
+
|
|
101
133
|
def state(name = nil, time: @context.current_time)
|
|
102
134
|
return get(name)&.state(time) if name
|
|
103
135
|
|
|
@@ -118,6 +150,7 @@ module Deftones
|
|
|
118
150
|
end
|
|
119
151
|
|
|
120
152
|
alias stopAll stop_all
|
|
153
|
+
alias stopAllAndDispose stop_all_and_dispose
|
|
121
154
|
|
|
122
155
|
private
|
|
123
156
|
|