faust2ck - A FAUST wrapper-generator for ChucK Ugens. ======== FAUST is a functional language for defining DSP structures that can be used for real-time audio computing. It can generate block diagrams and also C++ code which will execute the given routine. This is an ideal way to create unit generators (UGens) for ChucK. The ChucK data structures can be a bit weird and the learning curve for hacking on ChucK can be a little steap. Using FAUST, you can concentrate on the DSP algorithm, and allow faust2ck to take care of creating ChucK-compatible C++ code so that you can instantiate your new DSP object in a ChucK program. That said, in order to take advantage of faust2ck, you'll need a little C++ knowledge. At least enough to know how to compile ChucK and add a line or two to its source code, which is explained in this document. Please see the FAUST websites for more details, online tutorials, and even an online compiler that you can try: http://faust.grame.fr/ http://sourceforge.net/projects/faudiostream/ This document describes how to use faust2ck. Usage ===== Running faust2ck with no parameters will give you a usage string: $ ./faust2ck Usage: faust2ck You can see that it requires a single input, the name of an XML file. This XML file can be generated with FAUST by giving it the "-xml" parameter: $ faust -xml filename.dsp faust2ck will then generate a file called "filename.dsp-wrapper.cpp". (Where "filename" is whatever your file is called.) This program is yet another input for FAUST. It is a C++ file, but it has some tags in it that tell FAUST where to put the DSP code. So you must run FAUST again, this time telling it to use the wrapper: $ faust -a filename.dsp-wrapper.cpp -o filename.cpp filename.dsp This gives you a final C++ file containing the algorithm along with routines necessary to plug it in to ChucK. Adding to ChucK --------------- Unfortunately there are a couple of manual steps necessary here. ChucK does not currently support (rightly in my opinion) loadable external modules, like other audio programs such as PureData. So, you need to re-compile ChucK. First, we need to tell it about our new C++ file. The first thing to do is create a header file. It only needs to contain one line of code, enough to tell ChucK about a single function declared in the C++ file. If your DSP object is called 'mydsp', then your file, mydsp.h, should look like this: #ifndef __MYDSP_H__ #define __MYDSP_H__ DLL_QUERY mydsp_query( Chuck_DL_Query * QUERY ); #endif // __MYDSP_H__ Put this file in the ChucK source directory. Now you just have to add a few lines to the file, 'chuck_compile.cpp'. At the top of chuck_compile.cpp, add an #include statement for our header file: #include "mydsp.h" Now, find the function "load_internal_modules()", which should be around line 516. Add the necessary code to load your new module, which is done by passing ChucK the "mydsp_query" function, where "mydsp" is the name of your FAUST object. EM_log( CK_LOG_SEVERE, "class 'mydsp'..." ); if( !load_module( env, mydsp_query, "mydsp", "global" ) ) goto error; That's it! The last thing to do is to add your C++ file to the makefile so that it gets compiled along with ChucK. Depending on which operating system you are using, load "makefile.alsa" for Linux, or "makefile.win32" for Windows, or "makefile.osx" for OS X, etc. There is a big list of filenames in a variable called OBJS. You need to add "mydsp.o" to this list. Lastly, you need to add a build target for mydsp.o to the bottom of the file: mydsp.o: mydsp.h mydsp.cpp $(CXX) $(FLAGS) mydsp.cpp Now, run "make", and if all goes well, ChucK will be built with your object. $ make Time to create a test program! Make a simple ChucK program that looks something like the following: Impulse i => mydsp m => dac; 100 => t; 1 => i.next; while (t>0) { <<>>; t--; samp => now; } This will print the impulse response your object to the console. Good luck! Have fun! If you have any questions you can email me at: Stephen Sinclair License ======= This code is licensed under the GNU General Public License (GPL) v2 or above. Please see COPYING for details, or visit: http://www.gnu.org/licenses/old-licenses/gpl-2.0.html Technical ========= You wrote a string parsing and XML reading program like this, in 2008, in C ???! Are you mad? -------------------------------------------------- Yes, a sane person would probably not do such a thing. The FAUST XML output is so simple, however, and generally predictable, that I didn't feel it was necessary to bring in a lot of external dependencies for such a small application. So I ended up basically writing a small, inadequate macro replacement parser, which was actually kind of fun. Of course, if it ever called for more a complex solution I'd probably re-do it in Perl or Python. But this'll do for now, and it means the program is tiny and easy to execute on any computer without installing huge libraries. Of course, it does depend on 'sed' to generate the template for inclusion in the final program. I wanted to include it right in the executable instead of having faust2ck load an external file that would always be exactly the same. Unfortunately the C preprocessor doesn't make it easy, so sed is just used to wrap lines of the template file in double-quotes ("") and backslash-escape a few special characters. faust2ck is distributed with the template already generated so that it can be compiled without needing to have sed around. Why generate the C++ wrapper on demand instead of just writing a better wrapper that doesn't need to change? ---------------------------------------------------------------- If you look at the FAUST wrappers for PureData and Max/MSP, for example, they are just a single file where FAUST replaces a certain line with the DSP code class. Unfortunately FAUST doesn't provide much for dynamically generating code from the wrapper, other than a single replacement within the file where it provides the DSP class. Specifically, for parameters (declared in FAUST with "UI" elements like hslider and vslider), it calls a function at run-time to inform the UI code about each parameter. In the case of PureData and Max/MSP, it's possible to instantiate parameters (inlets) at run-time, so this works well. In ChucK however, each parameter needs a getter and setting function, called "cget" and "ctrl", which must be made available statically at compile time. (This isn't strictly true, these functions are "linked in" to the rest of ChucK at run-time, but ChucK doesn't provide the function with enough information to figure out which parameter it's modifying, so each parameter needs its own function.) Now, before writing faust2ck, I tried very hard to come up with some kind of combination of preprocessor macros or C++ templates that would do the job of instantiating a cget and ctrl function for each parameter, but I just couldn't figure out a way to do it. If anyone wants to give it a try, it's a nice challenging problem. In any case, I realized I was wasting my time when a quick little program could generate the appropriate wrapper easily enough, hence faust2ck.