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
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "digest"
|
|
4
|
+
|
|
5
|
+
module Ruby2Faust
|
|
6
|
+
# Intermediate Representation node for DSP graphs.
|
|
7
|
+
# Nodes are immutable value objects representing DSP operations.
|
|
8
|
+
Node = Struct.new(:type, :args, :inputs, :channels, keyword_init: true) do
|
|
9
|
+
def initialize(type:, args: [], inputs: [], channels: 1)
|
|
10
|
+
super(type: type, args: args.freeze, inputs: inputs.freeze, channels: channels)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def fingerprint
|
|
14
|
+
content = [type, args, inputs.map(&:fingerprint)].inspect
|
|
15
|
+
Digest::SHA1.hexdigest(content)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def same_structure?(other)
|
|
19
|
+
fingerprint == other.fingerprint
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Node type constants - comprehensive Faust library coverage
|
|
24
|
+
module NodeType
|
|
25
|
+
# === Comments/Documentation ===
|
|
26
|
+
COMMENT = :comment # // line comment
|
|
27
|
+
DOC = :doc # /* inline comment */ attached to node
|
|
28
|
+
|
|
29
|
+
# === Oscillators (os.) ===
|
|
30
|
+
OSC = :osc # os.osc(freq) - sine
|
|
31
|
+
SAW = :saw # os.sawtooth(freq)
|
|
32
|
+
SQUARE = :square # os.square(freq)
|
|
33
|
+
TRIANGLE = :triangle # os.triangle(freq)
|
|
34
|
+
PHASOR = :phasor # os.phasor(tablesize, freq)
|
|
35
|
+
LF_SAW = :lf_saw # os.lf_sawpos(freq) - low-freq sawtooth 0-1
|
|
36
|
+
LF_TRIANGLE = :lf_triangle
|
|
37
|
+
LF_SQUARE = :lf_square
|
|
38
|
+
IMPTRAIN = :imptrain # os.lf_imptrain(freq) - impulse train
|
|
39
|
+
PULSETRAIN = :pulsetrain # os.lf_pulsetrain(freq, duty)
|
|
40
|
+
|
|
41
|
+
# === Noise (no.) ===
|
|
42
|
+
NOISE = :noise # no.noise - white
|
|
43
|
+
PINK_NOISE = :pink_noise # no.pink_noise
|
|
44
|
+
|
|
45
|
+
# === Filters (fi.) ===
|
|
46
|
+
LP = :lp # fi.lowpass(order, freq)
|
|
47
|
+
HP = :hp # fi.highpass(order, freq)
|
|
48
|
+
BP = :bp # fi.bandpass(order, freq, q)
|
|
49
|
+
RESONLP = :resonlp # fi.resonlp(freq, q, gain)
|
|
50
|
+
RESONHP = :resonhp
|
|
51
|
+
RESONBP = :resonbp
|
|
52
|
+
ALLPASS = :allpass # fi.allpass_comb(maxdelay, delay, feedback)
|
|
53
|
+
DCBLOCK = :dcblock # fi.dcblocker
|
|
54
|
+
PEAK_EQ = :peak_eq # fi.peak_eq(freq, q, gain_db)
|
|
55
|
+
|
|
56
|
+
# SVF (State Variable Filter)
|
|
57
|
+
SVF_LP = :svf_lp # fi.svf.lp(freq, q)
|
|
58
|
+
SVF_HP = :svf_hp # fi.svf.hp(freq, q)
|
|
59
|
+
SVF_BP = :svf_bp # fi.svf.bp(freq, q)
|
|
60
|
+
SVF_NOTCH = :svf_notch # fi.svf.notch(freq, q)
|
|
61
|
+
SVF_AP = :svf_ap # fi.svf.ap(freq, q)
|
|
62
|
+
SVF_BELL = :svf_bell # fi.svf.bell(freq, q, gain)
|
|
63
|
+
SVF_LS = :svf_ls # fi.svf.ls(freq, q, gain) - low shelf
|
|
64
|
+
SVF_HS = :svf_hs # fi.svf.hs(freq, q, gain) - high shelf
|
|
65
|
+
|
|
66
|
+
# Other filters
|
|
67
|
+
LOWPASS3E = :lowpass3e # fi.lowpass3e(freq) - 3rd order elliptic
|
|
68
|
+
HIGHPASS3E = :highpass3e # fi.highpass3e(freq)
|
|
69
|
+
LOWPASS6E = :lowpass6e # fi.lowpass6e(freq) - 6th order elliptic
|
|
70
|
+
HIGHPASS6E = :highpass6e # fi.highpass6e(freq)
|
|
71
|
+
BANDSTOP = :bandstop # fi.bandstop(order, freq, q)
|
|
72
|
+
NOTCHW = :notchw # fi.notchw(freq, width)
|
|
73
|
+
LOW_SHELF = :low_shelf # fi.low_shelf(freq, q, gain)
|
|
74
|
+
HIGH_SHELF = :high_shelf # fi.high_shelf(freq, q, gain)
|
|
75
|
+
PEAK_EQ_CQ = :peak_eq_cq # fi.peak_eq_cq(freq, q, gain)
|
|
76
|
+
FI_POLE = :fi_pole # fi.pole(p)
|
|
77
|
+
FI_ZERO = :fi_zero # fi.zero(z)
|
|
78
|
+
TF1 = :tf1 # fi.tf1(b0, b1, a1)
|
|
79
|
+
TF2 = :tf2 # fi.tf2(b0, b1, b2, a1, a2)
|
|
80
|
+
TF1S = :tf1s # fi.tf1s(b0, b1, a1)
|
|
81
|
+
TF2S = :tf2s # fi.tf2s(b0, b1, b2, a1, a2)
|
|
82
|
+
IIR = :iir # fi.iir(bcoeffs, acoeffs)
|
|
83
|
+
FIR = :fir # fi.fir(coeffs)
|
|
84
|
+
CONV = :conv # fi.conv(impulse, size)
|
|
85
|
+
FBCOMBFILTER = :fbcombfilter # fi.fbcombfilter(maxdel, del, fb)
|
|
86
|
+
FFCOMBFILTER = :ffcombfilter # fi.ffcombfilter(maxdel, del)
|
|
87
|
+
|
|
88
|
+
# === Delays (de.) ===
|
|
89
|
+
DELAY = :delay # de.delay(maxdelay, delay)
|
|
90
|
+
FDELAY = :fdelay # de.fdelay(maxdelay, delay) - fractional
|
|
91
|
+
SDELAY = :sdelay # de.sdelay(maxdelay, interp, delay) - smooth
|
|
92
|
+
|
|
93
|
+
# === Envelopes (en.) ===
|
|
94
|
+
AR = :ar # en.ar(attack, release, gate)
|
|
95
|
+
ASR = :asr # en.asr(attack, sustain_level, release, gate)
|
|
96
|
+
ADSR = :adsr # en.adsr(attack, decay, sustain, release, gate)
|
|
97
|
+
ADSRE = :adsre # en.adsre with exponential segments
|
|
98
|
+
|
|
99
|
+
# === Math (primitives + ma.) ===
|
|
100
|
+
GAIN = :gain # *(x)
|
|
101
|
+
ADD = :add # +
|
|
102
|
+
MUL = :mul # *
|
|
103
|
+
SUB = :sub # -
|
|
104
|
+
DIV = :div # /
|
|
105
|
+
NEG = :neg # 0 - x
|
|
106
|
+
ABS = :abs # abs
|
|
107
|
+
MIN = :min # min(a, b)
|
|
108
|
+
MAX = :max # max(a, b)
|
|
109
|
+
CLIP = :clip # max(min_val, min(max_val, x))
|
|
110
|
+
POW = :pow # pow(base, exp)
|
|
111
|
+
SQRT = :sqrt # sqrt
|
|
112
|
+
EXP = :exp # exp
|
|
113
|
+
LOG = :log # log
|
|
114
|
+
LOG10 = :log10 # log10
|
|
115
|
+
SIN = :sin # sin
|
|
116
|
+
COS = :cos # cos
|
|
117
|
+
TAN = :tan # tan
|
|
118
|
+
ASIN = :asin
|
|
119
|
+
ACOS = :acos
|
|
120
|
+
ATAN = :atan
|
|
121
|
+
ATAN2 = :atan2
|
|
122
|
+
TANH = :tanh # ma.tanh - saturating
|
|
123
|
+
SINH = :sinh
|
|
124
|
+
COSH = :cosh
|
|
125
|
+
ASINH = :asinh
|
|
126
|
+
ACOSH = :acosh
|
|
127
|
+
ATANH = :atanh
|
|
128
|
+
FLOOR = :floor
|
|
129
|
+
CEIL = :ceil
|
|
130
|
+
RINT = :rint # round to int
|
|
131
|
+
FMOD = :fmod # fmod(x, y)
|
|
132
|
+
REMAINDER = :remainder
|
|
133
|
+
MOD = :mod # %
|
|
134
|
+
|
|
135
|
+
# === Comparison ===
|
|
136
|
+
LT = :lt # <
|
|
137
|
+
GT = :gt # >
|
|
138
|
+
LE = :le # <=
|
|
139
|
+
GE = :ge # >=
|
|
140
|
+
EQ = :eq # ==
|
|
141
|
+
NEQ = :neq # !=
|
|
142
|
+
|
|
143
|
+
# === Bitwise ===
|
|
144
|
+
BAND = :band # &
|
|
145
|
+
BOR = :bor # | (bitwise, not parallel)
|
|
146
|
+
XOR = :xor # xor
|
|
147
|
+
|
|
148
|
+
# === Conversion (ba.) ===
|
|
149
|
+
DB2LINEAR = :db2linear # ba.db2linear
|
|
150
|
+
LINEAR2DB = :linear2db # ba.linear2db
|
|
151
|
+
SAMP2SEC = :samp2sec # ba.samp2sec
|
|
152
|
+
SEC2SAMP = :sec2samp # ba.sec2samp
|
|
153
|
+
MIDI2HZ = :midi2hz # ba.midikey2hz
|
|
154
|
+
HZ2MIDI = :hz2midi # ba.hz2midikey
|
|
155
|
+
TAU2POLE = :tau2pole # ba.tau2pole
|
|
156
|
+
POLE2TAU = :pole2tau # ba.pole2tau
|
|
157
|
+
BA_IF = :ba_if # ba.if(cond, then, else)
|
|
158
|
+
SELECTOR = :selector # ba.selector(n, sel, inputs)
|
|
159
|
+
BA_TAKE = :ba_take # ba.take(idx, tuple)
|
|
160
|
+
|
|
161
|
+
# === Smoothing (si.) ===
|
|
162
|
+
SMOOTH = :smooth # si.smooth(ba.tau2pole(tau))
|
|
163
|
+
SMOO = :smoo # si.smoo - default 5ms smooth
|
|
164
|
+
POLYSMOOTH = :polysmooth # si.polySmooth(s, n)
|
|
165
|
+
|
|
166
|
+
# === Selectors ===
|
|
167
|
+
SELECT2 = :select2 # select2(cond, a, b)
|
|
168
|
+
SELECTN = :selectn # ba.selectn(n, idx, ...)
|
|
169
|
+
|
|
170
|
+
# === Routing (si./ro.) ===
|
|
171
|
+
BUS = :bus # si.bus(n) - n parallel wires
|
|
172
|
+
BLOCK = :block # si.block(n) - terminate n signals
|
|
173
|
+
|
|
174
|
+
# === Reverbs (re.) ===
|
|
175
|
+
FREEVERB = :freeverb # re.mono_freeverb(fb1, fb2, damp, spread)
|
|
176
|
+
ZITA_REV = :zita_rev # re.zita_rev1_stereo(...)
|
|
177
|
+
JPVERB = :jpverb # re.jpverb(...)
|
|
178
|
+
|
|
179
|
+
# === Compressors (co.) ===
|
|
180
|
+
COMPRESSOR = :compressor # co.compressor_mono(ratio, thresh, attack, release)
|
|
181
|
+
LIMITER = :limiter # co.limiter_1176_R4_mono
|
|
182
|
+
|
|
183
|
+
# === Spatial (sp.) ===
|
|
184
|
+
PANNER = :panner # sp.panner(pan) - stereo pan
|
|
185
|
+
|
|
186
|
+
# === UI Controls ===
|
|
187
|
+
SLIDER = :slider
|
|
188
|
+
VSLIDER = :vslider
|
|
189
|
+
NENTRY = :nentry
|
|
190
|
+
BUTTON = :button
|
|
191
|
+
CHECKBOX = :checkbox
|
|
192
|
+
HGROUP = :hgroup
|
|
193
|
+
VGROUP = :vgroup
|
|
194
|
+
TGROUP = :tgroup
|
|
195
|
+
|
|
196
|
+
# === Composition ===
|
|
197
|
+
SEQ = :seq # :
|
|
198
|
+
PAR = :par # ,
|
|
199
|
+
SPLIT = :split # <:
|
|
200
|
+
MERGE = :merge # :>
|
|
201
|
+
FEEDBACK = :feedback # ~
|
|
202
|
+
REC = :rec # letrec style
|
|
203
|
+
LETREC = :letrec # letrec { 'x = expr; 'y = expr; } result
|
|
204
|
+
|
|
205
|
+
# === Iteration ===
|
|
206
|
+
FPAR = :fpar # par(i, n, expr)
|
|
207
|
+
FSEQ = :fseq # seq(i, n, expr)
|
|
208
|
+
FSUM = :fsum # sum(i, n, expr)
|
|
209
|
+
FPROD = :fprod # prod(i, n, expr)
|
|
210
|
+
|
|
211
|
+
# === Lambda ===
|
|
212
|
+
LAMBDA = :lambda # \(x).(body)
|
|
213
|
+
PARAM = :param # Parameter reference
|
|
214
|
+
|
|
215
|
+
# === Tables ===
|
|
216
|
+
RDTABLE = :rdtable # rdtable(n, init, ridx)
|
|
217
|
+
RWTABLE = :rwtable # rwtable(n, init, widx, wsig, ridx)
|
|
218
|
+
WAVEFORM = :waveform # waveform{...}
|
|
219
|
+
|
|
220
|
+
# === Additional Routing ===
|
|
221
|
+
ROUTE = :route # route(ins, outs, connections)
|
|
222
|
+
SELECT3 = :select3 # select3(sel, a, b, c)
|
|
223
|
+
|
|
224
|
+
# === Utility ===
|
|
225
|
+
WIRE = :wire # _
|
|
226
|
+
CUT = :cut # !
|
|
227
|
+
LITERAL = :literal # raw Faust expression
|
|
228
|
+
MEM = :mem # mem (1-sample delay)
|
|
229
|
+
INT = :int # int(x)
|
|
230
|
+
FLOAT = :float # float(x)
|
|
231
|
+
|
|
232
|
+
# === Constants ===
|
|
233
|
+
SR = :sr # ma.SR
|
|
234
|
+
PI = :pi # ma.PI
|
|
235
|
+
TEMPO = :tempo # ma.tempo
|
|
236
|
+
|
|
237
|
+
# === Antialiasing (aa.) ===
|
|
238
|
+
AA_TANH1 = :aa_tanh1 # aa.tanh1
|
|
239
|
+
AA_TANH2 = :aa_tanh2 # aa.tanh2
|
|
240
|
+
AA_ARCTAN = :aa_arctan # aa.arctan
|
|
241
|
+
AA_SOFTCLIP = :aa_softclip # aa.softclip
|
|
242
|
+
AA_HARDCLIP = :aa_hardclip # aa.hardclip
|
|
243
|
+
AA_PARABOLIC = :aa_parabolic # aa.parabolic
|
|
244
|
+
AA_SIN = :aa_sin # aa.sin
|
|
245
|
+
AA_CUBIC1 = :aa_cubic1 # aa.cubic1
|
|
246
|
+
AA_CUBIC2 = :aa_cubic2 # aa.cubic2
|
|
247
|
+
|
|
248
|
+
# === Analyzers (an.) ===
|
|
249
|
+
AMP_FOLLOWER = :amp_follower # an.amp_follower(t)
|
|
250
|
+
AMP_FOLLOWER_AR = :amp_follower_ar # an.amp_follower_ar(attack, release)
|
|
251
|
+
AMP_FOLLOWER_UD = :amp_follower_ud # an.amp_follower_ud(up, down)
|
|
252
|
+
RMS_ENVELOPE_RECT = :rms_envelope_rect # an.rms_envelope_rect(period)
|
|
253
|
+
RMS_ENVELOPE_TAU = :rms_envelope_tau # an.rms_envelope_tau(tau)
|
|
254
|
+
ABS_ENVELOPE_RECT = :abs_envelope_rect # an.abs_envelope_rect(period)
|
|
255
|
+
ABS_ENVELOPE_TAU = :abs_envelope_tau # an.abs_envelope_tau(tau)
|
|
256
|
+
MS_ENVELOPE_RECT = :ms_envelope_rect # an.ms_envelope_rect(period)
|
|
257
|
+
MS_ENVELOPE_TAU = :ms_envelope_tau # an.ms_envelope_tau(tau)
|
|
258
|
+
PEAK_ENVELOPE = :peak_envelope # an.peak_envelope(t)
|
|
259
|
+
|
|
260
|
+
# === Effects (ef.) ===
|
|
261
|
+
CUBICNL = :cubicnl # ef.cubicnl(drive, offset)
|
|
262
|
+
GATE_MONO = :gate_mono # ef.gate_mono(thresh, att, hold, rel)
|
|
263
|
+
GATE_STEREO = :gate_stereo # ef.gate_stereo(thresh, att, hold, rel)
|
|
264
|
+
EF_COMPRESSOR_MONO = :ef_compressor_mono # ef.compressor_mono
|
|
265
|
+
EF_COMPRESSOR_STEREO = :ef_compressor_stereo # ef.compressor_stereo
|
|
266
|
+
EF_LIMITER_1176_MONO = :ef_limiter_1176_mono # ef.limiter_1176_R4_mono
|
|
267
|
+
EF_LIMITER_1176_STEREO = :ef_limiter_1176_stereo # ef.limiter_1176_R4_stereo
|
|
268
|
+
ECHO = :echo # ef.echo(maxdel, del, fb)
|
|
269
|
+
TRANSPOSE = :transpose # ef.transpose(w, x, s)
|
|
270
|
+
FLANGER_MONO = :flanger_mono # ef.flanger_mono(...)
|
|
271
|
+
FLANGER_STEREO = :flanger_stereo # ef.flanger_stereo(...)
|
|
272
|
+
PHASER2_MONO = :phaser2_mono # ef.phaser2_mono(...)
|
|
273
|
+
PHASER2_STEREO = :phaser2_stereo # ef.phaser2_stereo(...)
|
|
274
|
+
WAH4 = :wah4 # ef.wah4(fr)
|
|
275
|
+
AUTO_WAH = :auto_wah # ef.auto_wah(level)
|
|
276
|
+
CRYBABY = :crybaby # ef.crybaby(wah)
|
|
277
|
+
VOCODER = :vocoder # ef.vocoder(bands, range)
|
|
278
|
+
SPEAKERBP = :speakerbp # ef.speakerbp(flo, fhi)
|
|
279
|
+
DRY_WET_MIXER = :dry_wet_mixer # ef.dryWetMixer(mix)
|
|
280
|
+
DRY_WET_MIXER_CP = :dry_wet_mixer_cp # ef.dryWetMixerConstantPower(mix)
|
|
281
|
+
|
|
282
|
+
# === Metadata ===
|
|
283
|
+
DECLARE = :declare
|
|
284
|
+
end
|
|
285
|
+
end
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "ir"
|
|
4
|
+
require_relative "emitter"
|
|
5
|
+
|
|
6
|
+
module Ruby2Faust
|
|
7
|
+
# Live reload support: graph diffing, compilation, crossfade.
|
|
8
|
+
module Live
|
|
9
|
+
module_function
|
|
10
|
+
|
|
11
|
+
# Check if two graphs have different structure
|
|
12
|
+
#
|
|
13
|
+
# @param old_graph [DSP, Node] Previous graph
|
|
14
|
+
# @param new_graph [DSP, Node] New graph
|
|
15
|
+
# @return [Boolean] True if structures differ
|
|
16
|
+
def changed?(old_graph, new_graph)
|
|
17
|
+
old_node = old_graph.is_a?(DSP) ? old_graph.node : old_graph
|
|
18
|
+
new_node = new_graph.is_a?(DSP) ? new_graph.node : new_graph
|
|
19
|
+
!old_node.same_structure?(new_node)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Compile a DSP graph to a Faust file
|
|
23
|
+
#
|
|
24
|
+
# @param graph [DSP] The DSP graph to compile
|
|
25
|
+
# @param output [String] Output file path (.dsp)
|
|
26
|
+
# @param imports [Array<String>] Libraries to import
|
|
27
|
+
# @return [String] Path to the output file
|
|
28
|
+
def compile(graph, output:, imports: Emitter::DEFAULT_IMPORTS)
|
|
29
|
+
code = Emitter.program(graph, imports: imports)
|
|
30
|
+
File.write(output, code)
|
|
31
|
+
output
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Generate crossfade DSP code for smooth transitions
|
|
35
|
+
# Creates a Faust program that crossfades between old and new DSP
|
|
36
|
+
#
|
|
37
|
+
# @param old_process [String] Faust expression for old process
|
|
38
|
+
# @param new_process [String] Faust expression for new process
|
|
39
|
+
# @param duration [Float] Crossfade duration in seconds (default 0.05)
|
|
40
|
+
# @return [String] Faust source with crossfade
|
|
41
|
+
def crossfade_dsp(old_process, new_process, duration: 0.05)
|
|
42
|
+
<<~FAUST
|
|
43
|
+
import("stdfaust.lib");
|
|
44
|
+
|
|
45
|
+
// Crossfade envelope
|
|
46
|
+
xfade = hslider("xfade", 0, 0, 1, 0.001) : si.smoo;
|
|
47
|
+
|
|
48
|
+
// Old and new processes
|
|
49
|
+
old = #{old_process};
|
|
50
|
+
new = #{new_process};
|
|
51
|
+
|
|
52
|
+
// Crossfade: (1-x)*old + x*new
|
|
53
|
+
process = old * (1 - xfade), new * xfade :> _;
|
|
54
|
+
FAUST
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Run the Faust compiler on a .dsp file
|
|
58
|
+
# Requires faust to be in PATH
|
|
59
|
+
#
|
|
60
|
+
# @param dsp_file [String] Path to .dsp file
|
|
61
|
+
# @param target [Symbol] Compilation target (:wasm, :cpp, :llvm)
|
|
62
|
+
# @param output_dir [String] Output directory (default: same as input)
|
|
63
|
+
# @return [Boolean] True if compilation succeeded
|
|
64
|
+
def faust_compile(dsp_file, target: :cpp, output_dir: nil)
|
|
65
|
+
output_dir ||= File.dirname(dsp_file)
|
|
66
|
+
basename = File.basename(dsp_file, ".dsp")
|
|
67
|
+
|
|
68
|
+
cmd = case target
|
|
69
|
+
when :wasm
|
|
70
|
+
"faust2wasm #{dsp_file} -o #{output_dir}/#{basename}.wasm"
|
|
71
|
+
when :cpp
|
|
72
|
+
"faust -a minimal.cpp #{dsp_file} -o #{output_dir}/#{basename}.cpp"
|
|
73
|
+
when :llvm
|
|
74
|
+
"faust -lang llvm #{dsp_file} -o #{output_dir}/#{basename}.ll"
|
|
75
|
+
else
|
|
76
|
+
raise ArgumentError, "Unknown target: #{target}"
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
system(cmd)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
data/lib/ruby2faust.rb
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "ruby2faust/version"
|
|
4
|
+
require_relative "ruby2faust/ir"
|
|
5
|
+
require_relative "ruby2faust/dsl"
|
|
6
|
+
require_relative "ruby2faust/emitter"
|
|
7
|
+
require_relative "ruby2faust/live"
|
|
8
|
+
|
|
9
|
+
module Ruby2Faust
|
|
10
|
+
class Error < StandardError; end
|
|
11
|
+
|
|
12
|
+
# Convenience method to generate Faust code from a block
|
|
13
|
+
#
|
|
14
|
+
# @example
|
|
15
|
+
# code = Ruby2Faust.generate do
|
|
16
|
+
# osc(440).then(gain(0.3))
|
|
17
|
+
# end
|
|
18
|
+
#
|
|
19
|
+
# @yield Block that returns a DSP
|
|
20
|
+
# @return [String] Faust source code
|
|
21
|
+
def self.generate(pretty: false, &block)
|
|
22
|
+
context = Object.new
|
|
23
|
+
context.extend(DSL)
|
|
24
|
+
process = context.instance_eval(&block)
|
|
25
|
+
Emitter.program(process, pretty: pretty)
|
|
26
|
+
end
|
|
27
|
+
end
|