1 /************************************************************************
2  FAUST Architecture File
3  Copyright (C) 2021 GRAME, Centre National de Creation Musicale
4  ---------------------------------------------------------------------
5  This Architecture section is free software; you can redistribute it
6  and/or modify it under the terms of the GNU General Public License
7  as published by the Free Software Foundation; either version 3 of
8  the License, or (at your option) any later version.
9 
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  GNU General Public License for more details.
14 
15  You should have received a copy of the GNU General Public License
16  along with this program; If not, see <http://www.gnu.org/licenses/>.
17 
18  EXCEPTION : As a special exception, you may create a larger work
19  that contains this FAUST architecture section and distribute
20  that work under terms of your choice, so long as this FAUST
21  architecture section is not modified.
22 
23  ************************************************************************/
24 
25 #include <libgen.h>
26 #include <iostream>
27 #include <string>
28 #include <vector>
29 
30 #include "faust/dsp/libfaust-box.h"
31 #include "faust/dsp/llvm-dsp.h"
32 #include "faust/dsp/interpreter-dsp.h"
33 #include "faust/dsp/poly-dsp.h"
34 #include "faust/audio/jack-dsp.h"
35 #include "faust/gui/GTKUI.h"
36 #include "faust/gui/MidiUI.h"
37 #include "faust/misc.h"
38 
39 using namespace std;
40 
41 /**
42  * Return the current runtime sample rate.
43  *
44  * Reproduce the 'SR' definition in platform.lib: SR = min(192000.0, max(1.0, fconstant(int fSamplingFreq, <math.h>)));
45  *
46  * @return the current runtime sample rate.
47  */
getSampleRate()48 inline Box getSampleRate()
49 {
50     return boxMin(boxReal(192000.0), boxMax(boxReal(1.0), boxFConst(SType::kSInt, "fSamplingFreq", "<math.h>")));
51 }
52 
53 /**
54  * Return the current runtime buffer size.
55  *
56  * Reproduce the 'BS' definition in platform.lib: BS = fvariable(int count, <math.h>);
57  *
58  * @return the current runtime buffer size.
59  */
getBufferSize()60 inline Box getBufferSize()
61 {
62     return boxFVar(SType::kSInt, "count", "<math.h>");
63 }
64 
65 #define COMPILER(exp)    \
66 {                        \
67     createLibContext();  \
68     exp                  \
69     destroyLibContext(); \
70 }                        \
71 
compile(const string & name,Box box,int argc=0,const char * argv[]=nullptr)72 static void compile(const string& name, Box box, int argc = 0, const char* argv[] = nullptr)
73 {
74     string error_msg;
75     dsp_factory_base* factory = createCPPDSPFactoryFromBoxes(name, box, argc, argv, error_msg);
76     if (factory) {
77         factory->write(&cout);
78         delete(factory);
79     } else {
80         cerr << error_msg;
81     }
82 }
83 
84 // process = 7,3.14;
85 
test1()86 static void test1()
87 {
88     COMPILER
89     (
90         Box box = boxPar(boxInt(7), boxReal(3.14));
91 
92         compile("test1", box);
93     )
94 }
95 
96 // process = _,3.14 : +;
97 
test2()98 static void test2()
99 {
100     COMPILER
101     (
102         Box box = boxSeq(boxPar(boxWire(), boxReal(3.14)), boxAdd());
103 
104         compile("test2", box);
105     )
106 }
107 
108 // Alternate version with the binary 'boxAdd' version
109 // process = +(_,3.14);
110 
test3()111 static void test3()
112 {
113     COMPILER
114     (
115         Box box = boxAdd(boxWire(), boxReal(3.14));
116 
117         compile("test3", box);
118     )
119 }
120 
121 
122 // process = _,_ : +;
123 
test4()124 static void test4()
125 {
126     COMPILER
127     (
128         Box box = boxSeq(boxPar(boxWire(), boxWire()), boxAdd());
129 
130         compile("test4", box);
131     )
132 }
133 
134 // Connection error
135 // process = _ : +;
136 
test5()137 static void test5()
138 {
139     COMPILER
140     (
141         Box box = boxSeq(boxWire(), boxMul());
142 
143         compile("test5", box);
144     )
145 }
146 
147 // process = @(_,7);
148 
test6()149 static void test6()
150 {
151     COMPILER
152     (
153         Box box = boxDelay(boxWire(), boxInt(7));
154 
155         compile("test6", box);
156     )
157 }
158 
159 // process = @(_,7);
160 
test7()161 static void test7()
162 {
163     createLibContext();
164     Box box = boxDelay(boxWire(), boxInt(7));
165 
166     compile("test7", box, 3, (const char* []){ "-vec", "-lv", "1" });
167     destroyLibContext();
168 }
169 
170 
171 // process = _ <: @(500) + 0.5, @(3000) * 1.5;
172 
test8()173 static void test8()
174 {
175     COMPILER
176     (
177         Box box = boxSplit(boxWire(), boxPar(boxAdd(boxDelay(boxWire(), boxReal(500)), boxReal(0.5)),
178                                              boxMul(boxDelay(boxWire(), boxReal(3000)), boxReal(1.5))));
179 
180         compile("test8", box);
181     )
182 }
183 
184 // Equivalent signal expressions
185 
equivalent1()186 static void equivalent1()
187 {
188     COMPILER
189     (
190          Box b1 = boxAdd(boxDelay(boxWire(), boxReal(500)), boxReal(0.5));
191          Box box = boxPar(b1, b1);
192 
193          compile("equivalent1", box);
194     )
195 }
196 
equivalent2()197 static void equivalent2()
198 {
199     COMPILER
200     (
201         Box box = boxPar(boxAdd(boxDelay(boxWire(), boxReal(500)), boxReal(0.5)),
202                          boxAdd(boxDelay(boxWire(), boxReal(500)), boxReal(0.5)));
203 
204         compile("equivalent2", box);
205     )
206 }
207 
208 // process = _,hslider("Freq [midi:ctrl 7][style:knob]", 100, 100, 2000, 1) : *;
209 
test9()210 static void test9()
211 {
212     COMPILER
213     (
214         Box box = boxMul(boxWire(), boxHSlider("Freq [midi:ctrl 7][style:knob]", boxReal(100), boxReal(100), boxReal(2000), boxReal(1)));
215 
216         compile("test9", box);
217     )
218 }
219 
220 /*
221  import("stdfaust.lib");
222 
223  freq = vslider("h:Oscillator/freq", 440, 50, 1000, 0.1);
224  gain = vslider("h:Oscillator/gain", 0, 0, 1, 0.01);
225 
226  process = freq * gain;
227  */
228 
test10()229 static void test10()
230 {
231     COMPILER
232     (
233         Box box = boxMul(boxVSlider("h:Oscillator/freq", boxReal(440), boxReal(50), boxReal(1000), boxReal(0.1)),
234                          boxVSlider("h:Oscillator/gain", boxReal(0), boxReal(0), boxReal(1), boxReal(0.01)));
235 
236         compile("test10", box);
237     )
238 }
239 
240 // import("stdfaust.lib");
241 // process = ma.SR, ma.BS;
242 
test11()243 static void test11()
244 {
245     COMPILER
246     (
247         Box box = boxPar(getSampleRate(), getBufferSize());
248 
249         compile("test11", box);
250     )
251 }
252 
253 // process = waveform { 0, 100, 200, 300, 400 };
254 
test12()255 static void test12()
256 {
257     COMPILER
258     (
259          tvec waveform;
260          // Fill the waveform content vector
261          for (int i = 0; i < 5; i++) {
262              waveform.push_back(boxReal(100*i));
263          }
264          Box box = boxWaveform(waveform);   // the waveform content
265 
266          compile("test12", box);
267     )
268 }
269 
270 // process = _ <: +;
271 
test13()272 static void test13()
273 {
274     COMPILER
275     (
276         Box box = boxSplit(boxWire(), boxAdd());
277 
278         compile("test13", box);
279      )
280 }
281 
282 // process = _,_ <: !,_,_,! :> _,_;
283 
test14()284 static void test14()
285 {
286     COMPILER
287     (
288         Box box = boxSplit(boxPar(boxWire(), boxWire()),
289                            boxMerge(boxPar4(boxCut(), boxWire(), boxWire(), boxCut()),
290                                     boxPar(boxWire(), boxWire())));
291 
292         compile("test14", box);
293     )
294 }
295 
296 // process = + ~ _;
297 
test15()298 static void test15()
299 {
300     COMPILER
301     (
302         Box box = boxRec(boxAdd(), boxWire());
303 
304         compile("test15", box);
305     )
306 }
307 
308 /*
309 import("stdfaust.lib");
310 process = phasor(440)
311 with {
312     decimalpart = _,int(_) : -;
313     phasor(f) = f/ma.SR : (+ <: decimalpart) ~ _;
314 };
315 */
316 
decimalpart()317 static Box decimalpart()
318 {
319     return boxSub(boxWire(), boxIntCast(boxWire()));
320 }
321 
phasor(Box f)322 static Box phasor(Box f)
323 {
324     return boxSeq(boxDiv(f, getSampleRate()), boxRec(boxSplit(boxAdd(), decimalpart()), boxWire()));
325 }
326 
test16()327 static void test16()
328 {
329     COMPILER
330     (
331         Box box = phasor(boxReal(440));
332 
333         compile("test16", box);
334     )
335 }
336 
337 /*
338  import("stdfaust.lib");
339  process = osc(440), osc(440)
340  with {
341     decimalpart(x) = x-int(x);
342     phasor(f) = f/ma.SR : (+ : decimalpart) ~ _;
343     osc(f) = sin(2 * ma.PI * phasor(f));
344  };
345  */
346 
osc(Box f)347 static Box osc(Box f)
348 {
349     return boxSin(boxMul(boxMul(boxReal(2.0), boxReal(3.141592653)), phasor(f)));
350 }
351 
test17()352 static void test17()
353 {
354     COMPILER
355     (
356         Box box = boxPar(osc(boxReal(440)), osc(boxReal(440)));
357 
358         compile("test17", box);
359     )
360 }
361 
362 // process = 0,0 : soundfile("sound[url:{'tango.wav'}]", 2);
363 
test18()364 static void test18()
365 {
366     COMPILER
367     (
368         Box box = boxSoundfile("sound[url:{'tango.wav'}]", boxInt(2),  boxInt(0),  boxInt(0));
369 
370         compile("test18", box);
371     )
372 }
373 
374 // process = 10,1,int(_) : rdtable;
375 
test19()376 static void test19()
377 {
378     COMPILER
379     (
380         Box box = boxReadOnlyTable(boxInt(10), boxInt(1), boxIntCast(boxWire()));
381 
382         compile("test19", box);
383     )
384 }
385 
386 // process = 10,1,int(_),int(_),int(_) : rwtable;
387 
test20()388 static void test20()
389 {
390     COMPILER
391     (
392         Box box = boxWriteReadTable(boxInt(10), boxInt(1), boxIntCast(boxWire()), boxIntCast(boxWire()), boxIntCast(boxWire()));
393 
394         compile("test20", box);
395     )
396 }
397 
398 /*
399  import("stdfaust.lib");
400  process = osc(f1), osc(f2)
401  with {
402      decimalpart(x) = x-int(x);
403      phasor(f) = f/ma.SR : (+ : decimalpart) ~ _;
404      osc(f) = sin(2 * ma.PI * phasor(f));
405      f1 = vslider("Freq1", 300, 100, 2000, 0.01);
406      f2 = vslider("Freq2", 500, 100, 2000, 0.01);
407  };
408  */
409 
410 // Using the LLVM backend.
test21(int argc,char * argv[])411 static void test21(int argc, char* argv[])
412 {
413     createLibContext();
414     {
415         Box sl1 = boxVSlider("h:Oscillator/Freq1", boxReal(300), boxReal(100), boxReal(2000), boxReal(0.01));
416         Box sl2 = boxVSlider("h:Oscillator/Freq2", boxReal(300), boxReal(100), boxReal(2000), boxReal(0.01));
417         Box box = boxPar(osc(sl1), osc(sl2));
418 
419         string error_msg;
420         llvm_dsp_factory* factory = createDSPFactoryFromBoxes("FaustDSP", box, 0, nullptr, "", error_msg);
421 
422         if (factory) {
423             dsp* dsp = factory->createDSPInstance();
424             assert(dsp);
425 
426             // Allocate audio driver
427             jackaudio audio;
428             audio.init("Test", dsp);
429 
430             // Create GUI
431             GTKUI gtk_ui = GTKUI((char*)"Organ", &argc, &argv);
432             dsp->buildUserInterface(&gtk_ui);
433 
434             // Start real-time processing
435             audio.start();
436 
437             // Start GUI
438             gtk_ui.run();
439 
440             // Cleanup
441             audio.stop();
442             delete dsp;
443             deleteDSPFactory(factory);
444         } else {
445             cerr << "Cannot create factory" << error_msg << endl;
446         }
447     }
448     destroyLibContext();
449 }
450 
451 // Using the Interpreter backend.
test22(int argc,char * argv[])452 static void test22(int argc, char* argv[])
453 {
454     createLibContext();
455     {
456         Box sl1 = boxHSlider("v:Oscillator/Freq1", boxReal(300), boxReal(100), boxReal(2000), boxReal(0.01));
457         Box sl2 = boxHSlider("v:Oscillator/Freq2", boxReal(300), boxReal(100), boxReal(2000), boxReal(0.01));
458         Box box = boxPar(osc(sl1), osc(sl2));
459 
460         string error_msg;
461         interpreter_dsp_factory* factory = createInterpreterDSPFactoryFromBoxes("FaustDSP", box, 0, nullptr, error_msg);
462 
463         if (factory) {
464             dsp* dsp = factory->createDSPInstance();
465             assert(dsp);
466 
467             // Allocate audio driver
468             jackaudio audio;
469             audio.init("Test", dsp);
470 
471             // Create GUI
472             GTKUI gtk_ui = GTKUI((char*)"Organ", &argc, &argv);
473             dsp->buildUserInterface(&gtk_ui);
474 
475             // Start real-time processing
476             audio.start();
477 
478             // Start GUI
479             gtk_ui.run();
480 
481             // Cleanup
482             audio.stop();
483             delete dsp;
484             deleteInterpreterDSPFactory(factory);
485         } else {
486             cerr << "Cannot create factory" << error_msg << endl;
487         }
488     }
489     destroyLibContext();
490 }
491 
492 // Using the Interpreter backend.
test23(int argc,char * argv[])493 static void test23(int argc, char* argv[])
494 {
495     interpreter_dsp_factory* factory = nullptr;
496     string error_msg;
497 
498     createLibContext();
499     {
500         Box sl1 = boxHSlider("v:Oscillator/Freq1", boxReal(300),
501                              boxReal(100), boxReal(2000), boxReal(0.01));
502         Box sl2 = boxHSlider("v:Oscillator/Freq2", boxReal(300),
503                              boxReal(100), boxReal(2000), boxReal(0.01));
504         Box box = boxPar(osc(sl1), osc(sl2));
505 
506         // Compile the 'bo'x to 'signals'
507         tvec signals = boxesToSignals(box, error_msg);
508 
509         // Then compile the 'signals' to a DSP factory
510         factory = createInterpreterDSPFactoryFromSignals("FaustDSP",
511                                                          signals, 0,
512                                                          nullptr, error_msg);
513     }
514     destroyLibContext();
515 
516     // Use factory outside of the createLibContext/destroyLibContext scope
517     if (factory) {
518         dsp* dsp = factory->createDSPInstance();
519         assert(dsp);
520 
521         // Allocate audio driver
522         jackaudio audio;
523         audio.init("Test", dsp);
524 
525         // Create GUI
526         GTKUI gtk_ui = GTKUI("Organ", &argc, &argv);
527         dsp->buildUserInterface(&gtk_ui);
528 
529         // Start real-time processing
530         audio.start();
531 
532         // Start GUI
533         gtk_ui.run();
534 
535         // Cleanup
536         audio.stop();
537         delete dsp;
538         deleteInterpreterDSPFactory(factory);
539     } else {
540         cerr << error_msg;
541     }
542 }
543 
544 /*
545  import("stdfaust.lib");
546  process = organ, organ
547  with {
548      decimalpart(x) = x-int(x);
549      phasor(f) = f/ma.SR : (+ : decimalpart) ~ _;
550      osc(f) = sin(2 * ma.PI * phasor(f));
551      freq = nentry("freq", 100, 100, 3000, 0.01);
552      gate = button("gate");
553      gain = nentry("gain", 0.5, 0, 1, 0.01);
554      organ = gate * (osc(freq) * gain + osc(2 * freq) * gain);
555  };
556  */
557 
558 // Simple polyphonic DSP.
test24(int argc,char * argv[])559 static void test24(int argc, char* argv[])
560 {
561     interpreter_dsp_factory* factory = nullptr;
562     string error_msg;
563 
564     createLibContext();
565     {
566         // Follow the freq/gate/gain convention, see: https://faustdoc.grame.fr/manual/midi/#standard-polyphony-parameters
567         Box freq = boxNumEntry("freq", boxReal(100), boxReal(100), boxReal(3000), boxReal(0.01));
568         Box gate = boxButton("gate");
569         Box gain = boxNumEntry("gain", boxReal(0.5), boxReal(0), boxReal(1), boxReal(0.01));
570         Box organ = boxMul(gate, boxAdd(boxMul(osc(freq), gain), boxMul(osc(boxMul(freq, boxInt(2))), gain)));
571         // Stereo
572         Box box = boxPar(organ, organ);
573 
574         factory = createInterpreterDSPFactoryFromBoxes("FaustDSP", box, 0, nullptr, error_msg);
575     }
576     destroyLibContext();
577 
578     // Use factory outside of the createLibContext/destroyLibContext scope
579     if (factory) {
580         dsp* dsp = factory->createDSPInstance();
581         assert(dsp);
582 
583         // Allocate polyphonic DSP
584         dsp = new mydsp_poly(dsp, 8, true, true);
585 
586         // Allocate MIDI/audio driver
587         jackaudio_midi audio;
588         audio.init("Organ", dsp);
589 
590         // Create GUI
591         GTKUI gtk_ui = GTKUI((char*)"Organ", &argc, &argv);
592         dsp->buildUserInterface(&gtk_ui);
593 
594         // Create MIDI controller
595         MidiUI midi_ui = MidiUI(&audio);
596         dsp->buildUserInterface(&midi_ui);
597 
598         // Start real-time processing
599         audio.start();
600 
601         // Start MIDI
602         midi_ui.run();
603 
604         // Start GUI
605         gtk_ui.run();
606 
607         // Cleanup
608         audio.stop();
609         delete dsp;
610         deleteInterpreterDSPFactory(factory);
611     } else {
612         cerr << "Cannot create factory" << error_msg << endl;
613     }
614 }
615 
616 list<GUI*> GUI::fGuiList;
617 ztimedmap GUI::gTimedZoneMap;
618 
main(int argc,char * argv[])619 int main(int argc, char* argv[])
620 {
621     test1();
622     test2();
623     test3();
624     test4();
625     test5();
626     test6();
627     test7();
628     equivalent1();
629     equivalent2();
630     test8();
631     test9();
632     test10();
633     test11();
634     test12();
635     test13();
636     test14();
637     test15();
638     test16();
639     test17();
640     test18();
641     test19();
642     test20();
643 
644     // Test with audio, GUI and LLVM backend
645     test21(argc, argv);
646 
647     // Test with audio, GUI and Interp backend
648     test22(argc, argv);
649 
650     // Test with audio, GUI and Interp backend and using 'boxesToSignals' function
651     test23(argc, argv);
652 
653     // Test with audio, GUI, MIDI and Interp backend
654     test24(argc, argv);
655 
656     return 0;
657 }
658 
659