48
flext C++ programming layer for cross-platform development of PD and Max/MSP externals An introduction by Thomas Grill Abschlussarbeit des Lehrgangs Computermusik und elektronische Medien Institut für Komposition und Elektroakustik Universität für Musik und darstellende Kunst Wien

C++ programming layer for cross-platform development of - grrrr.org

  • Upload
    others

  • View
    3

  • Download
    0

Embed Size (px)

Citation preview

Page 1: C++ programming layer for cross-platform development of - grrrr.org

flext

C++ programming layer for cross-platform development of PD and Max/MSP externals

An introduction

by Thomas Grill

Abschlussarbeit des Lehrgangs Computermusik und elektronische Medien Institut für Komposition und Elektroakustik

Universität für Musik und darstellende Kunst Wien

Page 2: C++ programming layer for cross-platform development of - grrrr.org

flext - C++ programming layer for cross-platform development of PD and Max/MSP externals

page 2

Abstract flext1 seeks to represent a uniform programming interface for extending the most common modular real-time audio systems Max/MSP and Pure Data (PD) with external modules, or short externals. These modules provide a way to tailor such a system for one’s special needs and supply additional functionality. Source code based on flext is able to exploit nearly all features of the respective real-time framework while staying completely independent of the actual host system and platform (hardware and operating system). flext currently supports PD for Linux, Windows and OSX as well as Max/MSP for OS9 and OSX (and shortly Windows). Support for jMax under Linux, OSX and Windows and other systems can follow in the near future. flext stellt eine einheitliche Schnittstelle für die Erweiterung der verbreitetsten Echtzeit-Audio-Systeme Max/MSP und Pure Data (PD) mit externen Modulen, oder kurz externals, zur Verfügung. Solche Module bieten die Möglichkeit, diese Systeme für spezielle Anforderungen maßzuschneidern bzw. um zusätzliche Funktionalität zu erweitern. Programmcode, der auf flext basiert, kann beinahe alle Funktionen des jeweiligen Echtzeitsystems ausschöpfen und dennoch völlig unabhängig vom tatsächlichen eingesetzten Zielsystem oder der Plattform (Hardware und Betriebsystem) bleiben. flext unterstützt derzeit PD für Linux, Windows und OSX, sowie Max/MSP für OS9 und OSX (und in Kürze Windows). Unterstützung für jMax unter Linux, OSX und Windows und andere Systeme kann in der Zukunft folgen.

Page 3: C++ programming layer for cross-platform development of - grrrr.org

flext - C++ programming layer for cross-platform development of PD and Max/MSP externals

page 3

Contents Abstract ...................................................................................................................................... 2 Contents...................................................................................................................................... 3 I. Introduction ........................................................................................................................ 4 II. Basics ................................................................................................................................. 8

1. Prerequisites ................................................................................................................... 8 a) Build environments .................................................................................................... 9 b) Installing the real-time-system SDK ........................................................................ 10 c) Obtaining and installing the flext library.................................................................. 10 d) Programming in C++................................................................................................ 11 e) License issues ........................................................................................................... 11

2. Building blocks of a simple external............................................................................ 12 3. How to build flext-based externals............................................................................... 14

III. Examples ...................................................................................................................... 15 1. Simple message based externals .................................................................................. 15 2. More advanced message based externals ..................................................................... 18 3. Using attributes ............................................................................................................ 20 4. DSP (signal-based) externals ....................................................................................... 24 5. Using sample buffers.................................................................................................... 26 6. Using timers ................................................................................................................. 30 7. Binding to symbols....................................................................................................... 32 8. Building libraries of externals ...................................................................................... 35 9. Using threads................................................................................................................ 37 10. Interfacing to other DSP frameworks....................................................................... 40

a) STK .......................................................................................................................... 40 b) SndObj...................................................................................................................... 43

IV. Applications ................................................................................................................. 45 1. xsample......................................................................................................................... 45 2. VASP modular ............................................................................................................. 45 3. py/pyext........................................................................................................................ 45 4. others ............................................................................................................................ 46

Biography................................................................................................................................. 47 References ................................................................................................................................ 48

Page 4: C++ programming layer for cross-platform development of - grrrr.org

flext - C++ programming layer for cross-platform development of PD and Max/MSP externals

page 4

I. Introduction With the advent of powerful computers the task of producing and processing digital sounds has become less tiresome than it has been in the past. Traditionally, all operations on sampled audio material have been file-based, which means that a transformation step takes the audio data from a source file and produces a file consisting of the results of the operation. In the course of producing a piece of electronic music a lot of these steps are necessary, involving a large number of intermediate files and the related handling of data. In order to facilitate things for the user, all of these systems (should) have scripting capabilities, which means that a list of operations or more complex description of transformations and their parameters can be passed to the system (as a “batch file”), which then follows the instructions, processes the data and again produces an audio file as a result. Examples of such computer music environments are Csound2, CDP3 or fftbox/NMS44. Striving for increased user-friendliness two main advancements of dealing with these descriptions of transformation processes have crystallized:

• The functional approach: The “batch” scripting capabilities have been expanded into a fully-fledged and more or less general programming language. The transformation steps are represented by function-like unit generators (UGENs)a which can be combined into complex formulas. Parameters to the processes are variables of the script language. The most prominent example utilizing this methodology is SuperCollider5.

Figure I-1 – A relatively simple SuperCollider script with 8 parallel voices, with UGENs suffixed by .ar or .kr

• The visual approach:

Here, the transformation steps are visualized as boxes (modules) that are interconnected. Audio data is running through cables from one box outlet (as a data source) to another box inlet (data sink) and also the parameters controlling the transformations are passed as “messages” through similar box chains. Due to the intuitive handling and the shallow learning curve several systems have become popular which are mostly based on the work of Miller Puckette6 at IRCAM. These are the similar frameworks Pure Data (PD)7, Max/MSP8, jMax9 and not very different from them, Reaktor10.

a From the SuperCollider glossary: UGEN – An object that produces or processes audio data

Page 5: C++ programming layer for cross-platform development of - grrrr.org

flext - C++ programming layer for cross-platform development of PD and Max/MSP externals

page 5

Figure I-2 – A typical Max/MSP patch featuring some message and signal objects

and some graphical elements for user interaction.

In order to incorporate user input (which is made up of relatively slow parameter changes) or other transformation-controlling parameter values into the operation in an efficient manner, all of the newer systems have split their processing tasks into two levels:

• Calculation at control rate (the “message domain”): Most parameters controlling a transformation do not change very fast – a fact that can be exploited to speed up audio calculations because values steering an algorithm (like for example a filter formula) are known to be constant for a larger number of audio samples to be processed. The control rate is typically no more than about 1 kHz, which results in a time granularity of approximately 1 ms. This is supposed to be fast enough for typical non-audio data. Furthermore, the data flow at control rate is asynchronous, which means that data can be passed, but need not. This implies that a parameter value that is constant over a longer period of time need not be sent at every control tick but only when a change of the value occurs. The message domain is for most systems also capable of handling non-numerical data, as for PD, Max/MSP and jMax there are symbols (character strings that are internally cached) or chunks of numbers and symbols (so called lists).

• Calculation at signal rate (the “DSP domain”): The signal rate is related and in most cases equivalent to the sample rate of the audio hardware, although for most systems internal up- or downsampling of audio data is possible. Calculation in the DSP domain is in all major systems constrained to one data type (which typically is the IEEE 32-bit floating point format) which is handled most efficiently by the CPU. The signal rate is always a fixed integer (power of two) multiple of the control rate, that is, one block (vector) of audio data can be processed

Page 6: C++ programming layer for cross-platform development of - grrrr.org

flext - C++ programming layer for cross-platform development of PD and Max/MSP externals

page 6

while the steering parameters to the algorithm - determined at control rate - remain constant, leading to the above mentioned gains in efficiency. Modern CPUs featuring a SIMDb instruction set (like “AltiVec” on the PPC G4/G5, or “MMX” and “SSE” on the Intel processors) can be exploited to further speed up calculation. The data in the DSP domain is calculated synchronously – this means that each block of audio data is continually recalculated. The data can be considered to be constantly “streaming” through the module boxes and out of each signal box outlet into the next box inlet.

The processing tasks of these domains are further organized into modules, or objects, which can be simple (like the addition of two numbers) or complex (as is a Fourier transformation or a networking interface). Quite often a module serves both domains at a time by defining inlets and/or outlets for signals and receiving control values as messages.

Figure I-3 - The [*~ ] object in PD has a signal sent into its left inlet which is multiplied by a message value

received in the right inlet. The result of the operation is output as a signal again.

It is a good thing that most systems under examination (e.g. PD, Max/MSP, SuperCollider 3, Aura11, GStreamer12) can be extended with external modules (a.k.a. externals) which are made up of exactly the same building blocks as the functions or objects (internals) that the system itself provides. Hence, there is virtually no difference between externals and internals apart from the fact that the latter are delivered along with the system. Due to the fact that an external can be developed by anyone, numerous collections of extensions for the different real-time systems have evolved. There are really very few additional major elements inside a real-time system (and these are partly also realized by special internals): One is a core mechanism providing the organization of the several UGENs and their interconnection (as patches or scripts) as well as the formation of reusable building-blocks (abstractions or macros). Another point is the interfacing to the audio hardware and the scheduling involved to drive the DSP and message calculation just in time. Thirdly, the system manages resources that the transformation objects or functions can use, for example memory buffers containing sampled audio data or, less obvious, a pool of symbols for often-used messages etc. Additionally there may exist a graphical user interface for the patcher system and input of parameters or visualization of results, providing feedback for interactive operation.

b SIMD stands for „single instruction, multiple data” – one instruction to the CPU can calculate multiple instances of similar data

Page 7: C++ programming layer for cross-platform development of - grrrr.org

flext - C++ programming layer for cross-platform development of PD and Max/MSP externals

page 7

Figure I-4 – Typical GUI elements (sliders, bang, toggle button , radio buttons, numerical field, canvas) in PD

Typically, graphical elements can be created from within externals. They are not really special but just utilize the graphical interface functionality provided by the real-time system to visualize themselves as graphical rather than box-shaped. These graphics interfaces are different from system to system and flext does not attempt to unite them. They are simply too different. However, other independent graphics systems may (and will) be based on flext. For the following we will concentrate on the development of externals for the systems PD and Max/MSP, but it is once more emphasized that under the hood all the mentioned modular real-time systems are very similar and that flext may well once cover more of these systems.

Page 8: C++ programming layer for cross-platform development of - grrrr.org

flext - C++ programming layer for cross-platform development of PD and Max/MSP externals

page 8

II. Basics All elements in a real-time systems have to be as fast as possible to keep latency low and allow as many voices or transformations as possible. This is the main reason why externals are in general realized as readily compiled binary modules that are loaded into the system. “Compiled” means that the program code (almost exclusively in the C/C++ language) describing an external has been translated into machine code (which is directly executable by the CPU) before actual usage. All real-time systems that are extensible by modules provide a programming interface (API) so that the modules can communicate with the system and vice versa. For example a module would announce that it wants to have 2 inlets and 1 outlet by calling the respective function of the API. Again, the real-time system would tell the module that a message has been sent to it by using a so-called callback function. Callback functions are entry points inside the external module, so that it can receive information from the real-time system. A module announces the existence of such a callback function when it is created. Typically, each message (be it of type float, list etc.) that can be received by the module needs a designated callback function, and likewise the signal processing needs one that will be called whenever a block of audio data wants to be processed. There exist excellent tutorials on how to write native externals for Max/MSP13 or PD14, respectively. However, when working through the pages it becomes obvious that there are fundamental differences in the APIs of the different systems that make the externals incompatible. Hence, one system is not able to use an external module made for another system although the functionality may be exactly the same. Even worse, not even the C/C++ program code of an external module is the same for different systems (although for Max/MSP and PD there is some resemblance due to their common origin in the works of Miller Puckette). This is where flext comes into play. It provides an API of its own which stays exactly the same no matter if the external will be used for Max/MSP or PD. It is important to realize that while the C++ source code stays the same, the external has to be targeted for one real-time system at the time of compilation where the actual loadable module is produced. flext does all the translation between the program code and the programming interface of the real-time system along with the necessary callback functionality. As flext-based externals are written in C++, they make use of inheritance, which is a handy feature of this object-oriented programming language. Multiple objects can be derived from one more general one, inheriting all its features, complemented by new more special ones. Furthermore there are numerous functions built into flext which tremendously facilitate the task of developing an external. This can be seen in the tutorial examples below, each one presenting a different aspect of the flext library.

1. Prerequisites flext is a programming library which implies that when using it you will have to do some programming in C++. However, programming isn’t black magic and it need not be difficult either – nevertheless some hurdles have to be cleared in the beginning. An important one is the handling of a development environment that is needed to build external modules.

Page 9: C++ programming layer for cross-platform development of - grrrr.org

flext - C++ programming layer for cross-platform development of PD and Max/MSP externals

page 9

a) Build environments flext supports all major build environments for the respective platforms. The library itself and also the tutorial examples come with building scripts for each of them. It is beyond the scope of this introduction to describe the several frameworks in detail but the (tested) possible choices shall be listed shortly:

i) Microsoft Windows

Currently, PD runs on Windows and Max/MSP will follow in the near future.

• Microsoft Visual C++ 6.0 or 7.0 (.NET) This is the most commonly used development package. Especially version 7.0 is able to exploit all features of flext.

• Borland C++ 5.5 This compiler package is lightweight and it’s freely downloadable from the Borland homepagec. It has a good C++ support but cannot handle the more advanced multi-threading features of flext.

• cygwin gcc cygwind is a Unix environment capable of running under Windows. It’s free and it comes with the GNU C++ compiler, which is recommended if of version 3.2 and above.

ii) Mac OS9

This operating system is not supported by Apple any longer and therefore slowly dieing. Only Max/MSP is (still) running here, PD never will.

• Metrowerks Codewarrior, version 6 upwards This is the standard compiler for MacOS. Be sure to have version 8.3 at least to avoid some unpleasant bugs in the C++ language support.

Earlier versions of flext also supported the free Apple MPW environment and it may still work but there’s no guarantee for that… (it’s simply too weak)

iii) Mac OSX

OSX has inherited a burden from its predecessor: it’s the schizophrenic case of two different binary file formats that are totally incompatible. While Max/MSP currently needs all externals to be built in the old OS9 CFM format, PD only wants the new Mach-O format.

• Metrowerks Codewarrior, version 6 upwards This compiler is capable of building both CFM and Mach-O formats but currently only Max/MSP externals can be built with it due to a deficiency of the linker.

c Borland C++ 5.5 download - http://www.borland.com/products/downloads/download_cbuilder.html# d cygwin - http://www.cygwin.com

Page 10: C++ programming layer for cross-platform development of - grrrr.org

flext - C++ programming layer for cross-platform development of PD and Max/MSP externals

page 10

• Project Builder This build environment is part of the Apple OSX Developer Toolse and it is based on the GNU c++ 3.1 compiler and it is for free. Since it can solely produce Mach-O binaries, only PD externals can be made.

iv) Linux

Max/MSP won’t run on Linux in the foreseeable future, but PD does very well. Luckily, every Linux distribution comes with a standard compiler:

• GNU g++ It is freef and it is ok (however, it’s not a fast as other compilers) and it can be recommended as working reliably from version 3.2 upwards.

b) Installing the real-time-system SDK As already mentioned earlier, external modules need to communicate with the respective hosting real-time-system through a well-defined programming interface. The SDK (Software Development Kit) with one or more C-language header files that contain these definitions doesn’t come with the standard distribution of PD or Max/MSP necessarily but you can download them from the respective website. For PD you’ll have to download its source code distribution packageg and for Max/MSP you’ll need the Max/MSP Software Development Kith.

c) Obtaining and installing the flext library After downloadingi a current flext release for your platform and unzipping the compressed file you will find a readme.txt containing some information, the license texts license.txt and gpl.txt as well as several build-*, config-* and make* files located in the main folder. Building and installing the library should be as easy as editing the appropriate config-*.txt file so that the various settings therein fit to your system and then running the matching build-*.bat or .sh file, depending on your platform. The readme.txt tells you which combination to choose. For PD under Windows for example you’ll than have a subfolder flext to your pd installation containing various *.h C-header files as well as several *.lib flext library files. If you want to use the CodeWarrior build environment to compile flext for Max/MSP, things are a bit different. You’ll have to open the flext.cw project file and edit or add a number of “Source Tree” definitions to your CodeWarrior configuration. Refer to the CodeWarrior documentation for how to do that. Finally, flext should be usable now and you can start developing your externals. If you have no experience of programming flext-based externals you should download the tutorial package15 containing a number of examples (some of which are shown below) demonstrating all major aspects of the flext framework. Probably this preceding description is too light-minded to get you started with the development system and the flext library. Anyhow, I’d really recommend that you find someone to help you with the initial steps to become acquainted and comfortable with it all.

e Apple OSX Developer Tools - http://developer.apple.com/tools f GNU g++ - http://gcc.gnu.org g PD source code packages - http://www-crca.ucsd.edu/~msp/software.html h Max/MSP SDK - http://www.cycling74.com/products/dlmaxmsp.html i flext download - http://www.parasitaere-kapazitaeten.net/ext/flext

Page 11: C++ programming layer for cross-platform development of - grrrr.org

flext - C++ programming layer for cross-platform development of PD and Max/MSP externals

page 11

d) Programming in C++ Flext massively uses nearly all aspects of the C++ language. However, this doesn’t imply that you have to do that as well. By looking at the code of several simple flext-based objects you’ll surely recognize the elements that these objects are made up of. In this sense it’s definitely possible that you can start with programming externals with no prior knowledge of C++, provided that you are not the faint of heart. You can acquire a profound knowledge of the language by – having some patience - just using and experimenting with it, nevertheless a good C++ textbook can help you over some beginner’s difficulties.

e) License issues Flext is distributed under the GPL licensej, which you should carefully read. This means that download and usage of flext is free and that the source code of flext is fully disclosed. The GPL has several other implications, one of which is that externals programmed with flext need to be distributed open-source under the GPL as well. This is a good thing since other people can learn from these externals again and the common wisdom will grow and the world become a better one.

j GNU General Public License - http://www.gnu.org/copyleft/gpl.html

Page 12: C++ programming layer for cross-platform development of - grrrr.org

flext - C++ programming layer for cross-platform development of PD and Max/MSP externals

page 12

2. Building blocks of a simple external In the following, the C++ source code of the example object simple1 will be analyzed. The actual functionality of the external will be described with the examples a bit later. First of all, the flext header file must be included. It is a part of the flext distribution and contains all the necessary definitions used throughout a typical flext-based external.

#include <flext.h> Immediately afterwards we check for the flext version to see if it is up to date. This is important because some features contained in a current version of flext may not have been present in an earlier one.

#if !defined(FLEXT_VERSION) || (FLEXT_VERSION < 400) #error You need at least flext version 0.4.0 #endif

Next, we can start with the object definition. A flext object is simply represented by a C++ class derived from the base class flext_base. The base class already contains a number of essential features that are automatically inherited by simple1, in this case. For a stand-alone external (which is not part of a library of externals) as we want to have it here, it is important that the class name matches the name of the object we want the create. In our case the object in PD or Max/MSP will be [simple1].

class simple1: public flext_base { FLEXT_HEADER(simple1,flext_base)

The statement FLEXT_HEADER (or its variant FLEXT_HEADER_S) is necessary to include some hidden commands (that we don’t need to care about) into the class code. Again, the current class simple1 and its base class flext_base must be specified. Following, we define some members of the class: First, the constructor, a function that is called when an instance of this class is created. This happens when an object [simple1] is placed in our PD or Max/MSP patch. The constructor is used for initialization purposes and has always the name of the class itself. The constructor takes no arguments and so will the [simple1] object.

public: simple1()

{ AddInAnything(); // add one inlet for any message AddOutFloat(); // add one float outlet (has index 0) FLEXT_ADDMETHOD(0,m_float); // register method for inlet 0 }

As documented inline (after the // ) the constructor first creates an inlet that can receive any type of message (the left-most inlet must be of that type) and also an outlet that can send a float message (and nothing else).

Page 13: C++ programming layer for cross-platform development of - grrrr.org

flext - C++ programming layer for cross-platform development of PD and Max/MSP externals

page 13

The FLEXT_ADDMETHOD statement binds the m_float method (see below) to the inlet 0 (the numbering starts with 0 for the left-most inlet). As m_float takes floating point values as arguments, only those messages will be handled by it. Secondly, we define the method (a function that is a member of a class) m_float already mentioned in the constructor. It takes one parameter of the C type float, which represents a floating point value. The method simply calculates the inverse of an input value (= 1 / value). If the value is zero (which can’t be inverted) an error message is output to the console (and the result is set to zero as well). Afterwards the resulting value is output to the outlet.

void m_float(float input) // method for float values { float result; if(input == 0) { // special case 0 post("%s - zero can't be inverted!",thisName()); result = 0; } else // normal case result = 1/input; // output value to outlet ToOutFloat(0,result); }

As a link to PD or Max/MSP we need to have a so called callback wrapper for a method we want to be triggered by messages received at an inlet. In this case, we do that for the method m_float already defined above that has 1 argument of type float.

FLEXT_CALLBACK_1(m_float,float) };

This is all we need to define for a simple external. At the end, we tell the system explicitly how the class shall be named and what creation arguments it takes (none in this case).

FLEXT_NEW("simple1",simple1)

Page 14: C++ programming layer for cross-platform development of - grrrr.org

flext - C++ programming layer for cross-platform development of PD and Max/MSP externals

page 14

3. How to build flext-based externals Now that you have flext installed on your system and the source code of an external readily at hand the next step would be to render this source code into a usable external module that can be loaded by your real-time system. Two main steps have to be taken to accomplish this:

• The source code file(s) must be compiled into a machine-readable object format. This is what a compiler does. You can do that inside the project space of a development system (as with Microsoft Visual C++ or CodeWarrior) or at the command line of the console (Microsoft Visual C++, BorlandC++ and all ports of GNU g++). It is beyond our scope to go into details but a few main points have to be considered: o You’ll have to specify so called “include file paths” (typically with the –I

command line option of the compiler) so that the compiler can find the header files of the real-time-system SDK and of the flext library

o You’ll have to set a compiler definition (typically with the –D command line flag) to specify the target platform of your flext-based external. For PD, you would specify –DFLEXT_SYS=2 and for Max/MSP –DFLEXT_SYS=1 . There are several other switches that can be set but for now we are fine with these.

• The object file(s) must be linked with the flext library and the library files of the real-time-system SDK to a loadable so-called dynamic (or shared) library file. This is the binary format that PD or Max/MSP can load. It is common that the compilation and the linking is done in one step (by just omitting the –c flag to the compiler, so that it calls the linker after producing the object files itself or within a project space of a development environment where you hardly notice the linking step explicitly) The important point is that again some prerequisites have to be fulfilled: o PD requires a special naming of the external modules. Apart of the main file name

which is just the name of the desired object (like e.g. “sine~” or “router”), depending on the platform the extension of the binary file has to be .pd_win, .pd_linux or .pd_darwin (the latter for OSX) so that it can be recognized as a loadable module. Max/MSP doesn’t want an extension at all, although .mxe might be valid in the future.

o The linker needs to find the necessary libraries to be included - for example the flext library flext.lib for Windows or flext.a for Linux or OSX, respectively, or the SDK library files (like pd.lib for PD under Windows or maxlib and maxaudiolib for Max/MSP). This can be provided by specifying a “library path” with the –L command line flag.

Once the building of an external has been successful it can be attempted to load it into the real-time-system. For that the location must be announced to the system (with the –path command line flag for PD or the file preferences in Max) or the file must be copied to a standard place where externals typically reside (like the PD extra subfolder or the externals folder for Max/MSP).

Page 15: C++ programming layer for cross-platform development of - grrrr.org

flext - C++ programming layer for cross-platform development of PD and Max/MSP externals

page 15

III. Examples In the following you will find a few of the more representative examples of the flext tutorial package. These shall introduce you to all of the features of the Max/MSP and PD real-time systems that flext supports or the functionality which it additionally provides.

1. Simple message based externals You already know the first one. It is the [simple1] object that has already been analyzed above – but here again in full glory.

Figure III-1 – simple1, an object that takes numbers for input, calculates the inverse,

and outputs the result at the outlet. Additionally, a help text describing its usage can be displayed.

// include flext header #include <flext.h> // check for appropriate flext version #if !defined(FLEXT_VERSION) || (FLEXT_VERSION < 400) #error You need at least flext version 0.4.0 #endif // define the class that stands for a pd/Max object // Attention: the class name must be the same as the object name!! (without an eventual ~) // Special names are possible with the usage of libraries (see the lib1 tutorial example) class simple1: // inherit from basic flext class public flext_base { // obligatory flext header (class name,base class name) FLEXT_HEADER(simple1,flext_base) public: // constructor simple1() { // define inlets: // first inlet must always be of type anything (or signal for dsp objects) AddInAnything(); // add one inlet for any message // define outlets: AddOutFloat(); // add one float outlet (has index 0)

Page 16: C++ programming layer for cross-platform development of - grrrr.org

flext - C++ programming layer for cross-platform development of PD and Max/MSP externals

page 16

// register methods FLEXT_ADDMETHOD(0,m_float); // register method "m_float" for inlet 0 } protected: void m_float(float input) // method for float values { float result; if(input == 0) { // special case 0 post("%s - zero can't be inverted!",thisName()); result = 0; } else // normal case result = 1/input; // output value to outlet ToOutFloat(0,result); // (0 stands for the outlet index 0 - the leftmost outlet) } private: FLEXT_CALLBACK_1(m_float,float) // callback for method "m_float" }; FLEXT_NEW("simple1",simple1) // instantiate the class

The second example of these basic message based externals is one that adds two numbers. [simple2] has got two inlets, of which the right one just stores the input value, while the left one takes the input and triggers the calculation. The result of the addition is sent to the outlet. Additionally it shows that flext-based objects always understand the [help( message. Without special measures within the class definition (overloading of the m_help virtual function) this prints a default text to the console.

Figure III-2 – simple2 adds two numbers. The one sent into the right (“cold”) inlet is stored internally until a number is sent into the left (“hot”) inlet. Then, the addition is calculated and the result output at the outlet.

Page 17: C++ programming layer for cross-platform development of - grrrr.org

flext - C++ programming layer for cross-platform development of PD and Max/MSP externals

page 17

/* flext tutorial - simple 2 Copyright (c) 2002,2003 Thomas Grill ([email protected]) For information on usage and redistribution, and for a DISCLAIMER OF ALL WARRANTIES, see the file, "license.txt," in this distribution. ------------------------------------------------------------------------- This is an example of a simple object doing a float addition */ // include flext header #include <flext.h> // check for appropriate flext version #if !defined(FLEXT_VERSION) || (FLEXT_VERSION < 400) #error You need at least flext version 0.4.0 #endif class simple2: public flext_base { FLEXT_HEADER(simple2,flext_base) public: // constructor with float argument simple2(float init); protected: void m_float1(float f); void m_float2(float f); // stored argument of right inlet float arg; private: // FLEXT_CALLBACK_F(...) is a shortcut for FLEXT_CALLBACK_1(...,float) FLEXT_CALLBACK_F(m_float1) // callback for method "m_float1" (with one float argument) FLEXT_CALLBACK_F(m_float2) // callback for method "m_float2" (with one float argument) }; // instantiate the class (constructor has one float argument) FLEXT_NEW_1("simple2",simple2,float) simple2::simple2(float init): arg(init) // store argument { // define inlets AddInAnything(); // first inlet of type anything (index 0) AddInFloat(); // additional float inlet (index 1) // define outlets AddOutFloat(); // one float outlet (has index 0) // register methods FLEXT_ADDMETHOD(0,m_float1); // register method (for floats) "m_float1" for inlet 0 FLEXT_ADDMETHOD(1,m_float2); // register method (for floats) "m_float2" for inlet 1 } void simple2::m_float1(float f) { float res; res = arg+f; // output value to outlet ToOutFloat(0,res); // (0 stands for the outlet index 0) } void simple2::m_float2(float f) { // store float arg = f; }

Page 18: C++ programming layer for cross-platform development of - grrrr.org

flext - C++ programming layer for cross-platform development of PD and Max/MSP externals

page 18

2. More advanced message based externals The tutorial includes a few examples of more advanced treatment of PD or Max/MSP messages. The one presented here is an adaptation of the [counter] object conceived by IOhannes Zmölnig which he presents in his “Howto write an external for puredata”. It has been chosen so that a direct comparison of a flext external to a native one is possible. Most of the features of the original object translate one to one, while some must be implemented differently with flext. This is explained in the source code comments.

Figure III-3 – adv3 is a port of the counter example from Iohannes Zmölnigs PD external tutorial.

/* flext tutorial - advanced 3 Copyright (c) 2002,2003 Thomas Grill ([email protected]) For information on usage and redistribution, and for a DISCLAIMER OF ALL WARRANTIES, see the file, "license.txt," in this distribution. ------------------------------------------------------------------------- This is a port of Iohannes Zmölnigs "counter" example to the flext paradigm. Find the original at http://iem.kug.ac.at/pd/externals-HOWTO/node5.html The functionality is exactly the same, with one exception: flext doesn't support default arguments, hence a message "bound 1" will translate into "bound 1 0" in the original example, but won't be recognized with flext. This can be easily circumvented by using a method digesting a variable argument list, but was omitted for the sake of clearness. Apart from that you'll notice several differences to the original C object: - with flext, callbacks have to be declared for all registered methods - Flext allows the full usage of integer types - there are no real "passive" methods with flext. These can be emulated by methods, or more flexibly, attributes (see example "attr3") - Help symbols can't be defined that freely. This is because in Max/MSP help files always have the name of the object with a suffix .help appended. However with flext, a path to the respective help file may be specified */

Page 19: C++ programming layer for cross-platform development of - grrrr.org

flext - C++ programming layer for cross-platform development of PD and Max/MSP externals

page 19

// include flext header #include <flext.h> // check for appropriate flext version #if !defined(FLEXT_VERSION) || (FLEXT_VERSION < 401) #error You need at least flext version 0.4.1 #endif class adv3: public flext_base { FLEXT_HEADER_S(adv3,flext_base,setup) public: // constructor with no arguments adv3(int argc,t_atom *argv): i_step(1) { // --- initialize bounds and step size --- int f1 = 0,f2 = 0; switch(argc) { default: case 3: i_step = GetInt(argv[2]); case 2: f2 = GetInt(argv[1]); case 1: f1 = GetInt(argv[0]); case 0: ; } if(argc < 2) f2 = f1; m_bound(f1,f2); i_count = i_down; // --- define inlets and outlets --- AddInAnything("bang, reset, etc."); // default inlet AddInList("bounds (2 element list)"); // inlet for bounds AddInInt("step size"); // inlet for step size AddOutInt("counter"); // outlet for integer count AddOutBang("overflow bang"); // outlet for bang } protected: void m_reset() { i_count = i_down; } void m_set(int argc,t_atom *argv) { i_count = argc?GetAInt(argv[0]):0; } void m_bang() { int f = i_count; i_count += i_step; if(i_down != i_up) { if((i_step > 0) && (i_count > i_up)) { i_count = i_down; ToOutBang(1); } else if(i_count < i_down) { i_count = i_up; ToOutBang(1); } } ToOutInt(0,f); } void m_bound(int f1,int f2) {

Page 20: C++ programming layer for cross-platform development of - grrrr.org

flext - C++ programming layer for cross-platform development of PD and Max/MSP externals

page 20

i_down = f1 < f2?f1:f2; i_up = f1 > f2?f1:f2; } void m_step(int s) { i_step = s; } int i_count,i_down,i_up,i_step; private: static void setup(t_classid c) { // --- set up methods (class scope) --- // register a bang method to the default inlet (0) FLEXT_CADDBANG(c,0,m_bang); // set up tagged methods for the default inlet (0) // the underscore _ after CADDMETHOD indicates that a message tag is used // no, variable list or anything and all single arguments are recognized automatically, ... FLEXT_CADDMETHOD_(c,0,"reset",m_reset); FLEXT_CADDMETHOD_(c,0,"set",m_set); // ..., more complex types (combinations of types) have to be specified explicitly FLEXT_CADDMETHOD_II(c,0,"bound",m_bound); // two int arguments // set up methods for inlets 1 and 2 // no message tag used FLEXT_CADDMETHOD(c,1,m_bound); // variable arg type recognized automatically FLEXT_CADDMETHOD(c,2,m_step); // single int arg also recognized automatically } // for every registered method a callback has to be declared FLEXT_CALLBACK(m_bang) FLEXT_CALLBACK(m_reset) FLEXT_CALLBACK_V(m_set) FLEXT_CALLBACK_II(m_bound) FLEXT_CALLBACK_I(m_step) }; // instantiate the class (constructor has a variable argument list) // let "counter" be an alternative name // before the colon define the name of the path to the help file FLEXT_NEW_V("help, adv3 counter",adv3)

3. Using attributes Another advanced feature of flext-based externals is the incorporation of the Max/Jitter16-like attribute functionality. Attributes solve the problem of how the state of an object can be consistently set and queried. A formalism has been introduced with Jitter which has then consequently been adopted by flext. Attributes can either be set by an object’s creation arguments (with e.g. @attribute 1) or by the use of a setter message into the leftmost inlet (like for example [attribute 1( ) and can be queried by sending a complementary getter message into this same inlet (e.g. [getattribute( ). The current state of the attribute is then output at the rightmost outlet, which is reserved just for attribute values. Every attribute-enabled flext external has this additional attribute outlet. The rather lengthy source code of the example object attr2 shows how attributes can be used with simple calculational tasks. It also shows the usage of a setup function (defined with FLEXT_HEADER_S) which initializes some data at the time when the external is loaded. A setup function is only called once, while the constructor is called upon the creation of each attr2 object in a patch.

Page 21: C++ programming layer for cross-platform development of - grrrr.org

flext - C++ programming layer for cross-platform development of PD and Max/MSP externals

page 21

Figure III-4 – Attributes are an extremely useful feature introduced with Max/Jitter. With flext, attributes can be

used with plain Max/MSP and PD as well – attr2 shows how to do that

/* flext tutorial - attributes 2 Copyright (c) 2002,2003 Thomas Grill ([email protected]) For information on usage and redistribution, and for a DISCLAIMER OF ALL WARRANTIES, see the file, "license.txt," in this distribution. ------------------------------------------------------------------------- This is an example of an object doing various float operations. Methods and attributes are registered at class level (opposed to object level in example "attr1"). For details, see also example "adv2" */ // IMPORTANT: enable attribute processing (specify before inclusion of flext headers!) // For clarity, this is done here, but you'd better specify it as a compiler definition // FLEXT_ATTRIBUTES must be 0 or 1, #define FLEXT_ATTRIBUTES 1 // include flext header #include <flext.h> // check for appropriate flext version #if !defined(FLEXT_VERSION) || (FLEXT_VERSION < 401) #error You need at least flext version 0.4.1 #endif #include <math.h>

Page 22: C++ programming layer for cross-platform development of - grrrr.org

flext - C++ programming layer for cross-platform development of PD and Max/MSP externals

page 22

class attr2: public flext_base { // compulsory flext header with a class setup function FLEXT_HEADER_S(attr2,flext_base,setup) public: // constructor attr2(); protected: void m_trigger(float f); float arg; // stored argument of operation float res; // stored result enum operation { op_set,op_add,op_sub,op_mul,op_div,op_pow } op; static const t_symbol *sym_set,*sym_add,*sym_sub,*sym_div,*sym_mul,*sym_pow; private: static void setup(t_classid); // callback for method "m_trigger" (with one float argument) FLEXT_CALLBACK_F(m_trigger) // define attribute callbacks for variable "arg" ("ATTRVAR" means GET and SET) FLEXT_ATTRVAR_F(arg) // define attribute callbacks for variable "res" (GET only) FLEXT_ATTRGET_F(res) // methods for getting/setting the operation mode void opget(const t_symbol *&s) const; void opset(const t_symbol *&s); // define attribute callbacks for variable "res" (GET only) FLEXT_CALLGET_S(opget) FLEXT_CALLSET_S(opset) }; // instantiate the class FLEXT_NEW("attr2",attr2) // instantiate static variables const t_symbol *attr2::sym_set, *attr2::sym_add,*attr2::sym_sub, *attr2::sym_div,*attr2::sym_mul, *attr2::sym_pow; void attr2::setup(t_classid c) { // Upon class creation setup some symbols // This is done only upon creation of of the first "attr2" object sym_set = MakeSymbol("="); sym_add = MakeSymbol("+"); sym_sub = MakeSymbol("-"); sym_mul = MakeSymbol("*"); sym_div = MakeSymbol("/"); sym_pow = MakeSymbol("**"); // setup methods and attributes at class scope // register method (for floats) "m_trigger" for inlet 0 FLEXT_CADDMETHOD(c,0,m_trigger); // register attribute "arg" with the variable "arg" FLEXT_CADDATTR_VAR1(c,"arg",arg); // register attribute "result" with variable "res" FLEXT_CADDATTR_GET(c,"result",res); // register attribute "op" with methods "opget" and "opset" FLEXT_CADDATTR_VAR(c,"op",opget,opset); }

Page 23: C++ programming layer for cross-platform development of - grrrr.org

flext - C++ programming layer for cross-platform development of PD and Max/MSP externals

page 23

attr2::attr2(): arg(0),res(0), // initialize argument and result op(op_set) // initialize operation { // define inlets AddInAnything(); // first inlet of type anything (index 0) // define outlets AddOutFloat(); // one float outlet (has index 0) } // receive an operand, do the math operation and trigger the output void attr2::m_trigger(float f) { switch(op) { case op_set: res = f; break; case op_add: res = f+arg; break; case op_sub: res = f-arg; break; case op_mul: res = f*arg; break; case op_div: if(arg) res = f/arg; else { post("%s - argument to division is 0: result set to 0",thisName()); res = 0; } break; case op_pow: res = (float)pow(f,arg); break; #ifdef FLEXT_DEBUG default: ERRINTERNAL(); // operation not defined #endif } // output value to outlet ToOutFloat(0,res); // (0 stands for the outlet index 0) } // report the operation mode void attr2::opget(const t_symbol *&s) const { switch(op) { case op_set: s = sym_set; break; case op_add: s = sym_add; break; case op_sub: s = sym_sub; break; case op_mul: s = sym_mul; break; case op_div: s = sym_div; break; case op_pow: s = sym_pow; break; #ifdef FLEXT_DEBUG default: ERRINTERNAL(); // operation not defined #endif } } // set the operation mode void attr2::opset(const t_symbol *&s) { if(s == sym_set) op = op_set; else if(s == sym_add) op = op_add; else if(s == sym_sub) op = op_sub; else if(s == sym_mul) op = op_mul; else if(s == sym_div) op = op_div; else if(s == sym_pow) op = op_pow; else { post("%s - operation is not defined, set to =",thisName()); op = op_set; } }

Page 24: C++ programming layer for cross-platform development of - grrrr.org

flext - C++ programming layer for cross-platform development of PD and Max/MSP externals

page 24

4. DSP (signal-based) externals The main domain of most externals is, of course, some kind of signal processing. Flext fully supports all variants of it, although it uses a slightly different approach than the PD or Max/MSP API does. In a flext-based signal external you simply override the m_signal virtual function. This means that the m_signal method is trivially implemented in the flext_base class, and has to be redefined in the (in this case signal1) child class to provide the special DSP functionality of this class. Here it is the panning of a mono input signal to left and right stereo channels. The amount of panning is controlled by a value sent into the right-most inlet (handled by the method setPan). This example has again been taken from IOhannes Zmölnigs tutorial and has been ported to flext by Frank Barknecht17.

Figure III-5 – signal1~ illustrates the handling of signals within a flext-based external.

It’s an adaptation of the pan~ object from IOhannes Zmölnigs PD tutorial, written by Frank Barknecht

// signal1~ - a flext tutorial external written by Frank Barknecht // // This is a commented port of the pan~ example from the PD-Externals-Howto to // illustrate the usage of flext. You can get the original code at // http://iem.kug.ac.at/pd/externals-HOWTO/ #include <flext.h> #if !defined(FLEXT_VERSION) || (FLEXT_VERSION < 401) #error You need at least flext version 0.4.1 #endif // A flext dsp external ("tilde object") inherits from the class flext_dsp class signal1: public flext_dsp { // Each external that is written in C++ needs to use #defines from flbase.h // // The define // FLEXT_HEADER(NEW_CLASS, PARENT_CLASS) // should be somewhere in your dsp file. // A good place is here:

Page 25: C++ programming layer for cross-platform development of - grrrr.org

flext - C++ programming layer for cross-platform development of PD and Max/MSP externals

page 25

FLEXT_HEADER(signal1, flext_dsp) public: signal1(): f_pan(0) // initialize f_pan { // The constructor of your class is responsible for // setting up inlets and outlets and for registering // inlet-methods: // The descriptions of the inlets and outlets are output // via the Max/MSP assist method (when mousing over them in edit mode). // PD will hopefully provide such a feature as well soon AddInSignal("left audio in"); // left audio in AddInSignal("right audio in"); // right audio in AddInFloat("panning parameter"); // 1 float in AddOutSignal("audio out"); // 1 audio out // Now we need to bind the handler function to our // panning inlet, which is inlet 2 (counting all inlets // from 0). We want the function "setPan" to get // called on incoming float messages: FLEXT_ADDMETHOD(2,setPan); // We're done constructing: post("-- pan~ with flext ---"); } // end of constructor protected: // here we declare the virtual DSP function virtual void m_signal(int n, float *const *in, float *const *out); private: float f_pan; // holds our panning factor // Before we can use "setPan" as a handler, we must register this // function as a callback to PD or Max. This is done using the // FLEXT_CALLBACK* macros. There are several of them. // // FLEXT_CALLBACK_F is a shortcut, that registers a function // expecting one float arg (thus ending in "_F"). There are // other shortcuts that register other types of functions. Look // into flext.h. No semicolon at the end of line!!! FLEXT_CALLBACK_F(setPan) // Now setPan can get declared and defined here. void setPan(float f) { // set our private panning factor "f_pan" to the inlet // value float "f" in the intervall [0,1] f_pan = (f<0) ? 0.0f : (f>1) ? 1.0f : f ; // if you want to debug if this worked, comment out the // following line: //post("Set panning to %.2f, maybe clipped from %.2f", f_pan,f); } // end setPan }; // end of class declaration for signal1 // Before we can run our signal1-class in PD, the object has to be registered as a // PD object. Otherwise it would be a simple C++-class, and what good would // that be for? Registering is made easy with the FLEXT_NEW_* macros defined // in flext.h. For tilde objects without arguments call: FLEXT_NEW_DSP("signal1~ pan~", signal1) // T.Grill: there are two names for the object: signal1~ as main name and pan~ as its alias // Now we define our DSP function. It gets this arguments: // // int n: length of signal vector. Loop over this for your signal processing. // float *const *in, float *const *out: // These are arrays of the signals in the objects signal inlets rsp. // oulets. We come to that later inside the function.

Page 26: C++ programming layer for cross-platform development of - grrrr.org

flext - C++ programming layer for cross-platform development of PD and Max/MSP externals

page 26

void signal1::m_signal(int n, float *const *in, float *const *out) { const float *ins1 = in[0]; const float *ins2 = in[1]; // As said above "in" holds a list of the signal vectors in all inlets. // After these two lines, ins1 holds the signal vector ofthe first // inlet, index 0, and ins2 holds the signal vector of the second // inlet, with index 1. float *outs = out[0]; // Now outs holds the signal vector at the one signal outlet we have. // We are now ready for the main signal loop while (n--) { // The "++" after the pointers outs, ins1 and ins2 walks us // through the signal vector with each n, of course. Before // each step we change the signal value in the outlet *outs // according to our panning factor "f_pan" and according to the // signals at the two signal inlets, *ins1 and *ins2 *outs++ = (*ins1++) * (1-f_pan) + (*ins2++) * f_pan; } } // end m_signal

5. Using sample buffers Closely related to DSP processing is the usage of sample buffers. These arrays of 32-bit floating point values are held in the RAM of the computer and are therefore instantly accessible (as opposed to sound files on a hard disk). Sample buffers are managed by PD or Max/MSP but flext provides various functions to access or modify them, as can be seen in the buffer1 example.

Figure III-6 – buffer1 shows how to access, query and modify sample buffers (arrays in PD)

Page 27: C++ programming layer for cross-platform development of - grrrr.org

flext - C++ programming layer for cross-platform development of PD and Max/MSP externals

page 27

/* flext tutorial - buffer 1 Copyright (c) 2003 Thomas Grill ([email protected]) For information on usage and redistribution, and for a DISCLAIMER OF ALL WARRANTIES, see the file, "license.txt," in this distribution. ------------------------------------------------------------------------- This is an example of a simple object doing some basic buffer operation */ // IMPORTANT: enable attribute processing (specify before inclusion of flext headers!) // For clarity, this is done here, but you'd better specify it as a compiler definition // FLEXT_ATTRIBUTES must be 0 or 1, #define FLEXT_ATTRIBUTES 1 // include flext header #include <flext.h> // check for appropriate flext version #if !defined(FLEXT_VERSION) || (FLEXT_VERSION < 400) #error You need at least flext version 0.4.0 #endif // define the class that stands for a pd/Max object class buffer1: // inherit from basic flext class public flext_base { // obligatory flext header (class name,base class name) featuring a setup function FLEXT_HEADER_S(buffer1,flext_base,setup) public: // constructor with a variable argument list buffer1(int argc,const t_atom *argv); protected: const t_symbol *bufname; buffer *buf; // set new buffer (or none if name omitted) void m_set(int argc,const t_atom *argv); // get buffer name void mg_buf(AtomList &lst) const; // set buffer name (simply reuse m_set method) inline void ms_buf(const AtomList &lst) { m_set(lst.Count(),lst.Atoms()); } // get buffer channels inline void mg_chns(int &chns) { chns = Check()?buf->Channels():0; } // get buffer length in frames inline void mg_frames(int &frames) { frames = Check()?buf->Frames():0; } // set buffer length in frames inline void ms_frames(int frames) { if(Check()) buf->Frames(frames); } // get sample (index channel) void m_peek(int argc,const t_atom *argv); // set sample (index value channel) void m_poke(int argc,const t_atom *argv); // delete eventual existing buffer void Clear(); // check and eventually update buffer reference (return true if valid) bool Check(); private: static void setup(t_classid c); FLEXT_CALLBACK_V(m_set) // wrapper for method m_set (with variable argument list) FLEXT_CALLBACK_V(m_peek) // wrapper for method m_peek (with variable argument list) FLEXT_CALLBACK_V(m_poke) // wrapper for method m_poke (with variable argument list) FLEXT_CALLVAR_V(mg_buf,ms_buf) // wrappers for attribute getter/setter mg_buffer/ms_buffer (with variable argument list)

Page 28: C++ programming layer for cross-platform development of - grrrr.org

flext - C++ programming layer for cross-platform development of PD and Max/MSP externals

page 28

FLEXT_CALLGET_I(mg_chns) // wrappers for attribute getter mg_chns (with integer arguments) FLEXT_CALLVAR_I(mg_frames,ms_frames) // wrappers for attribute getter/setter mg_frames/ms_frames (with integer arguments) }; // instantiate the class FLEXT_NEW_V("buffer1",buffer1) void buffer1::setup(t_classid c) { // register methods and attributes FLEXT_CADDMETHOD_(c,0,"set",m_set); // register method "set" for inlet 0 FLEXT_CADDMETHOD_(c,0,"peek",m_peek); // register method "peek" for inlet 0 FLEXT_CADDMETHOD_(c,0,"poke",m_poke); // register method "poke" for inlet 0 FLEXT_CADDATTR_VAR(c,"buffer",mg_buf,ms_buf); // register attribute "buffer" FLEXT_CADDATTR_GET(c,"channels",mg_chns); // register attribute "channels" FLEXT_CADDATTR_VAR(c,"frames",mg_frames,ms_frames); // register attribute "frames" } buffer1::buffer1(int argc,const t_atom *argv): // clear buffer buf(NULL),bufname(NULL) { // define inlets: // first inlet must always be of type anything (or signal for dsp objects) AddInAnything("message inlet"); // add one inlet for any message // peek outlet AddOutFloat("peek value outlet"); // set buffer according to creation arguments m_set(argc,argv); } void buffer1::Clear() { if(buf) { delete buf; buf = NULL; bufname = NULL; } } bool buffer1::Check() { if(!buf || !buf->Valid()) { post("%s (%s) - no valid buffer defined",thisName(),GetString(thisTag())); // return zero length return false; } else { if(buf->Update()) { // buffer parameters have been updated if(buf->Valid()) { post("%s (%s) - updated buffer reference",thisName(),GetString(thisTag())); return true; } else { post("%s (%s) - buffer has become invalid",thisName(),GetString(thisTag())); return false; } } else return true; } } void buffer1::m_set(int argc,const t_atom *argv) { if(argc == 0) { // argument list is empty // clear existing buffer Clear(); } else if(argc == 1 && IsSymbol(argv[0])) {

Page 29: C++ programming layer for cross-platform development of - grrrr.org

flext - C++ programming layer for cross-platform development of PD and Max/MSP externals

page 29

// one symbol given as argument // clear existing buffer Clear(); // save buffer name bufname = GetSymbol(argv[0]); // make new reference to system buffer object buf = new buffer(bufname); if(!buf->Ok()) { post("%s (%s) - warning: buffer is currently not valid!",thisName(),GetString(thisTag())); } } else { // invalid argument list, leave buffer as is but issue error message to console post("%s (%s) - message argument must be a symbol (or left blank)",thisName(),GetString(thisTag())); } } void buffer1::mg_buf(AtomList &lst) const { if(buf) { // buffer exists: return buffer name lst(1); SetSymbol(lst[0],bufname); } else // no buffer: set empty list lst(0); } void buffer1::m_poke(int argc,const t_atom *argv) { // if buffer is invalid bail out if(!Check()) return; bool ok = true; int ix,chn = 0; float val; if(argc == 3) { if(CanbeInt(argv[2])) // get channel index chn = GetAInt(argv[2]); else ok = false; } if(ok && (argc == 2 || argc == 3) && CanbeInt(argv[0]) && CanbeFloat(argv[1])) { // get frame index ix = GetAInt(argv[0]); // get value val = GetAFloat(argv[1]); } else ok = false; if(ok) { // correct syntax, set sample buf->Data()[ix] = val; } else post("%s (%s) - syntax error - use \"poke index value [channel]\"",thisName(),GetString(thisTag())); }

Page 30: C++ programming layer for cross-platform development of - grrrr.org

flext - C++ programming layer for cross-platform development of PD and Max/MSP externals

page 30

void buffer1::m_peek(int argc,const t_atom *argv) { // if buffer is invalid bail out if(!Check()) return; bool ok = true; int ix,chn = 0; if(argc == 2) { if(CanbeInt(argv[1])) // get channel index chn = GetAInt(argv[1]); else ok = false; } if(ok && (argc == 1 || argc == 2) && CanbeInt(argv[0])) { // get frame index ix = GetAInt(argv[0]); } else ok = false; if(ok) // correct syntax, output value ToOutFloat(0,buf->Data()[ix]); else post("%s (%s) - syntax error - use \"peek index [channel]\"",thisName(),GetString(thisTag())); }

6. Using timers Another resource that is managed by the real-time system itself is the timer functionality. Flext uses a two-fold approach to timers: First, a Timer class is present in the flext class which can be derived for special sub-classes. For the other approach, a timer method for the object can be registered with the FLEXT_ADDTIMER statement. Such a method must have a special callback wrapper set up with FLEXT_CALLBACK_T. This is shown in the timer1 example, where two timers tmrA and tmrB are controlled by messages to the object and can be set to either one-shot or periodic operation. Additionally, two functions for measuring time are presented which are triggered by messages.

Figure III-7 – timer1 shows some basic operations on timers provided by the real-time-system itself.

Page 31: C++ programming layer for cross-platform development of - grrrr.org

flext - C++ programming layer for cross-platform development of PD and Max/MSP externals

page 31

/* flext tutorial - timer 1 Copyright (c) 2003 Thomas Grill ([email protected]) For information on usage and redistribution, and for a DISCLAIMER OF ALL WARRANTIES, see the file, "license.txt," in this distribution. ------------------------------------------------------------------------- This is an example of an object using timers */ // enable flext attributes #define FLEXT_ATTRIBUTES 1 // include flext header #include <flext.h> // check for appropriate flext version #if !defined(FLEXT_VERSION) || (FLEXT_VERSION < 403) #error You need at least flext version 0.4.3 #endif // define the class that stands for a pd/Max object class timer1: // inherit from basic flext class public flext_base { // obligatory flext header (class name,base class name) FLEXT_HEADER_S(timer1,flext_base,Setup) public: // constructor timer1(); protected: // timers Timer tmrA,tmrB; void m_getostime(float &f) { f = (float)GetOSTime(); } // method for operating system time attribute void m_getrttime(float &f) { f = (float)GetTime(); } // method for real-time system time attribute void m_timerA(void *) { ToOutString(0,"Timer A"); } // timer A method void m_timerB(void *) { ToOutString(0,"Timer B"); } // timer B method void m_resetA() { tmrA.Reset(); } // timer A reset void m_resetB() { tmrB.Reset(); } // timer B reset void m_oneshotA(int del) { tmrA.Delay(del*0.001); } // timer A one shot void m_oneshotB(int del) { tmrB.Delay(del*0.001); } // timer B one shot void m_periodicA(int del) { tmrA.Periodic(del*0.001); } // timer A periodic void m_periodicB(int del) { tmrB.Periodic(del*0.001); } // timer B periodic private: static void Setup(t_classid c); // register timer callbacks FLEXT_CALLBACK_T(m_timerA) FLEXT_CALLBACK_T(m_timerB) // register method callbacks FLEXT_CALLGET_F(m_getostime) FLEXT_CALLGET_F(m_getrttime) FLEXT_CALLBACK(m_resetA) FLEXT_CALLBACK(m_resetB) FLEXT_CALLBACK_I(m_oneshotA) FLEXT_CALLBACK_I(m_oneshotB) FLEXT_CALLBACK_I(m_periodicA) FLEXT_CALLBACK_I(m_periodicB) }; // instantiate the class FLEXT_NEW("timer1",timer1)

Page 32: C++ programming layer for cross-platform development of - grrrr.org

flext - C++ programming layer for cross-platform development of PD and Max/MSP externals

page 32

// class setup function void timer1::Setup(t_classid c) { FLEXT_CADDATTR_GET(c,"ostime",m_getostime); // register attribute for OS time FLEXT_CADDATTR_GET(c,"time",m_getrttime); // register attribute for RT time FLEXT_CADDMETHOD_(c,0,"resetA",m_resetA); // register reset method for timer A FLEXT_CADDMETHOD_(c,0,"resetB",m_resetB); // register reset method for timer B FLEXT_CADDMETHOD_(c,0,"oneshotA",m_oneshotA); // register one shot method for timer A FLEXT_CADDMETHOD_(c,0,"oneshotB",m_oneshotB); // register one shot method for timer B FLEXT_CADDMETHOD_(c,0,"periodicA",m_periodicA); // register periodic method for timer A FLEXT_CADDMETHOD_(c,0,"periodicB",m_periodicB); // register periodic method for timer B } // class constructor timer1::timer1(): tmrA(false),tmrB(false) { AddInAnything("Control timers"); // add inlet for control commands AddOutAnything("Timer output"); // add outlet for timer output // register methods FLEXT_ADDTIMER(tmrA,m_timerA); // register method "m_timerA" for timer A FLEXT_ADDTIMER(tmrB,m_timerB); // register method "m_timerB" for timer B }

7. Binding to symbols As stated above symbols are character strings that are cached by the real-time system for rapid reuse. Once a symbol is used it will stay in the system until shutdown. This has pros and cons. A problem is that one should be cautious with the usage of symbols. If too many (like a few thousands automatically generated) different symbol strings are used, the system is likely to slow down noticeably. The big advantage is on the other hand that information can be attached (or bound) to a symbol and won’t be lost, since the symbol will never disappear. This is for example used with the [send] and [receive] objects delivered with PD and Max/MSP. The receiving part is bound to the symbol and gets a notification whenever the sender passes a message to the symbol. The binding functionality is more cultivated in PD than in Max/MSP but the flext implementation tries to hide this Max weakness. There are two possibilities to use binding within a flext-based external. First, the whole object can be bound to a symbol (with the flext::Bind function) which means that sending messages to the symbol (via the [send] object in PD or the [forward] object in Max/MSP) will be the same as sending those messages directly into the object’s leftmost inlet. The other way is (using FLEXT_BINDMETHOD) to bind a single class method to the symbol which is then called. Both approaches are depicted in the bind1 example object. Another function used therein is flext::forward. It mimics a [send] or [forward] object and passes a message to a symbol.

Page 33: C++ programming layer for cross-platform development of - grrrr.org

flext - C++ programming layer for cross-platform development of PD and Max/MSP externals

page 33

Figure III-8 – Binding objects or methods to symbols is an advanced form of communication of an external with

other elements of the real-time system. bind1 demonstrates how this is done.

/* flext tutorial - bind 1 Copyright (c) 2003 Thomas Grill ([email protected]) For information on usage and redistribution, and for a DISCLAIMER OF ALL WARRANTIES, see the file, "license.txt," in this distribution. ------------------------------------------------------------------------- This is an example of a simple object demonstrating method to symbol binding and message forwarding */ // include flext header #include <flext.h> // check for appropriate flext version #if !defined(FLEXT_VERSION) || (FLEXT_VERSION < 400) #error You need at least flext version 0.4.0 #endif // define the class that stands for a pd/Max object class bind1: // inherit from basic flext class public flext_base { // obligatory flext header (class name,base class name) featuring a setup function FLEXT_HEADER_S(bind1,flext_base,setup) public: // constructor with no arguments bind1() {

Page 34: C++ programming layer for cross-platform development of - grrrr.org

flext - C++ programming layer for cross-platform development of PD and Max/MSP externals

page 34

// define inlets: // first inlet must always be of type anything (or signal for dsp objects) AddInAnything("message inlet"); // add one inlet for any message AddInAnything("forwarding inlet"); // add one inlet for any message AddOutAnything("bound message"); // output received bound message } /* no destructor necessary here: flext frees all eventually remaining bound symbols when the object is destroyed (but NOT the data that can be passed via the FLEXT_BINDMETHOD call!) */ protected: const t_symbol *bufname; buffer *buf; // bind object void m_bind(const t_symbol *s) { if(!Bind(s)) { post("%s (%s) - Binding failed",thisName(),GetString(thisTag())); } } // unbind object void m_unbind(const t_symbol *s) { if(!Unbind(s)) { post("%s (%s) - Binding failed",thisName(),GetString(thisTag())); } } // bind method void m_bindmethod(const t_symbol *s) { if(!FLEXT_BINDMETHOD(s,m_bound,NULL)) { post("%s (%s) - Binding failed",thisName(),GetString(thisTag())); } } // unbind method void m_unbindmethod(const t_symbol *s) { if(!FLEXT_UNBINDMETHOD(s)) { post("%s (%s) - Binding failed",thisName(),GetString(thisTag())); } } // forward message void m_forward(const t_symbol *s,int argc,const t_atom *argv) { Forward(s,argc,argv); } // method for symbol-bound messages void m_bound(const t_symbol *sym,int argc,const t_atom *argv,void *data) { ToOutAnything(0,sym,argc,argv); } // method for binding test void m_test(float value) { post("%s - TEST METHOD: value %f",thisName(),value); } private: static void setup(t_classid c) { // register methods FLEXT_CADDMETHOD_(c,0,"bind",m_bind); // register method "bind" for inlet 0 FLEXT_CADDMETHOD_(c,0,"unbind",m_unbind); // register method "unbind" for inlet 0 FLEXT_CADDMETHOD_(c,0,"bindmethod",m_bindmethod); // register method "bindmethod" for inlet 0

Page 35: C++ programming layer for cross-platform development of - grrrr.org

flext - C++ programming layer for cross-platform development of PD and Max/MSP externals

page 35

FLEXT_CADDMETHOD_(c,0,"unbindmethod",m_unbindmethod); // register method "unbindmethod" for inlet 0 FLEXT_CADDMETHOD_(c,0,"test",m_test); // register method m_test for inlet 0 FLEXT_CADDMETHOD(c,1,m_forward); // register method m_forward for inlet 1 } FLEXT_CALLBACK_S(m_bind) // wrapper for method m_bind (with symbol argument) FLEXT_CALLBACK_S(m_unbind) // wrapper for method m_unbind (with symbol argument) FLEXT_CALLBACK_S(m_bindmethod) // wrapper for method m_bindmethod (with symbol argument) FLEXT_CALLBACK_S(m_unbindmethod) // wrapper for method m_unbindmethod (with symbol argument) FLEXT_CALLBACK_A(m_forward) // wrapper for method m_forward (with anything argument) FLEXT_CALLBACK_AX(m_bound) // wrapper for method m_bound (anything+data arguments) FLEXT_CALLBACK_F(m_test) // wrapper for method m_test (one float argument) }; // instantiate the class FLEXT_NEW("bind1",bind1)

8. Building libraries of externals Libraries of external objects are usable with PD by default. Several packages (like GEMk or zexyl) use the fact that it is handy to have all externals of a kind bundled together in one file. PD can load all these externals at once by using the –lib command line parameter. Max/MSP originally has no such feature but flext provides it nevertheless. Here, you would either have to place the library in the max-startup folder or provide a object mappings file which has been introduced with Max/MSP for OSX.

Figure III-9 – Instead of just one object per external module libraries allow the bundling of multiple objects in

one file, which simplifies inheritance of features and sharing of code and data.

k GEM – Graphics Environment for Multimedia - http://gem.iem.at/ l zexy - http://iem.at/pd/

Page 36: C++ programming layer for cross-platform development of - grrrr.org

flext - C++ programming layer for cross-platform development of PD and Max/MSP externals

page 36

/* flext tutorial - library 1 Copyright (c) 2002,2003 Thomas Grill ([email protected]) For information on usage and redistribution, and for a DISCLAIMER OF ALL WARRANTIES, see the file, "license.txt," in this distribution. ------------------------------------------------------------------------- This is an example of an external library containing a few simple objects. It uses attributes, so be sure that you've already worked through attr1 and attr2 */ // Enable attribute processing // For clarity, this is done here, but you'd better specify it as a compiler definition #define FLEXT_ATTRIBUTES 1 // include flext header #include <flext.h> // check for appropriate flext version #if !defined(FLEXT_VERSION) || (FLEXT_VERSION < 400) #error You need at least flext version 0.4.0 #endif // ------------------------------------------------------------------------------------- // Define the base class // Note that you don't have to instantiate the base class (with FLEXT_NEW or variants) class libbase: // inherit from basic flext class public flext_base { // obligatory flext header (class name,base class name) FLEXT_HEADER(libbase,flext_base) public: // constructor libbase(); protected: void Output(float f) const { ToOutFloat(0,f); } // method for floats into left inlet virtual void m_trigger(float f) = 0; float arg; // argument variable private: FLEXT_CALLBACK_F(m_trigger) // callback for method "m_trigger" (with one float argument) FLEXT_ATTRVAR_F(arg) }; libbase::libbase(): arg(0) // initialize argument { // define inlets: // first inlet must always by of type anything (or signal for dsp objects) AddInAnything(); // add one inlet for any message // define outlets: AddOutFloat(); // add one float outlet (has index 0) // register methods FLEXT_ADDMETHOD(0,m_trigger); // register method (for float messages) "m_float" for inlet 0 // register attributes FLEXT_ADDATTR_VAR1("arg",arg); // register attribute "arg" } // ------------------------------------------------------------------ // Define the actual library objects (derived from the base class) // These classes have an implementation of the virtual function m_trigger

Page 37: C++ programming layer for cross-platform development of - grrrr.org

flext - C++ programming layer for cross-platform development of PD and Max/MSP externals

page 37

class libadd: public libbase { // obligatory flext header, inherit from libbase FLEXT_HEADER(libadd,libbase) public: virtual void m_trigger(float f) { Output(f+arg); } }; FLEXT_LIB("lib1.+",libadd); class libsub: public libbase { // obligatory flext header, inherit from libbase FLEXT_HEADER(libsub,libbase) public: virtual void m_trigger(float f) { Output(f-arg); } }; FLEXT_LIB("lib1.-",libsub); class libmul: public libbase { // obligatory flext header, inherit from libbase FLEXT_HEADER(libmul,libbase) public: virtual void m_trigger(float f) { Output(f*arg); } }; FLEXT_LIB("lib1.*",libmul); // ------------------------------------------------ // Do the library setup static void lib_setup() { post("flext tutorial lib1, (C)2002 Thomas Grill"); post("lib1: lib1.+ lib1.- lib1.*"); post(""); // call the objects' setup routines FLEXT_SETUP(libadd); FLEXT_SETUP(libsub); FLEXT_SETUP(libmul); } // setup the library FLEXT_LIB_SETUP(lib1,lib_setup)

9. Using threads Normally, real-time systems have one thread of execution, which means that no two parts of the system or of an object can run concurrently. Multi-threading on the other hand allows the concurrent execution of functions. This can be very handy when a function triggered by a message runs for a longer time and would therefore block the real-time system, causing audio drop-outs. With a FLEXT_THREAD definition for the callback wrapper a method is designated to run as a detached thread whenever it is called. No matter how long the function takes, the message handler immediately returns, letting the function run in the background until it finishes operation. Multi-threading is tricky, though. Since more then one piece of code can access object data at the same time, causing inconsistencies, the data has to be protected. This can be done by a thread mutex which is represented by the flext::ThrMutex class. Additionally, there is the flext::ThrCond class used to broadcast signals to other threads. Using threads requires maximum caution, as otherwise timing problems will occur. Therefore, it is still considered an experimental flext feature.

Page 38: C++ programming layer for cross-platform development of - grrrr.org

flext - C++ programming layer for cross-platform development of PD and Max/MSP externals

page 38

Figure III-10 – Multi-threading is another advanced flext feature. It enables an object to run in the background

for a longer time, therefore not blocking the real-time system.

/* flext tutorial - threads 2 Copyright (c) 2002,2003 Thomas Grill ([email protected]) For information on usage and redistribution, and for a DISCLAIMER OF ALL WARRANTIES, see the file, "license.txt," in this distribution. ------------------------------------------------------------------------- This shows an example of multiple threads and syncing with a thread conditional */ /* define FLEXT_THREADS for thread usage. Flext must also have been compiled with that defined! it's even better to define that as a compiler flag (-D FLEXT_THREADS) for all files of the flext external */ #ifndef FLEXT_THREADS #define FLEXT_THREADS #endif #include <flext.h> #if !defined(FLEXT_VERSION) || (FLEXT_VERSION < 400) #error You need at least flext version 0.4.0 #endif class thread2: public flext_base { FLEXT_HEADER(thread2,flext_base) public: thread2(int del); protected: void m_start(int st); void m_stop(); void m_text(); void m_textout();

Page 39: C++ programming layer for cross-platform development of - grrrr.org

flext - C++ programming layer for cross-platform development of PD and Max/MSP externals

page 39

private: FLEXT_THREAD_I(m_start) // define threaded callback for method m_start FLEXT_CALLBACK(m_stop) // normal callback for m_stop FLEXT_CALLBACK(m_text) // turn on console output FLEXT_THREAD(m_textout) // text output float delay; volatile int count; // caution: CodeWarrior seems to ignore volatile modifier!! volatile bool stopit,running,blipping; // flags for running and stopping // thread conditional for stop signal ThrCond cond; }; FLEXT_NEW_1("thread2",thread2,int) thread2::thread2(int del): delay(del/1000.f), stopit(false), running(false),blipping(false) { AddInAnything(); AddOutInt(2); FLEXT_ADDMETHOD(0,m_start); // register start for integer numbers (floats in PD) FLEXT_ADDMETHOD_(0,"text",m_text); // register m_text method for "text" tag FLEXT_ADDMETHOD_(0,"stop",m_stop); // register m_text method for "stop" tag } void thread2::m_start(int st) { // if already running, just set back the counter if(running) { count = st; return; } running = true; // loop until either the system exit flag or the "stopit" flag is set for(count = st; !ShouldExit() && !stopit; ++count) { Sleep(delay); ToOutInt(0,count); // output loop count } running = false; // change state flag cond.Signal(); // signal changed flag to waiting "stop" method } void thread2::m_stop() { stopit = true; // set termination flag while(*(&running) || *(&blipping)) // workaround for CodeWarrior (doesn't honor volatile modifier!) { cond.Wait(); // wait for signal by running threads } // --- Here, the threads should have stopped --- stopit = false; // reset flag } void thread2::m_text() { FLEXT_CALLMETHOD(m_textout); } void thread2::m_textout() { if(blipping) return; blipping = true;

Page 40: C++ programming layer for cross-platform development of - grrrr.org

flext - C++ programming layer for cross-platform development of PD and Max/MSP externals

page 40

while(!ShouldExit() && !stopit) { post("%i",count); Sleep(1.f); } blipping = false; // change state flag cond.Signal(); // signal changed flag to waiting "stop" method }

10. Interfacing to other DSP frameworks Besides the patcher-based DSP systems there are other ones that just consist of programming interfaces but have no graphical representation. Externals can help here by providing an appropriate glue layer between the patcher-based real-time system and these frameworks – consequently, those DSP routines can be used inside PD or Max/MSP just as any other objects. For that, flext has built-in support classes for the two major C++ DSP frameworks.

a) STK18 The Synthesis ToolKit (STK) is a set of open source audio signal processing and algorithmic synthesis classes written in C++. It has been wisely designed to be platform-independent and it’s free as well, so it is therefore perfectly fitted for interfacing with flext. There is a large number of classes from simple delay and filter stuff to complex instruments based on physical modelling. Externals using STK objects can use the flext_stk base class providing the appropriate C++ interface. Three virtual functions have to be overridden for that: NewObjs, FreeObjs and ProcessObjs, doing obvious things as can be seen in the stk2 example.

Figure III-11 – STK (the Synthesis Toolkit) is a powerful set of unit generators.

Flext provides an interface to access this functionality.

Page 41: C++ programming layer for cross-platform development of - grrrr.org

flext - C++ programming layer for cross-platform development of PD and Max/MSP externals

page 41

/* flext tutorial - stk 2 Copyright (c) 2002,2003 Thomas Grill ([email protected]) For information on usage and redistribution, and for a DISCLAIMER OF ALL WARRANTIES, see the file, "license.txt," in this distribution. ------------------------------------------------------------------------- This is an example of an external using the STK ("synthesis toolkit") library. For STK see http://ccrma-www.stanford.edu/software/stk STK needs C++ exceptions switched on. The STK tutorial examples assume that a static stk library exists which contains all the source files (except rt*.cpp) of the stk/src directory. The library should be compiled multithreaded and with the appropriate compiler flags for the respective platform (e.g. __OS_WINDOWS__ and __LITTLE_ENDIAN__ for Windows) */ #include <flstk.h> #if !defined(FLEXT_VERSION) || (FLEXT_VERSION < 401) #error You need at least flext version 0.4.1 #endif #include "PitShift.h" class stk2: public flext_stk { FLEXT_HEADER_S(stk2,flext_stk,Setup) public: stk2(); void m_sh1(float f) { if(inst[0]) inst[0]->setShift(f); } void m_sh2(float f) { if(inst[1]) inst[1]->setShift(f); } // these are obligatory! virtual bool NewObjs(); // create STK instruments virtual void FreeObjs(); // destroy STK instruments virtual void ProcessObjs(int n); // do DSP processing PitShift *inst[2]; MY_FLOAT *vec; private: static void Setup(t_class *c); FLEXT_CALLBACK_F(m_sh1) FLEXT_CALLBACK_F(m_sh2) }; FLEXT_NEW_DSP("stk2~",stk2) stk2::stk2() { AddInSignal(); AddOutSignal(2); inst[0] = inst[1] = NULL; } void stk2::Setup(t_class *c) { FLEXT_CADDMETHOD_F(c,0,"shL",m_sh1); FLEXT_CADDMETHOD_F(c,0,"shR",m_sh2); }

Page 42: C++ programming layer for cross-platform development of - grrrr.org

flext - C++ programming layer for cross-platform development of PD and Max/MSP externals

page 42

// create STK instruments bool stk2::NewObjs() { bool ok = true; try { // set up objects for(int i = 0; i < 2; ++i) inst[i] = new PitShift; // reserve one signal vector too vec = new MY_FLOAT[Blocksize()]; } catch (StkError &) { post("%s - Creation failed!",thisName()); ok = false; } return ok; } // destroy the STK instruments void stk2::FreeObjs() { for(int i = 0; i < 2; ++i) if(inst[i]) delete inst[i]; if(vec) delete[] vec; } // this is called on every DSP block void stk2::ProcessObjs(int n) { for(int i = 0; i < 2; ++i) Outlet(i).tick( inst[i]->tick( Inlet(0).tick(vec,n) ,n) ,n); }

Page 43: C++ programming layer for cross-platform development of - grrrr.org

flext - C++ programming layer for cross-platform development of PD and Max/MSP externals

page 43

b) SndObj19 From its general structure and existing objects the Sound Object Library is very similar to STK, although it uses a slightly different way to do the DSP processing. Therefore it has been easy to find an example that does exactly the same as the above STK object. The base class flext_sndobj uses again the virtual functions NewObjs, FreeObjs and ProcessObjs which have to be overridden in a derived class.

Figure III-12 – Like STK, SndObj provide a number of useful transformation objects

which can be accessed by using a special flext C++ base class.

/* flext tutorial - sndobj 1 Copyright (c) 2002,2003 Thomas Grill ([email protected]) For information on usage and redistribution, and for a DISCLAIMER OF ALL WARRANTIES, see the file, "license.txt," in this distribution. ------------------------------------------------------------------------- This is an example of an external using the SndObj library. See http://www.may.ie/academic/music/musictec/SndObj/ The SndObj library should be compiled multithreaded. This external features simple stereo pitch shifting. */ #define FLEXT_ATTRIBUTES 1 #include <flsndobj.h>

Page 44: C++ programming layer for cross-platform development of - grrrr.org

flext - C++ programming layer for cross-platform development of PD and Max/MSP externals

page 44

#if !defined(FLEXT_VERSION) || (FLEXT_VERSION < 401) #error You need at least flext version 0.4.1 #endif class sndobj1: public flext_sndobj { FLEXT_HEADER_S(sndobj1,flext_sndobj,Setup) public: sndobj1(); // these are obligatory! virtual bool NewObjs(); virtual void FreeObjs(); virtual void ProcessObjs(); // space for a few sndobjs Pitch *obj1,*obj2; float sh1,sh2; private: static void Setup(t_class *c); FLEXT_ATTRVAR_F(sh1) FLEXT_ATTRVAR_F(sh2) }; FLEXT_NEW_DSP("sndobj1~",sndobj1) sndobj1::sndobj1(): sh1(1),sh2(1), obj1(NULL),obj2(NULL) { AddInSignal(2); // audio ins AddOutSignal(2); // audio outs } void sndobj1::Setup(t_class *c) { FLEXT_CADDATTR_VAR1(c,"shL",sh1); FLEXT_CADDATTR_VAR1(c,"shR",sh2); } // construct needed SndObjs bool sndobj1::NewObjs() { // set up objects obj1 = new Pitch(.1f,&InObj(0),sh1,Blocksize(),Samplerate()); obj2 = new Pitch(.1f,&InObj(1),sh2,Blocksize(),Samplerate()); return true; } // destroy the SndObjs void sndobj1::FreeObjs() { if(obj1) delete obj1; if(obj2) delete obj2; } // this is called on every DSP block void sndobj1::ProcessObjs() { // set current pitch shift obj1->SetPitch(sh1); obj2->SetPitch(sh2); // do processing here!! obj1->DoProcess(); obj2->DoProcess(); // output *obj1 >> OutObj(0); *obj2 >> OutObj(1); }

Page 45: C++ programming layer for cross-platform development of - grrrr.org

flext - C++ programming layer for cross-platform development of PD and Max/MSP externals

page 45

IV. Applications Naturally, flext doesn’t stand for itself but has been created for purposes of making developing externals easier, faster and less error-prone. It can be claimed that a large library such as VASP would not have been ready for use in a comparable time without flext. Although a prerequisite, flext has been developed in parallel to these external libraries, influencing one another. Thus, by the time, flext has become more stable and feature-rich.

1. xsamplem This was the first flext-based external library, now comprising three objects: xgroove~, xplay~ and xrecord~. The development was motivated by the fact that the sample objects built into PD were not very comfortable, while the ones delivered with Max/MSP exhibited some bugs that made actual usage difficult. Since then, the xsample library has emancipated and features efficiency and functionality (like several interpolation, looping and unit modes, signal-triggered recording, mix-in capability and a cross-fading loop zone) that is unparalleled by other comparable externals.

2. VASP modularn As described in more detail on the VASP website, this is a framework that complements the hosting real-time system by supplying functions to process array-based data rather than streaming signals. As there is hardly any such functionality delivered with standard PD or Max/MSP (although the optional Jitter framework does similar things) VASP has to provide numerous basic and more advanced functions, realized as individual external objects combined in one library. The emphasis of the system lies on granular and spectral transformations that are not limited to the usual constraints of signal block size or real-time-capability. VASP for PD is well-documented and more examples and tutorials will follow.

3. py/pyexto One of the major deficiencies of patcher-centered visual programming techniques is the lack of a general structural script language. Certain things (like more complicated loop constructs or access to computer or network resources) are notoriously difficult to solve with vanilla PD or Max/MSP. This is where py/pyext has its strengths. It’s an external that opens up the power of the Python20 script language to the real-time world. The external can load and execute a Python script, while providing an interface for it resembling any other normal native C or C++-based external object. As Python has a huge community of users, libraries and documentation this is just another universe to discover. As a script language that doesn’t need to be compiled, Python serves extremely well for rapid prototyping of externals or general experimentation. Future version of Python will be capable of steering signal processing as well, then providing much of the functionality of Supercollider or Aura. As the Python interpreter is not cleanly real-time-capable by itself, the external makes use of flext multi-

m xsample - extended sampling objects - http://www.parasitaere-kapazitaeten.net/ext/xsample n VASP modular – vector assembling signal processor - http://www.parasitaere-kapazitaeten.net/vasp o py/pyext – Python script externals - http://www.parasitaere-kapazitaeten.net/ext/py

Page 46: C++ programming layer for cross-platform development of - grrrr.org

flext - C++ programming layer for cross-platform development of PD and Max/MSP externals

page 46

threading techniques to let the code run in the background, therefore avoiding influences to the real-time-system’s own flow of operation.

4. othersp Several other externals and libraries thereof have been developed by me and others. I am extremely thankful to the contributions that other developers made to the flext idea and i’m happy to fix bugs as they become apparent or include new useful features to the library.

p various externals made by Thomas Grill – http://www.parasitaere-kapazitaeten.net/ext

Page 47: C++ programming layer for cross-platform development of - grrrr.org

flext - C++ programming layer for cross-platform development of PD and Max/MSP externals

page 47

Biography21 Thomas Grill, born 1972 1990+ study of technical physics at the University of Linz/Austria 1991+ self-education at the drum set, main interest in improvised and experimental music 1993+ concerts and performances with various ensembles at various places 1995+ intense engagement in electronic music, programming of audio software 2000+ study at the Institute for Composition and Electro-Acoustics at the University of

Music in Vienna/Austria 2000+ living and working in Vienna, cooking for Maggie. Concerts and musical activities in the years 2000-2003:

07.12.2000 Galerie Maerz/Linz - Festival Elektrokomplex: “Masterfoods” (pickup microphone and sweets), Geräuschorchester (drums) 15.03.2001 Alte Schmiede/Wien – Geräuschorchester (drums) 18.05.2001 Ruprechtskirche/Wien – „Misterios“ of Angélica Castelló and Albert Moser (sound engineering) 21.06.2001 Musikuniversität/Wien – Klassenabend ELAK „Musik im Raum“ Premiere of „Odem“ (Video/Sound/Performance) together with Johann Neumeister „Trio“ of Robert Kellner (drums) 12.10.2001 Künstlerhauspassage/Wien – Festival „inpotenza“: Geräuschorchester (drums) “Gnostische Behelligung I“ - Public performance together with Johann Neumeister „drei plus eins“ – Castelló*2/Kellner/Grill (electronic drums) CD recording „Trio“ with Robert Kellner and Angélica Castelló 05.06.2001 Stift St.Lambrecht – Geräuschorchester (drums)

Zuckerfabrik – Musical concept for the exhibition „Stairway to Heaven“ of Evelyn Grill Universität Wien/Aula – Geräuschorchester (Clarinet in B)

05.03.2002 Hörgänge Konzerthaus/Wien – „Prolog: Fremd I Vertraut“ (CD recording, electronics) 17.05.2002 VHS Stöbergasse/Wien – Sound engineering for the concert evening „XN-Nachtfalter“ of Angélica

Castelló/Katharina Klement/Johannes Kretz ELAK – CD recording with Angélica Castelló

14.05.2002 Musikuniversität Wien – X-String-Quartet (live electronics) 21.06.2002 Tanzhalle 1030 – Cooperation with Tanz*Hotel, Live electronics with Akemi Takeya 25.07.2002 Sarvar – Live electronics for Igor Lintz Maués “Kadenz”

Sound effects for the movie “All about Eve” by Martin Arnold 01.10.2002 Brucknerkonservatorium Linz – Adaptation of John Zorns “Cobra” game piece by Hannes Löschel, member

of the UNO-Ensemble (sampling, live electronics) 21.10.2002 Ruprechtskirche/Wien – Vocals for “full” of Thorunn Björnsdottir 22.10.2002 ELAK - CD recording and production with Angélica Castelló 09.11.2002 Wels/Austria – Festival “music unlimited” – Member of the UNO-Ensemble directed by Hannes Löschel 30.11.2002 Galerie 5020/Salzburg: Member of the X-String-Quartet (live electronics), Live sound installation “stabil/labil” 30.01.2003 Musikuniversität/Wien – Klassenabend ELAK: Live sound installation “stabil/labil” 07.02.2003 Posthof/Linz – Composition and musical director of „3-Torus“ by Monika Huemer with the X-String-

Quartet 03.03.2003 MICA/Echoraum/WUK - Festival “inpotenza”: 3 times Pollack/Bruckner/Schellander/Grill (drums) 07.03.2003 Echoraum/Wien – improvising Castelló/Heginger/Grill (sampling) 13.04.2003 Tanzhalle 1020/Wien – Music for the Tanz*Hotel dance performance “Quadrat*Quadrat” together with

Florian Bogner and Gilbert Handler (11 evenings) 24.04.2003 Tanzhalle 1020/Wien – Pollack/Schellander/Grill (electronics) 10.05.2003 Lange Nacht der Musik/Wien - Pollack/Bruckner/Schellander/Grill (electronics) 22.05.2003 Gymnasium Hegelgasse/Wien – Music for the adaptation of Ravels „L’enfant et les sortileges” by Marius

Schebella (7 performances) 24.05.2003 Studio Amann/Wien – Studio concert with Angélica Castelló and Susanna Borsch (electronics) 28.05.2003 fluc/Wien - Pollack/Bruckner/Schellander/Grill (drums) 31.05.2003 Porgy&Bess/Wien – Member of the UNO-Ensemble directed by Hannes Löschel 14.06.2003 Halle 1030 Hyegasse/Wien – A tribute to Dieter Feichtner (remix) 21.06.2003 Musikuniversität/Wien – Klassenabend ELAK: „ride“ (CD piece) 24.06.2003 Grabenfesttage/Wien – „fruitmarket gallery“ (Unterpertinger/Zach/Sperlich/Grill) playing „tettigonia

viridissima”

Page 48: C++ programming layer for cross-platform development of - grrrr.org

flext - C++ programming layer for cross-platform development of PD and Max/MSP externals

page 48

References 1 flext - http://www.parasitaere-kapazitaeten.net/ext/flext 2 Csound - e.g. http://www.csounds.com 3 CDP - http://www.bath.ac.uk/~masjpf/CDP/CDP.htm 4 fftbox/NMS4 (Günther Rabl) - http://www.canto-crudo.com 5 SuperCollider (James McCartney) - http://www.audiosynth.com 6 Miller Puckette - http://crca.ucsd.edu/~msp 7 PD (Miller Puckette) - http://sf.net/projects/pure-data 8 Max/MSP (David Zicarelli) - http://www.cycling74.com 9 jMax (IRCAM) - http://www.ircam.fr/produits/logiciels/jmax-e.html 10 Reaktor (Native Instruments) - http://www.nativeinstruments.de/index.php?reaktor_us 11 Aura (Roger Dannenberg) - http://www-2.cs.cmu.edu/~music/aura 12 Gstreamer - http://www.gstreamer.net 13 “Max external tutorial” by Ichiro Fujinaga -

http://gigue.peabody.jhu.edu/~ich/classes/dmp/Max.External.Tutorial.2.pdf 14 “Howto write an external for puredata” by IOhannes Zmölnig -

http://iem.kug.ac.at/pd/externals-HOWTO 15 flext tutorial and reference – downloadable at

http://www.parasitaere-kapazitaeten.net/ext/flext 16 Max/Jitter - http://www.cycling74.com/products/jitter.html 17 Frank Barknecht – http://www.footils.org 18 STK (Perry R. Cook, Gary P. Scavone) - http://ccrma-www.stanford.edu/software/stk 19 SndObjs (Victor Lazzarini) - http://www.may.ie/academic/music/musictec/SndObj 20 Python – http://www.python.org 21 Thomas Grill – http://www.parasitaere-kapazitaeten.net/~thomas