Download - Grill 2004 Flext
-
8/6/2019 Grill 2004 Flext
1/48
flextC++ programming layer for cross-platform
development of PD and Max/MSP externals
An introduction
by Thomas Grill
Abschlussarbeit des Lehrgangs Computermusik und elektronische MedienInstitut fr Komposition und Elektroakustik
Universitt fr Musik und darstellende Kunst Wien
-
8/6/2019 Grill 2004 Flext
2/48
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 ones 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). flextcurrently 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 fr die Erweiterung der verbreitetsten Echtzeit-
Audio-Systeme Max/MSP und Pure Data (PD) mit externen Modulen, oder kurz externals,
zur Verfgung. Solche Module bieten die Mglichkeit, diese Systeme fr spezielle
Anforderungen mazuschneidern bzw. um zustzliche Funktionalitt zu erweitern.
Programmcode, der auf flext basiert, kann beinahe alle Funktionen des jeweiligen
Echtzeitsystems ausschpfen und dennoch vllig unabhngig vom tatschlichen eingesetzten
Zielsystem oder der Plattform (Hardware und Betriebsystem) bleiben.flextuntersttzt derzeit
PD fr Linux, Windows und OSX, sowie Max/MSP fr OS9 und OSX (und in Krze
Windows). Untersttzung fr jMax unter Linux, OSX und Windows und andere Systeme
kann in der Zukunft folgen.
-
8/6/2019 Grill 2004 Flext
3/48
flext - C++ programming layer for cross-platform development of PD and Max/MSP externals
page 3
Contents
Abstract ......................................................................................................................................2
Contents...................................................................................................................................... 3
I. Introduction ........................................................................................................................ 4II. Basics ................................................................................................................................. 8
1. Prerequisites ................................................................................................................... 8
a) Build environments .................................................................................................... 9
b) Installing the real-time-system SDK ........................................................................ 10
c) Obtaining and installing theflextlibrary..................................................................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 .......................................................................................................................... 40b) SndObj......................................................................................................................43
IV. Applications ................................................................................................................. 45
1. xsample......................................................................................................................... 45
2. VASP modular .............................................................................................................45
3. py/pyext........................................................................................................................ 45
4. others ............................................................................................................................ 46
Biography................................................................................................................................. 47
References ................................................................................................................................ 48
-
8/6/2019 Grill 2004 Flext
4/48
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, CDP
3or fftbox/NMS4
4.
Striving for increased user-friendliness two main advancements of dealing with these
descriptions of transformation processes have crystallized:
The functionalapproach: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 visualapproach: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/MSP
8, jMax
9and not very
different from them, Reaktor10.
a From the SuperCollider glossary: UGEN An object that produces or processes audio data
-
8/6/2019 Grill 2004 Flext
5/48
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 tickbut onlywhen 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
-
8/6/2019 Grill 2004 Flext
6/48
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 constantlystreaming 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,Aura
11, GStreamer
12) 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 theirinterconnection (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
-
8/6/2019 Grill 2004 Flext
7/48
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 flextdoes not attempt to unite them. They are simply too
different. However, other independent graphics systems may (and will) be based onflext.
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 thatflextmay well once cover more of these systems.
-
8/6/2019 Grill 2004 Flext
8/48
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 flextcomes 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.
flextdoes 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 differentaspect of theflextlibrary.
1. Prerequisitesflext is a programming library which implies that when using it you will have to do some
programming in C++. However, programming isnt 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.
-
8/6/2019 Grill 2004 Flext
9/48
flext - C++ programming layer for cross-platform development of PD and Max/MSP externals
page 9
a) Build environments
flextsupports 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 offlext.
Borland C++ 5.5This compiler package is lightweight and its freely downloadable from the Borland
homepagec. It has a good C++ support but cannot handle the more advanced multi-
threading features offlext. cygwin gcc
cygwind
is a Unix environment capable of running under Windows. Its 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 upwardsThis 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 offlextalso supported the free Apple MPW environment and it may still
work but theres no guarantee for that (its simply too weak)
iii) Mac OSX
OSX has inherited a burden from its predecessor: its 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.
cBorland C++ 5.5 download - http://www.borland.com/products/downloads/download_cbuilder.html#
d cygwin - http://www.cygwin.com
-
8/6/2019 Grill 2004 Flext
10/48
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) LinuxMax/MSP wont 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, its 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
doesnt come with the standard distribution of PD or Max/MSP necessarily but you can
download them from the respective website. For PD youll have to download its source code
distribution packageg
and for Max/MSP youll need the Max/MSP Software Development
Kith.
c) Obtaining and installing the flextlibrary
After downloadingia currentflextrelease for your platform and unzipping the compressed file
you will find areadme.txt
containing some information, the license textslicense.txt
andgpl.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 youll than have a subfolder flext to your pd installation
containing various *.h C-header files as well as several *.libflextlibrary files.
If you want to use the CodeWarrior build environment to compile flextfor Max/MSP, things
are a bit different. Youll 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 theflextframework.
Probably this preceding description is too light-minded to get you started with the
development system and the flext library. Anyhow, Id 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/toolsf GNU g++ - http://gcc.gnu.orgg
PD source code packages - http://www-crca.ucsd.edu/~msp/software.htmlh Max/MSP SDK - http://www.cycling74.com/products/dlmaxmsp.htmli flextdownload - http://www.parasitaere-kapazitaeten.net/ext/flext
-
8/6/2019 Grill 2004 Flext
11/48
flext - C++ programming layer for cross-platform development of PD and Max/MSP externals
page 11
d) Programming in C++
Flextmassively uses nearly all aspects of the C++ language. However, this doesnt imply that
you have to do that as well. By looking at the code of several simpleflext-based objects youll
surely recognize the elements that these objects are made up of. In this sense its 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 beginners difficulties.
e) License issues
Flextis distributed under the GPL licensej, which you should carefully read. This means that
download and usage offlext is free and that the source code offlext is fully disclosed. The
GPL has several other implications, one of which is that externals programmed withflextneed
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 worldbecome a better one.
j GNU General Public License - http://www.gnu.org/copyleft/gpl.html
-
8/6/2019 Grill 2004 Flext
12/48
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. Theactual 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 typicalflext-based external.
#include
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 offlext 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 dont 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 afloat message (and nothing else).
-
8/6/2019 Grill 2004 Flext
13/48
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 afloating point value.
The method simply calculates the inverse of an input value (= 1 / value). If the value is zero
(which cant 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 wrapperfor 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)
-
8/6/2019 Grill 2004 Flext
14/48
flext - C++ programming layer for cross-platform development of PD and Max/MSP externals
page 14
3. How to build flext-based externalsNow that you haveflextinstalled 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 canbe 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 objectformat. Thisis 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 Youll 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 theflextlibrary
o Youll 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 objectfile(s) must be linked with theflextlibrary 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 doesnt 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
flextlibrary 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).
-
8/6/2019 Grill 2004 Flext
15/48
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 thatflextsupports or the functionality which it additionally provides.
1. Simple message based externalsYou 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
// 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)
-
8/6/2019 Grill 2004 Flext
16/48
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 thatflext-basedobjects 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.
-
8/6/2019 Grill 2004 Flext
17/48
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
// 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 methodsFLEXT_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;
}
-
8/6/2019 Grill 2004 Flext
18/48
flext - C++ programming layer for cross-platform development of PD and Max/MSP externals
page 18
2. More advanced message based externalsThe 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 Zmlnig which he presents in his Howto write an external for puredata. It has
been chosen so that a direct comparison of aflextexternal to a native one is possible.Most of the features of the original object translate one to one, while some must be
implemented differently withflext. This is explained in the source code comments.
Figure III-3 adv3 is a port of the counterexample from Iohannes Zmlnigs 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 Zmlnigs "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
*/
-
8/6/2019 Grill 2004 Flext
19/48
flext - C++ programming layer for cross-platform development of PD and Max/MSP externals
page 19
// include flext header
#include
// 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);
}
elseif(i_count < i_down) {
i_count = i_up;
ToOutBang(1);
}
}
ToOutInt(0,f);
}
void m_bound(int f1,int f2)
{
-
8/6/2019 Grill 2004 Flext
20/48
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:
staticvoid 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 attributesAnother advanced feature offlext-based externals is the incorporation of the Max/Jitter
16-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 thenconsequently been adopted byflext.
Attributes can either be set by an objects creation arguments (with e.g. @attribute 1) or by
the use of a settermessage into the leftmost inlet (like for example [attribute 1( ) and can be
queried by sending a complementary gettermessage 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-enabledflextexternal 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.
-
8/6/2019 Grill 2004 Flext
21/48
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 beused 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
// check for appropriate flext version
#if !defined(FLEXT_VERSION) || (FLEXT_VERSION < 401)
#error You need at least flext version 0.4.1
#endif
#include
-
8/6/2019 Grill 2004 Flext
22/48
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;
staticconst t_symbol *sym_set,*sym_add,*sym_sub,*sym_div,*sym_mul,*sym_pow;
private:
staticvoid 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);
}
-
8/6/2019 Grill 2004 Flext
23/48
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;elseif(s == sym_add)
op = op_add;
elseif(s == sym_sub)
op = op_sub;
elseif(s == sym_mul)
op = op_mul;
elseif(s == sym_div)
op = op_div;
elseif(s == sym_pow)
op = op_pow;
else {
post("%s - operation is not defined, set to =",thisName());
op = op_set;
}
}
-
8/6/2019 Grill 2004 Flext
24/48
flext - C++ programming layer for cross-platform development of PD and Max/MSP externals
page 24
4. DSP (signal-based) externalsThe main domain of most externals is, of course, some kind of signal processing. Flextfully
supports all variants of it, although it uses a slightly different approach than the PD or
Max/MSP API does.
In aflext-based signal external you simply override the m_signal virtual function. This meansthat 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 Zmlnigs tutorial and has been ported to
flextby Frank Barknecht17
.
Figure III-5 signal1~ illustrates the handling of signals within a flext-based external.
Its an adaptation of the pan~ object from IOhannes Zmlnigs 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
#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:
-
8/6/2019 Grill 2004 Flext
25/48
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
virtualvoid 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 = (f1) ? 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.
-
8/6/2019 Grill 2004 Flext
26/48
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)
{
constfloat *ins1 = in[0];
constfloat *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 buffersClosely 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 butflextprovides 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)
-
8/6/2019 Grill 2004 Flext
27/48
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
// 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)
inlinevoid ms_buf(const AtomList &lst) { m_set(lst.Count(),lst.Atoms()); }
// get buffer channels
inlinevoid mg_chns(int &chns) { chns = Check()?buf->Channels():0; }
// get buffer length in frames
inlinevoid mg_frames(int &frames) { frames = Check()?buf->Frames():0; }
// set buffer length in frames
inlinevoid 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:
staticvoid 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)
-
8/6/2019 Grill 2004 Flext
28/48
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
returnfalse;
}
else {
if(buf->Update()) {
// buffer parameters have been updated
if(buf->Valid()) {
post("%s (%s) - updated buffer reference",thisName(),GetString(thisTag()));
returntrue;}
else {
post("%s (%s) - buffer has become invalid",thisName(),GetString(thisTag()));
returnfalse;
}
}
else
returntrue;
}
}
void buffer1::m_set(int argc,const t_atom *argv)
{
if(argc == 0) {
// argument list is empty
// clear existing buffer
Clear();
}
elseif(argc == 1 && IsSymbol(argv[0])) {
-
8/6/2019 Grill 2004 Flext
29/48
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()));
}
-
8/6/2019 Grill 2004 Flext
30/48
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 timersAnother 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 classwhich 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 aspecial 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.
-
8/6/2019 Grill 2004 Flext
31/48
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
// 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 systemtime 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:
staticvoid 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)
-
8/6/2019 Grill 2004 Flext
32/48
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 AFLEXT_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 symbolsAs 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 wont 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 objects 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.
-
8/6/2019 Grill 2004 Flext
33/48
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
// 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()
{
-
8/6/2019 Grill 2004 Flext
34/48
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:
staticvoid 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
-
8/6/2019 Grill 2004 Flext
35/48
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 externalsLibraries of external objects are usable with PD by default. Several packages (like GEM
kor
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 flextprovides 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/
-
8/6/2019 Grill 2004 Flext
36/48
-
8/6/2019 Grill 2004 Flext
37/48
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:
virtualvoid 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:
virtualvoid 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:
virtualvoid m_trigger(float f) { Output(f*arg); }
};
FLEXT_LIB("lib1.*",libmul);
// ------------------------------------------------
// Do the library setup
staticvoid 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 threadsNormally, real-time systems have one thread of execution, which means that no two parts ofthe 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 threadwhenever 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 experimentalflextfeature.
-
8/6/2019 Grill 2004 Flext
38/48
flext - C++ programming layer for cross-platform development of PD and Max/MSP externals
page 38
Figure III-10 Multi-threading is another advancedflextfeature. 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
#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();
-
8/6/2019 Grill 2004 Flext
39/48
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;volatileint count;
// caution: CodeWarrior seems to ignore volatile modifier!!
volatilebool 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 setfor(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;
-
8/6/2019 Grill 2004 Flext
40/48
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 frameworksBesides 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,flexthas 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
its 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.
Flextprovides an interface to access this functionality.
-
8/6/2019 Grill 2004 Flext
41/48
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
#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!virtualbool NewObjs(); // create STK instruments
virtualvoid FreeObjs(); // destroy