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.
Files changed (135) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +5 -0
  3. data/CHANGELOG.md +12 -0
  4. data/LICENSE.txt +21 -0
  5. data/README.md +197 -0
  6. data/Rakefile +8 -0
  7. data/examples/poly_chord.rb +14 -0
  8. data/examples/render_sampler.rb +16 -0
  9. data/examples/render_sequence.rb +13 -0
  10. data/examples/render_synth.rb +18 -0
  11. data/lib/deftones/analysis/analyser.rb +162 -0
  12. data/lib/deftones/analysis/dc_meter.rb +56 -0
  13. data/lib/deftones/analysis/fft.rb +128 -0
  14. data/lib/deftones/analysis/meter.rb +83 -0
  15. data/lib/deftones/analysis/waveform.rb +93 -0
  16. data/lib/deftones/component/amplitude_envelope.rb +8 -0
  17. data/lib/deftones/component/biquad_filter.rb +7 -0
  18. data/lib/deftones/component/channel.rb +109 -0
  19. data/lib/deftones/component/compressor.rb +64 -0
  20. data/lib/deftones/component/convolver.rb +99 -0
  21. data/lib/deftones/component/cross_fade.rb +67 -0
  22. data/lib/deftones/component/envelope.rb +160 -0
  23. data/lib/deftones/component/eq3.rb +73 -0
  24. data/lib/deftones/component/feedback_comb_filter.rb +75 -0
  25. data/lib/deftones/component/filter.rb +75 -0
  26. data/lib/deftones/component/follower.rb +55 -0
  27. data/lib/deftones/component/frequency_envelope.rb +24 -0
  28. data/lib/deftones/component/gate.rb +46 -0
  29. data/lib/deftones/component/lfo.rb +88 -0
  30. data/lib/deftones/component/limiter.rb +11 -0
  31. data/lib/deftones/component/lowpass_comb_filter.rb +43 -0
  32. data/lib/deftones/component/merge.rb +45 -0
  33. data/lib/deftones/component/mid_side_compressor.rb +43 -0
  34. data/lib/deftones/component/mid_side_merge.rb +53 -0
  35. data/lib/deftones/component/mid_side_split.rb +54 -0
  36. data/lib/deftones/component/mono.rb +8 -0
  37. data/lib/deftones/component/multiband_compressor.rb +70 -0
  38. data/lib/deftones/component/multiband_split.rb +133 -0
  39. data/lib/deftones/component/one_pole_filter.rb +71 -0
  40. data/lib/deftones/component/pan_vol.rb +26 -0
  41. data/lib/deftones/component/panner.rb +75 -0
  42. data/lib/deftones/component/panner3d.rb +322 -0
  43. data/lib/deftones/component/solo.rb +56 -0
  44. data/lib/deftones/component/split.rb +47 -0
  45. data/lib/deftones/component/volume.rb +31 -0
  46. data/lib/deftones/context.rb +213 -0
  47. data/lib/deftones/core/audio_block.rb +82 -0
  48. data/lib/deftones/core/audio_node.rb +262 -0
  49. data/lib/deftones/core/clock.rb +91 -0
  50. data/lib/deftones/core/computed_signal.rb +69 -0
  51. data/lib/deftones/core/delay.rb +44 -0
  52. data/lib/deftones/core/effect.rb +66 -0
  53. data/lib/deftones/core/emitter.rb +51 -0
  54. data/lib/deftones/core/gain.rb +39 -0
  55. data/lib/deftones/core/instrument.rb +109 -0
  56. data/lib/deftones/core/param.rb +31 -0
  57. data/lib/deftones/core/signal.rb +452 -0
  58. data/lib/deftones/core/signal_operator_methods.rb +73 -0
  59. data/lib/deftones/core/signal_operators.rb +138 -0
  60. data/lib/deftones/core/signal_shapers.rb +83 -0
  61. data/lib/deftones/core/source.rb +213 -0
  62. data/lib/deftones/core/synced_signal.rb +88 -0
  63. data/lib/deftones/destination.rb +132 -0
  64. data/lib/deftones/draw.rb +100 -0
  65. data/lib/deftones/dsp/biquad.rb +129 -0
  66. data/lib/deftones/dsp/delay_line.rb +41 -0
  67. data/lib/deftones/dsp/helpers.rb +25 -0
  68. data/lib/deftones/effect/auto_filter.rb +92 -0
  69. data/lib/deftones/effect/auto_panner.rb +57 -0
  70. data/lib/deftones/effect/auto_wah.rb +98 -0
  71. data/lib/deftones/effect/bit_crusher.rb +38 -0
  72. data/lib/deftones/effect/chebyshev.rb +36 -0
  73. data/lib/deftones/effect/chorus.rb +73 -0
  74. data/lib/deftones/effect/distortion.rb +22 -0
  75. data/lib/deftones/effect/feedback_delay.rb +38 -0
  76. data/lib/deftones/effect/freeverb.rb +11 -0
  77. data/lib/deftones/effect/frequency_shifter.rb +89 -0
  78. data/lib/deftones/effect/jc_reverb.rb +11 -0
  79. data/lib/deftones/effect/modulation_control.rb +159 -0
  80. data/lib/deftones/effect/phaser.rb +72 -0
  81. data/lib/deftones/effect/ping_pong_delay.rb +40 -0
  82. data/lib/deftones/effect/pitch_shift.rb +156 -0
  83. data/lib/deftones/effect/reverb.rb +71 -0
  84. data/lib/deftones/effect/stereo_widener.rb +34 -0
  85. data/lib/deftones/effect/tremolo.rb +52 -0
  86. data/lib/deftones/effect/vibrato.rb +47 -0
  87. data/lib/deftones/event/callback_behavior.rb +61 -0
  88. data/lib/deftones/event/loop.rb +53 -0
  89. data/lib/deftones/event/part.rb +51 -0
  90. data/lib/deftones/event/pattern.rb +94 -0
  91. data/lib/deftones/event/sequence.rb +87 -0
  92. data/lib/deftones/event/tone_event.rb +77 -0
  93. data/lib/deftones/event/transport.rb +276 -0
  94. data/lib/deftones/instrument/am_synth.rb +56 -0
  95. data/lib/deftones/instrument/duo_synth.rb +68 -0
  96. data/lib/deftones/instrument/fm_synth.rb +60 -0
  97. data/lib/deftones/instrument/membrane_synth.rb +60 -0
  98. data/lib/deftones/instrument/metal_synth.rb +61 -0
  99. data/lib/deftones/instrument/mono_synth.rb +88 -0
  100. data/lib/deftones/instrument/noise_synth.rb +56 -0
  101. data/lib/deftones/instrument/pluck_synth.rb +41 -0
  102. data/lib/deftones/instrument/poly_synth.rb +96 -0
  103. data/lib/deftones/instrument/sampler.rb +97 -0
  104. data/lib/deftones/instrument/synth.rb +60 -0
  105. data/lib/deftones/io/buffer.rb +352 -0
  106. data/lib/deftones/io/buffers.rb +77 -0
  107. data/lib/deftones/io/recorder.rb +89 -0
  108. data/lib/deftones/listener.rb +120 -0
  109. data/lib/deftones/music/frequency.rb +128 -0
  110. data/lib/deftones/music/midi.rb +206 -0
  111. data/lib/deftones/music/note.rb +58 -0
  112. data/lib/deftones/music/ticks.rb +106 -0
  113. data/lib/deftones/music/time.rb +209 -0
  114. data/lib/deftones/music/transport_time.rb +94 -0
  115. data/lib/deftones/music/unit_helpers.rb +30 -0
  116. data/lib/deftones/offline_context.rb +46 -0
  117. data/lib/deftones/portaudio_support.rb +112 -0
  118. data/lib/deftones/source/am_oscillator.rb +42 -0
  119. data/lib/deftones/source/fat_oscillator.rb +49 -0
  120. data/lib/deftones/source/fm_oscillator.rb +47 -0
  121. data/lib/deftones/source/grain_player.rb +198 -0
  122. data/lib/deftones/source/karplus_strong.rb +51 -0
  123. data/lib/deftones/source/noise.rb +99 -0
  124. data/lib/deftones/source/omni_oscillator.rb +175 -0
  125. data/lib/deftones/source/oscillator.rb +74 -0
  126. data/lib/deftones/source/player.rb +228 -0
  127. data/lib/deftones/source/players.rb +133 -0
  128. data/lib/deftones/source/pulse_oscillator.rb +38 -0
  129. data/lib/deftones/source/pwm_oscillator.rb +49 -0
  130. data/lib/deftones/source/tone_buffer_source.rb +136 -0
  131. data/lib/deftones/source/tone_oscillator_node.rb +65 -0
  132. data/lib/deftones/source/user_media.rb +519 -0
  133. data/lib/deftones/version.rb +5 -0
  134. data/lib/deftones.rb +542 -0
  135. metadata +221 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 80388e6ab20e1b3a9affc4cca0a73812c482003d1ac124b80ccfb1714c94a4a5
4
+ data.tar.gz: 30a59df05c0faec27f12794bb167d80b5ab0ce61c6273a3c2d77df72106a11fc
5
+ SHA512:
6
+ metadata.gz: 4fed0062263092487ce098080789d29f754151f6991fbd6479fe6cfa35f8c3f248a2e5c2f8d4c06206cfbdc3447ea0569d46052f72f4763aa6a9617a0a3549b1
7
+ data.tar.gz: 611b45acf4df1b5621a137892ff04ecb286634c96185a29d34b41e21e8bfec57181f0e296b24e6fab634815a036d95e9716786d40093e2fb49d12176cf0bbac3
data/.yardopts ADDED
@@ -0,0 +1,5 @@
1
+ --markup markdown
2
+ --protected
3
+ --private
4
+ README.md
5
+ lib/**/*.rb
data/CHANGELOG.md ADDED
@@ -0,0 +1,12 @@
1
+ # Changelog
2
+
3
+ ## Unreleased
4
+
5
+ - Added realtime PortAudio stream integration with lazy context startup.
6
+ - Added `UserMedia` live capture support through pluggable capture backends.
7
+ - Added `Buffers`, compressed audio import, MIDI I/O helpers, and lower-level routing primitives.
8
+ - Added MP3 export support for offline rendering when `ffmpeg` or `afconvert` is available.
9
+
10
+ ## 0.1.0
11
+
12
+ - Initial public MVP release.
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2026 Yudai Takada
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,197 @@
1
+ # Deftones
2
+
3
+ Deftones is a Ruby audio synthesis library with a flexible node graph, oscillator and synth variants, effects, transport/event scheduling, sample playback, analysis utilities, offline rendering, and an optional PortAudio-backed realtime context.
4
+
5
+ ## Features
6
+
7
+ - Pull-based `AudioNode` graph with `connect`, `>>`, `chain`, `fan`, and `to_output`
8
+ - `Context`, `OfflineContext`, `Destination`, `Draw`, `Listener`, `Emitter`, `Clock`, and `Delay`
9
+ - `Signal` automation, note/frequency/time wrappers, MIDI device I/O wrappers, and compatibility top-level helpers
10
+ - `Param`, `ToneAudioNode`, `UserMedia`, `CrossFade`, `Merge`, and `Split` for lower-level graph construction
11
+ - Oscillators: basic, pulse, PWM, FM, AM, fat, omni, noise
12
+ - Instruments: `Synth`, `MonoSynth`, `FMSynth`, `AMSynth`, `DuoSynth`, `NoiseSynth`, `PluckSynth`, `MembraneSynth`, `MetalSynth`, `PolySynth`, `Sampler`
13
+ - Effects: distortion, crusher, chebyshev, delays, reverbs, chorus, phaser, tremolo, vibrato, auto-filter, auto-panner, auto-wah, shifter, pitch shift, widener
14
+ - Filters, EQ, compressor, limiter, gate, convolution, comb, mid/side, multiband, and channel utilities
15
+ - Transport, loops, parts, sequences, and patterns
16
+ - Player, players, grain player, `ToneBufferSource`, `ToneOscillatorNode`, recorder, `ToneAudioBuffer`, `ToneAudioBuffers`, analyser, meter, FFT, waveform, DC meter
17
+ - Standalone sources and modulation effects can be scheduled against the transport with `sync` / `start` / `stop`
18
+ - Instruments expose `volume`, `mute`, camelCase trigger helpers, `PolySynth#releaseAll`, and `Sampler#add` / `get` / `has?`
19
+
20
+ ## Installation
21
+
22
+ ```bash
23
+ bundle add deftones
24
+ ```
25
+
26
+ For realtime output on macOS:
27
+
28
+ ```bash
29
+ brew install portaudio
30
+ ```
31
+
32
+ For development in this repository:
33
+
34
+ ```bash
35
+ bundle install
36
+ bundle exec rspec
37
+ bundle exec yard doc
38
+ ```
39
+
40
+ ## Quick Start
41
+
42
+ ### Offline synth render
43
+
44
+ ```ruby
45
+ require "deftones"
46
+
47
+ Deftones.render_to_file("output.wav", duration: 1.0) do |context|
48
+ synth = Deftones::Synth.new(context: context, type: :sawtooth).to_output
49
+ synth.play("C4", duration: "8n", at: 0.0)
50
+ synth.play("E4", duration: "8n", at: "4n")
51
+ synth.play("G4", duration: "8n", at: "2n")
52
+ end
53
+
54
+ Deftones.render_to_file("output.mp3", duration: 1.0) do |context|
55
+ Deftones::Synth.new(context: context).to_output.play("A4", duration: "4n")
56
+ end
57
+ ```
58
+
59
+ ### Transport + sequence
60
+
61
+ ```ruby
62
+ require "deftones"
63
+
64
+ context = Deftones::OfflineContext.new(duration: 1.0)
65
+ synth = Deftones::MonoSynth.new(context: context).to_output
66
+
67
+ Deftones.transport.bpm = 120
68
+ Deftones::Sequence.new(notes: ["C4", "E4", "G4", "B4"], subdivision: "8n", loop: false) do |time, note|
69
+ synth.play(note, duration: "16n", at: time)
70
+ end.start(0)
71
+
72
+ context.render.save("sequence.wav")
73
+ ```
74
+
75
+ ### Sample playback
76
+
77
+ ```ruby
78
+ require "deftones"
79
+
80
+ context = Deftones::OfflineContext.new(duration: 0.5)
81
+ player = Deftones::Player.new(buffer: "kick.wav", context: context)
82
+ player >> context.output
83
+ player.start(0.0)
84
+ context.render.save("player.wav")
85
+ ```
86
+
87
+ ### Buffer collections and MIDI output
88
+
89
+ ```ruby
90
+ require "deftones"
91
+
92
+ buffers = Deftones::Buffers.new(kick: "kick.wav", snare: "snare.ogg")
93
+ Deftones::Midi.note_on("C4", velocity: 100, device: "IAC Driver Bus 1")
94
+ ```
95
+
96
+ ### Realtime context and live input
97
+
98
+ ```ruby
99
+ require "deftones"
100
+
101
+ mic = Deftones::UserMedia.new(live: true).to_output.start
102
+ sleep 2
103
+ mic.stop
104
+ ```
105
+
106
+ ### Compatibility helpers
107
+
108
+ ```ruby
109
+ require "deftones"
110
+
111
+ Deftones.start(use_realtime: false)
112
+ Deftones.destination.volume.value = -6
113
+
114
+ clock = Deftones::Clock.new(frequency: 2)
115
+ time = Deftones.time("4n")
116
+ freq = Deftones.frequency("A4")
117
+
118
+ puts [clock.nextTickTime(0.25), time.to_seconds, freq.to_hz].inspect
119
+ ```
120
+
121
+ ### Synced sources and modulation effects
122
+
123
+ ```ruby
124
+ require "deftones"
125
+
126
+ context = Deftones::OfflineContext.new(duration: 0.6, sample_rate: 100)
127
+ source = Deftones::Oscillator.new(frequency: 5, context: context).sync
128
+ effect = Deftones::Tremolo.new(frequency: 5, depth: 1.0, context: context).sync
129
+
130
+ Deftones.transport.bpm = 120
131
+ source >> effect >> context.output
132
+ source.start("8n")
133
+ effect.start("8n")
134
+ source.stop("4n")
135
+ effect.stop("4n")
136
+
137
+ context.render.save("synced.wav")
138
+ ```
139
+
140
+ ## Main API Surface
141
+
142
+ ### Core and globals
143
+
144
+ `Context`, `OfflineContext`, `BaseContext`, `AudioNode`, `ToneAudioNode`, `Gain`, `Param`, `Signal`, `SyncedSignal`, `Emitter`, `Clock`, `Delay`, `Destination`, `Draw`, `Listener`
145
+
146
+ Top-level helpers include `start`, `loaded`, `supported`, `getContext`, `setContext`, `getDestination`, `getDraw`, `getListener`, `getTransport`, `connect`, `disconnect`, `connectSeries`, `connectSignal`, `fanIn`, `dbToGain`, `gainToDb`, `intervalToFrequencyRatio`, `frequency`, `midi`, `time`, `ticks`, and `transportTime`.
147
+
148
+ Every `AudioNode` also exposes shared helpers such as `toDestination`, `toMaster`, `toSeconds`, `toTicks`, `toFrequency`, `toMidi`, `set`, `get`, and `toString`.
149
+
150
+ `Signal` and `Param` expose automation helpers including `setValueCurveAtTime`, `setTargetAtTime`, `linearRampToValueAtTime`, `exponentialRampToValueAtTime`, `cancelAndHoldAtTime`, `targetRampTo`, and `getValueAtTime`.
151
+
152
+ ### Sources
153
+
154
+ `Oscillator`, `Noise`, `UserMedia`, `PulseOscillator`, `FMOscillator`, `AMOscillator`, `FatOscillator`, `PWMOscillator`, `OmniOscillator`, `Player`, `Players`, `GrainPlayer`, `ToneBufferSource`, `ToneOscillatorNode`
155
+
156
+ ### Instruments
157
+
158
+ `Synth`, `MonoSynth`, `FMSynth`, `AMSynth`, `DuoSynth`, `NoiseSynth`, `PluckSynth`, `MembraneSynth`, `MetalSynth`, `PolySynth`, `Sampler`
159
+
160
+ Common helpers include `volume`, `mute`, `set`, `get`, `triggerAttack`, `triggerRelease`, and `triggerAttackRelease`.
161
+
162
+ ### Effects and Dynamics
163
+
164
+ `Distortion`, `BitCrusher`, `Chebyshev`, `FeedbackDelay`, `PingPongDelay`, `Reverb`, `Freeverb`, `JCReverb`, `Chorus`, `Phaser`, `Tremolo`, `Vibrato`, `AutoFilter`, `AutoPanner`, `AutoWah`, `FrequencyShifter`, `PitchShift`, `StereoWidener`, `Filter`, `EQ3`, `Compressor`, `Limiter`, `Gate`
165
+
166
+ LFO-based effects also support `start`, `stop`, `restart`, `state`, `sync`, and `unsync`.
167
+
168
+ ### Scheduling
169
+
170
+ `Transport`, `ToneEvent`, `Loop`, `Part`, `Sequence`, `Pattern`
171
+
172
+ ### Analysis and Utilities
173
+
174
+ `Analyser`, `Meter`, `FFT`, `Waveform`, `DCMeter`, `Volume`, `Panner`, `Panner3D`, `PanVol`, `Solo`, `Channel`, `CrossFade`, `Merge`, `Split`, `Param`, `Buffer`, `Buffers`, `ToneAudioBuffer`, `ToneAudioBuffers`, `Recorder`, `Note`, `Frequency`, `Time`, `Ticks`, `TransportTime`, `Midi`
175
+
176
+ Alias constants matching the docs are also available: `FrequencyClass`, `MidiClass`, `TimeClass`, `TicksClass`, and `TransportTimeClass`.
177
+
178
+ ## Examples
179
+
180
+ Runnable examples live in [`examples/`](examples).
181
+
182
+ Release history lives in [`CHANGELOG.md`](CHANGELOG.md).
183
+
184
+ ## Notes
185
+
186
+ - Offline rendering is the most stable path and is fully covered by specs.
187
+ - The default realtime context starts lazily when you connect to output.
188
+ - Realtime output and `UserMedia.new(live: true)` use the `portaudio` gem when available.
189
+ - WAV I/O uses the `wavify` gem, and MP3/OGG loading falls back to `ffmpeg` when installed.
190
+ - `render_to_file` and `Buffer#save` can export WAV, MP3, and OGG when an encoder backend is installed.
191
+ - MIDI device discovery and I/O wrappers use `unimidi` when available.
192
+ - Unit-style classes map to Ruby wrappers where possible; `Frequency`, `Time`, `Ticks`, and `TransportTime` are available directly, while `Midi` also keeps device I/O class methods.
193
+ - Unit wrappers expose conversion helpers such as `toNotation`, `toMilliseconds`, `toSamples`, `transpose`, `harmonize`, `quantize`, `dispose`, and `toString`.
194
+
195
+ ## License
196
+
197
+ Released under the MIT License. See [LICENSE.txt](LICENSE.txt).
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task default: :spec
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "deftones"
4
+
5
+ Deftones.render_to_file("poly_chord.wav", duration: 0.75) do |context|
6
+ synth = Deftones::PolySynth.new(
7
+ Deftones::Synth,
8
+ voices: 4,
9
+ context: context
10
+ ).to_output
11
+
12
+ synth.play(%w[C4 E4 G4 B4], duration: 0.2, at: 0.0)
13
+ synth.play(%w[A3 C4 E4 G4], duration: 0.2, at: 0.25)
14
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "deftones"
4
+
5
+ sample_rate = 44_100
6
+ transient = Array.new((sample_rate * 0.25).to_i) do |index|
7
+ time = index.to_f / sample_rate
8
+ envelope = Math.exp(-12 * time)
9
+ Math.sin(2.0 * Math::PI * 110 * time) * envelope
10
+ end
11
+ sample = Deftones::Buffer.from_mono(transient, sample_rate: sample_rate)
12
+
13
+ Deftones.render_to_file("render_sampler.wav", duration: 1.0) do |context|
14
+ sampler = Deftones::Sampler.new(samples: { "C4" => sample }, context: context).to_output
15
+ sampler.play(%w[C4 E4 G4], duration: 0.2, at: 0.0)
16
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "deftones"
4
+
5
+ context = Deftones::OfflineContext.new(duration: 1.0)
6
+ synth = Deftones::MonoSynth.new(context: context, type: :sawtooth).to_output
7
+
8
+ Deftones.transport.bpm = 120
9
+ Deftones::Sequence.new(notes: ["C4", "E4", "G4", "B4"], subdivision: "8n", loop: false) do |time, note|
10
+ synth.play(note, duration: "16n", at: time)
11
+ end.start(0)
12
+
13
+ context.render.save("render_sequence.wav")
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "deftones"
4
+
5
+ Deftones.render_to_file("render_synth.wav", duration: 1.0) do |context|
6
+ synth = Deftones::Synth.new(
7
+ context: context,
8
+ type: :sawtooth,
9
+ attack: 0.01,
10
+ decay: 0.08,
11
+ sustain: 0.35,
12
+ release: 0.2
13
+ ).to_output
14
+
15
+ synth.play("C4", duration: "8n", at: 0.0)
16
+ synth.play("E4", duration: "8n", at: "4n")
17
+ synth.play("G4", duration: "8n", at: "2n")
18
+ end
@@ -0,0 +1,162 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Deftones
4
+ module Analysis
5
+ class Analyser < Core::AudioNode
6
+ TYPES = %i[fft waveform].freeze
7
+ RETURN_TYPES = %i[float byte].freeze
8
+
9
+ attr_reader :size, :type, :return_type, :min_decibels, :max_decibels
10
+ attr_accessor :normal_range
11
+
12
+ def initialize(size: 1024, type: :fft, return_type: :float, smoothing: 0.8,
13
+ normal_range: false, min_decibels: -100.0, max_decibels: 0.0,
14
+ context: Deftones.context)
15
+ super(context: context)
16
+ @type = normalize_type(type)
17
+ @return_type = normalize_return_type(return_type)
18
+ @normal_range = !!normal_range
19
+ @min_decibels = min_decibels.to_f
20
+ @max_decibels = max_decibels.to_f
21
+ self.smoothing = smoothing
22
+ self.size = size
23
+ end
24
+
25
+ def waveform
26
+ Waveform::Snapshot.new(@recent_samples.dup)
27
+ end
28
+
29
+ def fft
30
+ FFT.magnitudes(@recent_samples)
31
+ end
32
+
33
+ def size=(value)
34
+ @size = [value.to_i, 1].max
35
+ recent = @recent_samples || []
36
+ padding = [@size - recent.length, 0].max
37
+ @recent_samples = Array.new(padding, 0.0) + recent.last(@size)
38
+ @smoothed_waveform = @recent_samples.dup
39
+ @smoothed_fft = FFT.decibels(@recent_samples, floor: @min_decibels)
40
+ end
41
+
42
+ def type=(value)
43
+ @type = normalize_type(value)
44
+ end
45
+
46
+ def return_type=(value)
47
+ @return_type = normalize_return_type(value)
48
+ end
49
+
50
+ def smoothing
51
+ @smoothing
52
+ end
53
+
54
+ def smoothing=(value)
55
+ @smoothing = Deftones::DSP::Helpers.clamp(value.to_f, 0.0, 1.0)
56
+ end
57
+
58
+ def min_decibels=(value)
59
+ @min_decibels = value.to_f
60
+ @smoothed_fft = FFT.decibels(@recent_samples, floor: @min_decibels)
61
+ end
62
+
63
+ def max_decibels=(value)
64
+ @max_decibels = value.to_f
65
+ end
66
+
67
+ def get_value
68
+ if @type == :waveform
69
+ format_waveform_values(@smoothed_waveform)
70
+ else
71
+ format_fft_values(@smoothed_fft)
72
+ end
73
+ end
74
+
75
+ def multichannel_process?
76
+ true
77
+ end
78
+
79
+ def process(input_block, num_frames, _start_frame, _cache)
80
+ analysed = input_block.mono.first(num_frames)
81
+ @recent_samples.concat(analysed)
82
+ @recent_samples = @recent_samples.last(@size)
83
+ @smoothed_waveform = smooth_values(@smoothed_waveform, @recent_samples)
84
+ @smoothed_fft = smooth_values(@smoothed_fft, FFT.decibels(@recent_samples, floor: @min_decibels))
85
+ input_block
86
+ end
87
+
88
+ alias getValue get_value
89
+ alias returnType return_type
90
+ alias normalRange normal_range
91
+ alias minDecibels min_decibels
92
+ alias maxDecibels max_decibels
93
+
94
+ def returnType=(value)
95
+ self.return_type = value
96
+ end
97
+
98
+ def normalRange=(value)
99
+ self.normal_range = value
100
+ end
101
+
102
+ def minDecibels=(value)
103
+ self.min_decibels = value
104
+ end
105
+
106
+ def maxDecibels=(value)
107
+ self.max_decibels = value
108
+ end
109
+
110
+ private
111
+
112
+ def normalize_type(value)
113
+ normalized = value.to_s.downcase.to_sym
114
+ raise ArgumentError, "Unsupported analyser type: #{value}" unless TYPES.include?(normalized)
115
+
116
+ normalized
117
+ end
118
+
119
+ def normalize_return_type(value)
120
+ normalized = value.to_s.downcase.to_sym
121
+ raise ArgumentError, "Unsupported analyser return type: #{value}" unless RETURN_TYPES.include?(normalized)
122
+
123
+ normalized
124
+ end
125
+
126
+ def smooth_values(previous, current)
127
+ return current.dup if @smoothing.zero?
128
+
129
+ previous.zip(current).map do |prior, sample|
130
+ (prior.to_f * @smoothing) + (sample.to_f * (1.0 - @smoothing))
131
+ end
132
+ end
133
+
134
+ def format_waveform_values(values)
135
+ if @return_type == :byte
136
+ values.map { |sample| (((Deftones::DSP::Helpers.clamp(sample, -1.0, 1.0) + 1.0) * 0.5) * 255.0).round }
137
+ elsif @normal_range
138
+ values.map { |sample| Deftones::DSP::Helpers.clamp((sample + 1.0) * 0.5, 0.0, 1.0) }
139
+ else
140
+ values.map { |sample| Deftones::DSP::Helpers.clamp(sample, -1.0, 1.0) }
141
+ end
142
+ end
143
+
144
+ def format_fft_values(values)
145
+ return values.map { |value| scale_to_byte_range(value) } if @return_type == :byte
146
+ return values.map { |value| scale_to_normal_range(value) } if @normal_range
147
+
148
+ values.map { |value| Deftones::DSP::Helpers.clamp(value, @min_decibels, @max_decibels) }
149
+ end
150
+
151
+ def scale_to_normal_range(value)
152
+ return 0.0 if @max_decibels <= @min_decibels
153
+
154
+ Deftones::DSP::Helpers.clamp((value - @min_decibels) / (@max_decibels - @min_decibels), 0.0, 1.0)
155
+ end
156
+
157
+ def scale_to_byte_range(value)
158
+ (scale_to_normal_range(value) * 255.0).round
159
+ end
160
+ end
161
+ end
162
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Deftones
4
+ module Analysis
5
+ class DCMeter < Core::AudioNode
6
+ def initialize(smoothing: 0.8, channels: 1, context: Deftones.context)
7
+ super(context: context)
8
+ @channels = [channels.to_i, 1].max
9
+ @offsets = Array.new(@channels, 0.0)
10
+ self.smoothing = smoothing
11
+ end
12
+
13
+ def offset
14
+ @offsets.length == 1 ? @offsets.first : @offsets.dup
15
+ end
16
+
17
+ def smoothing
18
+ @smoothing
19
+ end
20
+
21
+ def smoothing=(value)
22
+ @smoothing = Deftones::DSP::Helpers.clamp(value.to_f, 0.0, 1.0)
23
+ end
24
+
25
+ def get_value
26
+ @offsets.length == 1 ? @offsets.first : @offsets.dup
27
+ end
28
+
29
+ def multichannel_process?
30
+ true
31
+ end
32
+
33
+ def process(input_block, num_frames, _start_frame, _cache)
34
+ analysis_block = input_block.fit_channels(@channels)
35
+
36
+ @channels.times do |channel_index|
37
+ segment = analysis_block.channel_data[channel_index].first(num_frames)
38
+ instantaneous_offset = segment.sum / [segment.length, 1].max
39
+ @offsets[channel_index] = smooth(@offsets[channel_index], instantaneous_offset)
40
+ end
41
+
42
+ input_block
43
+ end
44
+
45
+ alias getValue get_value
46
+
47
+ private
48
+
49
+ def smooth(previous, current)
50
+ return current if @smoothing.zero?
51
+
52
+ (previous.to_f * @smoothing) + (current.to_f * (1.0 - @smoothing))
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,128 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Deftones
4
+ module Analysis
5
+ class FFT < Core::AudioNode
6
+ def self.magnitudes(samples)
7
+ size = samples.length
8
+ half = size / 2
9
+
10
+ Array.new(half) do |bin|
11
+ real = 0.0
12
+ imaginary = 0.0
13
+
14
+ samples.each_with_index do |sample, index|
15
+ angle = (2.0 * Math::PI * bin * index) / size
16
+ real += sample * Math.cos(angle)
17
+ imaginary -= sample * Math.sin(angle)
18
+ end
19
+
20
+ Math.sqrt((real * real) + (imaginary * imaginary)) / [size, 1].max
21
+ end
22
+ end
23
+
24
+ def self.decibels(samples, floor: -100.0)
25
+ magnitudes(samples).map do |magnitude|
26
+ [Deftones.gain_to_db([magnitude, 1.0e-12].max), floor.to_f].max
27
+ end
28
+ end
29
+
30
+ def initialize(size: 1024, smoothing: 0.8, return_type: :float, normal_range: false,
31
+ min_decibels: -100.0, max_decibels: 0.0, context: Deftones.context)
32
+ super(context: context)
33
+ @delegate = Analysis::Analyser.new(
34
+ size: size,
35
+ type: :fft,
36
+ smoothing: smoothing,
37
+ return_type: return_type,
38
+ normal_range: normal_range,
39
+ min_decibels: min_decibels,
40
+ max_decibels: max_decibels,
41
+ context: context
42
+ )
43
+ end
44
+
45
+ def size
46
+ @delegate.size
47
+ end
48
+
49
+ def size=(value)
50
+ @delegate.size = value
51
+ end
52
+
53
+ def smoothing
54
+ @delegate.smoothing
55
+ end
56
+
57
+ def smoothing=(value)
58
+ @delegate.smoothing = value
59
+ end
60
+
61
+ def return_type
62
+ @delegate.return_type
63
+ end
64
+
65
+ def return_type=(value)
66
+ @delegate.return_type = value
67
+ end
68
+
69
+ def normal_range
70
+ @delegate.normal_range
71
+ end
72
+
73
+ def normal_range=(value)
74
+ @delegate.normal_range = value
75
+ end
76
+
77
+ def min_decibels
78
+ @delegate.min_decibels
79
+ end
80
+
81
+ def min_decibels=(value)
82
+ @delegate.min_decibels = value
83
+ end
84
+
85
+ def max_decibels
86
+ @delegate.max_decibels
87
+ end
88
+
89
+ def max_decibels=(value)
90
+ @delegate.max_decibels = value
91
+ end
92
+
93
+ def get_value
94
+ @delegate.get_value
95
+ end
96
+
97
+ alias getValue get_value
98
+ alias returnType return_type
99
+ alias normalRange normal_range
100
+ alias minDecibels min_decibels
101
+ alias maxDecibels max_decibels
102
+
103
+ def returnType=(value)
104
+ self.return_type = value
105
+ end
106
+
107
+ def normalRange=(value)
108
+ self.normal_range = value
109
+ end
110
+
111
+ def minDecibels=(value)
112
+ self.min_decibels = value
113
+ end
114
+
115
+ def maxDecibels=(value)
116
+ self.max_decibels = value
117
+ end
118
+
119
+ def process(input_buffer, num_frames, start_frame, cache)
120
+ @delegate.process(input_buffer, num_frames, start_frame, cache)
121
+ end
122
+
123
+ def multichannel_process?
124
+ true
125
+ end
126
+ end
127
+ end
128
+ end