frausto 0.2.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/LICENSE.txt +21 -0
- data/README.md +45 -0
- data/bin/faust2ruby +124 -0
- data/bin/ruby2faust +129 -0
- data/faust2ruby.md +523 -0
- data/lib/faust2ruby/ast.rb +315 -0
- data/lib/faust2ruby/ir_builder.rb +413 -0
- data/lib/faust2ruby/lexer.rb +255 -0
- data/lib/faust2ruby/library_mapper.rb +249 -0
- data/lib/faust2ruby/parser.rb +596 -0
- data/lib/faust2ruby/ruby_generator.rb +708 -0
- data/lib/faust2ruby/version.rb +5 -0
- data/lib/faust2ruby.rb +82 -0
- data/lib/frausto.rb +8 -0
- data/lib/ruby2faust/dsl.rb +1332 -0
- data/lib/ruby2faust/emitter.rb +599 -0
- data/lib/ruby2faust/ir.rb +285 -0
- data/lib/ruby2faust/live.rb +82 -0
- data/lib/ruby2faust/version.rb +5 -0
- data/lib/ruby2faust.rb +27 -0
- data/ruby2faust.md +334 -0
- metadata +106 -0
data/ruby2faust.md
ADDED
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
# ruby2faust
|
|
2
|
+
|
|
3
|
+
A Ruby DSL that generates Faust DSP code. Ruby describes the graph; Faust compiles and runs it.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```ruby
|
|
8
|
+
require 'ruby2faust'
|
|
9
|
+
|
|
10
|
+
# Idiomatic Ruby style
|
|
11
|
+
code = Ruby2Faust.generate do
|
|
12
|
+
# Use numeric extensions: .midi, .hz, .db
|
|
13
|
+
freq = 60.midi >> smoo
|
|
14
|
+
|
|
15
|
+
# Arithmetic operators for signal mixing
|
|
16
|
+
(osc(freq) + noise) * -6.db
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
puts code
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Output:
|
|
23
|
+
```faust
|
|
24
|
+
import("stdfaust.lib");
|
|
25
|
+
|
|
26
|
+
process = ((os.osc(ba.midikey2hz(60)) + no.noise) * ba.db2linear(-6));
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Composition
|
|
30
|
+
|
|
31
|
+
Ruby2Faust maps Faust's composition operators to Ruby methods and operators:
|
|
32
|
+
|
|
33
|
+
```ruby
|
|
34
|
+
# Sequential: signal flows through a chain
|
|
35
|
+
osc(440) >> lp(800) >> gain(0.3)
|
|
36
|
+
|
|
37
|
+
# Arithmetic operators (Infix)
|
|
38
|
+
osc(440) + noise # Mix / Sum
|
|
39
|
+
osc(440) * 0.3 # Gain
|
|
40
|
+
osc(440) - osc(442) # Subtraction
|
|
41
|
+
-osc(440) # Negate
|
|
42
|
+
|
|
43
|
+
# Parallel: signals run side by side
|
|
44
|
+
osc(440) | osc(442) # Stereo
|
|
45
|
+
|
|
46
|
+
# Split (fan-out)
|
|
47
|
+
osc(440).split(gain(0.5) | gain(0.3))
|
|
48
|
+
|
|
49
|
+
# Feedback loop
|
|
50
|
+
wire ~ (delay(44100, 22050) * 0.5)
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
| Faust | Meaning | Ruby | Method |
|
|
54
|
+
|-------|---------|-----------|--------|
|
|
55
|
+
| `:` | Sequential | `>>` | `.then` |
|
|
56
|
+
| `+` | Add / Mix | `+` | n/a |
|
|
57
|
+
| `-` | Subtract | `-` | n/a |
|
|
58
|
+
| `*(x)` | Gain | `* x` | `gain(x)` |
|
|
59
|
+
| `,` | Parallel | `\|` | `.par` |
|
|
60
|
+
| `<:` | Fan-out | n/a | `.split` |
|
|
61
|
+
| `:>` | Fan-in | n/a | `.merge` |
|
|
62
|
+
| `~` | Feedback | `~` | `.feedback` |
|
|
63
|
+
|
|
64
|
+
## Ruby-isms
|
|
65
|
+
|
|
66
|
+
### Numeric Extensions
|
|
67
|
+
Convenient conversions for common audio units:
|
|
68
|
+
```ruby
|
|
69
|
+
60.midi # ba.midikey2hz(60)
|
|
70
|
+
-6.db # ba.db2linear(-6)
|
|
71
|
+
0.1.sec # ba.sec2samp(0.1)
|
|
72
|
+
10.ms # ba.sec2samp(0.01)
|
|
73
|
+
440.hz # 440
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Block UI Groups
|
|
77
|
+
```ruby
|
|
78
|
+
hgroup("Master") {
|
|
79
|
+
vgroup("Osc") { osc(freq) } |
|
|
80
|
+
vgroup("FX") { reverb }
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## DSL Reference
|
|
85
|
+
|
|
86
|
+
### Oscillators (os.)
|
|
87
|
+
```ruby
|
|
88
|
+
osc(freq) # Sine wave
|
|
89
|
+
saw(freq) # Sawtooth
|
|
90
|
+
square(freq) # Square wave
|
|
91
|
+
triangle(freq) # Triangle wave
|
|
92
|
+
lf_saw(freq) # Low-freq sawtooth (0-1)
|
|
93
|
+
imptrain(freq) # Impulse train
|
|
94
|
+
phasor(n, freq) # Table phasor
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Noise (no.)
|
|
98
|
+
```ruby
|
|
99
|
+
noise # White noise
|
|
100
|
+
pink_noise # Pink noise
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Filters (fi.)
|
|
104
|
+
```ruby
|
|
105
|
+
lp(freq, order: 1) # Lowpass
|
|
106
|
+
hp(freq, order: 1) # Highpass
|
|
107
|
+
bp(freq, q: 1) # Bandpass
|
|
108
|
+
resonlp(freq, q, gain) # Resonant lowpass
|
|
109
|
+
resonhp(freq, q, gain) # Resonant highpass
|
|
110
|
+
allpass(max, d, fb) # Allpass comb
|
|
111
|
+
dcblock # DC blocker
|
|
112
|
+
peak_eq(freq, q, db) # Parametric EQ
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Delays (de.)
|
|
116
|
+
```ruby
|
|
117
|
+
delay(max, samples) # Integer delay
|
|
118
|
+
fdelay(max, samples) # Fractional delay
|
|
119
|
+
sdelay(max, interp, d) # Smooth delay
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Envelopes (en.)
|
|
123
|
+
```ruby
|
|
124
|
+
ar(attack, release, gate)
|
|
125
|
+
asr(attack, sustain, release, gate)
|
|
126
|
+
adsr(attack, decay, sustain, release, gate)
|
|
127
|
+
adsre(attack, decay, sustain, release, gate) # Exponential
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Math
|
|
131
|
+
```ruby
|
|
132
|
+
gain(x) # Multiply
|
|
133
|
+
add # Sum (+)
|
|
134
|
+
mul # Multiply (*)
|
|
135
|
+
sub # Subtract (-)
|
|
136
|
+
div # Divide (/)
|
|
137
|
+
abs_ # Absolute value
|
|
138
|
+
min_(a, b) # Minimum
|
|
139
|
+
max_(a, b) # Maximum
|
|
140
|
+
clip(min, max) # Clamp
|
|
141
|
+
pow(base, exp)
|
|
142
|
+
sqrt_, exp_, log_, log10_
|
|
143
|
+
sin_, cos_, tan_, tanh_
|
|
144
|
+
floor_, ceil_, rint_
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Conversion (ba.)
|
|
148
|
+
```ruby
|
|
149
|
+
db2linear(x) # dB to linear
|
|
150
|
+
linear2db(x) # Linear to dB
|
|
151
|
+
midi2hz(x) # MIDI note to Hz
|
|
152
|
+
hz2midi(x) # Hz to MIDI note
|
|
153
|
+
samp2sec(x) # Samples to seconds
|
|
154
|
+
sec2samp(x) # Seconds to samples
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Smoothing (si.)
|
|
158
|
+
```ruby
|
|
159
|
+
smooth(tau) # Smooth with time constant
|
|
160
|
+
smoo # Default 5ms smooth
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Selectors
|
|
164
|
+
```ruby
|
|
165
|
+
select2(cond, a, b) # 2-way select
|
|
166
|
+
select3(sel, a, b, c) # 3-way select
|
|
167
|
+
selectn(n, index, *signals) # N-way select
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Iteration
|
|
171
|
+
Ruby blocks map to Faust's iteration constructs:
|
|
172
|
+
```ruby
|
|
173
|
+
fpar(4) { |i| osc((i + 1) * 100) } # par(i, 4, osc((i+1)*100)) - 4 parallel oscillators
|
|
174
|
+
fseq(3) { |i| lp(1000 * (i + 1)) } # seq(i, 3, fi.lowpass(1, 1000*(i+1))) - cascaded filters
|
|
175
|
+
fsum(4) { |i| osc((i + 1) * 100) } # sum(i, 4, osc((i+1)*100)) - sum of 4 oscillators
|
|
176
|
+
fprod(3) { |i| osc((i + 1) * 100) } # prod(i, 3, osc((i+1)*100)) - ring modulation
|
|
177
|
+
|
|
178
|
+
# Ruby 3.4+ implicit 'it' parameter also works:
|
|
179
|
+
fpar(4) { osc((it + 1) * 100) } # par(it, 4, osc((it+1)*100))
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Lambda
|
|
183
|
+
```ruby
|
|
184
|
+
flambda(:x) { |x| x * 2 } # \(x).(x * 2)
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### Tables
|
|
188
|
+
```ruby
|
|
189
|
+
waveform(0, 0.5, 1, 0.5) # waveform{0, 0.5, 1, 0.5}
|
|
190
|
+
rdtable(size, init, ridx) # Read-only table
|
|
191
|
+
rwtable(size, init, widx, wsig, ridx) # Read/write table
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Routing (si./ro.)
|
|
195
|
+
```ruby
|
|
196
|
+
bus(n) # N parallel wires
|
|
197
|
+
block(n) # Terminate N signals
|
|
198
|
+
route(ins, outs, [[1,2],[2,1]]) # Signal routing matrix
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### Reverbs (re.)
|
|
202
|
+
```ruby
|
|
203
|
+
freeverb(fb1, fb2, damp, spread)
|
|
204
|
+
zita_rev(rdel, f1, f2, t60dc, t60m, fsmax)
|
|
205
|
+
jpverb(t60, damp, size, ...)
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### Compressors (co.)
|
|
209
|
+
```ruby
|
|
210
|
+
compressor(ratio, thresh, attack, release)
|
|
211
|
+
limiter
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### Spatial (sp.)
|
|
215
|
+
```ruby
|
|
216
|
+
panner(pan) # Stereo panner (0-1)
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### UI Controls
|
|
220
|
+
```ruby
|
|
221
|
+
slider("name", init:, min:, max:, step: 0.01)
|
|
222
|
+
vslider("name", init:, min:, max:, step: 0.01)
|
|
223
|
+
nentry("name", init:, min:, max:, step: 1)
|
|
224
|
+
button("name")
|
|
225
|
+
checkbox("name")
|
|
226
|
+
hgroup("name", content)
|
|
227
|
+
vgroup("name", content)
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
**Slider metadata kwargs:**
|
|
231
|
+
```ruby
|
|
232
|
+
slider("freq", init: 440, min: 20, max: 2000,
|
|
233
|
+
style: :knob, # [style:knob]
|
|
234
|
+
unit: "Hz", # [unit:Hz]
|
|
235
|
+
tooltip: "Freq", # [tooltip:Freq]
|
|
236
|
+
order: 0, # [0] (UI ordering)
|
|
237
|
+
scale: :log # [scale:log]
|
|
238
|
+
)
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
Or use inline Faust metadata:
|
|
242
|
+
```ruby
|
|
243
|
+
slider("[0]freq[style:knob][unit:Hz]", init: 440, min: 20, max: 2000)
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### Comments / Documentation
|
|
247
|
+
```ruby
|
|
248
|
+
# Inline comment attached to a node
|
|
249
|
+
saw(freq).doc("Main oscillator")
|
|
250
|
+
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### Constants
|
|
254
|
+
```ruby
|
|
255
|
+
sr, SR # Sample rate (ma.SR)
|
|
256
|
+
pi, PI # Pi (ma.PI)
|
|
257
|
+
tempo, TEMPO # BPM tempo (ma.tempo)
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### Utility
|
|
261
|
+
```ruby
|
|
262
|
+
wire # Pass-through (_)
|
|
263
|
+
cut # Terminate (!)
|
|
264
|
+
mem # 1-sample delay
|
|
265
|
+
literal("expr") # Raw Faust expression
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
## Metadata & Emitter Options
|
|
269
|
+
|
|
270
|
+
```ruby
|
|
271
|
+
prog = Ruby2Faust::Program.new do
|
|
272
|
+
declare :name, "MySynth"
|
|
273
|
+
declare :author, "Me"
|
|
274
|
+
import "analyzers.lib"
|
|
275
|
+
|
|
276
|
+
osc(440) * 0.5
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
# Generate pretty-printed Faust with indentation and newlines
|
|
280
|
+
puts Ruby2Faust::Emitter.program(prog, pretty: true)
|
|
281
|
+
|
|
282
|
+
# The generate helper also supports pretty: true
|
|
283
|
+
puts Ruby2Faust.generate(pretty: true) do
|
|
284
|
+
hgroup("Synth") { osc(440) + noise }
|
|
285
|
+
end
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
## Example: Subtractive Synth
|
|
289
|
+
|
|
290
|
+
```ruby
|
|
291
|
+
require 'ruby2faust'
|
|
292
|
+
include Ruby2Faust::DSL
|
|
293
|
+
|
|
294
|
+
gate = button("gate")
|
|
295
|
+
freq = slider("freq", init: 220, min: 20, max: 2000, style: :knob) >> smoo
|
|
296
|
+
cutoff = slider("cutoff", init: 1000, min: 100, max: 8000, style: :knob) >> smoo
|
|
297
|
+
|
|
298
|
+
env = adsr(0.01, 0.2, 0.6, 0.3, gate)
|
|
299
|
+
|
|
300
|
+
process = saw(freq) >> resonlp(cutoff, 4, 1) >> gain(env) >> panner(0.5)
|
|
301
|
+
|
|
302
|
+
prog = Ruby2Faust::Program.new(process)
|
|
303
|
+
.declare(:name, "SubSynth")
|
|
304
|
+
|
|
305
|
+
puts Ruby2Faust::Emitter.program(prog)
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
## CLI
|
|
309
|
+
|
|
310
|
+
```bash
|
|
311
|
+
ruby2faust compile synth.rb # Generate .dsp
|
|
312
|
+
ruby2faust compile -o out.dsp synth.rb
|
|
313
|
+
ruby2faust run synth.rb # Compile + run Faust
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
## faust2ruby
|
|
317
|
+
|
|
318
|
+
The reverse converter is also included: convert Faust DSP code to Ruby DSL. See [faust2ruby.md](faust2ruby.md) for details.
|
|
319
|
+
|
|
320
|
+
```bash
|
|
321
|
+
faust2ruby input.dsp -o output.rb
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
## Live Reload
|
|
325
|
+
|
|
326
|
+
```ruby
|
|
327
|
+
if Ruby2Faust::Live.changed?(old_graph, new_graph)
|
|
328
|
+
Ruby2Faust::Live.compile(new_graph, output: "synth.dsp")
|
|
329
|
+
end
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
## License
|
|
333
|
+
|
|
334
|
+
MIT
|
metadata
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: frausto
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.2.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- David Lowenfels
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 2026-01-20 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: minitest
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '5.0'
|
|
19
|
+
type: :development
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '5.0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: rake
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '13.0'
|
|
33
|
+
type: :development
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '13.0'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: yard
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - "~>"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '0.9'
|
|
47
|
+
type: :development
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - "~>"
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '0.9'
|
|
54
|
+
description: Build faust-executable DSP graphs in Ruby DSL with ruby2faust; or convert
|
|
55
|
+
Faust to Ruby with faust2ruby.
|
|
56
|
+
email:
|
|
57
|
+
- dfl@alum.mit.edu
|
|
58
|
+
executables:
|
|
59
|
+
- ruby2faust
|
|
60
|
+
- faust2ruby
|
|
61
|
+
extensions: []
|
|
62
|
+
extra_rdoc_files: []
|
|
63
|
+
files:
|
|
64
|
+
- LICENSE.txt
|
|
65
|
+
- README.md
|
|
66
|
+
- bin/faust2ruby
|
|
67
|
+
- bin/ruby2faust
|
|
68
|
+
- faust2ruby.md
|
|
69
|
+
- lib/faust2ruby.rb
|
|
70
|
+
- lib/faust2ruby/ast.rb
|
|
71
|
+
- lib/faust2ruby/ir_builder.rb
|
|
72
|
+
- lib/faust2ruby/lexer.rb
|
|
73
|
+
- lib/faust2ruby/library_mapper.rb
|
|
74
|
+
- lib/faust2ruby/parser.rb
|
|
75
|
+
- lib/faust2ruby/ruby_generator.rb
|
|
76
|
+
- lib/faust2ruby/version.rb
|
|
77
|
+
- lib/frausto.rb
|
|
78
|
+
- lib/ruby2faust.rb
|
|
79
|
+
- lib/ruby2faust/dsl.rb
|
|
80
|
+
- lib/ruby2faust/emitter.rb
|
|
81
|
+
- lib/ruby2faust/ir.rb
|
|
82
|
+
- lib/ruby2faust/live.rb
|
|
83
|
+
- lib/ruby2faust/version.rb
|
|
84
|
+
- ruby2faust.md
|
|
85
|
+
homepage: https://github.com/dfl/ruby2faust
|
|
86
|
+
licenses:
|
|
87
|
+
- MIT
|
|
88
|
+
metadata: {}
|
|
89
|
+
rdoc_options: []
|
|
90
|
+
require_paths:
|
|
91
|
+
- lib
|
|
92
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - ">="
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: 3.0.0
|
|
97
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
98
|
+
requirements:
|
|
99
|
+
- - ">="
|
|
100
|
+
- !ruby/object:Gem::Version
|
|
101
|
+
version: '0'
|
|
102
|
+
requirements: []
|
|
103
|
+
rubygems_version: 3.6.2
|
|
104
|
+
specification_version: 4
|
|
105
|
+
summary: Ruby↔Faust DSP transpiler
|
|
106
|
+
test_files: []
|