Grill 2004 Flext

download Grill 2004 Flext

of 48

Transcript of 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