The API presented here is subject to changes.
This first version of the documentation is adapted from the FTS DSP Objects
Programming Guide
and some inconsistancies may subsist.
This document describes the FTS DSP objects programming interface. DSP objects are objects that computes samples and are scheduled at a fixed rate synchonous to the sample rate. DSP objects use the FTS message system, described in Control Objects Developer's Guide, but have special functions for sample computation and special methods for compilation of the DSP program.
The DSP program is a program written for a internal FTS DSP virtual machine, called FTL, that allows a better efficiency on DSP computations that a direct interpretation of the object network. It is constructed when needed. It consists of instructions with arguments, typically functions calls, and private data. The DSP program is executed each time a new buffer of samples is needed by the device that is associated with audio output.
FTS DSP objects differ from usual control objects by the following points :
put
method
this special method is the method that will generate the code pertinen to this
object in the DSP program
This is done using the 2 following functions :
void fts_dsp_declare_inlet( fts_class_t *class, int winlet)
void fts_dsp_declare_outlet( fts_class_t *class, int woutlet)
The arguments of these functions have the following meaning :
A DSP function has a special signature, which is the following :
void <my_dsp_function>( fts_word_t *args)
DSP functions are discussed in details later.
In a DSP class instantiation function, a DSP function is declared with the following function :
void fts_dsp_declare_function( fts_symbol_t *name, void (*dsp_function)(fts_word_t *))
The arguments of this function have the following meaning :
fts_symbol_t
used as first argument to dsp_declare_function
will
be reused later, it is convenient to store it in a variable local to the class being
defined. This is detailled in the example below.
DSP specific methods declaration
A DSP class must declare 3 methods that are mandatory : init, delete and put methods.
This is done using the fts_method_define
function, which is described in
details in
Control Objects Developer's Guide.
These methods are associated with
the system inlet, with the following selectors and arguments :
init
delete
put
typedef struct { fts_object_t obj; float s; } sma_t; static fts_symbol_t *sma_function = 0; /* Declaration of DSP function : content will be detailled later */ static void sma_dsp_function( fts_word_t *args); /* Declarations of methods (will be detailled later) */ static void sma_init( fts_object_t *o, int winlet, fts_symbol_t *s, int ac, const fts_atom_t *at); static void sma_delete( fts_object_t *o, int winlet, fts_symbol_t *s, int ac, const fts_atom_t *at); static void sma_put( fts_object_t *o, int winlet, fts_symbol_t *s, int ac, const fts_atom_t *at); static void sma_set( fts_object_t *o, int winlet, fts_symbol_t *s, int ac, const fts_atom_t *at); static fts_status_t sma_instantiate(fts_class_t *cl, int ac, const fts_atom_t *at) { fts_symbol_t a[2]; /* class initialization : 2 inlets, 1 outlet */ fts_class_init(cl, sizeof(sma_t), 2, 1, 0); /* definition of DSP specific methods */ fts_method_define_varargs( cl, fts_SystemInlet, fts_s_init, sma_init); fts_method_define_varargs( cl, fts_SystemInlet, fts_s_delete, sma_delete); fts_method_define_varargs(cl, fts_SystemInlet, fts_s_put, sma_put); /* definition of other methods */ fts_method_define_varargs(cl, 0, fts_s_int, sma_set); fts_method_define_varargs(cl, 0, fts_s_float, sma_set); /* declaration of DSP inlets and outlets */ fts_dsp_declare_inlet(cl, 0); fts_dsp_declare_inlet(cl, 1); fts_dsp_declare_outlet(cl, 0); /* declare DSP function and keep the associated symbol */ sma_function = fts_new_symbol("sma"); fts_dsp_declare_function( sma_function, sma_dsp_function); return fts_Success; }
this->obj_ftl_data = ftl_data_alloc(sizeof(dsp_data_t));
dsp_data_t
must be the C type of the data structure to be allocated.
The function return a ftl_data_t
value, that must be stored
in the object for later use, namely to access and modifing the data, and
to pass it to the DSP program in the put
method; an object is free
to allocate as many ftl data item as needed.
The ftl data items should then be initialized to some default or
initial values; see the paragraph
Modifing The FTL Data.
It should be stressed that a ftl_data_t
value is not
necessarly a C pointer, and the only was to access it are those
explained in this chapter; see the paragraph
Limitations of FTL Data items for details.
The other task that init
method of a DSP class must
perform is to insert the object being initialized in the list of DSP
objects. This list is maintained by the system for DSP program
compilation.This is done by calling the following function :
void fts_dsp_add_object( fts_object_t *object)
The argument of this function is a pointer to the object that is to be inserted.
Delete method
The delete
method of a DSP class must free all the ftl data
items allocated in the init
method, by calling the ftl_data_free
function on each of them, like in:
ftl_data_free(this->obj_ftl_data)
The delete
method of a DSP class must also call the following function
in order to remove the object being deleted from the list of DSP objects :
void fts_dsp_remove_object( fts_object_t *object)
The argument of this function is a pointer to the object that is to be
removed. It must point to an object that has been inserted using
function fts_dsp_add_object
.
The init
method, or any other method can change the content
of a ftl data item.
To get the pointer associated with a FTL data, the following macro is provided:
ftl_data_get_ptr( DATA)
. The value of this macro is the pointer
contained in the FTL data handle.
Limitations of FTL Data items
FTL data are not freely accessible C data structure; the type ftl_data_t
is not a C pointer to the data structure; in general, there is no way for the DSP
object to know the current content of a ftl data item; if a particular value or parameter
is needed also in control computation, should be stored also inside the DSP object.
The reason for these limitations is that ftl data items are entity that live in the
DSP program execution; they may be implemented by memory in the same address space
of the control computation, but they also reside in an other address space, in an other
processor or in an other "logical time".
In particular, in future multi-thread release of FTS, the DSP
computation and the control computation will happen in separate
threads, and they will not be necessarly synchronius; a particular ftl
data item may actually correspond to the status seen by the control
thread one o many scheduling loops before; this is why the control
cannot directly access the DSP program memory.
Also, in architecture with small caches (like the ISPW), DSP data can
be dynamically rearranged to maximize locality.
Example of init and delete methods
Below is the example of the init
and delete
methods of
the DSP object sma
which has already been introduced.
static void sma_init( fts_object_t *o, int winlet, fts_symbol_t *s, int ac, const fts_atom_t *at) { sma_t *this = (sma_t *)o; float *ptr; /* allocate ftl data */ this->sma_ftl_data = ftl_data_alloc(sizeof(float)); /* initializing the ftl data */ ptr = (float *)ftl_data_get_ptr( this->sma_ftl_data); *ptr = fts_get_float_arg(at, ac, 1, 0.0); /* add object to the DSP graph */ fts_dsp_add_object(o); } static void sma_delete( fts_object_t *o, int winlet, fts_symbol_t *s, int ac, const fts_atom_t *at) { /* free ftl data */ ftl_data_free(this->sma_ftl_data); /* remove object from the DSP graph */ fts_dsp_remove_object(o); }
set
method of
the DSP object sma
which need to change a parameter used
by the object DSP function.
static void sma_set( fts_object_t *o, int winlet, fts_symbol_t *s, int ac, const fts_atom_t *at) { sma_t *this = (sma_t *)o; float *p; /* setting the ftl data */ p = (float *)ftl_data_get_ptr( this->sma_ftl_data); *p = fts_get_float_arg( at, ac, 1, 0.0); }
void
A DSP function has a unique argument of type fts_word_t *
.
This type is an union containing either a int, a float, a fts_symbol_t
or a pointer. The fts_word_t
and fts_symbol_t
structures are discussed in details in
FTS Kernel Reference Manual
.
DSP function arguments
The arguments of the DSP function are stored in an array of elements of type
fts_word_t
.
This type is an union containing either a int, a float, a fts_symbol_t
or an void
pointer, specified as ftl_data_t
in the
put
method. The fts_word_t
and fts_symbol_t
data structures are discussed in details in the
FTS Kernel Reference Manual .
Arguments are accessed using the following macros :
fts_word_get_symbol
( AP)
fts_symbol_t *
fts_word_get_string
( AP)
const char *
fts_word_get_obj
( AP)
void *
fts_word_get_int
( AP)
int
fts_word_get_float
( AP)
float
sma
object already
described.
static void sma_dsp_function( fts_word_t *args) { float *in1 = (float *) fts_word_get_obj(args); /* first input buffer */ float *in2 = (float *) fts_word_get_obj(args + 1); /* second input buffer */ float *out = (float *) fts_word_get_obj(args + 2); /* output buffer */ float *ps = (float *) fts_word_get_obj(args + 3); /* pointer to scalar */ int n = fts_word_get_int(args + 4); /* size of buffers */ float s; int i; s = *ps; for ( i = 0; i < n; i++) out[i] = s * in1[i] + in2[i]; }The previous code can be compared with the code performing the same task that would be found in a usual DSP library :
static void sma( float *src1, float *src2, float *out, float *s, int n) { float s; int i; s = *ps; for ( i = 0; i < n; i++) out[i] = s * in1[i] + in2[i]; }
put
method is a method specific to DSP objects, that
is called by the system when computing the DSP program. The put
method must complete the 2 following tasks, in this order :
put
method is called during the computation of the DSP program.
As previously mentionned, it has one argument of type fts_Object
.
This argument is a pointer to a fts_dsp_descr_t
structure containing the necessary
information for DSP program building.
fts_dsp_descr_t
structurefts_dsp_descr_t
structure is a opaque structure used by the
DSP compiler to pass information about the code to be generated to the
put
method.
The structure is passed to the put
method (see below) as
first argument, and can be accessed in this way:
fts_dsp_descr_t *dsp = (fts_dsp_descr_t *)fts_get_obj_arg(at, ac, 0, 0);
n
Programmer should never access the structure directly, but should
instead the access macros documented below; this macros allow to access
the input and output sample buffers, their size and their corresponding
sample rate.
The sample buffers are referred by name; the buffer name are FTS
symbol generated by the DSP compiler; they must be passed to the dsp
function as name, the dsp compiler will then convert them to the
correct float *
pointer.
In this release of FTS, all the inputs have the same size and sample rate;
also all the inputs and outputs have the same size and sample rate.
The macros are:
fts_symbol_t *fts_dsp_get_input_name(fts_dsp_descr_t *desc, int in);
in
.
int fts_dsp_get_input_size(fts_dsp_descr_t *desc, int in);
in
.
int fts_dsp_get_input_srate(fts_dsp_descr_t *desc, int in);
in
.
int fts_dsp_get_is_input_null(fts_dsp_descr_t *desc, int in);
in
is the null buffer, i.e. if there are no signals connected to the input.
fts_symbol_t *fts_dsp_get_output_name(fts_dsp_descr_t *desc, int out);
out
.
int fts_dsp_get_output_size(fts_dsp_descr_t *desc, int out);
out
.
int fts_dsp_get_output_srate(fts_dsp_descr_t *desc, int out);
out
.
void fts_dsp_add_function( fts_symbol_t *name, int argc, fts_atom_t *argv)
The arguments of this function have the following meaning :
fts_dsp_declare_function
which have already been described.
The corresponding atom of the arguments array must be filled using the macros described above, according to the following rules :
int
and its value to the
length
member of the dsp_signal
structure. For example, to set
the fourth argument to the size of the first input buffer :
fts_set_int(args + 3, fts_dsp_get_input_size(dsp, 0);
name
member of the dsp_signal
structure. For example, to set
the first argument to the float array of the first input buffer :
fts_set_symbol(args, fts_dsp_get_input_name(dsp, 0);
The reason for this is that the buffers are not allocated during DSP program building, but after finishing the DSP program building. The buffer names are used as handles to pass them to the DSP program interpreter, which keeps its internal symbol tables.
fts_set_ftl_data
macro must be used, like in:
fts_set_ftl_data(args, this->sma_ftl_data);
Example of a put method
Below is the code of the put
method of the sma
object
already mentionned. The code of the DSP function of this object is given again
in order to make reading easier.
static void sma_put(fts_object_t *o, int winlet, fts_symbol_t *s, int ac, const fts_atom_t *at) { sma_t *this = (sma_t *)o; fts_dsp_descr_t *dsp = (fts_dsp_descr_t *)fts_get_obj_arg(at, ac, 0, 0); fts_atom_t args[5]; fts_set_ftl_data(args + 0, this->sma_ftl_data); fts_set_symbol(args + 1, fts_dsp_get_input_name(dsp, 0)); fts_set_symbol(args + 2, fts_dsp_get_input_name(dsp, 1)); fts_set_symbol(args + 3, fts_dsp_get_output_name(dsp, 0)) fts_set_int(args + 4, fts_dsp_get_input_size(dsp, 0)); fts_dsp_add_function(sma_function, 5, args); } static void sma_dsp_function( fts_word_t *args) { float *ptr = (float *) fts_word_get_obj(args + 0); /* pointer to scalar */ float *in1 = (float *) fts_word_get_obj(args + 1); /* first input buffer */ float *in2 = (float *) fts_word_get_obj(args + 2); /* second input buffer */ float *out = (float *) fts_word_get_obj(args + 3); /* output buffer */ int n = fts_word_get_int(args + 4); /* size of buffers */ int i; for(i=0; i<n; i++) out[i] = *ptr * in1[i] + in2[i]; }
Copyright © 1995,1999 IRCAM. All rights reserved. |