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.
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.
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
The error handling support is not documented in this release of the document.
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
clock
, in fts terminology);
all the fts time related primitives will work with any time source.
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
tick
dsp_msec
dsp_tick
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.
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.
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 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.
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
provide_next_tick
require
freerun
control
sig_out
sig_in
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_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.
This programming interface is not not documented in this release of this document.
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.
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.
This programming interface is not not documented in this release of this document
Authors : Maurizio De Cecco, François Déchelle
Copyright © 1995 IRCAM.