|
|
fx2c
Documentation
If you haven't read the effects system overview, you should do before
reading this document, otherwise it probably won't make a lot of
sense. As described, the fx2c build tool takes an fx file and generates
a C file for the effect. This document explains the format that
an fx file should take and provides an example.
Notation
All FX directives are in upper case (and are case sensitive). An
FX
directive and any parameters must be placed on one line with nothing
(other than a comment) on it, and the directive must be at the start
of the line. If you wish to put comments at any point in the module
file you should use the C commenting convention of /* ... */. These
will be stripped out by fx2c before any processing of the file begins.
FX File Directives
The first line of an FX file starts with a MODULE directive, which
must be placed before anything else (besides comments). The MODULE
directive is followed by the name of the effect module. By convention
the name of the FX file will match the name given to the module.
Immediately beneath the MODULE directive are a number of directives
that specify what parameters the module requires and accepts. Any
other parameters found in an Effect chunk for this module (other
than the module name) that are not listed in either of these sections
will result in a parse error in the core. Any required parameters
that are not specified in the an Effect chunk for this module will
also result in a parse error.
The PARAMREQUIRE directive should be followed by a comma seperated
list of required parameters. If none are required then this directive
should be followed by the word NONE (in uppercase).
The PARAMACCEPT directive should be followed by a comma seperated
list of parameters that are accepted, but not required. If you want
to accept all parameters then this directive should be followed
by the word ALL.
Directives after this are not expected to be in any particular order,
although putting PREAMBLE first and POSTAMBLE last would probably
make sense. Unlike previous directives these ones are expected to
be followed by C code, which continues until the next directive
is encountered. Apart from PREAMBLE and POSTAMBLE, the code below
a directive is the body of a function. For most of them you are
expected to return something, and you do it just using the normal
C "return" keyword. The list of parameters after the directive
works on a positional basis; that is, you can call them whatever
you like, but the first one will always be the global context (apart
from the GLOBALINIT one) and so on. All fx2c does is wrap your code
into a function body with an appropriate prototype, and the variable
names you supply will be used.
PREAMBLE
Code placed after the optional PREAMBLE directive (which takes no
parameters) will be placed before any other functions or other code
in the effect module. This is the place to put any #include style
directives along with any function prototypes etc.
POSTAMBLE
Code placed after the optional POSTAMBLE directive (which takes
no
prototypes) will be placed after any other code in the effect module.
This is the best place to put any custom functions that you want
to use in your effects module.
GOBALINIT
Code placed after this directive is executed as soon as the effect
module is loaded (e.g. it only happens once). It is optional to
provide this directive. It gives you the option of setting up global
context that will be accessible from all individual applications
of the effect and spanning all effect chunks. This code is expected
to return a void pointer, which will get passed as the first parameter
to all other chunks.
CHUNKINIT global
Code placed after this directive is executed once per Effect chunk
that uses the module. It is optional to provide this directive.
This gives you the chance to set up chunk level context that will
be passed to all applications of the effect from placements that
relate to a particular chunk. This is a good place to parse any
parameters and stash them away in a structure so you can use them
once per effect application. You should return a void* pointer to
this structure (or NULL if you don"t want one). This directive"s
first parameter is the name of a void pointer that will contain
whatever was returned from GLOBALINIT, or NULL if you didn"t
have a GLOBALINIT.
INIT global, chunk
The INIT directive is to be followed by code that is executed to
initialise the effect module. This is called once per effect application
(e.g. before an effect is about to be applied to a placement). This
can be used to set up any initial conditions from the parameters,
allocate any buffers, etc. The code is expected to return a (void*),
which will be passed to functions that apply the effect. It is expected
that most effects modules will define their own struct, malloc it
and set it up it in this INIT function and return a pointer to it,
cast to a (void*). This directive"s first parameter is the
name of a void pointer that will contain whatever was returned from
GLOBALINIT, or NULL if you didn"t have a GLOBALINIT. This directive"s
second parameter is the name of a void pointer that will contain
whatever was returned from GLOBALCHUNK, or NULL if you didn"t
have a GLOBALINIT.
APPLY global, chunk, current, buffer, bufferSampleLength
The APPLY directive is to be followed by code that is called to
apply the effect to a buffer of data. The global parameter is a
void pointer the same as that returned by GLOBALINIT. The chunk
parameter is a void pointer the same as that returned by CHUNKINIT.
The current parameter is a void pointer the same as that returned
by INIT. The buffer parameter is an int* which points to the data
that you are to process. You should do the processing destructively,
e.g. to that buffer of data itself. bufferSampleLength is the number
of samples, independent of the number of channels, of data you have.
For example, if you have 2 channels then *buffer will point to the
first
sample for the first channel, *(buffer + 1) will point to the first
sample for the second channel, etc - just as in a standard PCM WAVE
file. Note that the number of channels and sampling rate are available
through macros, described later in this document.
IPCHANDLER global, chunk
Called by the IPC message dispatch routine when an effect chunk
handled by this module receives an IPC meesage. The module should
check if it knows how to handle the message and handle it appropriately
if so. If the message is syntactically invalid or the module is
unable to handle it, then 0 should be returned. If it can be handled,
1 should be returned. If the handling of the message is completed
successfully, then the IPCCONFIRM() macro should be called (like
a function call); otherwise, the IPCFAIL() macro should be used.
See the macro section for details on how to get details of the IPC
message.
DESTROY global, chunk, current
The DESTROY directive is to be followed by code that is called once
effect application to an individual placement has been completed.
The global parameter is a void pointer the same as that returned
by GLOBALINIT. The chunk parameter is a void pointer the same as
that returned by CHUNKINIT. The current parameter is a void pointer
the same as that returned by INIT. This is where you should free
any memory related to what you returned from INIT.
CHUNKDESTROY global, chunk
Code placed after this optional directive is executed on a per-chunk
basis, and usually just before the module is unloaded (note that
all calls to CHUNKDESTROY will be completed before GLOBALDESTROY
is called). The global parameter is a void pointer the same as that
returned by GLOBALINIT. The chunk parameter is a void pointer the
same as that returned by CHUNKINIT. This is where you should free
any memory related to what you returned at CHUNKINIT.
GLOBALDESTROY global
Code placed after this optional directive is executed before the
effect
module is unloaded. The global parameter is a void pointer the same
as that returned by GLOBALINIT. This is the place to free any memory
associated with the global pointer.
FX File Macros
There are certain bits of data that you may wish to fetch from your
FX
module. Rather than requiring deep knowledge of AMaMP structures,
some simple FX file "macros" are provided that fx2c replaces
with appropriate C code. This means that changes to the core to
effect module interface can be changed without having to modify
the FX files.
The macros fall into two categories. Those that have their parameters
in brackets can be placed anywhere in your C code, as if they were
a normal statement. Those without parameters in brackets must go
on a line of their own and code between them and their respective
end directive should expect to be put in a block.
GETPARAMETER(char*)
Not valid in GLOBALINIT and GLOBALDESTROY. This macro expects a
pointer to a NULL terminated string which names the parameter to
fetch (of course, this can just be the name of the parameter in
double quotes). Returns a poiner to the NULL terminated string containing
the value of the parameter (which you should NOT free or modify)
or NULL if the parameter does not exist.
ITERATEPARAMETERS name, value
....
ENDITERATEPARAMETERS
Not valid in GLOBALINIT and GLOBALDESTROY. This macro allows all
parameters to be iterated over. The variable named as the first
parameter will be a pointer to a NULL terminated string containing
the name of the parameter. The variable named as the second parameter
will be a pointer to a NULL terminated string containing the value
of the parameter.
DEBUGMESSAGE("The message")
This macro expects a pointer to a NULL terminated string which contains
text to use in a debug message. If the core was compiled to emit
debug messages, a debug message will be generated. The message should
not contain any newline characters.
THROWWARNING("The
message")
This macro expects a pointer to a NULL terminated string which contains
a warning message that you want to send, through the core, to the
application invoking the AMaMP core. The message should not contain
any newline characters.
THROWERROR("The message")
This macro expects a pointer to a NULL terminated string which contains
an error message that you want to send, through the core, to the
application invoking the AMaMP core. Note that throwing an ERROR
will *terminate the entire core*. THROWERROR is only to be used
in situations where you really can not go on, e.g. memory allocation
fails. The message should not contain any newline characters.
GETCHANNELS()
Returns an integer containing the number of channels in the data
buffer that is passed to the code beneath the APPLY directive. You
must NOT assume there will always be 2 channels. There may be 1,
there may be 5.
GETSAMPLINGRATE()
Returns an integer containing the number of samples per second in
the data buffer that is passed to the code beneath the APPLY directive.
GETIPCMESSAGETYPE()
Only valid inside IPCHANDLER. Returns the type of the message as
a NULL terminated string. At the time of writing, this will always
be "effect".
GETIPCPARAMETER(char*)
Only valid inside IPCHANDLER. Takes the name of an IPC message parameter
and returns the value of that parameter as a NULL terminated string,
or NULL if that parameter doesn't exist.
ITERATEIPCPARAMETERS
name, value
....
ENDITERATEPARAMETERS
Only valid inside IPCHANDLER. This macro allows all parameters in
an IPC message to be iterated over. The variable named as the first
parameter will be a pointer to a NULL terminated string containing
the name of the parameter. The variable named as the second parameter
will be a pointer to a NULL terminated string containing the value
of the parameter.
IPCCONFIRM()
Only valid inside IPCHANDLER. Call this macro when an IPC message
has been processed successfully to get the core to send a confirmation
message to the requesting front end.
IPCFAIL()
Only valid inside IPCHANDLER. Call this macro when an IPC message
was syntactically valid (e.g. 1 is going to be returned by IPCHANDLER
rather than 0), but a problem occurred during processing. The core
will send a failure message to the requesting front end.
Example
The following example simply emits debug messages for the various
INIT and DESTROY calls and halves the volume on application. If
you want more examples, simply look in the effects folder itself.
/* This is a dummy effects module to help with debugging
the system. All it does is halve the volume. */
MODULE Dummy
PARAMREQUIRE NONE
GLOBALINIT
DEBUGMESSAGE("Global Init Called");
return NULL;
CHUNKINIT global
DEBUGMESSAGE("Chunk Init Called");
return NULL;
INIT global, chunk
DEBUGMESSAGE("Init Called");
return NULL;
APPLY global, chunk, current, buffer, bufferSampleLength
/* This is a dummy module. We'll simply halve the volume. */
int *curPos = buffer;
while (curPos < buffer + (bufferSampleLength * GETCHANNELS()))
{
*curPos = (int) (0.5 * *curPos);
curPos ++;
}
DESTROY global, chunk, current
DEBUGMESSAGE("Destroy Called");
CHUNKDESTROY global, chunk
DEBUGMESSAGE("Chunk Destroy Called");
GLOBALDESTROY global
DEBUGMESSAGE("Global Destroy Called");
|
|