The FTS Kernel Reference Manual.

This file document the FTS Kernel programming interface.

Introduction

The FTS kernel is a set of functionalities on top of which FTS application are built; all these functionalities are available to the application programmer to build its own applications, or part of them (like user objects for the FTS message system).

Moreover, the kernel itself is extensible, and users can easily add new kernel modules, like new device drivers to use new hardware for existing platforms.

This manual document the programming interface for all the FTS kernel functionalities.

In order to use any of the functions or data type defined in this document, you need to include the file "ftskern.h" in your sources.

The current release of this manual is not complete; we privileged documenting functionalities needed by user object writers for the FTS message system, while other more system related aspects were left behind; anyway, they are always described in the general lines; if you are in the situation you need the kind of functionalities that are described but not fully documented, and you cannot wait for future versions, contact the FTS developement team to get detailed information, by using the fts@ircam.fr mailing list.

FTS 1.3.x do not guarantee binary compatibility with future releases; some of the functions documented in this manual may actually be macros, and their definition may change, or they may become functions and so on.

Moreover, you should not make assumption on the current content of include files in the distribution, like the definition of a data structure, or the size of data type and so on, unless they are explicitly documented in this manual; anything that is not documented may change, or disappear, from a release to an other; if you really think you need to use something defined in an include file, but not documented here, first check with the FTS developement team.


Memory Management

FTS provides itself a number of memory management operations that substitute the standard C lib ones; all the applications written for FTS should use these primitives, in order to simplify statistics on memory usage and similar things; but it is legal for an object to bypass the fts memory management and to access directly the libc primitives.

The memory management primitives closely resemble the C standard functions.

void *fts_malloc(int size)

Allocate a block of memory of the requested size. Remember that by definition, the memory unit is such that sizeof(char) == 1; for example, in the TMS320C40 a char is implemented with a 32 bit word, so sizeof(char) == sizeof(long) == 1. The conclusion is: never pass a constant to fts_malloc, always compute the structure size using sizeof.

void *fts_zalloc(int size)

Like fts_alloc, but fills the newly allocated structure with zeros.

void *fts_realloc(void *p, int size)

Changes the size of the block pointed to by p to size bytes and returns a pointer to the (possibly moved) block. The contents will be unchanged up to the lesser of the new and old sizes.

void fts_free(void *p)

The memory block pointer by p is returned to the memory management system; p should point to a memory block allocated by fts_alloc or fts_realloc; the memory block should not be referred anymore after a call to this function.


Heaps

Typically the fts_malloc primitive is mapped to the native memory allocation subsystem, if any; to get more efficency in handling an heap of omogenous objects, FTS provide a predefined heap allocator module. The heap function are not yet documented in this release of the document

Error Handling

The Error Handling support subsystem provide a set of data structure, functions and conventions to represent errors, to handle errors and to signal errors to clients in FTS applications.

The error handling support is not documented in this release of the document.


FTS data structures

The FTS kernel defines a number of basic data types, and the relative operations, used in most of the kernel API, and freely usable from the user to build applications. Vectors are documented in a specific document.

Symbols

Symbols are unique representations of strings, in the LISP symbol style; if two pointers point to two symbols representing the same string, they are actually two pointers to the same C object. FTS symbols do not store any other information other than the name they are coding. Users cannot modify or access the implementation of a symbol; it is a completely opaque structure; the fts_symbol_t type is defined a const pointer to an internal structure.

fts_symbol_t *fts_new_symbol(const char *name)

Creates a new symbol corresponding to the string name; the passed string is not copied, so it cannot be reused; use this function with string constants, use the next function for locally allocated buffers. Returns a pointer to the new symbol. If a symbol with the same name already existed, it return a pointer to the existing one, instead of creating a new one; so two successive calls to fts_new_symbol with the same name argument will return the same symbol. Note that symbols are never destroyed, so that values returned by fts_new_symbol are valid until the end of the session.

fts_symbol_t *fts_new_symbol_copy(const char *name)

Creates a new symbol corresponding to the string name; the passed string is internally copied, so it can be reused or freed as the caller needs. Returns a pointer to the new symbol. If a symbol with the same name already existed, it return a pointer to the existing one, instead of creating a new one; so two successive calls to fts_new_symbol with the same name argument will return the same symbol. Note that symbols are never destroyed, so that values returned by fts_new_symbol are valid until the end of the session.

const char *fts_symbol_name(const fts_symbol_t *sym)

Returns the name corresponding to a given symbol. The user should not write or modify in any way the returned value.

FTS Atoms

Atoms are opaque objects of type fts_atom_t; an atom is a run time typed data item; an atom can store a floating point number, an long integer, a symbol, a string or a pointer. Atoms and array of atoms are used almost anywhere in FTS to represent lists of ethereogenous informations, like in messages, device options and so on. The FTS kernel provides a number of functions to fill an atom, to retrieve it's type or value, and to check its content type. Some` of the functions may be implemented as macros.

void fts_set_symbol(fts_atom_t *a, fts_symbol_t *s)

Sets the type of a to symbol, and it's value to s.

void fts_set_string(fts_atom_t *a, char *str)

Sets the type of a to string, and it's value to str.

void fts_set_obj(fts_atom_t *a, void *p)

Sets the type of a to obj, and it's value to p.

void fts_set_long(fts_atom_t *a, long l)

Sets the type of a to long, and it's value to l.

void fts_set_float(fts_atom_t *a, float f)

Sets the type of a to float, and it's value to f.

void fts_set_nothing(fts_atom_t *a)

Sets the type of a to nothing, i.e. Sets the atom to it's initial, uninitalized status.

fts_symbol_t *fts_get_symbol(fts_atom_t *a)

If the atom is of type symbol, returns it's contents converted to fts_symbol_t; if not, the resulting value is not specified.

char *fts_get_string(fts_atom_t *a)

If the atom is of type string, returns it's contents converted to char *; if not, the resulting value is not specified.

void *fts_get_obj(fts_atom_t *a)

If the atom is of type obj (void * pointer), returns it's contents converted to void *; if not, the resulting value is not specified.

long fts_get_long(fts_atom_t *a)

If the atom is of type long, returns it's contents converted to long; if not, the results value is not specified.

float fts_get_float(fts_atom_t *a)

If the atom is of type float, returns it's contents converted to float; if not, the results value is not specified.

int/float fts_get_float_long(fts_atom_t *a)

If the atom is of type float or long, returns it's contents converted to float or long, depending on the context (this is always a macro); if not, the resulting value is not specified.

int fts_is_nothing(fts_atom_t *a)

Returns 1 if the atom a is of type nothing (non initialized), 0 otherwise.

int fts_is_symbol(fts_atom_t *a)

Returns 1 if the atom a is of type symbol, 0 otherwise.

int fts_is_string(fts_atom_t *a)

Returns 1 if the atom a is of type string, 0 otherwise.

int fts_is_obj(fts_atom_t *a)

Returns 1 if the atom a is of type obj (void pointer), 0 otherwise.

int fts_is_long(fts_atom_t *a)

Returns 1 if the atom a is of type long, 0 otherwise.

int fts_is_float(fts_atom_t *a)

Returns 1 if the atom a is of type float, 0 otherwise.

int fts_is_float_long(fts_atom_t *a)

Returns 1 if the atom a is of type float or long , 0 otherwise.

int fts_get_type(fts_atom_t *a)

Returns one of the constants FTS_NOTHING, FTS_LONG, FTS_FLOAT, FTS_SYM, FTS_OBJ, FTS_STRING, depending on the type of the atom a. Most of the time it is better to use one of the types predicate above instead of this one. This function is really only usefull inside switch statements on the whole set of FTS types.

int fts_same_types(fts_atom_t *a1, fts_atom_t *a2)

Returns 1 if the two atoms a1 and a2 have the same type, 0 otherwise.

Atom Lists

Atom Lists are data structures representing a variable length list of atoms; Atom List Iterators are abstractions that allow iterating on an Atom List.

Atom lists and atom list iterators can be allocated explicitly or implicitly as part of other structures. The kernel provides a set of functions for list manipulations and a mechanism to simplify sending the contents of an atom list to a client.

void fts_atom_list_init(fts_atom_list_t *list)

Initialize a statically or implicitly allocated atom list. Calling any other atom list function on a non initialized atom list has unspecified results.

fts_atom_list_t *fts_atom_list_new(void)

Explicitly allocates, initializes and returns a new atom list.

void fts_atom_list_destroy(fts_atom_list_t *list)

Empties the atom list list, freeing all the internally allocated storage. It can be used to clean an atom list, and is recomended before to stopping use of an implicitly or statically allocated atom list.

void fts_atom_list_free(fts_atom_list_t *list)

Freesan explicitly allocated atom list, i.e. one created using fts_atom_list_new; users should not use or refer to the list atom list after calling this function.

void fts_atom_list_append(fts_atom_list_t *list, int ac, const fts_atom_t *atom)

Appends ac atoms stored in the atom array to the end of the atom list list; it is the user's responsability to make sure that atom actually points to an array at least ac atom long.

int fts_atom_list_length( const fts_atom_list_t *list)

Returnsthe length of the atom list, i.e. the number of atoms currently stored in the atom list.

void fts_atom_list_iterator_init( fts_atom_list_iterator_t *iter, const fts_atom_list_t *list)

Initializes an atom list iterator statically or implicitly allocated to point to the first element of the atom list list. Calling any other atom list iterator function on a non initialized atom list iterator has unspecified results.

fts_atom_list_iterator_t *fts_atom_list_iterator_new( const fts_atom_list_t *list)

Explicitly allocates, and returns a new atom list iterator, initialized to point to the atom list list first element.

void fts_atom_list_iterator_next( fts_atom_list_iterator_t *iter)

Advances the atom list iterator iter to point to the next element of its atom list.

int fts_atom_list_iterator_end( const fts_atom_list_iterator_t *iter)

Returns zero if the atom list iterator points beyond the end of the list.

fts_atom_t *fts_atom_list_iterator_current( const fts_atom_list_iterator_t *iter)

Returns a pointer to the atom currently pointed to by the atom list iterator iter.

Hash Tables

FTS hash tables are data structures mapping symbols to void * pointers. They are intended to be used any time there is the need for naming internal objects. It is recomended that a hash table contains homogenous objects, i.e. that every time objects are named, a different name space is implemented. FTS hash table iterators are abstractions that allow iterations on all the pairs symbol value contained in a hash table.

void fts_hash_table_init( fts_hash_table_t*ht)

Initializes a statically or implicitly allocated hash table. Calling any other atom list function on a non initialized hash table has unspecified results.

fts_hash_table_t *fts_hash_table_new( void)

Explicitly allocates, initializes and returns a new hash table.

void fts_hash_table_destroy(fts_hash_table_t *ht)

Empties the hash table ht, freeing all the internally allocated storage. It can be used to clean an hash table, and is recomended before stopping use of an implicitly or statically allocated hash table.

void fts_hash_table_free(fts_hash_table_t *ht)

Frees an explicitly allocated hash table, i.e. one created using fts_hash_table_new. Users should not use or refer to the ht hash table after calling this function.

int fts_hash_table_lookup(fts_hash_table_t *ht, fts_symbol_t *sym, void **data)

Looks up for the value associated with the symbol sym in the hash table ht, and stores it in the pointer pointed by data. Returns non zero if the value has been found, zero otherwise. The value of the pointer pointed by data is not specified in this case.

int fts_hash_table_insert(fts_hash_table_t *ht, fts_symbol_t *sym, void *data)

If there is no value associated with sym in the hash table sym, it adds the value data associated to the symbol sym and returns non zero, otherwise it returns zero.

void fts_hash_table_apply( fts_hash_table_t*ht, void (*fun)( fts_symbol_t *sym, void *data))

For every association symbol/value defined in the hash table ht, it calls the function fun, passing the symbol as argument sym, and the value as argument data.

int fts_hash_table_remove( fts_hash_table_t*ht, t_symbol *sym)

Removes the symbol sym and its associated value from the hash table ht.

void fts_hash_table_iterator_init( fts_hash_table_iterator_t*iter, const fts_hash_table_t*ht)

Initializes an hash table iterator statically or implicitly allocated to first symbol/value pair of the hash table ht; calling any other hash table iterator function on a non initialized hash table iterator has unspecified results.

fts_hash_table_iterator_t *fts_hash_table_iterator_new( const fts_hash_table_t *ht)

Explicitly allocates, and returns a new hashtable iterator, initialized to point to the first symbol/value pair of the hash table ht.

void fts_hash_table_iterator_next( fts_hash_table_iterator_t*iter)

Advances the hash table iterator iter to point to the next symbol/value pair in its hash table. The order followed by the hash table iterator is not specified, but is guaranteed to cover the whole table.

int fts_hash_table_iterator_end( const fts_hash_table_iterator_t*iter)

Returns zero if the atom list iterator points beyond the end of the table.

fts_symbol_t *fts_hash_table_iterator_current_symbol( const fts_hash_table_iterator_t*iter)

Returns the symbol of the symbol/value pair pointed to by the iterator.

void *fts_hash_table_iterator_current_data( const fts_hash_table_iterator_t*iter)

Returns (as a void *) the value of the symbol/value pair pointed to by the iterator.

Property Lists

Property lists are another way to represent symbol to value association, more time and memory efficients in case the values to bstore are just a few, or usefull when a symbol can be associated to multiple values. The property lists are not yet documented in this release of the document


Time Handling

The FTS kernel provides a number of primitives to cope with time; the time itself is a relative concept for FTS, and different times, also user defined, can be defined by definining a new time souce (a clock, in fts terminology); all the fts time related primitives will work with any time source.

Clocks

A clock is the definition of a time source; from the FTS point of view, a time source is just a floating point number (actually, a double precision float) that may change from one scheduling loop to another. The FTS time system is not responsable to actually change this number, but only to react to this changes accordingly to the user requests; the actual time changing must be handled by the module that define the clock.

There are two issues that must be considered while defining and using a clock; the clock time can be expressed in any units, but the time resolution of the FTS time system is always the scheduling tick; i.e., clocks are checked in the control domain, and the control is undersampled with respect to signals by a fixed factor, usually 64.

This have two resulting effects: first, since times are always quantized at run time to the control sampling rate (i.e. an action is executed at the next scheduling tick), so you cannot have a time grain in control smaller than a scheduling tick; second, for the same reason you can have beating between the scheduling tick time and the time used in user algorithm, if the unit used by the user clocks are not multiple of the scheduling tick time; this was a well known problem with the old Max 0.26, that offered only the millisecond as a clock unit, and a scheduling tick of 1.4 ms at 44.1 Khz.

Clocks are named, and are addressed by name in the whole code; users have no access to the clock implementation; FTS provides a number of pre-defined clocks, that are the following:

msec
A free running clock, counting the milliseconds since the FTS boot.
tick
A free running clock, counting the scheduling ticks since the FTS boot.
dsp_msec
A clock running during dsp, counting the milliseconds since dsp started.
dsp_tick
A clock running during dsp, counting the scheduling ticks since dsp started.
All the time related primitives get a clock name (as symbol) as argument; if this clock name is not specified (by passing a null pointer) the default msec clock is taken.

Note that a clock do not need to be defined before the time related objects that use them; for example, the user can allocates an alarm for a clock before this clock is defined.

Using the tick or dsp_tick clocks guarantee that there will be no problem due to the beating between the scheduling clock and the user clock; but the semantic of a patch written with these clocks depend on the sampling rate.

void fts_clock_define(fts_symbol_t *clock_name, double *clock)

Define a new clock, named clock_name, represented by the double pointer by clock.

void fts_clock_undefine(fts_symbol_t *clock_name)

Undefine the clock: undefining a clock *do not* destroy the clock, but just undo the association between the clock name and the long value established with fts_clock_define. The clock is so stopped until the user redefine a clock with the same name.

Note anyway that undefining and redefining a clock can have weird temporary effects on object using the clock; the transition is not guaranteed to be smooth, depend on the value of the clock. For the alarms, the effect is the same as with a clock value change (at the redefinition).

Also, system defined clocks cannot be undefined.

int fts_clock_exists(fts_symbol_t *clock_name)

Returns non zero if a clock named clock_name has been already defined; remember, anyway, that in general you do not need to define a clock before using it.

double fts_clock_get_time(fts_symbol_t *clock_name)

Returns a double representing the current time of the clock named clock_name. The current time is not the current value of the clock variable, but is the time FTS consider as current, i.e. already took in account in scheduling. The difference is somehow subtle: current time change only during scheduling tick, while the physical clock variable can change due to any cause, including interrupt; also it is guaranted that while executing a alarm scheduled for time t, this function will returns t, and not the actual value of the clock variable; this is needed to avoid accumulation errors using clocks units different from ticks.

Alarms

An alarm is an object of type fts_alarm_t; it can be either allocated and accessed by means of a pointer, or used as a part of an other structure; as most of the kernel objects, it's internal structure is not documented or available to the user; all accesses should be done by means of the following API.

An alarm declare a function to be called when the named clock arrive to a given time; a time can armed, i.e. active, or unarmed; if the alarm is unarmed, the function will not executed, but the alarm keep its timing information.

When the alarm is fired, and its callback function called, the alarm is not unarmed; if needed it must be unarmed by the callback function itself; the reason behind this is that with some clock the time can actually jump backward, and a fired alarm refired; consider for example an alarm that need to be fired one second the start of the dsp engine: it can be created using the "dsp_msec" clock, and left armed after fired, so it will refire again the next time the dsp start.

An alarm can be put a cycle mode: it will fire regularly with a given period; the difference with an alarm that reschedule itself in its callback function is that in case of backward jumping time (like the "dsp_msec" time that is reset at the start of the dsp engine) a cycled alarm will continue to fire, keeping phase coherency with respect to the clock.

void fts_alarm_init(fts_alarm_t *alarm, fts_symbol_t *clock_name, void (* fun)(fts_alarm_t *, void *), void *arg)

Initializes a statically or implicitly allocated alarm. The alarm will use the clock named clock_name; its callback function is fun; the callback function will be called with two arguments, a pointer to the alarm itself, and the void * pointer arg, that will be stored in the alarm; there is no way to change the callback function or its argument after the alarm creation.

fts_alarm_t *fts_alarm_new(fts_symbol_t *clock_name, void (* fun)(fts_alarm_t *, void *), void *arg)

Allocates and init new alarm. The alarm use the clock named clock_name; its callback function is fun; the callback function will be called with two arguments, a pointer to the alarm itself, and the void * pointer arg, that will be stored in the alarm; there is no way to change the callback function or its argument after the alarm creation.

void fts_alarm_free(fts_alarm_t *alarm)

Unarm and frees the alarm alarm; after freeing an alarm, it should not be referred any more.

void fts_alarm_set_time(fts_alarm_t *alarm, double when)

Sets the absolute time at which the alarm callback function will be called; this time, when, is expressed in the time units used by the alarm clock, and can be in the future or in the past (relative to the used clock); this function do not change the armed/unarmed status of the alarm.

void fts_alarm_set_delay(fts_alarm_t *alarm, double when)

Sets the relative time (i.e. time from now) at which the alarm callback function will be called; this time, when, is expressed in the time units used by the alarm clock, and it is relative to the clock current time; it can be positive or negative. This function do not change the armed/unarmed status of the alarm.

void fts_alarm_set_cycle(fts_alarm_t *alarm, double cycle)

Sets the alarm in the cycled mode, and Sets the period which the alarm callback function will be called; this time, cycle, is expressed in the time units used by the alarm clock, and must be positive. The phase of the cycle is set so that the current time is the beginning of the cycle period; note that anyway the alarm will not fire the callback function until it is armed, but the period phase relatioship is set in this function. This function do not change the armed/unarmed status of the alarm.

void fts_alarm_arm(fts_alarm_t *alarm)

Arm the alarm, i.e. enable it to actually fire the callback function when the time come.

void fts_alarm_unarm(fts_alarm_t *alarm)

Unarm the alarm, i.e. prevent it to actually fire the callback function. The time information of the alarm will not be deleted, so an call to fts_alarm_unarm followed by a call to fts_alarm_arm leave the alarm in its initial state. Remember that an alarm is not automatically unarmed after fired, so you should unarm is explicitly in the callback function, if so wished.

int fts_alarm_is_in_future(fts_alarm_t *alarm)

Returns non zero if the alarm is set to fire in some time in the future (with respect to its clock); the result of this function do not depend on the armed/unarmed status of the alarm.

int fts_alarm_is_armed(fts_alarm_t *alarm)

Returns non zero if the alarm is armed, zero otherwise.

Timers

An timer is an object of type fts_timer_t; it can be either allocated and accessed by means of a pointer, or used as a part of an other structure; as most of the kernel objects, it's internal structure is not documented or available to the user; all accesses should be done by means of the following API.

A timer implement a simple stop watch, to evalute elapsed time since an specific event, or accumulate multiple elapsed times.

void fts_timer_init(fts_timer_t *timer, fts_symbol_t *clock_name)

Initializes a statically or implicitly allocated timer. The timer will use the clock named clock_name; the internally accumulated elapsed time is set to zero.

fts_timer_t *fts_timer_new(fts_symbol_t *clock_name)

Explicitly allocates, initializes and returns a timer. The timer will use the clock named clock_name; the internally accumulated elapsed time is set to zero.

void fts_timer_free(fts_timer_t *timer)

Frees an explicitly allocated timer, i.e. one created using fts_timer_new; users should not use or refer to the timer timer after calling this function.

void fts_timer_zero(fts_timer_t *timer)

Sets the accumulated elapsed time of timer to zero.

void fts_timer_start(fts_timer_t *timer)

Start the counting of elapsed time of timer.

void fts_timer_stop(fts_timer_t *timer)

Suspend the counting of elapsed time of timer, but leave the current value.

double fts_timer_elapsed_time(fts_timer_t *timer)

Returns the timer accumulated elapsed time.

Time Gates

A time gate is an object of type fts_time_gate_t; it can be either allocated and accessed by means of a pointer, or used as a part of an other structure; as most of the kernel objects, it's internal structure is not documented or available to the user; all accesses should be done by means of the following API.

A time gate is a gate that when is closed stay closed for a given amount of time; while closed, the gate do not accept any further close command; the main purpose of the time gate is to avoid fast repetition of the same event, like error messages.

void fts_time_gate_init(fts_time_gate_t *gate, fts_symbol_t *clock_name)

Initializes a statically or implicitly allocated time gate. The time gate will use the clock named clock_name; the gate is set to open.

fts_time_gate_t *fts_time_gate_new(fts_symbol_t *clock_name)

Explicitly allocates, initializes and returns a time gate. The time gate will use the clock named clock_name; the gate is set to open.

void fts_time_gate_free(fts_time_gate_t *gate)

Frees an explicitly allocated time gate, i.e. one created using fts_timer_new; users should not use or refer to the gate time gate after calling this function.

int fts_time_gate_close(fts_time_gate_t *gate, double interval)

If the gate is closed, returns zero; otherwise, close it for interval and returns non zero.interval is expressed in the time gate clock units.


Client Communication

The communication with the client happen accordingly to the FTS client/server communication protocol, documented in the document "The FTS C Client Library"; the FTS kernel provides low level access to this communication, roughly at the same level of the client library; an application can format and send explicitly a message, and can receive a message selected upon its type.

On top of the basic functionalities FTS, together with the client library, provides a set of richer functionalities: the event subsystem, and the user messages subsystem.

The event subsystem allow an application to send an event (see "The FTS C Client Library" for details) to a client; an event is essentially a message whose destination depend on the content of the message; the client can install handlers, i.e. functions able to deal with specific type of content; the client library fully support this mechanism.

The FTS kernel event subsystem provides buffering and flow control to send events in a multiprocessor configuration, avoiding to fill up the complete bandwidth available between fts and the clients.

The user message subsystem provides a set of formatting and printing function, whose final purpose is to show a message to the end user.

Client Id Handling

A client is identified by a unique int called the client id; when fts is processing, directly or indirectly, a client message, the client id of the originator of the message is available; this allow, for example, sending an error message to the originator of the computation that produced the error message.

long fts_get_clientid(void)

Returns the current client id, i.e. the id of the client for which FTS is doing computation; if there is no current client id, the returned value is minus one; note that client sending messages interpret minus one as unspecified client, and send the message to the main client, so in most of the cases the user don't need to test if the returns value of this function is minus one.

Request Id Handling

A request, i.e. a message coming from a client asking of some kind of service, is identified by a unique int called the request id; when fts is processing, directly or indirectly, a request message, the request id of the originator of the message is available; this allow, for example, noting in an error message the id of the request generating the error, allowing the client to properly recover the error, or to give the correct error message to the user. All the high level messages to the client, including events and print request, include automatically the corresponding request id; if the user use or program other type of messages to the client, it must include the request id, or specify the convential minus one value to specify the absence of a corresponding client request.

long fts_get_requestid(void)

Returns the current request id, i.e. the id of the request for which FTS is doing computation; if there is no current request id, the returned value is minus one; note that request sending messages interpret minus one as unspecified request, so in most of the cases the user don't need to test if the returns value of this function is minus one.

Receiving a Client Message

Some time an application want to add a subprotocol to the client server communication protocol, so to add some specific, application relative messages

A client/server message is defined by a message type (a char), a command (that is still a char) and a list of atoms; the application programmer can install a function to be called when a message of a given type is received; the interpretation of the semantic of the message is left to this function; refer to the client library documentation for more details on the protocol.

void fts_client_mess_install(char type, void (* fun) (char cmd, int ac, fts_atom_t *av))

Install an handler for client messages of type type; when FTS will receive a message of this type, it will call the function fun with the number of received atoms in argument ac and the atoms in the atom vector av. This function can be called at any time, but usually make more sense to call it a init time, so to be sure to catch all the messages of type type.

Sending a Client Message

The server to client low level programming interface is not documented in this release of the manual; most, if not all, the communication need from the server to the client are usually satisfable with the Event subsystem

Events

An event is a message to a client, whose destination is selected by matching a pair event/tag (both integers) with similar tags declared by the client; this matching is handled by the FTS client library.

Events are used for many purposes, from error signalling to graphic updates, to actual data transfer; application writers are free to define their own events and tags; events values should be greater than 2^16-1.

To raise an event means to send the event message to the client; events can be raised in two ways: the flow-controlled mode, that is the default mode, and the immediate mode; in the flow controlled mode, the number of events per second that can be sent depend on the bandwidth available with the client; in the immediate mode, events are sent immediately anyway: the result is that if the bandwidth is not available, FTS will block on write to the client operation, and, in case of DSP application, can cause data loss on the real time i/o channels, like dacs and adcs; the idea is that immediate mode should be used only for error situations, were data loss would not be significative anyway, or already happening.

In multiprocessor platoforms the event subsystem will take care of message routing, and the flow control is extended to take in account also bandwith between the processors.

void fts_raise_event(int clientid, long ev, long tag, int ac, fts_atom_t *args)

Raise a flow controlled event to the client clientid; the event number will be ev and the tag tag; the event will have ac arguments, stored in the args atoms vector. It should be called only in situation where fts_can_raise_event would returns non zero.

int fts_can_raise_event(void)

Returns non zero if there is enough bandwidth to send an event to clients. Applications should check this function before sending events; the FTS message system provides higher level messages based on this function to schedule graphic updates and similar kind of events.

void fts_raise_event_immediate(int clientid, long ev, long tag, int ac, fts_atom_t *args)

Raise a event in immediate mode to the client clientid; the event number will be ev and the tag tag; the event will have ac arguments, stored in the args atoms vector.

User Messages

The FTS kernel provides a couple of functions to print messages to the user; the messages are actually sent to a client as events, and the client is requested to print them in the preferred way.

No post function can be called at init time, i.e. from a module init function, beacause it need the scheduler running; if you want to print a message from an init function, like printing the name of an optional installed module, you should use the fts_add_welcome function (see below).

void post(const char *format , ...)

void cpost(int clientid, const char *format , ...)

Print its arguments to the client output stream, according to the format string (in printf format); in the post version, the post request is sent to the current client, while the cpost version require the clientid as first argument.

void postvector(int n, float *fp)

void cpostvector(int clientid, int n, float *fp)

Print a vector of float of length n to the client output stream; in the postvector version, the post request is sent to the current client, while the cpostvector version require the clientid as first argument.

void fts_add_welcome(fts_welcome_t *w)

Add a welcome to the current welcome list; a welcome is a string to be shown to the user, usually at start up time; the UCS command show welcome will post all the current welcome to the main client.

Welcomes should be used to print startup messages from application modules, or short description of the current configuration; a welcome can be installed at any time, but it usually make sense to install a welcome during the initialization of a module.

The w argument should be a pointer to a static function declared as in the following example:

static fts_welcome_t my_welcome = {"A warm, example welcome";}

Debug I/O

In platforms where a source debugger is not available, it is usual to debug applications by means of prints put in the code; in the FTS case, you can do it just by using standard libc output calls like fprintf on the standard error stream.


The Module System

Modules are a simple tools to organize and structure FTS applications; from the strict programming point of view a module is simply a data structure that define an initialization and a shutdown function; conventionally, this data structure should correspond to a set of source files implementing a set of coherent functionalities, like a new data type with its operations, or a library of FTS objects and so on.

The init function is often very important; under FTS, the execution of application code usually happen as a reaction to some kind of event, like a timer has elapsed, or data is available on the MIDI input, or a given client message is arrived, or a scheduling phase has been completed.

It may be easy to think the FTS kernel as a set of specialized event dispatching table, mapping certain kind of events to certain parts of application code.

This mapping between system events and application code is realized in the init function of a module; the function declare the functions to be called in the different situations, and install them with the various programming interface availables.

Most of the time, the only non static C symbol declared in an FTS application is the module itself, because all the other functions are installed as pointers in some system structure.

void fts_install_module(fts_module *s)

Install the module pointed by s; a module should be a structure defined as in the following example.

fts_module fts_mess_module = {"Frob", "The new frobber module", frob_init, frob_shutdown;}

The first two component of the structure are a name and a module description, used in debugging messages; the third is a function with zero arguments and returning void that is called as init functin, and the fourth is a similar function called as shutdown function.


Scheduling

Most of the programmer will never need the services related to scheduling; but in special cases FTS allow a deep access to its scheduling and running model, allowing the customization or extension of FTS for use in situations like embedded or standalone applications, or with other kind of medias requiring access to the system scheduling. The FTS execution model is divided in four parts; startup, initialization, running and shutdown.

The startup part is the part of the application executed before entering FTS; this part is responsible to install all the application modules, possibly parsing command line arguments, and calling the fts_main function documented below. The application specific initializations should be performed, when possible, by the application modules initialization functions.

void fts_main(int argc, char **argv, const char **bootdata)

Run FTS; should be called by the application main; in platforms where command line arguments are available, they should be passed to fts_main as argc and argv, possibly after deleting some application specific options.

The bootdata argument, if not null, should be a pointer to a null terminated array of nul terminated strings; the strings are interpreted as client messages to be read by fts before starting the connection with the client; this bootdata can contain fixed configuration data, or installation dependent data; it is possible, for example, to define an array to include a complete Max patch, in order to delive an executable with Max patch preloaded.

After the startup phase, FTS will call all the init function declared in the modules; this is called the initialization phase; not all the FTS services are ready to run, during this phase; the init functions are called in module installation order: first all the kernel modules, and then all the application modules.

After the init phase, the scheduler is started; once the scheduler started, FTS always execute a number of tasks in a fixed loop, called scheduling loop or tick; this loop is syncronious if some of the task syncronize itself to an external source of time (for example the output dacs), otherwise it is executed as fast as possible.

Every task is implemented as a function call; the order by which the function calls are called in the scheduling loop is computed after init time by the fts scheduler, and cannot be changed at run time; every module can define its function to be scheduled, at init time, giving to the scheduler some declarative information about its ordering.

The model used to give the information is based on the require/provide pair of concepts; every task can provides a number of features to the following tasks, or require a number of features to be already provided by the previous tasks in the order.

void fts_sched_declare(void (* fun)(), enum declaration_verb decl, fts_symbol_t *feature, const char *name)

Declare that the tasks implemented by the function fun have the decl dependency with the feature feature.

The feature is represented by a symbol, while the dependency from the feature is represented by a "verb" that can assume the following values:

provide
Provides the specified feature for the taks following this in the scheduling loop.
provide_next_tick
Provides the specified feature for the next scheduling loop.
require
Require the feature to be provided by the tasks before this in the scheduling loop. If multiple tasks provide the required feature, this task must be runned after all the tasks providing it; i.e. the require use a logic and semantic.
freerun
The taks do not require or provides any feature, can be runned at any position in the scheduling loop.
An application including multiple scheduling functions can defined any number of feature, and/or make reference to the features already defined by the kernel, in order to syncronize themselves with the scheduled tasks in the kernel. Defined features are:

control
Every tasks that can process or produce control messages at any level should be declared to provide this feature; tasks requiring all the control processing to be done in order to be executed should require this feature.
sig_out
Any tasks computing sound outputs to be sent to the audio output library should be declared to provide this feature.
sig_in
Any tasks requiring the sound input of the audio input library should be declared to require this feature.
void fts_halt(void)

Halt the FTS scheduler, after the completion of the scheduling loop.

After the scheduler has been halted, the shutdown phase is entered: all the modules shutdown functions are called, in order reversed with respect to the initialization phase, and the program is exited.


Audio Streams

FTS applications are free to access audio input output devices directly with the device standard programming interface; the audio stream subsystem simplify the handling of audio I/O, from one side providing the audio_in and audio_out logical devices, and from the other integrating audio device handling inside the scheduler.

An audio stream is a audio input or output channel; streams are numbered; any audio physical device can be mapped to any index by means of UCS commands; the audio stream subsystem handle buffer allocation and scheduling for input and output operations

This programming interface is not not documented in this release of this document.


MIDI Input Output

FTS MIDI handling provides multiport MIDI configurations, MIDI unparsing with callback based input, and output operations.

This programming interface is not not documented in this release of this document.


The UCS Configuration System

FTS provides a integrated tool for user configuration support, called UCS; the UCS language itself, and the implemented commands are documented in a dedicated document; this chapter document the programming interface to the UCS system.

The FTS kernel provides a way to a add new UCS command, that can be used to configurate user application, or to give any kind of commands the application writer wish; it also provides a function to directly execute an UCS command, so that an application can define its own user interface to execute ucs commands, or directly configurate the system from the user code by means of ucs commands.


void fts_ucs_define_command(fts_symbol_t *opcode,
                            fts_symbol_t *sub_opcode,
			    fts_status_t (* fun)(int argc, fts_atom_t *argv),
                            const char *usage_string, 
                            const char *help_string);

Define a new UCS command, having opcode as main command, and sub_opcode as subopcode (optional, define a single word command if NULL); when the UCS system will receive, from any source, a command of the form opcode sub_opcode [args]* wil call the function fun, with two arguments, the number of the ucs command arguments argc, and the argument themselves passed as array of atoms in argv. The usage_string and the help_string will be used by the ucs help command.

fts_status_t fts_ucs_execute_command(fts_symbol_t *opcode, fts_symbol_t *sub_opcode, int argc, const fts_atom_t *argv);

It execute the ucs command opcode sub_opcode [args]* passing as arguments argc argument to it taken from the argv vector.


The FTS Device System

The FTS device system represent an abstraction layer over the I/O capability of the hardware it run on; it establish a simple and uniform programming interface to open and close the connection with a I/O channel and to send and receive on that channel.

Depending on the undelying platform, FTS device can correspond to actual device drivers, like on the ISPW or TMS32C40 platform, or to wrappers around existing I/O libraries or devices, like on the SGI platform; a uniform interface will allow complete portability of the sources between the different architectures.

FTS define (for the moment) two kind of devices: character devices and signal devices; character device are very similar to Unix character devices; they model a byte-stream, supporting byte-oriented I/O operations, and optionally seek operations; signal devices are devices modelling the MIDI input

The device system is conceptually divided in two parts: the standard programming interface every device support (documented in this chapter), and a set of functions and data structure to help building a new device driver when needed (documented in the next chapter).

As a complent, the FTS kernel define the concept of logical device, that essentially allow the definition of the role of a specific physical device in a given configuration (like use serial line one as MIDI input, use serial line 2 as client connection and so on); the configuration of logical devices is fully perfomed with the UCS configuration system.


FTS Character Device API

The FTS Device System is not documented in this release of the document.

FTS Signal Device API

The FTS Device System is not documented in this release of the document.


Device Creation Support

FTS include a library of macros and functions to aid the device driver developement.

This programming interface is not not documented in this release of this document


The FTS Kernel Reference Manual.

Authors : Maurizio De Cecco, François Déchelle

Copyright © 1995 IRCAM.