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
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
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,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
|