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,77 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deftones
|
|
4
|
+
module IO
|
|
5
|
+
class Buffers
|
|
6
|
+
include Enumerable
|
|
7
|
+
|
|
8
|
+
def initialize(buffers = {})
|
|
9
|
+
@buffers = {}
|
|
10
|
+
@disposed = false
|
|
11
|
+
merge(buffers)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def add(name, buffer)
|
|
15
|
+
@buffers[key_for(name)] = normalize_buffer(buffer)
|
|
16
|
+
self
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def get(name)
|
|
20
|
+
self[name]
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def [](name)
|
|
24
|
+
@buffers[key_for(name)]
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def fetch(name)
|
|
28
|
+
@buffers.fetch(key_for(name))
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def has?(name)
|
|
32
|
+
@buffers.key?(key_for(name))
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def loaded?
|
|
36
|
+
!@disposed
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def each(&block)
|
|
40
|
+
return enum_for(:each) unless block
|
|
41
|
+
|
|
42
|
+
@buffers.each(&block)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def keys
|
|
46
|
+
@buffers.keys
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def to_h
|
|
50
|
+
@buffers.dup
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def merge(buffers)
|
|
54
|
+
buffers.each { |name, buffer| add(name, buffer) }
|
|
55
|
+
self
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def dispose
|
|
59
|
+
@buffers.clear
|
|
60
|
+
@disposed = true
|
|
61
|
+
self
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
alias loaded loaded?
|
|
65
|
+
|
|
66
|
+
private
|
|
67
|
+
|
|
68
|
+
def key_for(name)
|
|
69
|
+
name.to_sym
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def normalize_buffer(buffer)
|
|
73
|
+
buffer.is_a?(Buffer) ? buffer : Buffer.load(buffer)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deftones
|
|
4
|
+
module IO
|
|
5
|
+
class Recorder
|
|
6
|
+
attr_reader :state, :mime_type, :captured_buffer
|
|
7
|
+
|
|
8
|
+
MIME_TYPES = {
|
|
9
|
+
wav: "audio/wav",
|
|
10
|
+
mp3: "audio/mpeg",
|
|
11
|
+
ogg: "audio/ogg"
|
|
12
|
+
}.freeze
|
|
13
|
+
|
|
14
|
+
def initialize(context:, node: nil, mime_type: "audio/wav")
|
|
15
|
+
@context = context
|
|
16
|
+
@node = node || context.output
|
|
17
|
+
@captured_buffer = nil
|
|
18
|
+
@mime_type = mime_type
|
|
19
|
+
@state = :stopped
|
|
20
|
+
@started_at = nil
|
|
21
|
+
@recorded_duration = 0.0
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def start
|
|
25
|
+
@captured_buffer = nil
|
|
26
|
+
@started_at = @context.current_time
|
|
27
|
+
@state = :started
|
|
28
|
+
self
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def stop(path: nil, format: nil, duration: nil)
|
|
32
|
+
return @captured_buffer if @state == :stopped && @captured_buffer
|
|
33
|
+
|
|
34
|
+
@recorded_duration = duration || elapsed_duration
|
|
35
|
+
@captured_buffer = capture_buffer(duration: @recorded_duration)
|
|
36
|
+
@state = :stopped
|
|
37
|
+
save(path, format: resolve_format(format, path)) if path
|
|
38
|
+
@captured_buffer
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def record(duration: nil, format: :wav, path: nil)
|
|
42
|
+
start
|
|
43
|
+
stop(path: path, format: format, duration: duration)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def save(path, format: :wav)
|
|
47
|
+
raise ArgumentError, "Nothing recorded yet" unless @captured_buffer
|
|
48
|
+
|
|
49
|
+
@captured_buffer.save(path, format: format)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def dispose
|
|
53
|
+
@captured_buffer = nil
|
|
54
|
+
@state = :stopped
|
|
55
|
+
@started_at = nil
|
|
56
|
+
@recorded_duration = 0.0
|
|
57
|
+
self
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
alias mimeType mime_type
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
|
|
64
|
+
def capture_buffer(duration:)
|
|
65
|
+
if @context.is_a?(Deftones::OfflineContext)
|
|
66
|
+
@context.render
|
|
67
|
+
else
|
|
68
|
+
seconds = [duration.to_f, 1.0 / @context.sample_rate].max
|
|
69
|
+
frames = (seconds * @context.sample_rate).ceil
|
|
70
|
+
block = @node.send(:render_block, frames, 0, {}).fit_channels(@context.channels)
|
|
71
|
+
Buffer.new(block.interleaved, channels: @context.channels, sample_rate: @context.sample_rate)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def elapsed_duration
|
|
76
|
+
return 0.0 unless @started_at
|
|
77
|
+
|
|
78
|
+
[@context.current_time - @started_at, 0.0].max
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def resolve_format(format, path)
|
|
82
|
+
return format if format
|
|
83
|
+
return File.extname(path).delete_prefix(".").downcase.to_sym if path
|
|
84
|
+
|
|
85
|
+
MIME_TYPES.key(@mime_type) || :wav
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deftones
|
|
4
|
+
class Listener
|
|
5
|
+
attr_reader :forward_x, :forward_y, :forward_z, :position_x, :position_y, :position_z, :up_x, :up_y, :up_z
|
|
6
|
+
|
|
7
|
+
def initialize(context: Deftones.context)
|
|
8
|
+
@position_x = Core::Signal.new(value: 0.0, units: :number, context: context)
|
|
9
|
+
@position_y = Core::Signal.new(value: 0.0, units: :number, context: context)
|
|
10
|
+
@position_z = Core::Signal.new(value: 0.0, units: :number, context: context)
|
|
11
|
+
@forward_x = Core::Signal.new(value: 0.0, units: :number, context: context)
|
|
12
|
+
@forward_y = Core::Signal.new(value: 0.0, units: :number, context: context)
|
|
13
|
+
@forward_z = Core::Signal.new(value: -1.0, units: :number, context: context)
|
|
14
|
+
@up_x = Core::Signal.new(value: 0.0, units: :number, context: context)
|
|
15
|
+
@up_y = Core::Signal.new(value: 1.0, units: :number, context: context)
|
|
16
|
+
@up_z = Core::Signal.new(value: 0.0, units: :number, context: context)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def position_x=(value)
|
|
20
|
+
@position_x.value = value
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def position_y=(value)
|
|
24
|
+
@position_y.value = value
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def position_z=(value)
|
|
28
|
+
@position_z.value = value
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def forward_x=(value)
|
|
32
|
+
@forward_x.value = value
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def forward_y=(value)
|
|
36
|
+
@forward_y.value = value
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def forward_z=(value)
|
|
40
|
+
@forward_z.value = value
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def up_x=(value)
|
|
44
|
+
@up_x.value = value
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def up_y=(value)
|
|
48
|
+
@up_y.value = value
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def up_z=(value)
|
|
52
|
+
@up_z.value = value
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def set_position(x, y, z)
|
|
56
|
+
self.position_x = x
|
|
57
|
+
self.position_y = y
|
|
58
|
+
self.position_z = z
|
|
59
|
+
self
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def set_orientation(forward_x, forward_y, forward_z, up_x = 0.0, up_y = 1.0, up_z = 0.0)
|
|
63
|
+
self.forward_x = forward_x
|
|
64
|
+
self.forward_y = forward_y
|
|
65
|
+
self.forward_z = forward_z
|
|
66
|
+
self.up_x = up_x
|
|
67
|
+
self.up_y = up_y
|
|
68
|
+
self.up_z = up_z
|
|
69
|
+
self
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
alias positionX position_x
|
|
73
|
+
alias positionY position_y
|
|
74
|
+
alias positionZ position_z
|
|
75
|
+
alias forwardX forward_x
|
|
76
|
+
alias forwardY forward_y
|
|
77
|
+
alias forwardZ forward_z
|
|
78
|
+
alias upX up_x
|
|
79
|
+
alias upY up_y
|
|
80
|
+
alias upZ up_z
|
|
81
|
+
alias setPosition set_position
|
|
82
|
+
alias setOrientation set_orientation
|
|
83
|
+
|
|
84
|
+
def positionX=(value)
|
|
85
|
+
self.position_x = value
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def positionY=(value)
|
|
89
|
+
self.position_y = value
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def positionZ=(value)
|
|
93
|
+
self.position_z = value
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def forwardX=(value)
|
|
97
|
+
self.forward_x = value
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def forwardY=(value)
|
|
101
|
+
self.forward_y = value
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def forwardZ=(value)
|
|
105
|
+
self.forward_z = value
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def upX=(value)
|
|
109
|
+
self.up_x = value
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def upY=(value)
|
|
113
|
+
self.up_y = value
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def upZ=(value)
|
|
117
|
+
self.up_z = value
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deftones
|
|
4
|
+
module Music
|
|
5
|
+
class Frequency
|
|
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_hz
|
|
15
|
+
self.class.parse(value)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def to_frequency
|
|
19
|
+
to_hz
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def to_seconds
|
|
23
|
+
self.class.to_period(value)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def to_midi
|
|
27
|
+
self.class.to_midi(value)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def to_ticks
|
|
31
|
+
transport.seconds_to_ticks(to_seconds)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def to_bars_beats_sixteenths
|
|
35
|
+
transport.seconds_to_position(to_seconds)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def to_milliseconds
|
|
39
|
+
to_seconds * 1000.0
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def to_samples(sample_rate = Deftones.context.sample_rate)
|
|
43
|
+
UnitHelpers.samples_for_seconds(to_seconds, sample_rate)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def to_notation
|
|
47
|
+
UnitHelpers.closest_notation(to_seconds, transport: transport)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def to_note
|
|
51
|
+
Note.from_frequency(to_hz)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def transpose(interval)
|
|
55
|
+
self.class.new(Note.from_midi(to_midi + interval.to_i), transport: transport)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def harmonize(intervals)
|
|
59
|
+
Array(intervals).map { |interval| transpose(interval) }
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def quantize(subdiv, percent = 1.0)
|
|
63
|
+
quantized_seconds = UnitHelpers.quantize_seconds(to_seconds, subdiv, transport: transport, percent: percent)
|
|
64
|
+
1.0 / [quantized_seconds, 1.0e-6].max
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def from_type(type)
|
|
68
|
+
@value =
|
|
69
|
+
if type.respond_to?(:to_frequency)
|
|
70
|
+
type.to_frequency
|
|
71
|
+
elsif type.respond_to?(:value_of)
|
|
72
|
+
type.value_of
|
|
73
|
+
else
|
|
74
|
+
type
|
|
75
|
+
end
|
|
76
|
+
self
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def dispose
|
|
80
|
+
@disposed = true
|
|
81
|
+
self
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def disposed?
|
|
85
|
+
@disposed
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def to_s
|
|
89
|
+
value.to_s
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
alias toString to_s
|
|
93
|
+
|
|
94
|
+
def value_of
|
|
95
|
+
to_hz
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
class << self
|
|
99
|
+
def mtof(value)
|
|
100
|
+
Deftones.mtof(value)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def ftom(value)
|
|
104
|
+
Deftones.ftom(value)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def parse(value)
|
|
108
|
+
case value
|
|
109
|
+
when Numeric
|
|
110
|
+
value.to_f
|
|
111
|
+
when /\A(\d+(?:\.\d+)?)hz\z/i
|
|
112
|
+
Regexp.last_match(1).to_f
|
|
113
|
+
else
|
|
114
|
+
Note.to_frequency(value)
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def to_period(value)
|
|
119
|
+
1.0 / parse(value)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def to_midi(value)
|
|
123
|
+
Note.to_midi(Note.from_frequency(parse(value)))
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
begin
|
|
4
|
+
require "unimidi"
|
|
5
|
+
rescue LoadError
|
|
6
|
+
nil
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
module Deftones
|
|
10
|
+
module Music
|
|
11
|
+
class Midi
|
|
12
|
+
attr_reader :value, :transport
|
|
13
|
+
|
|
14
|
+
def initialize(value, transport: Deftones.transport)
|
|
15
|
+
@value = value
|
|
16
|
+
@transport = transport
|
|
17
|
+
@disposed = false
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def to_i
|
|
21
|
+
self.class.parse(value)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def to_frequency
|
|
25
|
+
Note.to_frequency(to_note)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def to_seconds
|
|
29
|
+
1.0 / to_frequency
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def to_ticks
|
|
33
|
+
transport.seconds_to_ticks(to_seconds)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def to_bars_beats_sixteenths
|
|
37
|
+
transport.seconds_to_position(to_seconds)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def to_milliseconds
|
|
41
|
+
to_seconds * 1000.0
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def to_samples(sample_rate = Deftones.context.sample_rate)
|
|
45
|
+
UnitHelpers.samples_for_seconds(to_seconds, sample_rate)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def to_notation
|
|
49
|
+
UnitHelpers.closest_notation(to_seconds, transport: transport)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def transpose(interval)
|
|
53
|
+
self.class.new(to_i + interval.to_i, transport: transport)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def harmonize(intervals)
|
|
57
|
+
Array(intervals).map { |interval| transpose(interval) }
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def quantize(subdiv, percent = 1.0)
|
|
61
|
+
quantized_seconds = UnitHelpers.quantize_seconds(to_seconds, subdiv, transport: transport, percent: percent)
|
|
62
|
+
Note.to_midi(Note.from_frequency(1.0 / [quantized_seconds, 1.0e-6].max))
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def from_type(type)
|
|
66
|
+
@value =
|
|
67
|
+
if type.respond_to?(:to_midi)
|
|
68
|
+
type.to_midi
|
|
69
|
+
elsif type.respond_to?(:value_of)
|
|
70
|
+
type.value_of
|
|
71
|
+
else
|
|
72
|
+
type
|
|
73
|
+
end
|
|
74
|
+
self
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def dispose
|
|
78
|
+
@disposed = true
|
|
79
|
+
self
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def disposed?
|
|
83
|
+
@disposed
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def to_s
|
|
87
|
+
value.to_s
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
alias toString to_s
|
|
91
|
+
|
|
92
|
+
def to_note
|
|
93
|
+
Note.from_midi(to_i)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def value_of
|
|
97
|
+
to_i
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
class << self
|
|
101
|
+
def parse(value)
|
|
102
|
+
return value.to_i if value.is_a?(Numeric)
|
|
103
|
+
|
|
104
|
+
Note.to_midi(value)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def available?
|
|
108
|
+
!!defined?(UniMIDI)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def input_devices
|
|
112
|
+
return [] unless available?
|
|
113
|
+
|
|
114
|
+
UniMIDI::Input.all
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def output_devices
|
|
118
|
+
return [] unless available?
|
|
119
|
+
|
|
120
|
+
UniMIDI::Output.all
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def find_input(name = nil)
|
|
124
|
+
find_device(input_devices, name)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def find_output(name = nil)
|
|
128
|
+
find_device(output_devices, name)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def open_input(name = nil, *args, &block)
|
|
132
|
+
open_device(find_input(name), *args, &block)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def open_output(name = nil, *args, &block)
|
|
136
|
+
open_device(find_output(name), *args, &block)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def receive(name = nil, *args)
|
|
140
|
+
open_input(name) do |input|
|
|
141
|
+
input.gets(*args)
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def send_message(message, device: nil)
|
|
146
|
+
open_output(device) do |output|
|
|
147
|
+
output.puts(message)
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def note_on(note, velocity: 100, channel: 1, device: nil)
|
|
152
|
+
send_message([status_byte(0x90, channel), normalize_note(note), normalize_data_byte(velocity)], device: device)
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def note_off(note, velocity: 0, channel: 1, device: nil)
|
|
156
|
+
send_message([status_byte(0x80, channel), normalize_note(note), normalize_data_byte(velocity)], device: device)
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def control_change(controller, value, channel: 1, device: nil)
|
|
160
|
+
send_message(
|
|
161
|
+
[status_byte(0xB0, channel), normalize_data_byte(controller), normalize_data_byte(value)],
|
|
162
|
+
device: device
|
|
163
|
+
)
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
private
|
|
167
|
+
|
|
168
|
+
def find_device(devices, name)
|
|
169
|
+
return devices.first if name.nil?
|
|
170
|
+
|
|
171
|
+
devices.find { |device| device.name == name.to_s }
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def open_device(device, *args, &block)
|
|
175
|
+
raise ArgumentError, "MIDI support is unavailable" unless available?
|
|
176
|
+
raise ArgumentError, "No matching MIDI device found" unless device
|
|
177
|
+
|
|
178
|
+
return device.open(*args) unless block
|
|
179
|
+
|
|
180
|
+
device.open(*args)
|
|
181
|
+
begin
|
|
182
|
+
block.call(device)
|
|
183
|
+
ensure
|
|
184
|
+
device.close if device.respond_to?(:close)
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def normalize_note(note)
|
|
189
|
+
normalize_data_byte(parse(note))
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def normalize_data_byte(value)
|
|
193
|
+
value.to_i.clamp(0, 127)
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def status_byte(base, channel)
|
|
197
|
+
base + normalize_channel(channel)
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def normalize_channel(channel)
|
|
201
|
+
[[channel.to_i - 1, 0].max, 15].min
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deftones
|
|
4
|
+
module Music
|
|
5
|
+
class Note
|
|
6
|
+
NOTE_NAMES = %w[C C# D D# E F F# G G# A A# B].freeze
|
|
7
|
+
FLAT_MAP = {
|
|
8
|
+
"Cb" => "B",
|
|
9
|
+
"Db" => "C#",
|
|
10
|
+
"Eb" => "D#",
|
|
11
|
+
"Fb" => "E",
|
|
12
|
+
"Gb" => "F#",
|
|
13
|
+
"Ab" => "G#",
|
|
14
|
+
"Bb" => "A#"
|
|
15
|
+
}.freeze
|
|
16
|
+
|
|
17
|
+
class << self
|
|
18
|
+
def to_frequency(note_name)
|
|
19
|
+
midi_number = to_midi(note_name)
|
|
20
|
+
440.0 * (2.0**((midi_number - 69) / 12.0))
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def to_midi(note_name)
|
|
24
|
+
name, octave = parse_note_name(note_name)
|
|
25
|
+
NOTE_NAMES.index(name) + ((octave + 1) * 12)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def from_midi(midi_number)
|
|
29
|
+
integer = midi_number.to_i
|
|
30
|
+
octave = (integer / 12) - 1
|
|
31
|
+
"#{NOTE_NAMES[integer % 12]}#{octave}"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def from_frequency(frequency)
|
|
35
|
+
midi_number = (12 * Math.log2(frequency.to_f / 440.0) + 69).round
|
|
36
|
+
from_midi(midi_number)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def parse_note_name(note_name)
|
|
42
|
+
match = note_name.to_s.match(/\A([A-Ga-g][#b]?)(-?\d+)\z/)
|
|
43
|
+
raise ArgumentError, "Invalid note: #{note_name}" unless match
|
|
44
|
+
|
|
45
|
+
normalized_name = normalize_name(match[1])
|
|
46
|
+
raise ArgumentError, "Unsupported note name: #{note_name}" unless NOTE_NAMES.include?(normalized_name)
|
|
47
|
+
|
|
48
|
+
[normalized_name, match[2].to_i]
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def normalize_name(token)
|
|
52
|
+
canonical = token[0].upcase + token[1..]
|
|
53
|
+
FLAT_MAP.fetch(canonical, canonical.upcase)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|