Adding C functions to APRL

One of the most useful capabilities of APRL is a hook to allow you to write your own pattern-recognition or signal-processing functions in C or C++ and link them into APRL. APRL handles all the real-time scheduling aspects; your code only needs to handle blocks of data.

User-written C procedures should go into a file called userprocs.c. This file is linked in to the aprlwish program by the Makefile provided with APRL. If you add user functions in other files, you will need to modify the Makefile.

Sections:


How to call C functions from APRL

The usercmd opcode allows you to call your C functions: The cname parameter must match an entry in the cnames table (See
Providing names to APRL ).

The inputs i1,i2,... must be in a comma-separated list with no spaces.

If sample rate is not specified, it defaults to the fastest sample rate among the input sources.

If buffer time is not specified, it defaults to the longest among the inputs.


Providing names to APRL

All user-written C procedures should be of type UserFunc, which is a function void UserFunc(UC_parm *parms,UC_parm *retval, int ct); Ie, it returns void and accepts three parameters, discussed below.

The global variables num_cfuncs and cnames are used to register new C procedures with APRL. cnames is an array of type name_table, which is struct { char *name; UserFunc *func; } -- a string containing the name of the function and a pointer to the function itself.

The cnames[].name field is the name by which the new procedure is referred to in the APRL code. It is not necessary that the name provided in this field be identical to the name given the function in C, but it makes the code cleaner if there is an obvious relationship.

num_cfuncs should specify the number of new commands being registered in the cnames structure. An example cnames declaration might look like this:

UserFunc my_first_function,my_second_function;

name_table cnames[] = {
  {"func1",&my_first_function},
  {"func2",&my_second_function}
  } ;

Parameters to User Commands

A UserFunc takes three parameters. The first is an array of UC_parm structures, which contain the data provided as input to the object in APRL; the second, a pointer to a UC_parm, which you use to hold the output of the object; and the third is an int which contains the number of elements in the first parameter.

The UC_parm type is as follows:

typedef struct {
  char *name;
  int length;
  DATA *s;
  double samplerate;
} UC_parm;
The name field contains a string with the name of the source or pipe object whose output is connected to the input of this object.

The length field tells how many samples of data are available at this time from this source. This number will vary from iteration to iteration; it is dependant on the sampling rate of the source, but also the various buffer sizes of "upstream" sources, and the current processing load on the machine.

The s field contains an array of samples, where s[0] is the earliest sample in time.

The samplerate field contains the sampling rate of the source.


An example program.

Here is an example user command:
 
void gate(UC_parm *parms,UC_parm *retval,int ct) {
  /* parms[0] is an audio signal,
     parms[1] is a control signal.

     pass the audio signal through to the output whenever
     the control signal is above THRESH

     this is a multirate process; the audio signal is
     assumed to be at a higher sampling rate than the control
     signal.
  */
  int i,j,pos;
  double dsfactor;

  /* allocate space for the returned data */
  retval->s = (DATA *)calloc(retval->length,sizeof(DATA));

  /* calculate the rate ratio of the two inputs */
  dsfactor = parms[0].length / parms[1].length;
  pos = 0;
  
  for (i=0;i!=parms[1].length;i++) {
  /* for each sample in the control signal */
  
    for (j=0;j THRESH)  /* and do the gating */
	retval->s[pos] = parms[0].s[pos];
      else
	retval->s[pos] = 0;
      pos++;
    }
  }
}
There are a few important points to highlight. This function is running at the sampling rate of the faster (audio) input. The default sampling rate for a user command is that of the fastest of its inputs; if another sampling rate is desired, the length should be set appropriately in the *retval structure, and the other sampling rate specified on the APRL command line (see
Calling User Commands from APRL ).

Also, notice that the retval->s array is not pre-allocated. We use calloc() to create space for it. free() is called on the retval->s pointer upon return if non-NULL, so it is important that the value be set to NULL if the return value isn't being used.

The samples of data are of type DATA; they can be added, multiplied, compared, etc using the normal operators.

It is not necessary to set the name and samplerate value for the retval structure. It IS necessary to set the length value, though, as it is used upon return.


Back to APRL page.
Eric Scheirer <eds@media.mit.edu>
MIT Media Laboratory Perceptual Computing Section
$Header: /ti/http/projects/aprl/RCS/usercmd.html,v 1.2 95/02/26 19:01:28 dpwe Exp $