Developing jMax control objects

Disclaimer

The API presented here may be subject to small changes.

This first version of the documentation is adapted from the Ircam jMax documentation and some inconsistencies may subsist.

Introduction

This documentation introduces the jMax objects API, known in Max-Opcode as the “external” objects API. As a consequence of the client/server architecture of jMax, these objects are loaded and executes in the jMax server, called FTS. The documentation will then use the term FTS message system to denote the server objects API.

The FTS message system combines an object system and a message passing system. It is a new definition of Max/FTS message system already implemented in Max/Macintosh and in previous versions of Max/FTS on the ISPW.

The main concepts of FTS message system are:

  • objects : a FTS object is a state (some memory holding values)

and a set of connections used for message sending between objects

  • messages : a FTS message is a selector (a symbol) and a list

of arguments which are atoms

  • inlets and outlets :to each object is associated a set of receptors, the

inlets and a set of emitters, the outlets. An inlet receives messages and messages can be emitted on outlets

  • methods : the reception of a message on an inlet triggers the call of a method. A FTS

method is a C function with a particular signature

  • classes : a FTS class is a set of objects having a common interface

regarding the object model

  • metaclasses : a FTS metaclass is a set of classes sharing common behavior

The main point to get is that in FTS, each object in a class have the same behaviour of the other objects in the class, in terms of number of inlets and outlets and in terms of messages the object can respond to.

This is in apparent contradiction with the fact that objects with the same name (like trigger, for example) can have a different number of inlets and outlets; this is actually handled by the metaclass/class hierarchy. An object name correspond to a metaclass; instances of the same name with different behaviour actually have different classes, but the same metaclass.

[Apply to jMax 0.7 and above]. In jMax Phoenix, object developers have some more freedom on how to write objects: the API allows a style where the metaclass/class relationship is explicitly handled (the traditional jMax API), and an API (or more precisely an API extension) that allows developers to ignore the metaclass/class hierarchy and to program in a way that is very close to the old Max/FTS or to the Puredata API. The result is the same, in the second case the object system take in care the housekeeping of the metaclass/class hierarchy. This page is being updated with all the documentation about the API extension.

Objects

A FTS object is an abstraction that store information and can receive and emit messages. To an object is allocated some memory storing its state as a C structure.

typedef struct
{
  fts_object_t o;   /* MUST BE FIRST STRUCTURE MEMBER */
  long n;           /* the state of a integer object */
} integer_t;

An FTS object must always have a first structure member of type fts_object_t. This implements in C the fact that all FTS objects inherits from the fts_object_t type.

The only “system” member of the structure which is made mandatory by the FTS object system is the first member of type fts_object_t. The other members of the structure are “user” members and are not handled by the system.

An number of functions or macros are provided to get some of the object characteristic:

int fts_object_get_outlets_number(fts_object_t *obj);

Return the number of outlets the object obj have.

int fts_object_get_inlets_number(fts_object_t *obj);

Return the number of inlets the object obj have.

const char *fts_object_get_class_name(fts_object_t *obj);

Return as a C string the name of the object class; to be used only for user messages.

Methods

A method is a C-function that will be called when an object receives a message on one of its inlets. It has a standard signature, and all the methods access their arguments the same way.

Method Signature

All FTS methods have the same signature, i.e. the same number of arguments of the same types. This signature is the following :

void method(fts_object_t *object, int winlet, fts_symbol_t selector, int ac,  const fts_atom_t *at)

The arguments of the method have the following meaning :

* object: a pointer to the object receiving the message * winlet: the number of the inlet on which message was received * selector: the message selector, a symbol. The fts_symbol_t

 data structure is discussed in details in the FTS Kernel Reference Manual.

* ac: the number of arguments * at: the arguments array

Method Arguments

The arguments of a method are of type fts_atom_t. The fts_atom_t data structure is discussed in details in the FTS Kernel Reference Manual.

The arguments are passed by the message system as an array of constant atoms. This means that a method cannot modify its arguments. Modifying the arguments of a method in the body of the method can lead to unpredictable side effects. If this is needed for convenience, the arguments must first be copied.

Accessing Arguments

A method access its arguments via access macros, which handle both accessing the atoms and giving a default value if corresponding argument is optional.

It is strongly recommended to use the arguments access macros. Accessing directly the members of the fts_atom_t structure is guaranteed not to work across different releases of FTS, because fts_atom_t implementation may change.

This is an example of a simple method, taking one argument of type int, with a default value of 0, which stores the value of this argument in the state of the object :

void integer_in1(fts_object_t *object, int winlet, fts_symbol_t selector, 
                  int ac, const fts_atom_t *at)
{
  integer_t *this = (integer_t *)object;
 
  this->n = fts_get_int_arg(ac, at, 0, 0);
}

In this method, the object argument, typed as an fts_object_t * is in fact a pointer to an object of type integer_t (defined earlier), which first structure member is of type fts_object_t. This explains the cast made at beginning of method.

List of Access Macros

Below is the list of available access macros :

  • fts_get_int_arg(AC, AT, N, DEFAULT): gets an argument of type int.
  • fts_get_float_arg(AC, AT, N, DEFAULT): gets an argument of type float.
  • fts_get_symbol_arg(AC, AT, N, DEFAULT): gets an argument of type fts_symbol_t.
  • fts_get_string_arg(AC, AT, N, DEFAULT): gets an argument of type const char *.
  • fts_get_ptr_arg(AC, AT, N, DEFAULT): gets an argument of type void *.

The macro arguments have the following meaning :

  • AC: the number of atoms in array AT
  • AT: the atom array
  • N: the index of the requested argument
  • DEFAULT: the default value for the requested argument

All these macros have the same semantic : if N is less than AC, return the value of atom N in array AT, else return value DEFAULT.

Message Sending

Messages can be send on the outlets of an object, using the fts_outlet_send function.

The fts_outlet_send Function

fts_status_t  fts_outlet_send(fts_object_t *object, int woutlet, fts_symbol_t selector, int argc, const fts_atom_t *args)

The arguments of fts_outlet_send have the following meaning :

  • object: the object from which outlet the message is send
  • woutlet: the number of the outlet on which the message is send
  • selector: the message selector, a symbol
  • argc: the count of arguments of the message
  • args: the arguments of the message, an array of atoms

Message Sending Example

Message sending is most of the time done in the body of a method, as in the following example, a method sending on the outlet the content of the state of the object :

void integer_bang(fts_object_t *object, int winlet, fts_symbol_t selector,
                   int ac, const fts_atom_t *at)
{
  integer_t *this = (integer_t *)object;
  fts_atom_t a;
 
  fts_set_int(&a, this->n);
  fts_outlet_send(object, 0, fts_s_int, 1, &a);
}

Message Arguments Building

Typically, sending a message is done in 2 phases :

  • format the arguments in an array of atoms. This is done using

the fts_atom_t data structure access macros

  • call the fts_outlet_send function, passing it the arguments

array

It is strongly recommended to use the atoms setting macros to format the arguments. Accessing directly the members of the fts_atom_t structure is guaranteed not to work across different releases of FTS, because fts_atom_t implementation may change.

Message Selectors

A message selector is a fts_symbol_t. The fts_symbol_t data structure is not documented yet.

There is a number of predefined selectors which are defined in the header files for the message system. Below is the list of already defined selectors:

  • fts_s_int: “int”
  • fts_s_float: “float”
  • fts_s_bang: “bang”
  • fts_s_list: “list”
  • fts_s_symbol: “symbol”
  • fts_s_set: “set”
  • fts_s_sig: “sig”
  • fts_s_put: “put”

If a selector which is not already defined is needed, the fts_new_symbol or fts_new_symbol_copy functions generates a new symbol:

fts_symbol_t fts_new_symbol(const char *s)

returns a new symbol containing string s; the passed string is not copied, and should be a string literal.

fts_symbol_t fts_new_symbol_copy(const char *s)

returns a new symbol containing string s. This function is used when the passed string is not a constant.

Messages and Methods

[Apply to jMax 0.7 and above] There are two ways to define a method in jMax Phoenix.

Methods can be define at the class level, by using the fts_method_define_* family of functions, which are called when initializing the class, or they can be define in the object initialisation method, by using the fts_object_method_define_* family of functions. Typically, you use class level method definition in classes written using the metaclass/class oriented API, and the object level method definition when you use the fts_class_new function, but nothing prevent using the two declaration style in the same object, if this make sense to you.

A method definition function to which message the method will be associated, and which kind of arguments it expects.

Note that with respect to puredata and the other Max like applications, all the methods have exactly the same C signature, and there are no restrictions on which and how many messages/methods you can install on which inlet.

Actually, the object system provide an undocumented set of functions to install methods with arbitrary C signature on inlets, for special purpose applications. This API can be documented if there is interest.

The fts_method_define Function Family

A number of convenience functions and macros allows the definition of a method for a class. They share the meaning of most of the arguments, and provide slightly different behaviour.

fts_status_t fts_method_define(fts_class_t *class, int winlet, fts_symbol_t selector, fts_method_t method, int ac, fts_symbol_t *at)
fts_status_t fts_method_define_varargs(fts_class_t *class, int winlet, fts_symbol_t selector, fts_method_t method)
fts_status_t fts_method_define_optargs(fts_class_t *class, int winlet, fts_symbol_t selector, fts_method_t method, int ac, fts_symbol_t *at, int margs)

The fts_method_define function define a method that accept a message composed of one selector and a fixed number of arguments of a fixed type (for messages like int, float, complex, or n-uples of a given structure).

The fts_method_define_varargs function define a method that accept a message composed of one selector and a variable number of arguments (for messages like list).

The fts_method_define_optargs function define a method that accept a message composed of one selector a fixed number of arguments of a fixed type followed by a variable number of arguments (the more generic case).

The arguments have the following meaning :

  • class: the class in which the definition is installed.
  • winlet: the number of the inlet in which the method is installed.
  • selector: the associated message selector : when receiving a message having this

selector, the method will be called with the message arguments, after arguments type checking.

  • method: a function having the signature already defined.
  • ac: the number of argument type specified by the next argument
  • at: an array of symbols specifing the type of the arguments.
  • margs: the number of mandatory arguments in the specified types.

A number of message specific functions (actually, macros) simplify life for standard types.

fts_status_t fts_method_define_int(fts_class_t *class, int winlet,  fts_method_t method);
fts_status_t fts_method_define_float(fts_class_t *class, int winlet,  fts_method_t method);
fts_status_t fts_method_define_number(fts_class_t *class, int winlet,  fts_method_t method);
fts_status_t fts_method_define_symbol(fts_class_t *class, int winlet,  fts_method_t method);
fts_status_t fts_method_define_bang(fts_class_t *class, int winlet,  fts_method_t method);
fts_status_t fts_method_define_list(fts_class_t *class, int winlet,  fts_method_t method);
fts_status_t fts_method_define_anything(fts_class_t *class, int winlet,  fts_method_t method);

These macros are simply shortcuts to call the above functions, their use is not mandatory, but may make the code easier to read. The number version install the same method for ints and floats.

The anything version, and in general installing a method for the fts_s_anything selector will install a catch all method on the inlet that will be called for any message received.

The fts_object_method_define Function Family

[Applicable starting from jMax Phoenix 0.7] A number of convenience functions and macros allows the definition of a method for an object. Methods will actually be installed in the object class, and the object system will take care of keeping a globally consistent class structure. They share the meaning of most of the arguments, and provide slightly different behaviour.

fts_status_t fts_object_method_define(fts_object_t *object, int winlet, fts_symbol_t selector, fts_method_t method, int ac, fts_symbol_t *at)
fts_status_t fts_object_method_define_varargs(fts_object_t *object, int winlet, fts_symbol_t selector, fts_method_t method)
fts_status_t fts_object_method_define_optargs(fts_object_t *object, int winlet, fts_symbol_t selector, fts_method_t method, int ac, fts_symbol_t *at, int margs)

The fts_object_method_define function define a method that accept a message composed of one selector and a fixed number of arguments of a fixed type (for messages like int, float, complex, or n-uples of a given structure).

The fts_object_method_define_varargs function define a method that accept a message composed of one selector and a variable number of arguments (for messages like list).

The fts_object_method_define_optargs function define a method that accept a message composed of one selector a fixed number of arguments of a fixed type followed by a variable number of arguments (the more generic case).

The arguments have the following meaning :

  • object: the object to which the method is added.
  • winlet: the number of the inlet in which the method is installed.
  • selector: the associated message selector : when receiving a message having this

selector, the method will be called with the message arguments, after arguments type checking.

  • method: a function having the signature already defined.
  • ac: the number of argument type specified by the next argument
  • at: an array of symbols specifing the type of the arguments.
  • margs: the number of mandatory arguments in the specified types.

A number of message specific functions (actually, macros) simplify life for standard types.

fts_status_t fts_object_method_define_int(fts_object_t *object, int winlet,  fts_method_t method);
fts_status_t fts_object_method_define_float(fts_object_t *object, int winlet,  fts_method_t method);
fts_status_t fts_object_method_define_number(fts_object_t *object, int winlet,  fts_method_t method);
fts_status_t fts_object_method_define_symbol(fts_object_t *object, int winlet,  fts_method_t method);
fts_status_t fts_object_method_define_bang(fts_object_t *object, int winlet,  fts_method_t method);
fts_status_t fts_object_method_define_list(fts_object_t *object, int winlet,  fts_method_t method);
fts_status_t fts_object_method_define_anything(fts_object_t *object, int winlet,  fts_method_t method);

These macros are simply shortcuts to call the above functions, their use is not mandatory, but may make the code easier to read. The number version install the same method for ints and floats.

The anything version, and in general installing a method for the fts_s_anything selector will install a catch all method on the inlet that will be called for any message received.

Method Definition Example

Below is an example showing the installation of the method integer_in1 already defined for the integer object. This method is defined for inlet number 1 and takes one argument of type int.

fts_status_t integer_instantiate(fts_class_t *class, int ac, fts_atom_t *at)
{
  fts_method_define_varargs(class, 1, fts_s_int, integer_in1);
}

System Methods

Some of the methods of a class can be “system” methods, handling system level functions. These functions are now : object initialization and deletion, objects connection and disconnection.

All the system methods are associated to messages received on the system inlet, which is a symbolic constant defined by the message system. Except this, system methods are strictly identical to standard methods.

The init Method

The init method handles the initialization of an object, but doesn't need to handle memory allocation. It is very similar to a C++ constructor. If it is defined, it is called automatically by the system when an object is created.

The init method arguments, are the object creation arguments, including the class name; i.e. using FTS with the standard Max editor, the arguments of the init arguments are the content of the object box. The declaration of the types of these so-called creation arguments must take into account this feature.

The selector of the init method is the predefined symbol fts_s_init.

Below is the example of the init method for the integer object already defined. This method just initialize the state of the object with the value of its argument.

void integer_init(fts_object_t *object, int winlet, fts_symbol_t selector, 
                   int ac, const fts_atom_t *at)
{
  integer_t *this = (integer_t *)object;
 
  post("initializing an object of class %s", fts_symbol_name(fts_get_symbol_arg(ac, at, 0, 0)));
  this->n = fts_get_int_arg(ac, at, 1, 0);
}

This method is installed by the following code :

fts_status_t integer_instantiate(fts_class_t *class, int ac, fts_atom_t *at)
{
  fts_method_define_varargs(class, fts_SystemInlet, fts_s_init, integer_init);
}

The init method can detect an error in its argument; in order to signal the error, the following function should be called:

extern void fts_object_set_error(fts_object_t *obj, const char *format, ...);

The obj argument should be the object being initialized; the format argument is a string in the “printf” format, followed by a variable number of arguments; the user interface will then show the object as an invalid object (greyed) and the string passed by format and the following arguments will be used as error message for the object.

The delete Method

The delete method handles the destruction of an object, but doesn't need to handle memory de-allocation. It is very similar to a C++ destructor. If it is defined, it is called automatically by the system when an object is deleted, without arguments.

The selector of the delete method is the predefined symbol fts_s_delete.

A delete method can be installed for the integer object by the following code :

fts_status_t integer_instantiate(fts_class_t *class, int ac, fts_atom_t *at)
{
  fts_method_define_varargs(class, fts_SystemInlet, fts_s_delete, integer_delete);
}

The release and the redefining Methods

An object can be redefined by an other one as the result of two kind of actions: the direct editing of the object in the user interface, or an implicit change of arguments to the change of a patcher variable value.

In both case, the object is substited with a newly instantiated one; before deleting the old one, the system give an opportunity to copy some of the state of the old object to the new one; this can be very convenient if this state is a set of persistent data, like a table, that we don't want to loose just because one secondary parameter of the object changed; or for example, to allow reusing an edited data set on a slightly different object.

This opportunity is implemented by a modified deleting sequence; if the object being substitued implement a “release” method, then this is called; in this method the object should free global resources, like a global name, but it should not free internal resources, like a table content; then the system send to the newly created object a “redefining” (the fts_s_redefining C variable is defined) message to the system inlet, with as only argument the old object; the message is sent after the init message; it is the responsability of the method implementation to check that the old object is of a meaningful type; finally, the “delete” message is sent to the old object. If the old object do not define a method for the “release”, just the standard “delete” message is sent, and no “redefining” is tryed.

The “redefining” method should not send messages, either from the old or from the new object, because there is no guarantee that the connections structure are consistent at this point in time for either of two objects.

Outlet Typing

The outlets of an object can be statically typed. If an outlet is going to send only one kind of message selector, this selector and the types of associated arguments can be declared using the fts_outlet_type_define function.

The fts_outlet_type_define Function

fts_status_t fts_outlet_type_define_varargs(fts_class_t *class, int woutlet, fts_symbol_t selector)

The arguments of fts_outlet_type_define_varargs have the following meaning :

  • class: the class for which the type is defined
  • woutlet: the number of the outlet for which the type is defined
  • selector: the associated message selector

The type of an outlet will be used by the system to do a type checking, at connection-time and at run-time (when needed).

Outlet typing do not affect the efficiency of method calling; the method dispatching use a entry dynamic cache that optimize methods calls for all objects, not only statically type ones.

The type of an outlet is unique : if an outlet is supposed to send different kinds of messages, then it must not be typed; this means that the symbol fts_s_anything is not accepted as argument to this function.

Outlet Typing Example

The following code defines the type of the outlet for the integer object. The outlet number 1 is defined to send only one type of message, with selector int and one argument of type int.

fts_status_t integer_instantiate(fts_class_t *class, int ac, fts_atom_t *at)
{
  fts_outlet_type_define_varargs(class, 0, fts_s_int);
}

Classes

The definition of a class is the definition of :

  • its name
  • the size of the objects of the class
  • the number of inlets of the objects of the class
  • the number of outlets of the objects of the class
  • for each inlet, a list of (message selectors, associated method, arguments types)
  • for each outlet, the eventual message selector and arguments types

It comes from this definition that all objects of a class have same number of inlets and outlets. The mechanism for having variable number of inlets and outlets is the metaclass described below.

When a new class is installed, it is not fully initialized. The complete initialization will be done on demand, at the first instantiation of the class (i.e. when the first object of the class is created). The function that is responsible for this task is called the instantiation function.

Class Installation

A class is installed with the fts_class_install function, that declares its name and its instantiation function.

fts_status_t fts_class_install(fts_symbol_t name, fts_instantiate_fun_t instantiate_fun)

The arguments of fts_class_install have the following meaning :

  • name: the name of the class
  • instantiate_fun: the instantiation function of the class

For simplicity, aliases (usually abbreviations) can be defined for a class name, using the fts_class_alias function:

void fts_class_alias(fts_symbol_t new_name, fts_symbol_t old_name)

After a call to fts_class_alias, the name new_name, when used as a class name, will be automatically substituted by old_name.

Below is an example of use of the fts_class_install and fts_class_alias functions:

void integer_config(void)
{
  /* Install the metaclass "integer" */
  fts_class_install(fts_new_symbol("integer"), integer_instantiate);
 
  /* Register "i" to be an alias for "integer" */
  fts_class_alias(fts_new_symbol("i"), fts_new_symbol("integer"));
}

The Class Instantiation Function

The signature of a class instantiation function is the following :

fts_status_t my_class_instantiate(fts_class_t *class, int ac, fts_atom_t *at)

The arguments of the class instantiation function have the following meaning :

  • class: the class that is to be defined
  • ac: the number of arguments
  • at: the arguments array. These are the creation arguments of the first object of the class, and

are usually meaningless for a class.

The instantiation function contains at least the 3 following steps :

  • define the size of the objects of the class, the number of inlets and the number

of outlets. This is done by the fts_class_init function.

  • define the methods using the function fts_method_define previously described
  • define the types of the outlets using the function fts_outlet_type_define previously

described

The instantiation function can should return fts_Success if the class has been instantiate correctly, or fts_CannotInstantiate in case of errors.

The fts_class_init Function

fts_status_t fts_class_init(fts_class_t *class, unsigned int size, int ninlets, int noutlets, void *user_data)

The arguments of fts_class_init have the following meaning :

  • class: the class which is being initialized
  • size: the size of the objects of the class. It is the sizeof of the object

structure

  • ninlets: the number of inlets of the objects of the class
  • noutlets: the number of outlets of the objects of the class
  • user_data: this argument will be simply stored in the fts_class_t structure, giving

a simple way to share datas between all the objects of a class

Class Instantiation Function Example

Below is the example of the instantiation function of the integer object already defined. This instantiation function first initialize the class, then defines all the methods of the class, then defines the type of the outlet. The objects of this class have 2 inlets and 1 outlet :

  • the inlet 0 accepts 2 messages with selectors “bang” and “int”
  • the inlet 1 accepts 1 message with selector “int”
  • the outlet sends message with selector “int”
fts_status_t integer_instantiate(fts_class_t *class, int ac, fts_atom_t *at)
{
  fts_class_init(class, sizeof(integer_t), 2, 1, 0);
 
  /* init method */
  fts_method_define_varargs(class, fts_SystemInlet, fts_s_init, integer_init);
 
  /* message "int" on inlet 0 method */
  fts_method_define_varargs(class, 0, fts_s_int, integer_int);
 
  /* message "int" on inlet 1 method */
  fts_method_define_varargs(class, 1, fts_s_int, integer_in1);
 
  /* message "bang" on inlet 0 method */
  fts_method_define_varargs(class, 0, fts_s_bang, integer_bang, 0, 0);
 
  /* outlet type is int */
  fts_outlet_type_define_varargs(class, 0, fts_s_int);
 
  return fts_Success;
}

Metaclasses

A metaclass is a definition of a set of classes having common behavior and sharing the same instantiation function. To a metaclass is associated a base of already instantiated classes. A class is instantiated when an object is created, following the metaclass instantiation process described bellow.

Metaclass Instantiation Process

The metaclass instantiation process is based on the following assumption : the decision that 2 objects are in the same class can be made by comparing the objects creation arguments only.

A metaclass is instantiated on demand, when an object is created, through the following steps. The base of classes of the metaclass is searched for a candidate class by a matching based on the creation arguments of the object. This matching uses the equivalence function, with the following algorithm :

for all classes of the base
 {
   if (equivalence_function(creation_arguments(current_class), 
                              creation_arguments(object_to_be_created))
     {
       /* object to be created is of the current class */
 
       return current_class;
     }
 }

If no match is found, a class is instantiated : the class instantiation function is called, its arguments being the object creation arguments. This class is stored in the base of classes of the meta-class together with the creation arguments of the object, for later use by the equivalence function. Then, memory is allocated for the object and if defined, the init method is called for this object.

Equivalence Functions

The equivalence function is used by the message system at object creation time. It is used to decide if 2 objects are in the same class or not.

int an_equivalence_function(int ac0, const fts_atom_t *at0, int ac1, const fts_atom_t *at1)

The arguments of an equivalence_function have the following meaning :

  • ac0, ac1 : the number of atoms in array at0, at1
  • at0, at1 : the arrays of arguments, one being the creation arguments of object being created,

the other being the creation arguments of the class in the class base

The equivalence function should return true if the two set of arguments can correspond to the same instance class, i.e. if they are equivalent with respect to the class system.

Existing Equivalence Functions

The message system already provides a number of widely used equivalence functions. These functions are described in the table below.

  • fts_arg_equiv: return true (i.e. not zero) if the arguments are identical (same number, same types, same values).
  • fts_arg_type_equiv: return true (i.e. not zero) if the numbers and the types of the arguments are the same.
  • fts_first_arg_equiv: return true (i.e. not zero) if the first arguments are identical.
  • fts_narg_equiv: return true (i.e. not zero) if the numbers of arguments are the same.
  • fts_never_equiv: Always return false (i.e.zero). Using this equivalence function means that there will be a new class instantiated for each object instantiated. This is useful as a first “coarse” implementation of a metaclass.

Example of Equivalence Function

Below is the example of the bangbang class : the bangbang object has 1 inlet, receiving a bang, and a certain number of outlets. The number of outlets is given by the creation argument, which is of type int. When receiving a bang message on its inlet, the bangbang object outputs a bang message on all its outlets, starting from the last.

The bangbang class is a metaclass, with a simple equivalence function : it simply compares the identity of the arguments, which must be of type int.

static int bangbang_equiv(int ac0, const fts_atom_t *at0,
                          int ac1, const fts_atom_t *at1)
{
  if (ac0 == 1 && ac1 == 1 
      && fts_is_int(at0) && fts_is_int(at1)
      && fts_get_int(at0) == fts_get_int(at1))
    return 1;
  else
    return 0;
}
 
typedef struct {
  fts_object_t o;
  int noutlets;
} bangbang_t;
 
static void bangbang_bang(fts_object_t *object, int winlet, fts_symbol_t selector,
               int ac, const fts_atom_t *at)
{
  bangbang_t *this;
  int i;
 
  this = (bangbang_t *)object;
  for (i = this->noutlets-1; i >= 0; i--)
    fts_outlet_send(object, i, fts_s_bang, 0, 0);
}
 
static void bangbang_init(fts_object_t *object, int winlet, fts_symbol_t selector,
               int ac, const fts_atom_t *at)
{
  bangbang_t *this;
 
  this->noutlets = fts_get_int_arg(ac, at, 1, 2);
}
 
static fts_status_t bangbang_instantiate(fts_class_t *class, int ac, const fts_atom_t *at)
{
  int i, noutlets;
  fts_symbol_t t[2];
 
  if ((ac >= 1)  && fts_is_int(at))
    noutlets = fts_get_int(at);
  else
    noutlets = 1;
 
  fts_class_init(class, sizeof(bangbang_t), 1, noutlets, 0);
 
  fts_method_define_varargs(class, 0, fts_s_bang, bangbang_bang);
 
  fts_method_define_varargs(class, fts_SystemInlet, fts_s_init, bangbang_init);
 
  for (i = 0; i < noutlets; i++)
    fts_outlet_type_define(class, i, fts_s_bang, 0, 0);
 
  return fts_Success;
}

Metaclasses Installation

A metaclass is installed with the following function:

fts_metaclass_t *fts_metaclass_create(fts_symbol_t name, fts_instantiate_fun_t instantiate_function, fts_equiv_fun_t equiv_function)

The metaclass is named name; instantiate_function will be used to instantiate new classes, and equiv_function will be its equivalence function.

As for classes, metaclasses can be aliased, using the fts_metaclass_alias function:

void fts_metaclass_alias(fts_symbol_t new_name, fts_symbol_t old_name)

This function has the same behavior as fts_class_alias.

Below is an example of a metaclass installation:

void bangbang_config(void)
{
  /* Install the metaclass "bangbang" */
  fts_metaclass_install(fts_new_symbol("bangbang"), bangbang_instantiate, bangbang_equiv);
 
  /* Register "bb" to be an alias for "bangbang" */
  fts_metaclass_alias(fts_new_symbol("bb"), fts_new_symbol("bangbang"));
}

Object And Class Properties

In FTS each object and class can have properties associated with it; a property is simply a pair name value, where the name is an FTS symbol, and the value is any FTS value; a property is a kind of dynamic storage tied to the object; a class can provide default values for properties, that are valid for all the object instance of the class.

Also, the property system is extended with standard constraint propagation and daemon techniques: putting a property or getting a property from an object can transparently activate a number of functions that propagate the properties in the object network.

This subsystem is currently used in FTS to implement some experimental optimization and features in the DSP compiler.

Its API is stble but is not currently documented (soon to be), check the properties.h file in the sources.

 
package/controlobjects.txt · Last modified: 2010/01/11 16:28 by maurizio
 
Except where otherwise noted, content on this wiki is licensed under the following license:CC Attribution-Noncommercial-Share Alike 3.0 Unported
Recent changes RSS feed Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki