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,106 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deftones
|
|
4
|
+
module Music
|
|
5
|
+
class Ticks
|
|
6
|
+
attr_reader :value, :transport
|
|
7
|
+
|
|
8
|
+
def initialize(value, transport: Deftones.transport)
|
|
9
|
+
@value = value
|
|
10
|
+
@transport = transport
|
|
11
|
+
@disposed = false
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def to_i
|
|
15
|
+
self.class.parse(
|
|
16
|
+
value,
|
|
17
|
+
bpm: transport.bpm,
|
|
18
|
+
time_signature: transport.time_signature,
|
|
19
|
+
ppq: transport.ppq
|
|
20
|
+
).round
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def to_seconds
|
|
24
|
+
transport.ticks_to_seconds(to_i)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def to_ticks
|
|
28
|
+
to_i
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def to_bars_beats_sixteenths
|
|
32
|
+
transport.seconds_to_position(to_seconds)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def to_frequency
|
|
36
|
+
1.0 / [to_seconds, 1.0e-6].max
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def to_midi
|
|
40
|
+
Note.to_midi(Note.from_frequency(to_frequency))
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def to_milliseconds
|
|
44
|
+
to_seconds * 1000.0
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def to_samples(sample_rate = Deftones.context.sample_rate)
|
|
48
|
+
UnitHelpers.samples_for_seconds(to_seconds, sample_rate)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def to_notation
|
|
52
|
+
UnitHelpers.closest_notation(to_seconds, transport: transport)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def quantize(subdiv, percent = 1.0)
|
|
56
|
+
quantized_seconds = UnitHelpers.quantize_seconds(to_seconds, subdiv, transport: transport, percent: percent)
|
|
57
|
+
transport.seconds_to_ticks(quantized_seconds).round
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def from_type(type)
|
|
61
|
+
@value =
|
|
62
|
+
if type.respond_to?(:to_ticks)
|
|
63
|
+
type.to_ticks
|
|
64
|
+
elsif type.respond_to?(:value_of)
|
|
65
|
+
type.value_of
|
|
66
|
+
else
|
|
67
|
+
type
|
|
68
|
+
end
|
|
69
|
+
self
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def dispose
|
|
73
|
+
@disposed = true
|
|
74
|
+
self
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def disposed?
|
|
78
|
+
@disposed
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def to_s
|
|
82
|
+
value.to_s
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
alias toString to_s
|
|
86
|
+
|
|
87
|
+
def value_of
|
|
88
|
+
to_i
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
class << self
|
|
92
|
+
def parse(value, bpm: Deftones.transport.bpm, time_signature: Deftones.transport.time_signature,
|
|
93
|
+
ppq: Deftones.transport.ppq)
|
|
94
|
+
return value.to_f if value.is_a?(Numeric)
|
|
95
|
+
|
|
96
|
+
string_value = value.to_s
|
|
97
|
+
direct_ticks = string_value.match(/\A(-?\d+(?:\.\d+)?)i\z/i)
|
|
98
|
+
return direct_ticks[1].to_f if direct_ticks
|
|
99
|
+
|
|
100
|
+
seconds = Time.parse(value, bpm: bpm, time_signature: time_signature, ppq: ppq)
|
|
101
|
+
(seconds / (60.0 / bpm.to_f)) * ppq.to_f
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deftones
|
|
4
|
+
module Music
|
|
5
|
+
class Time
|
|
6
|
+
attr_reader :value, :transport
|
|
7
|
+
|
|
8
|
+
def initialize(value, transport: Deftones.transport)
|
|
9
|
+
@value = value
|
|
10
|
+
@transport = transport
|
|
11
|
+
@disposed = false
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def to_seconds
|
|
15
|
+
self.class.parse(
|
|
16
|
+
value,
|
|
17
|
+
bpm: transport.bpm,
|
|
18
|
+
time_signature: transport.time_signature,
|
|
19
|
+
ppq: transport.ppq
|
|
20
|
+
)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def to_ticks
|
|
24
|
+
transport.seconds_to_ticks(to_seconds)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def to_bars_beats_sixteenths
|
|
28
|
+
transport.seconds_to_position(to_seconds)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def to_frequency
|
|
32
|
+
1.0 / [to_seconds, 1.0e-6].max
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def to_midi
|
|
36
|
+
Note.to_midi(Note.from_frequency(to_frequency))
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def to_milliseconds
|
|
40
|
+
to_seconds * 1000.0
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def to_samples(sample_rate = Deftones.context.sample_rate)
|
|
44
|
+
UnitHelpers.samples_for_seconds(to_seconds, sample_rate)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def to_notation
|
|
48
|
+
UnitHelpers.closest_notation(to_seconds, transport: transport)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def quantize(subdiv, percent = 1.0)
|
|
52
|
+
UnitHelpers.quantize_seconds(to_seconds, subdiv, transport: transport, percent: percent)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def from_type(type)
|
|
56
|
+
@value = type.respond_to?(:value_of) ? type.value_of : type
|
|
57
|
+
self
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def dispose
|
|
61
|
+
@disposed = true
|
|
62
|
+
self
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def disposed?
|
|
66
|
+
@disposed
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def to_s
|
|
70
|
+
value.to_s
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
alias toString to_s
|
|
74
|
+
|
|
75
|
+
def value_of
|
|
76
|
+
to_seconds
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
SYMBOL_MAP = {
|
|
80
|
+
whole: "1n",
|
|
81
|
+
half: "2n",
|
|
82
|
+
quarter: "4n",
|
|
83
|
+
eighth: "8n",
|
|
84
|
+
sixteenth: "16n",
|
|
85
|
+
quarter_triplet: "4t",
|
|
86
|
+
eighth_triplet: "8t",
|
|
87
|
+
dotted_quarter: "4n.",
|
|
88
|
+
dotted_eighth: "8n.",
|
|
89
|
+
measure: "1m"
|
|
90
|
+
}.freeze
|
|
91
|
+
|
|
92
|
+
class << self
|
|
93
|
+
def parse(value, bpm: Deftones.transport.bpm, time_signature: Deftones.transport.time_signature,
|
|
94
|
+
ppq: Deftones.transport.ppq)
|
|
95
|
+
case value
|
|
96
|
+
when Numeric
|
|
97
|
+
value.to_f
|
|
98
|
+
when Symbol
|
|
99
|
+
parse(SYMBOL_MAP.fetch(value), bpm: bpm, time_signature: time_signature, ppq: ppq)
|
|
100
|
+
when String
|
|
101
|
+
return evaluate_expression(value, bpm: bpm, time_signature: time_signature, ppq: ppq) if arithmetic_expression?(value)
|
|
102
|
+
|
|
103
|
+
parse_literal(value, bpm: bpm, time_signature: time_signature, ppq: ppq)
|
|
104
|
+
when /\A(\d+)n\z/
|
|
105
|
+
parse_literal(value, bpm: bpm, time_signature: time_signature, ppq: ppq)
|
|
106
|
+
else
|
|
107
|
+
return value.to_seconds if value.respond_to?(:to_seconds)
|
|
108
|
+
end
|
|
109
|
+
rescue KeyError, ArgumentError
|
|
110
|
+
raise ArgumentError, "Unknown time format: #{value}"
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
private
|
|
114
|
+
|
|
115
|
+
def parse_literal(value, bpm:, time_signature:, ppq:)
|
|
116
|
+
case value
|
|
117
|
+
when /\A(\d+)n\z/
|
|
118
|
+
beats = 4.0 / Regexp.last_match(1).to_f
|
|
119
|
+
beats * beat_duration(bpm)
|
|
120
|
+
when /\A(\d+)t\z/
|
|
121
|
+
beats = (4.0 / Regexp.last_match(1).to_f) * (2.0 / 3.0)
|
|
122
|
+
beats * beat_duration(bpm)
|
|
123
|
+
when /\A(\d+)n\.\z/
|
|
124
|
+
parse("#{Regexp.last_match(1)}n", bpm: bpm, time_signature: time_signature, ppq: ppq) * 1.5
|
|
125
|
+
when /\A(\d+)m\z/i
|
|
126
|
+
measures = Regexp.last_match(1).to_f
|
|
127
|
+
beats_per_measure = Array(time_signature).first || 4
|
|
128
|
+
measures * beats_per_measure * beat_duration(bpm)
|
|
129
|
+
when /\A(\d+):(\d+):(\d+)\z/
|
|
130
|
+
bars = Regexp.last_match(1).to_f
|
|
131
|
+
beats = Regexp.last_match(2).to_f
|
|
132
|
+
sixteenths = Regexp.last_match(3).to_f
|
|
133
|
+
beats_per_measure = Array(time_signature).first || 4
|
|
134
|
+
((bars * beats_per_measure) + beats + (sixteenths * 0.25)) * beat_duration(bpm)
|
|
135
|
+
when /\A(\d+(?:\.\d+)?)hz\z/i
|
|
136
|
+
1.0 / Regexp.last_match(1).to_f
|
|
137
|
+
when /\A(-?\d+(?:\.\d+)?)i\z/i
|
|
138
|
+
(Regexp.last_match(1).to_f / ppq.to_f) * beat_duration(bpm)
|
|
139
|
+
else
|
|
140
|
+
Float(value)
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def arithmetic_expression?(value)
|
|
145
|
+
value.match?(/[+\-*\/()]/) && !value.match?(/\A-?\d+(?:\.\d+)?\z/)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def beat_duration(bpm)
|
|
149
|
+
60.0 / bpm.to_f
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def evaluate_expression(value, bpm:, time_signature:, ppq:)
|
|
153
|
+
compute_rpn(to_rpn(tokenize(value)), bpm: bpm, time_signature: time_signature, ppq: ppq)
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def tokenize(expression)
|
|
157
|
+
expression.scan(/\d+:\d+:\d+|\d+(?:\.\d+)?hz|-?\d+(?:\.\d+)?i|\d+n\.?|\d+t|\d+m|[()+\-*\/]|\d+(?:\.\d+)?/)
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def to_rpn(tokens)
|
|
161
|
+
output = []
|
|
162
|
+
operators = []
|
|
163
|
+
|
|
164
|
+
tokens.each do |token|
|
|
165
|
+
if operator?(token)
|
|
166
|
+
while operators.any? && operator?(operators.last) && precedence(operators.last) >= precedence(token)
|
|
167
|
+
output << operators.pop
|
|
168
|
+
end
|
|
169
|
+
operators << token
|
|
170
|
+
elsif token == "("
|
|
171
|
+
operators << token
|
|
172
|
+
elsif token == ")"
|
|
173
|
+
output << operators.pop until operators.last == "("
|
|
174
|
+
operators.pop
|
|
175
|
+
else
|
|
176
|
+
output << token
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
output.concat(operators.reverse)
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def compute_rpn(tokens, bpm:, time_signature:, ppq:)
|
|
184
|
+
stack = []
|
|
185
|
+
|
|
186
|
+
tokens.each do |token|
|
|
187
|
+
if operator?(token)
|
|
188
|
+
right = stack.pop
|
|
189
|
+
left = stack.pop
|
|
190
|
+
stack << left.public_send(token, right)
|
|
191
|
+
else
|
|
192
|
+
stack << parse_literal(token, bpm: bpm, time_signature: time_signature, ppq: ppq)
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
stack.first
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def operator?(token)
|
|
200
|
+
%w[+ - * /].include?(token)
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def precedence(token)
|
|
204
|
+
%w[* /].include?(token) ? 2 : 1
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
end
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deftones
|
|
4
|
+
module Music
|
|
5
|
+
class TransportTime
|
|
6
|
+
attr_reader :value, :transport
|
|
7
|
+
|
|
8
|
+
def initialize(value, transport: Deftones.transport)
|
|
9
|
+
@value = value
|
|
10
|
+
@transport = transport
|
|
11
|
+
@disposed = false
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def to_seconds
|
|
15
|
+
self.class.parse(
|
|
16
|
+
value,
|
|
17
|
+
bpm: transport.bpm,
|
|
18
|
+
time_signature: transport.time_signature,
|
|
19
|
+
ppq: transport.ppq
|
|
20
|
+
)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def to_ticks
|
|
24
|
+
transport.seconds_to_ticks(to_seconds)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def to_bars_beats_sixteenths
|
|
28
|
+
transport.seconds_to_position(to_seconds)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def to_frequency
|
|
32
|
+
1.0 / [to_seconds, 1.0e-6].max
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def to_midi
|
|
36
|
+
Note.to_midi(Note.from_frequency(to_frequency))
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def to_milliseconds
|
|
40
|
+
to_seconds * 1000.0
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def to_samples(sample_rate = Deftones.context.sample_rate)
|
|
44
|
+
UnitHelpers.samples_for_seconds(to_seconds, sample_rate)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def to_notation
|
|
48
|
+
UnitHelpers.closest_notation(to_seconds, transport: transport)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def quantize(subdiv, percent = 1.0)
|
|
52
|
+
UnitHelpers.quantize_seconds(to_seconds, subdiv, transport: transport, percent: percent)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def from_type(type)
|
|
56
|
+
@value =
|
|
57
|
+
if type.respond_to?(:to_bars_beats_sixteenths)
|
|
58
|
+
type.to_bars_beats_sixteenths
|
|
59
|
+
elsif type.respond_to?(:value_of)
|
|
60
|
+
type.value_of
|
|
61
|
+
else
|
|
62
|
+
type
|
|
63
|
+
end
|
|
64
|
+
self
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def dispose
|
|
68
|
+
@disposed = true
|
|
69
|
+
self
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def disposed?
|
|
73
|
+
@disposed
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def to_s
|
|
77
|
+
value.to_s
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
alias toString to_s
|
|
81
|
+
|
|
82
|
+
def value_of
|
|
83
|
+
to_seconds
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
class << self
|
|
87
|
+
def parse(value, bpm: Deftones.transport.bpm, time_signature: Deftones.transport.time_signature,
|
|
88
|
+
ppq: Deftones.transport.ppq)
|
|
89
|
+
Time.parse(value, bpm: bpm, time_signature: time_signature, ppq: ppq)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deftones
|
|
4
|
+
module Music
|
|
5
|
+
module UnitHelpers
|
|
6
|
+
NOTATION_CANDIDATES = %w[1m 1n 2n. 2n 4n. 4n 8n. 8n 8t 16n. 16n 16t 32n 64n 128n].freeze
|
|
7
|
+
|
|
8
|
+
class << self
|
|
9
|
+
def closest_notation(seconds, transport:)
|
|
10
|
+
NOTATION_CANDIDATES.min_by do |notation|
|
|
11
|
+
duration = Time.parse(notation, bpm: transport.bpm, time_signature: transport.time_signature, ppq: transport.ppq)
|
|
12
|
+
(duration - seconds).abs
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def quantize_seconds(seconds, subdivision, transport:, percent: 1.0)
|
|
17
|
+
quantum = Time.parse(subdivision, bpm: transport.bpm, time_signature: transport.time_signature, ppq: transport.ppq)
|
|
18
|
+
return seconds unless quantum.positive?
|
|
19
|
+
|
|
20
|
+
target = (seconds / quantum).round * quantum
|
|
21
|
+
seconds + ((target - seconds) * percent.to_f.clamp(0.0, 1.0))
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def samples_for_seconds(seconds, sample_rate)
|
|
25
|
+
(seconds * sample_rate.to_f).round
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deftones
|
|
4
|
+
class OfflineContext < Context
|
|
5
|
+
attr_reader :channels, :duration, :total_frames
|
|
6
|
+
|
|
7
|
+
def initialize(duration:, channels: 2, sample_rate: DEFAULT_SAMPLE_RATE,
|
|
8
|
+
buffer_size: DEFAULT_BUFFER_SIZE)
|
|
9
|
+
super(sample_rate: sample_rate, buffer_size: buffer_size, channels: channels, autostart: false)
|
|
10
|
+
@duration = duration.to_f
|
|
11
|
+
@total_frames = (@duration * sample_rate).ceil
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def current_time
|
|
15
|
+
0.0
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def state
|
|
19
|
+
"suspended"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def render
|
|
23
|
+
Deftones.transport.prepare_render(@duration)
|
|
24
|
+
Deftones.draw.prepare_render(@duration)
|
|
25
|
+
samples = Array.new(@total_frames * @channels, 0.0)
|
|
26
|
+
frames_processed = 0
|
|
27
|
+
|
|
28
|
+
while frames_processed < @total_frames
|
|
29
|
+
chunk_frames = [buffer_size, @total_frames - frames_processed].min
|
|
30
|
+
interleaved = render_block_frames(chunk_frames, frames_processed).fit_channels(@channels).interleaved
|
|
31
|
+
start_index = frames_processed * @channels
|
|
32
|
+
|
|
33
|
+
samples[start_index, interleaved.length] = interleaved
|
|
34
|
+
frames_processed += chunk_frames
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
IO::Buffer.new(samples, channels: @channels, sample_rate: sample_rate)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def render_to_file(path, format: nil)
|
|
41
|
+
rendered_buffer = render
|
|
42
|
+
rendered_buffer.save(path, format: format)
|
|
43
|
+
rendered_buffer
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "thread"
|
|
4
|
+
|
|
5
|
+
module Deftones
|
|
6
|
+
module PortAudioSupport
|
|
7
|
+
class << self
|
|
8
|
+
def available?
|
|
9
|
+
!!defined?(PortAudio)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def acquire!
|
|
13
|
+
raise Deftones::MissingRealtimeBackendError, "PortAudio backend is unavailable" unless available?
|
|
14
|
+
|
|
15
|
+
PortAudio.init
|
|
16
|
+
rescue StandardError => error
|
|
17
|
+
raise Deftones::MissingRealtimeBackendError, error.message
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def release
|
|
21
|
+
return unless available?
|
|
22
|
+
|
|
23
|
+
PortAudio.terminate
|
|
24
|
+
rescue StandardError
|
|
25
|
+
nil
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def output_parameters(channels)
|
|
29
|
+
build_stream_parameters(direction: :output, channels: channels)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def input_parameters(channels, device_id: nil, label: nil)
|
|
33
|
+
build_stream_parameters(direction: :input, channels: channels, device_id: device_id, label: label)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def check_error!(result, fallback: nil)
|
|
37
|
+
PortAudio.check_error!(result)
|
|
38
|
+
rescue StandardError => error
|
|
39
|
+
raise Deftones::MissingRealtimeBackendError, fallback || error.message
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def build_stream_parameters(direction:, channels:, device_id: nil, label: nil)
|
|
45
|
+
device =
|
|
46
|
+
resolve_device(direction: direction, device_id: device_id, label: label)
|
|
47
|
+
|
|
48
|
+
raise Deftones::MissingRealtimeBackendError, "No default #{direction} device available" unless device
|
|
49
|
+
|
|
50
|
+
{
|
|
51
|
+
device: device,
|
|
52
|
+
channels: channels,
|
|
53
|
+
format: :float32,
|
|
54
|
+
latency: suggested_latency(device, direction)
|
|
55
|
+
}
|
|
56
|
+
rescue StandardError => error
|
|
57
|
+
raise Deftones::MissingRealtimeBackendError, error.message
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def resolve_device(direction:, device_id:, label:)
|
|
61
|
+
return default_device(direction) if device_id.nil? && label.nil?
|
|
62
|
+
|
|
63
|
+
devices =
|
|
64
|
+
if PortAudio::Device.respond_to?(:all)
|
|
65
|
+
Array(PortAudio::Device.all)
|
|
66
|
+
elsif PortAudio::Device.respond_to?(:devices)
|
|
67
|
+
Array(PortAudio::Device.devices)
|
|
68
|
+
else
|
|
69
|
+
[]
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
matched = devices.find do |device|
|
|
73
|
+
matches_device_id?(device, device_id) || matches_device_label?(device, label)
|
|
74
|
+
end
|
|
75
|
+
return matched if matched
|
|
76
|
+
|
|
77
|
+
raise Deftones::MissingRealtimeBackendError, "No matching #{direction} device available"
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def default_device(direction)
|
|
81
|
+
case direction
|
|
82
|
+
when :input then PortAudio::Device.default_input
|
|
83
|
+
when :output then PortAudio::Device.default_output
|
|
84
|
+
else raise ArgumentError, "Unsupported PortAudio direction: #{direction}"
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def matches_device_id?(device, device_id)
|
|
89
|
+
return false if device_id.nil?
|
|
90
|
+
|
|
91
|
+
candidates = []
|
|
92
|
+
candidates << device.device_id if device.respond_to?(:device_id)
|
|
93
|
+
candidates << device.index if device.respond_to?(:index)
|
|
94
|
+
candidates << device.device_index if device.respond_to?(:device_index)
|
|
95
|
+
candidates.compact.any? { |candidate| candidate.to_s == device_id.to_s }
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def matches_device_label?(device, label)
|
|
99
|
+
return false if label.nil?
|
|
100
|
+
|
|
101
|
+
candidates = []
|
|
102
|
+
candidates << device.label if device.respond_to?(:label)
|
|
103
|
+
candidates << device.name if device.respond_to?(:name)
|
|
104
|
+
candidates.compact.any? { |candidate| candidate.to_s == label.to_s }
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def suggested_latency(device, direction)
|
|
108
|
+
direction == :input ? device.default_low_input_latency : device.default_low_output_latency
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deftones
|
|
4
|
+
module Source
|
|
5
|
+
class AMOscillator < Core::Source
|
|
6
|
+
attr_reader :frequency, :harmonicity, :detune
|
|
7
|
+
|
|
8
|
+
def initialize(frequency: 440.0, harmonicity: 2.0, detune: 0.0, phase: 0.0, context: Deftones.context)
|
|
9
|
+
super(context: context)
|
|
10
|
+
@frequency = Core::Signal.new(value: frequency, units: :frequency, context: context)
|
|
11
|
+
@harmonicity = Core::Signal.new(value: harmonicity, units: :number, context: context)
|
|
12
|
+
@detune = Core::Signal.new(value: detune, units: :number, context: context)
|
|
13
|
+
@carrier_phase = phase.to_f % 1.0
|
|
14
|
+
@modulator_phase = phase.to_f % 1.0
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def detune=(value)
|
|
18
|
+
@detune.value = value
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def process(_input_buffer, num_frames, start_frame, _cache)
|
|
22
|
+
frequencies = @frequency.process(num_frames, start_frame)
|
|
23
|
+
harmonicities = @harmonicity.process(num_frames, start_frame)
|
|
24
|
+
detunes = @detune.process(num_frames, start_frame)
|
|
25
|
+
|
|
26
|
+
Array.new(num_frames) do |index|
|
|
27
|
+
current_time = (start_frame + index).to_f / context.sample_rate
|
|
28
|
+
next 0.0 unless active_at?(current_time)
|
|
29
|
+
|
|
30
|
+
carrier_frequency = frequencies[index] * (2.0**(detunes[index].to_f / 1200.0))
|
|
31
|
+
modulator_frequency = carrier_frequency * harmonicities[index]
|
|
32
|
+
carrier = Math.sin(2.0 * Math::PI * @carrier_phase)
|
|
33
|
+
modulator = (Math.sin(2.0 * Math::PI * @modulator_phase) + 1.0) * 0.5
|
|
34
|
+
|
|
35
|
+
@carrier_phase = (@carrier_phase + (carrier_frequency / context.sample_rate)) % 1.0
|
|
36
|
+
@modulator_phase = (@modulator_phase + (modulator_frequency / context.sample_rate)) % 1.0
|
|
37
|
+
carrier * modulator
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deftones
|
|
4
|
+
module Source
|
|
5
|
+
class FatOscillator < Core::Source
|
|
6
|
+
attr_reader :frequency
|
|
7
|
+
attr_accessor :count, :spread, :type
|
|
8
|
+
|
|
9
|
+
def initialize(type: :sawtooth, frequency: 440.0, count: 3, spread: 20.0, context: Deftones.context)
|
|
10
|
+
super(context: context)
|
|
11
|
+
@type = type.to_sym
|
|
12
|
+
@frequency = Core::Signal.new(value: frequency, units: :frequency, context: context)
|
|
13
|
+
@count = count.to_i
|
|
14
|
+
@spread = spread.to_f
|
|
15
|
+
@phases = Array.new(@count, 0.0)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def process(_input_buffer, num_frames, start_frame, _cache)
|
|
19
|
+
frequencies = @frequency.process(num_frames, start_frame)
|
|
20
|
+
generator = Oscillator::GENERATORS.fetch(@type) { Oscillator::GENERATORS[:sawtooth] }
|
|
21
|
+
|
|
22
|
+
Array.new(num_frames) do |index|
|
|
23
|
+
current_time = (start_frame + index).to_f / context.sample_rate
|
|
24
|
+
next 0.0 unless active_at?(current_time)
|
|
25
|
+
|
|
26
|
+
detuned = detune_frequencies(frequencies[index])
|
|
27
|
+
samples = @phases.each_with_index.map do |phase, voice_index|
|
|
28
|
+
sample = generator.call(phase)
|
|
29
|
+
@phases[voice_index] = (phase + (detuned[voice_index] / context.sample_rate)) % 1.0
|
|
30
|
+
sample
|
|
31
|
+
end
|
|
32
|
+
samples.sum / samples.length.to_f
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def detune_frequencies(base_frequency)
|
|
39
|
+
offsets = Array.new(@count) do |index|
|
|
40
|
+
position = @count == 1 ? 0.0 : (index.to_f / (@count - 1)) - 0.5
|
|
41
|
+
cents = position * @spread
|
|
42
|
+
base_frequency * (2.0**(cents / 1200.0))
|
|
43
|
+
end
|
|
44
|
+
@phases = Array.new(@count, 0.0) if @phases.length != @count
|
|
45
|
+
offsets
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|