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.
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: []