Faust Audio DSP Language for JUCE Adrien Albouy, Stéphane Letz
Total Page:16
File Type:pdf, Size:1020Kb
Faust audio DSP language for JUCE Adrien Albouy, Stéphane Letz To cite this version: Adrien Albouy, Stéphane Letz. Faust audio DSP language for JUCE. Linux Audio Conference, 2017, Saint-Etienne, France. pp.61-68. hal-02158740 HAL Id: hal-02158740 https://hal.archives-ouvertes.fr/hal-02158740 Submitted on 18 Jun 2019 HAL is a multi-disciplinary open access L’archive ouverte pluridisciplinaire HAL, est archive for the deposit and dissemination of sci- destinée au dépôt et à la diffusion de documents entific research documents, whether they are pub- scientifiques de niveau recherche, publiés ou non, lished or not. The documents may come from émanant des établissements d’enseignement et de teaching and research institutions in France or recherche français ou étrangers, des laboratoires abroad, or from public or private research centers. publics ou privés. Faust audio DSP language for JUCE Adrien ALBOUY and Stéphane Letz GRAME 11, cours de Verdun (GENSOUL) 69002 LYON, FRANCE, {adrien.albouy, letz}@grame.fr Abstract automatic parallelization and take advantage of Faust [Functional Audio Stream] is a functional multicore machines. programming language specifically designed for real- The generated code can generally compete time signal processing and synthesis [1]. It consists with, and sometimes even outperform, C++ of a compiler that translates a Faust program into code written by seasoned programmers. It an equivalent C++ program, taking care of generat- works at the sample level, it is therefore suited ing the most efficient code. JUCE is an open-source to implement low-level DSP functions like recur- cross-platform C++ application framework devel- 1 sive filters up to fullscale audio applications. It oped since 2004, and bought by ROLI in Novem- can be easily embedded as it is selfcontained and ber 2014, used for the development of desktop and mobile applications. A new feature to the Faust does not depend of any DSP library or runtime environnement is the addition of architectures files system. Moreover it has a very deterministic to provide the glue between the Faust C++ output behavior and a constant memory footprint. and the JUCE framework. This article presents the Being a specification language the Faust overall design of the architecture files for JUCE. code says nothing about the audio drivers or the GUI toolkit to be used. It is the role of Keywords the architecture file to describe how to relate JUCE, Faust, Domain Specific Language, DSP, the DSP code to the external world [2]. This real-time, audio approach allows a single Faust program to be easily deployed to a large variety of audio stan- 1 Introduction dards (Max-MSP externals, PD externals, VST plugins, CoreAudio applications, JACK appli- From a technical point of view Faust2 (Func- tional Audio Stream) is a functional, syn- cations, etc.), and JUCE is now supported. chronous, domain specific language designed for The aim of JUCE[3] is to allow software to be real-time signal processing and synthesis. A written such that the same source code will com- unique feature of Faust, compared to other ex- pile and run identically on Windows, Mac OS X, isting languages like Max, PD, Supercollider, Linux platforms for the desktop devices, and on etc., is that programs are not interpreted, but Android and iOS for the mobile ones. A notable fully compiled. feature of JUCE when compared to other similar frameworks is its large set of audio functional- One can think of Faust as a specification lan- guage. It aims at providing the user with an ity. Those services, the user-interface possibil- adequate notation to describe signal processors ities and the multi-platform exportability posi- from a mathematical point of view. This spec- tion JUCE as a great framework for Faust to ification is free, as much as possible, from im- get exported on, to have in the future less code to maintain up-to-date, and simpler utilization. plementation details. It is the role of the Faust compiler to provide automatically the best pos- In section 2, the idea and the use of the GUI sible implementation. The compiler translates architecture file will be introduced. In section Faust programs into equivalent C++ programs 3, the JUCE Component hierarchy will be pre- taking care of generating the most efficient code. sented without going into many details. Sec- The compiler offers various options to control tion 4 is the main one, explaining in detail the the generated code, including options to do fully graphical architecture file for JUCE. MIDI and OSC architecture files are introduced in Section 1https://roli.com/ 5. Section 6 will treat of the "glue" between 2http://faust.grame.fr JUCE audio layers and Faust ones. Section 7 presents the faust2juce script. Section 8 is a support the following methods to setup this hi- quick tutorial on how to use JUCE for Faust. erarchy: 2 Faust GUI architecture files openTabBox (const char* label) A Faust UI architecture is a glue between a openHorizontalBox (const char* label) host control layer and a Faust module. It is openVerticalBox (const char* label) responsible to associate a Faust module pa- closeBox (const char* label) rameter to a user interface element and to up- Note that all the widgets are added to the cur- date the parameter value according to the user rent box. actions. This association is triggered by the dsp::buildUserInterface call, where the DSP 2.4 Metadata asks a UI object to build the module controllers. The Faust language allows widget labels Since the interface is basically graphic ori- to contain metadata enclosed in square ented, the main concepts are widget based: a brackets. These metadata are handled UI architecture is semantically oriented to han- at UI level by a declare method taking dle active widgets, passive widgets and widgets as argument, a pointer to the widget as- layout. sociated value, the metadata key and value: A Faust UI architecture derives a UI class, declare(float*, const char*, const char*). containing active widgets, passive widgets, lay- Metadata can also declare a DSP as polyphonic, out widgets, and metadata. with a line looking like declare nvoices "8" 2.1 Active widgets for 8 voices. This will always output a poly- phonic DSP, either you use the polyphonic Active widgets are graphical elements that option of the compiler or not. This number of control a parameter value. They are initialized voices can be changed with the compiler (cf. with the widget name and a pointer to the Section 7). linked value. The widget currently considered For instance, if the program needs a Slider are Button, ToggleButton, CheckButton, to be a Knob, those lines are written: RadioButton, Menu, VerticalSlider, HorizontalSlider, Knob and NumEntry. declare(&fVslider0, "style", "knob"); A UI architecture must implement a method addVerticalSlider("Vol", &fVslider0,...); addxxx (const char* name, float* zone, ...) for each active widget. Additional param- The can be a , , etc... de- eters are available to Slider, Knob, NumEntry, style knob menu pending on the program. RadioButton and Menu: the init value, the min and max values and the step (RadioButton, Multiple aspects of the items can be described Menu and Knob being special kind of Sliders, with the metadata, such as the type of the item cf. subsection 2.4, Metadata). just as seen before, the tooltip of the item, the unit, etc... 2.2 Passive widget Passive widgets are graphical elements that 3 JUCE Component class reflect values. Similarly to active widgets, To implement a complete program, the graph- they are initialized with the widget name and ical elements described in the previous section a pointer to the linked value. The widget need to be combined with JUCE classes. In the currently considered are NumDisplay, Led, JUCE Framework, the component class is the HorizontalBarGraph and VerticalBarGraph. base-class for all JUCE user-interface objects. A UI architecture must implement a method The following section explains the relationship addxxx (const char* name, float* zone, between Faust GUI architecture files, and the ...) for each passive widget. Additional JUCE mechanics. parameters are available, depending on the 3.1 Parent and child mechanics passive widget type. (NumDisplay and Led are a special kind of BarGraph, cf. Subsection 2.4). As most frameworks have, JUCE has a hi- erarchy of Component objects, organized in a 2.3 Widget layout tree structure. The common way to set a Generally, a UI is hierarchically organized into Component as child of another component is boxes and/or tab boxes. A UI architecture must to do parent->addAndMakeVisible(child);. This function sets the child component as vis- be adapted to the Juce::Component mechanics ible too, because it’s not by default. Multi- in an architecture file called JuceGUI.h. The ple functionalities are accessible to run through following section discusses annotated examples. this Component tree, with methods that give the child Component at index i, or give the parent. 4.1 Two different kinds of objects There’s even a function allowing to get the par- There are two kinds of object used in the adap- ent of a Component with a specific type, this type tation: being a derived class of Juce::Component. How- ever, this function does not exist for the child, • uiComponent, which are basically any items and imply that dynamic_cast has to be done if of the Faust program, like sliders or but- you want to get a child of a certain type. tons. 3.2 Component setup mechanics • uiBox, which is container component, and First of all, a Component is drawn if it’s visi- so can contain a uiComponent or some oth- ble, and its parent too. If a Component is not ers uiBox. visible, its child and all of its children, etc..