qzdoom/dumb/docs/dumb.txt

1700 lines
74 KiB
Plaintext

/* _______ ____ __ ___ ___
* \ _ \ \ / \ / \ \ / / ' ' '
* | | \ \ | | || | \/ | . .
* | | | | | | || ||\ /| |
* | | | | | | || || \/ | | ' ' '
* | | | | | | || || | | . .
* | |_/ / \ \__// || | |
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
* / \
* / . \
* dumb.txt - DUMB library reference. / / \ \
* | < / \_
* See readme.txt for general information on | \/ /\ /
* DUMB and how to set it up. \_ / > /
* | \ / /
* If you are new to DUMB, see howto.txt. | ' /
* \__/
*/
***********************************
*** Include Files and Libraries ***
***********************************
dumb.h
Include this if you only want the core DUMB library functions. You will
be able to load music files and render them into memory buffers at your
own pace. The core library is completely portable, and as such does not
access hardware; you must relay the sound data to the sound card yourself.
A stdio file input module is available, but you must actively register it
if you wish to use it (see dumb_register_stdfiles()); if you do not
register it, it will not be linked into your executable. You must register
it, or a DUMBFILE module of your own, in order to load stand-alone music
files.
Optimised: -ldumb or /link dumb.lib
Debugging: -ldumbd or /link dumbd.lib
aldumb.h
Include this if you wish to use DUMB with Allegro. This will provide you
with functions to play DUHs back through Allegro's audio streams and embed
music files in Allegro datafiles. A file input module using Allegro's
packfiles is provided; you have a choice between this and the stdio
module (or provide one of your own). You will be able to load datafiles
containing music files no matter which file input module you register, or
even if you register no file input module. However, you must register a
file input module in order to load stand-alone files.
Optimised: -laldmb -ldumb -lalleg or /link aldmb.lib alleg.lib dumb.lib
Debugging: -laldmd -ldumbd -lalld or /link aldmd.lib alld.lib dumbd.lib
aldmb or aldmd must be linked in first, so the symbols can be resolved
when linking in the other two libraries.
***************************
*** Version Information ***
***************************
#define DUMB_MAJOR_VERSION
#define DUMB_MINOR_VERSION
#define DUMB_REVISION_VERSION
Numeric constants representing this version of DUMB. If this were version
1.0, DUMB_MAJOR_VERSION would be 1 and DUMB_MINOR_VERSION would be 0.
DUMB_REVISION_VERSION will be 0 on any significant releases, and will be
incremented as releases with bugfixes and minor features are made.
Typical usage:
#if DUMB_MAJOR_VERSION < 1
#error This add-on requires DUMB v1.0 or higher. Please upgrade.
#endif
#define DUMB_VERSION
A numeric constant which appears in the format MMmmrr when displayed in
decimal (M for major, m for minor, r for revision). This is most useful
for comparing version numbers; it has little other practical use.
Typical usage:
#if DUMB_VERSION < 801
#error This game requires DUMB v0.8.1 or higher. Please upgrade.
#endif
#if DUMB_VERSION < 10002
#error This game requires DUMB v1.0.2 or higher. Please upgrade.
#endif
#define DUMB_VERSION_STR
String constant representing this version of DUMB. If this were Version
1.0, DUMB_VERSION_STR would be "1.0". DUMB_REVISION_VERSION will only
appear on the end if it is nonzero; then DUMB_VERSION_STR might be
"1.0.1".
#define DUMB_NAME
A string identifying DUMB and its version. If this were Version 1.0,
DUMB_NAME might be "DUMB v1.0". This constant is suitable for use in your
Credits screen if you wish to acknowledge the use of DUMB there.
#define DUMB_YEAR
#define DUMB_MONTH
#define DUMB_DAY
Numeric constants representing the year, month and day of this release of
DUMB. All four digits are included in the year. Please note that
DUMB_MONTH and DUMB_DAY were inadvertently swapped in the v0.8 release.
#define DUMB_YEAR_STR4
#define DUMB_YEAR_STR2
#define DUMB_MONTH_STR2
#define DUMB_MONTH_STR1
#define DUMB_DAY_STR2
#define DUMB_DAY_STR1
String constants representing the year, month and day of this release of
DUMB. DUMB_MONTH_STR2 and DUMB_DAY_STR2 include a leading zero if the
month or day respectively are less than ten; the STR1 variations do not.
DUMB_YEAR_STR2 contains only the two rightmost digits of the year, while
DUMB_YEAR_STR4 contains all four. I recommend using DUMB_YEAR_STR4,
especially so soon after the turn of the century (indeed the millennium).
However, it is a matter of personal preference which you use.
Please note that the month and day were inadvertently swapped in the v0.8
release.
#define DUMB_DATE
A numeric constant that appears in the form yyyymmdd when displayed in
decimal. This is most useful for comparing release dates; it has little
other practical use.
WARNING: The month and day were inadvertently swapped in the v0.8 release.
Please do not compare this constant against any date in 2002. In
any case, DUMB_VERSION is probably more useful for this purpose.
#define DUMB_DATE_STR
The date as a string. The format is "d.m.yyyy", with dots used as
separators, the day written first, four digits for the year, and no
leading zeros on the day or month. This is my preferred format. If you
don't like it, you can construct your own format using the other
constants. For example, "mm/dd/yy" could be constructed as follows:
DUMB_MONTH_STR2 "/" DUMB_DAY_STR2 "/" DUMB_YEAR_STR2
Please note that the month and day were inadvertently swapped in the v0.8
release.
*************************
*** Basic Sample Type ***
*************************
typedef int sample_t;
DUMB works internally with 32-bit integer samples, with a 'normal range'
from -0x800000 to 0x7FFFFF (as of DUMB v0.9.2; previously they ranged from
-0x8000 to 0x7FFF). Any samples that exceed this range will eventually be
clipped, and could cause integer overflow in extreme cases.
***********************************
*** Library Clean-up Management ***
***********************************
int dumb_atexit(void (*proc)(void));
Registers a function to be called at the end of your program. You can
register multiple functions to be called, and the one you register last
will be called first. If you try to register the same function twice, the
second attempt will have no effect.
See fnptr.txt for help with function pointers.
You must call dumb_exit() before exiting your program for this to work
properly. The library itself registers functions with dumb_atexit(), so it
is important to call dumb_exit() even if you do not use dumb_atexit()
yourself.
This function will return zero on success. It will return zero when
trying to install the same function twice. If it fails through lack of
memory, it will return nonzero. Generally you can ignore the return code;
in the worst case some memory will not be freed at the end. If it is
crucial that your function be called (e.g. to shut down some hardware or
save critical data), then you should call your function manually at the
end of the program instead of registering it here - or use the stdlib
function atexit(), guaranteed under ANSI C to succeed for at least 32
functions.
void dumb_exit(void);
You should call this before exiting your program if you have used any part
of DUMB in the program. Some parts of DUMB will allocate memory, and this
function will free it all up.
More specifically, this function will call any functions that have been
registered with dumb_atexit(). If a part of DUMB needs shutting down, the
shutdown procedure will have been registered in this way.
dumb_exit() will, of course, also call any functions you registered with
dumb_atexit() yourself.
After a call to dumb_exit(), the list of functions is erased. If you are
not ready to exit your program, you can start using DUMB anew as if your
program had just started. (Note that not everything will be reset in
practice - dumb_resampling_quality will retain whatever you set it to, for
example, though you should not assume it will.)
If you only need to call dumb_exit() once at the end of the program, you
can use the following to register dumb_exit() with stdlib.h atexit():
#include <stdlib.h>
atexit(&dumb_exit);
Then dumb_exit() will be called for you when your program exits. This is
the recommended method, since it will ensure clean-up even if your program
aborts. You should only call dumb_exit() manually if you need to shut DUMB
down prematurely, or if atexit() is unavailable for one reason or another.
*****************************
*** Sequential File Input ***
*****************************
DUMB provides a strictly sequential file input system which uses the
DUMBFILE struct. "Strictly sequential" means you cannot seek backwards.
However, the system will keep track of how many bytes you have read,
enabling you to seek forwards. DUMBFILEs provide a convenient error
detection system, so you do not have to check the return value from every
function call in the way you do with the ANSI C functions.
Note that DUMBFILEs cannot be used for output, nor can they be used
portably for text files.
If an error occurs when reading data from a DUMBFILE, the DUMBFILE will
become inoperative. All subsequent activities on the DUMBFILE will return
error codes without attempting to read from the file. The position in the
file will also be forgotten. You can find out if this has happened at any
stage with the dumbfile_error() function. You are still required to close
the DUMBFILE, and the return value from dumbfile_close() will tell you if
an error has occurred.
This system allows you to input large chunks of your file, neither
checking every return value nor wasting time accessing a file that has
already experienced an error. However, before you allocate an amount of
memory or read in a quantity of data depending on previous input from the
file, you should always check that such input was valid. In particular you
should avoid passing zero or negative numbers to malloc(), and avoid
passing negative numbers to dumbfile_skip() and dumbfile_getnc().
DUMBFILEs can be hooked. In other words, you can specify your own
functions to do the work of reading from a file. While DUMB contains two
modules for this purpose, it does not set them up for you automatically.
In most cases you must register one of these modules yourself, or provide
your own module. See register_dumbfile_system(), dumb_register_stdfiles()
and dumb_register_packfiles().
void register_dumbfile_system(DUMBFILE_SYSTEM *dfs);
Use this function to register a set of functions for use by the DUMBFILEs
(a DUMBFILE system). The DUMBFILE_SYSTEM struct contains the following
fields:
void *(*open)(const char *filename);
int (*skip)(void *f, long n);
int (*getc)(void *f);
long (*getnc)(char *ptr, long n, void *f);
void (*close)(void *f);
See fnptr.txt for help with function pointers such as these.
Your 'open' function should open the file specified and return a pointer
to a struct representing the open file. This pointer will be passed to
your other functions as 'f'. Your 'close' function should close the file
and free all memory pointed to by 'f'. Note that the 'close' operation
should never be able to fail; if you are calling a function with a return
value, you can generally ignore it.
Your 'getc' function should read one byte from the file and return its
value in the range 0 to 255. If an error occurs, you should return -1. Do
not worry about remembering that an error has occurred; DUMB will do that
for you.
'skip' is for skipping parts of the file, and should skip n bytes,
returning 0 on success or any other number on failure. 'getnc' should read
n bytes from the file, store them at 'ptr', and return the number of bytes
read (n on success, fewer on failure). However, these two functions are
optional, and you should only provide them if the operations can be done
more efficiently than with repeated calls to your 'getc' function. If this
is not the case, specify NULL for 'skip', 'getnc' or both, and DUMB will
use your 'getc' function to do the work.
Once you have written all your functions, you need to create a
DUMBFILE_SYSTEM struct to hold them, and pass its pointer to
register_dumbfile_system().
The DUMBFILE_SYSTEM struct must be permanent. In other words, it must be
either global or static, and you should not modify it later. DUMB will not
make its own copy.
You will most likely create your own struct to represent the open file,
but do not be tempted to specify that struct in the function prototypes
and pacify the compiler warnings by casting your function pointers. There
exist computer systems where a (void *) pointer and a (MY_STRUCT *)
pointer are represented differently in memory, and a cast of such a
pointer causes a tangible conversion to take place. If you cast the
function pointers, the computer cannot know when such a conversion is
necessary. Instead, use the following structure:
int myskip(void *f, long n)
{
FILE *file = f;
/* Do some stuff with 'file' */
return something;
}
If you need examples, have a look at the two existing DUMBFILE systems in
dumb/src/core/stdfile.c and dumb/src/allegro/packfile.c.
DUMBFILE *dumbfile_open(const char *filename);
Open the specified file for input. You must pass the DUMBFILE pointer
whenever you wish to operate on this file. When you have finished with the
file, you must pass it to dumbfile_close().
Before you use this function, make sure you have registered a DUMBFILE
system. See register_dumbfile_system(), dumb_register_stdfiles() and
dumb_register_packfiles().
You must check the return value from this function. If it is NULL, the
file could not be opened, and you must not pass the DUMBFILE to any other
function. The debugging library will abort if you get this wrong; the
optimised library will act weird.
DUMBFILE *dumbfile_open_ex(void *file, DUMBFILE_SYSTEM *dfs);
This function is provided for more specialised use. You should create a
DUMBFILE_SYSTEM specially for the purpose. Its 'open' field is irrelevant;
for neatness, set it to NULL, unless you are using this DUMBFILE_SYSTEM
with register_dumbfile_system() as well.
When you have called this function, the DUMBFILE struct it returned can be
used as normal. The specified DUMBFILE_SYSTEM will be used for all input,
with 'file' passed to your 'skip', 'getc' and 'getnc' functions as 'f'.
This can be used, for example, to read from an already open file.
Note that the position will always be initialised to 0 for this DUMBFILE.
This means for example that offsets in the file do not need adjusting when
embedding data in a larger file.
There are two ways to use this function. If you want 'file' to persist
after using a DUMBFILE returned by this function, you should make sure the
'close' field in the DUMBFILE is set to NULL. When the DUMBFILE is closed,
'file' will be left alone, and you can and should deal with it yourself
when the DUMBFILE has been closed.
Alternatively, you can provide a 'close' function to get rid of 'file' for
you when the DUMBFILE is closed. If you do this, you should not otherwise
use 'file' after a call to this function.
If dumbfile_open_ex() has to return NULL, owing to lack of memory, then
your 'close' function will be called if provided. In other words, if you
have provided a 'close' function, then you no longer need to worry about
'file' whether this function succeeds or not.
See dumb/src/helpers/stdfile.c and dumb/src/allegro/packfile.c for
examples of how to use this function. Neither provides a 'close' function,
so I hope my explanation here will suffice. If not, please feel free to
contact me so I can make the explanation clearer and help you do what you
want to do. Contact details are at the end of this file.
long dumbfile_pos(DUMBFILE *f);
Returns the number of bytes read from the DUMBFILE (or skipped) since it
was opened, or -1 if an error has occurred while reading.
int dumbfile_skip(DUMBFILE *f, long n);
Skips n bytes of the specified DUMBFILE. Returns zero on success.
int dumbfile_getc(DUMBFILE *f);
Reads one byte from the DUMBFILE and returns it in unsigned format (from 0
to 255). If an error occurs, or occurred before, this function returns -1.
int dumbfile_igetw(DUMBFILE *f);
Reads two bytes from the DUMBFILE and combines them into a word ranging
from 0 to 65535. The first byte read is the least significant byte, as
with Intel processors. This function returns -1 on error.
int dumbfile_mgetw(DUMBFILE *f);
Reads two bytes from the DUMBFILE and combines them into a word ranging
from 0 to 65535. The first byte read is the most significant byte, as
with the Apple Macintosh. This function returns -1 on error.
long dumbfile_igetl(DUMBFILE *f);
Reads four bytes from the DUMBFILE and combines them into a long integer
ranging from -2147483648 to 2147483647. The first byte read is the least
significant byte, as with Intel processors. This function returns -1 on
error, but -1 is also a valid return value. After a call to this function,
you can use dumbfile_error() to find out if an error occurred.
long dumbfile_mgetl(DUMBFILE *f);
Reads four bytes from the DUMBFILE and combines them into a long integer
ranging from -2147483648 to 2147483647. The first byte read is the most
significant byte, as with the Apple Macintosh. This function returns -1 on
error, but -1 is also a valid return value. After a call to this function,
you can use dumbfile_error() to find out if an error occurred.
unsigned long dumbfile_cgetul(DUMBFILE *f);
Reads an unsigned (nonnegative) integer from the DUMBFILE. The integer is
stored in a condensed format where smaller numbers use less space:
0 to 127 1 byte
128 to 16383 2 bytes
16384 to 2097151 3 bytes
2097152 to 268435455 4 bytes
268435456 to 4294967295 5 bytes
This format is the same as that used for the times between notes in MIDI
files.
If an error occurs, this function returns (unsigned long)(-1), but that
may be a valid return value. After a call to this function, you can use
dumbfile_error() to find out if an error occurred.
signed long dumbfile_cgetsl(DUMBFILE *f);
Reads a signed integer from the DUMBFILE. The integer is stored in a
condensed format where numbers closer to zero use less space:
-64 to 63 1 byte
-8192 to 8191 2 bytes
-1048576 to 1048575 3 bytes
-134217728 to 134217727 4 bytes
-2147483648 to 2147483647 5 bytes
If an error occurs, this function returns -1, but -1 is also a valid
return value. After a call to this function, you can use dumbfile_error()
to find out if an error occurred.
long dumbfile_getnc(char *ptr, long n, DUMBFILE *f);
Reads n bytes from the DUMBFILE and stores them at 'ptr'. Note that the
pointer is to a series of chars. You may also use this function to read in
a series of signed chars or unsigned chars (which are both officially
distinct types from char), but do not use this to read ints, structs or
any other data type from the file. Integers must be read one at a time
using dumbfile_igetl(), dumbfile_cgetul(), etc. To load a struct in, you
must read each field separately using an appropriate function for each
one. For complicated data types, you can simplify this process by writing
a function for each struct.
dumbfile_getnc() returns the number of bytes successfully read, which will
be less than n if an error occurs, and may be as low as zero. If
dumbfile_getnc() returns -1, that means an error occurred on this DUMBFILE
earlier, before this function was called.
int dumbfile_error(DUMBFILE *f);
This function returns -1 if an error has occurred with the specified
DUMBFILE, or 0 if all is well.
int dumbfile_close(DUMBFILE *f);
This function closes the DUMBFILE, after which the pointer will be
invalid. dumbfile_close() returns the value that dumbfile_error() would
have returned, which is -1 if an error occurred while reading or 0
otherwise. Regardless of the return value, the file will always be closed
properly.
*******************************
*** stdio File Input Module ***
*******************************
void dumb_register_stdfiles(void);
This function registers the stdio file input module for use by DUMBFILEs.
FILE structs and their corresponding functions, as defined by the ANSI C
header stdio.h, will be used internally for all DUMBFILE input (unless
opened with dumbfile_open_ex()).
This must be called before dumbfile_open() is used, or else an alternative
system must be registered (see register_dumbfile_system() and
dumb_register_packfiles()).
DUMBFILE *dumbfile_open_stdfile(FILE *p);
If you have a stdio FILE struct representing an open file, you can call
this if you wish to read from it using a DUMBFILE. This is useful when you
need to pass a DUMBFILE struct to a library function, to read an embedded
music file for example. When you close the DUMBFILE, you can continue
using the FILE struct to read what follows the embedded data.
********************************
*** Memory File Input Module ***
********************************
DUMBFILE *dumbfile_open_memory(const char *data, long size);
This function is useful if you have an image of a music file in memory.
You might have such an image if you use dat2s to encode a datafile
directly into the executable. Pass a pointer to the start of the memory,
and the size of the image to make sure DUMB doesn't overrun the buffer.
The resulting DUMBFILE will feed the contents of the image to you.
Note that the pointer is of type 'char *'. Files are series of chars, and
interpreting them directly as anything else isn't portable.
**********************
*** DUH Management ***
**********************
void unload_duh(DUH *duh);
Removes a DUH from memory. You must call this for all DUHs you load,
making sure they're not playing at the time.
long duh_get_length(DUH *duh);
Returns the length of a DUH; 65536 represents one second. This value is
calculated when the DUH is created, and this function simply lifts it from
the struct. It may not truly correspond to the time for which the DUH will
generate sound. For module files, it will represent the point at which the
module first loops (or, in the case of some XM and MOD files, freezes).
Any add-ons to DUMB will provide their own code for calculating this.
The algorithm for calculating the length of a module file can be fooled,
but only by very deliberate methods. In the early days, when modules could
only be played by their editors and had to be exported to .wav or similar
in order to be used elsewhere, musicians would sometimes make the player
think it was looping when it wasn't in order to prevent their music from
being exported properly. If the length of a module seems a lot less than
it should be, the module is probably protected in this way.
Getting around this protection reliably would be extremely difficult, but
after considering it for a while I decided it would be better not to. The
musician has a right to protect his or her music in this way, and I have
no interest in actively breaking that protection.
(On the other hand, some musicians were just showing off!)
***********************************
*** IT, XM, S3M and MOD Support ***
***********************************
int dumb_it_max_to_mix;
Specifies the maximum number of samples DUMB will mix at any one time. The
default number is 64. Regardless of this value, all samples will continue
to be processed up to an internal maximum of 256 (roughly speaking; in
fact it will process one sample for each channel plus up to 192 extra
samples that are continuing to play owing to Impulse Tracker's New Note
Actions), and samples that have been cut will sound again as soon as the
congestion clears. Samples are given priority according to their final
volume after all factors affecting the volume of a sample have been
considered.
If you play two or more modules at once, this value represents the
maximum number of samples for each one. You will have to reduce it further
if your computer cannot keep up.
Despite the name, this variable controls XM, S3M and MOD files as well as
IT files.
DUMB_IT_SIGDATA *duh_get_it_sigdata(DUH *duh);
This function attempts to retrieve the DUMB_IT_SIGDATA struct from a DUH.
This struct will exist for any IT, XM, S3M or MOD file, and you can use it
to obtain or override module-specific information. If 'duh' is NULL, or if
the DUH you pass contains something other than a music module, then this
function will return NULL (which can safely be passed to any other
function).
DUMB_IT_SIGRENDERER *duh_get_it_sigrenderer(DUH_SIGRENDERER *sigrenderer);
This function attempts to retrieve the DUMB_IT_SIGRENDERER struct from a
DUH_SIGRENDERER. This struct will exist for any currently playing IT, XM,
S3M or MOD file, and you can use it to obtain or override information
specific to module playback. If 'sigrenderer' is NULL, or if the
DUH_SIGRENDERER you pass is rendering something other than a music module,
then this function will return NULL (which can safely be passed to any
other function).
DUH_SIGRENDERER *dumb_it_start_at_order
(DUH *duh, int n_channels, int startorder);
This function, given a DUH containing an IT, XM, S3M or MOD file, will
start playing it at the specified order. If the DUH does not contain a
module, this function will fail and return NULL.
Note that starting at an arbitrary order may result in missing notes or
other playback oddities. It should be used primarily for modules that
contain multiple songs that start on different orders. If you wish just to
start some music in the middle, consider using duh_start_sigrenderer() or
al_start_duh() with the pos parameter set appropriately.
void dumb_it_set_loop_callback(DUMB_IT_SIGRENDERER *sigrenderer,
int (*callback)(void *data), void *data);
Installs a callback which will be called every time the module loops. You
can pass any data pointer you like, and it will be passed to the callback
for you. DUMB considers a file to loop when it reaches the end, or when a
'Jump to order' effect (Bxx in both IT/S3M and XM/MOD) jumps to the same
order or a preceding order. This can result in the loop callback being
called when the module isn't really looping, but this only happens if the
module has a very deliberate design. See duh_get_length() for further
musings on this subject.
If your callback returns nonzero, the music will stop abruptly. Samples
will be cut, and the main program will be notified that the
DUH_SIGRENDERER has ended.
Alternatively, if you pass the DUMB_IT_SIGRENDERER for 'data', or
otherwise arrange for it to be available to the callback, then you can
call:
dumb_it_sr_set_speed(sigrenderer, 0);
from inside the callback, and this will cause the music to freeze but
samples will be able to continue playing. The xm_speed_zero callback will
NOT be called in this case (see below for information on this callback).
Note also that setting the speed in this way will work equally for IT and
S3M files, even though a 'speed zero' effect can only exist in XM and MOD
files. Beware when using this method; samples might not fade at all!
A helper callback, dumb_it_callback_terminate(), is provided; installing
this will cause the music to terminate when it tries to loop for the first
time.
Pass NULL to remove the callback function; the module will then loop as
normal.
void dumb_it_set_xm_speed_zero_callback(DUMB_IT_SIGRENDERER *sigrenderer,
int (*callback)(void *data), void *data);
Installs a callback which is in many ways similar to the loop callback
(see dumb_it_set_loop_callback()). This callback will be called whenever
an F00 effect is encountered in a MOD or XM file, setting the speed to
zero. If the callback returns nonzero, the music will terminate. If not,
any currently playing samples will continue to play. You can pass any data
pointer you like to this function, and it will be passed to your callback
for you.
The helper callback, dumb_it_callback_terminate(), will also work here;
installing it will cause the music to terminate as soon as an F00 effect
is encountered.
Pass NULL to remove the callback function.
void dumb_it_set_midi_callback(DUMB_IT_SIGRENDERER *sigrenderer,
int (*callback)(void *data, int channel, unsigned char byte),
void *data);
Installs a callback function which will be called whenever MIDI data are
generated by an IT file. (No other module formats are capable of
generating MIDI data, so your callback will never be called.)
Zxx macros will generate MIDI data. These are most often used to set the
parameters for IT's low-pass resonant filters, and DUMB will handle these
messages by itself by default. See Impulse Tracker's documentation for
the MIDI messages that control filters. However, Zxx macros can be used
to send any kind of MIDI data.
If you wish to interpret MIDI messages yourself, you can use this
callback. Note that the only MIDI messages generated by DUMB at present
are from Zxx macros; there are no messages for note start, stop, or
anything else.
If you return 1 from this callback, DUMB will subsequently ignore the byte
of MIDI data. You can use this to prevent Zxx macros from controlling the
filters, useful if they were intended to do something else. Note that this
is NOT an effective way to disable filters, since instruments can have
filter envelopes and initial filter parameters. DUMB provides no means to
disable filters, as any IT file that uses them will sound wrong without
them. If you want lower processor consumption, use a different piece of
music.
A helper callback, dumb_it_callback_midi_block(), is provided for blocking
all MIDI messages and making Zxx macros do nothing.
Pass NULL to remove the callback.
int dumb_it_callback_terminate(void *data);
This is a helper callback that can be installed with both
dumb_it_set_loop_callback() and dumb_it_set_xm_speed_zero_callback(). In
each case it will cause the music to terminate abruptly.
int dumb_it_callback_midi_block(void *data, int channel, unsigned char byte);
This helper callback, for use with dumb_it_set_midi_callback(), will
absorb all MIDI messages, returning 1 to prevent DUMB from interpreting
them itself.
DUH *dumb_load_it(const char *filename);
Loads the specified Impulse Tracker file, encapsulating it in a DUH
struct. Once the file is loaded, it can be treated exactly the same as any
other DUH in memory. If this fails it will return NULL, but you can safely
pass this NULL value to DUMB's other functions, so you do not need to
check the return value explicitly.
DUH *dumb_read_it(DUMBFILE *f);
Reads an Impulse Tracker file from an already open DUMBFILE. This leaves
the DUMBFILE open, but the DUMBFILE may not be positioned at the end of
the IT data. If you are embedding an IT in another file, you are advised
to store the size of the IT file and make up for it at the end using
dumbfile_pos().
Otherwise, this function is identical to dumb_load_it().
WARNING: The behaviour of this function is undefined if you pass a
DUMBFILE from which data have already been read; it is likely not
to work. This oversight will be fixed in future releases.
DUH *dumb_load_xm(const char *filename);
Loads the specified Fast Tracker II file, encapsulating it in a DUH
struct. Once the file is loaded, it can be treated exactly the same as any
other DUH in memory. If this fails it will return NULL, but you can safely
pass this NULL value to DUMB's other functions, so you do not need to
check the return value explicitly.
DUH *dumb_read_xm(DUMBFILE *f);
Reads a Fast Tracker II file from an already open DUMBFILE. This leaves
the DUMBFILE open, but the DUMBFILE may not be positioned at the end of
the XM data. If you are embedding an XM in another file, you are advised
to store the size of the XM file and make up for it at the end using
dumbfile_pos().
Otherwise, this function is identical to dumb_load_xm().
WARNING: The behaviour of this function is undefined if you pass a
DUMBFILE from which data have already been read; it is likely not
to work. This oversight will be fixed in future releases.
DUH *dumb_load_s3m(const char *filename);
Loads the specified Scream Tracker 3 file, encapsulating it in a DUH
struct. Once the file is loaded, it can be treated exactly the same as any
other DUH in memory. If this fails it will return NULL, but you can safely
pass this NULL value to DUMB's other functions, so you do not need to
check the return value explicitly.
DUH *dumb_read_s3m(DUMBFILE *f);
Reads a Scream Tracker 3 file from an already open DUMBFILE. This leaves
the DUMBFILE open, but the DUMBFILE may not be positioned at the end of
the S3M data. If you are embedding an S3M in another file, you are advised
to store the size of the S3M file and make up for it at the end using
dumbfile_pos().
Otherwise, this function is identical to dumb_load_s3m().
WARNING: The behaviour of this function is undefined if you pass a
DUMBFILE from which data have already been read; it is likely not
to work. This oversight will be fixed in future releases.
DUH *dumb_load_mod(const char *filename);
Loads the specified Amiga module file, encapsulating it in a DUH struct.
Once the file is loaded, it can be treated exactly the same as any other
DUH in memory. If this fails it will return NULL, but you can safely pass
this NULL value to DUMB's other functions, so you do not need to check the
return value explicitly.
DUH *dumb_read_mod(DUMBFILE *f);
Reads an Amiga module file from an already open DUMBFILE. This leaves the
DUMBFILE open, but the DUMBFILE may not be positioned at the end of the
MOD data. If you are embedding a MOD in another file, you are advised to
store the size of the MOD file and make up for it at the end using
dumbfile_pos().
Otherwise, this function is identical to dumb_load_mod().
WARNING: The behaviour of this function is undefined if you pass a
DUMBFILE from which data have already been read; it is likely not
to work. This oversight will be fixed in future releases.
int dumb_it_sd_get_n_orders(DUMB_IT_SIGDATA *sd);
This function returns the number of orders in the module.
int dumb_it_sd_get_initial_global_volume(DUMB_IT_SIGDATA *sd);
void dumb_it_sd_set_initial_global_volume(DUMB_IT_SIGDATA *sd, int gv);
These functions obtain and set the initial global volume for the module.
This value ranges from 0 to 128 inclusive. The module can set the global
volume itself during playback, so your change may not last throughout the
playback.
int dumb_it_sd_get_mixing_volume(DUMB_IT_SIGDATA *sd);
void dumb_it_sd_set_mixing_volume(DUMB_IT_SIGDATA *sd, int mv);
These functions obtain and set the mixing volume for the module. This
value ranges from 0 to 128 inclusive, and does not change during playback.
IT files have the mixing volume stored in them; for other formats it is
set to 48 on loading.
int dumb_it_sd_get_initial_speed(DUMB_IT_SIGDATA *sd);
void dumb_it_sd_set_initial_speed(DUMB_IT_SIGDATA *sd, int speed);
int dumb_it_sd_get_initial_tempo(DUMB_IT_SIGDATA *sd);
void dumb_it_sd_set_initial_tempo(DUMB_IT_SIGDATA *sd, int tempo);
These functions obtain and set the initial speed and tempo for the module.
During module playback, everything happens on a tick. If a beat is 24
ticks, then the tempo is measured in beats per second. The speed is then
the number of ticks per row. With a speed of 6, a beat is then four rows.
Modules can set these values during playback, so your change may not last
throughout the playback. MOD files have to set the speed and tempo on the
first row if they want anything other than the default 6/125, so your
change may not be noticed at all!
int dumb_it_sd_get_initial_channel_volume(DUMB_IT_SIGDATA *sd, int channel);
void dumb_it_sd_set_initial_channel_volume(DUMB_IT_SIGDATA *sd, int channel,
int volume);
These functions obtain and set the initial volume for the specified
channel. The channel parameter is 0-based (contrary to the display in most
trackers so be careful), and can range from 0 to DUMB_IT_N_CHANNELS - 1,
i.e. from 0 to 63.
Modules can set their channel volumes during playback, so your changes may
not last throughout the playback.
int dumb_it_sr_get_current_order(DUMB_IT_SIGRENDERER *sr);
int dumb_it_sr_get_current_row(DUMB_IT_SIGRENDERER *sr);
These functions return the current order and row of playback. Both are
0-based. If the DUMB_IT_SIGRENDERER is invalid, or has been terminated
by a callback (see dumb_it_set_loop_callback() and
dumb_it_set_xm_speed_zero_callback()), these functions will both return
-1.
int dumb_it_sr_get_global_volume(DUMB_IT_SIGRENDERER *sr);
void dumb_it_sr_set_global_volume(DUMB_IT_SIGRENDERER *sr, int gv);
These functions obtain and set the current global volume for the module.
This value ranges from 0 to 128 inclusive. The module can set the global
volume itself during playback, so your change may not last.
int dumb_it_sr_get_tempo(DUMB_IT_SIGRENDERER *sr);
void dumb_it_sr_set_tempo(DUMB_IT_SIGRENDERER *sr, int tempo);
int dumb_it_sr_get_speed(DUMB_IT_SIGRENDERER *sr);
void dumb_it_sr_set_speed(DUMB_IT_SIGRENDERER *sr, int speed);
These functions obtain and set the current speed and tempo of the module.
See the dumb_it_sd_*() equivalents of these functions for details on what
the speed and tempo mean.
Modules can set these values during playback, so your change may not last.
int dumb_it_sr_get_channel_volume(DUMB_IT_SIGRENDERER *sr, int channel);
void dumb_it_sr_set_channel_volume(DUMB_IT_SIGRENDERER *sr, int channel,
int volume);
These functions obtain and set the current volume for the specified
channel. The channel parameter is 0-based (contrary to the display in most
trackers so be careful), and can range from 0 to DUMB_IT_N_CHANNELS - 1,
i.e. from 0 to 63.
Modules can set their channel volumes during playback, so your changes may
not last.
void dumb_it_sr_get_channel_state(DUMB_IT_SIGRENDERER *sr, int channel,
DUMB_IT_CHANNEL_STATE *state);
Returns the current playback state of the given channel. If you pass a
channel in the range 0 to DUMB_IT_N_CHANNELS-1 (0 to 63), you will get the
state of the most recently played note on that physical channel, if it is
still playing. For MOD, S3M and XM files, that's all there is to it.
IT files can have more than one note playing on a single channel, courtesy
of New Note Actions. This function also lets you query all the notes that
have been forced into the background and are still playing. For this, set
'channel' to a value from DUMB_IT_N_CHANNELS to DUMB_IT_TOTAL_CHANNELS-1.
DUMB_IT_TOTAL_CHANNELS is defined as follows:
#define DUMB_IT_TOTAL_CHANNELS \
(DUMB_IT_N_CHANNELS + DUMB_IT_N_NNA_CHANNELS)
Querying these background channels for MOD, S3M and XM files will not do
any harm; the function will report that these channels are inactive. For
all files, be sure not to query any channel numbers greater than or equal
to DUMB_IT_TOTAL_CHANNELS.
You must provide a pointer to a preallocated DUMB_IT_CHANNEL_STATE struct.
The easiest way to do this is as follows:
DUMB_IT_CHANNEL_STATE state;
dumb_it_sr_get_channel_state(sr, channel, &state);
or:
DUMB_IT_CHANNEL_STATE state[IT_TOTAL_CHANNELS];
dumb_it_sr_get_channel_state(sr, channel, &state[channel]);
This struct contains the following fields:
int channel;
int sample;
int freq;
float volume;
unsigned char pan;
signed char subpan;
unsigned char filter_cutoff;
unsigned char filter_subcutoff;
unsigned char filter_resonance;
The first field to check is 'sample'; if this is 0, then the channel is
inactive and the other fields are undefined. Otherwise, it is the index of
the currently playing sample, and is 1-based.
The channel number is returned, 0-based. This will be the same as the
channel number you passed, unless you are querying a background channel in
which case it will represent the channel the note originated on.
The freq field is the current playback frequency, taking into account all
phenomena such as slides, vibrato and arpeggio.
The volume field ranges from 0.0f to 1.0f. In practical terms, it will
rarely reach 1.0f; if it does, the module is probably clipping a lot. This
takes mixing volume into account, along with all the other volume
phenomena in the IT file. The only one it doesn't take into account is the
one you pass to duh_render() or duh_sigrenderer_get_samples(), or the one
you passed to al_start_duh() (these are in fact the same thing).
The pan field ranges from 0 to 64 for a normally panned sample, but will
be 100 if the sample is playing using IT's surround mode where the right-
hand channel is inverted. If you want a more accurate pan reading, use one
of the following to get one:
int scaled_pan = ((int)state.pan << 8) + state.subpan;
float float_pan = state.pan + state.subpan / 256.0f;
The first will give a scaled value ranging (strictly) from 0 to 64*256.
The second will give a floating-point value whose scale corresponds to
that of the pan field. These results will only be valid if surround mode
is off, so you should check that pan <= 64 before using the above
expressions. At the time of writing, pitch-pan separation and panning
envelopes take advantage of the extra accuracy offered by subpan.
Note that subpan is signed. This means applications that only look at the
pan field will get an unbiased reading.
The filter cut-off and resonance both range from 0 to 127. If the cut-off
is 127 and the resonance is 0, then no filters are applied. These
parameters only ever change from the default values for IT files.
While IT allows you to set 127 different filter cut-off levels in the
patterns and as a default value per instrument, it also allows you to
create a filter envelope, which will result in an actual cut-off somewhere
between 0 and the first-mentioned value. By the time this has been
calculated, the actual cut-off may lie in between two levels on the
original scale. If this is the case, filter_subcutoff will be nonzero and
you can combine it with filter_cutoff. Typically you will want to use one
of the following:
int scaled_cutoff = ((int)state.filter_cutoff << 8) +
state.filter_subcutoff;
float float_cutoff = state.filter_cutoff +
state.filter_subcutoff / 256.0f;
The first will give you a scaled value whose maximum is 127*256. The
second will give you a floating-point value whose scale corresponds to the
scale used by filter_cutoff. These match the expressions given further up
for pan and subpan, but in this case, filter_subcutoff is unsigned.
Note that filter_subcutoff will always be zero if filter_cutoff is 127, so
you need not check it if you simply wish to determine whether filters are
being applied.
*******************************
*** DUH Rendering Functions ***
*******************************
Use these functions to generate samples from a DUH. First you call
duh_start_sigrenderer() with the DUH, the number of channels you want and
the position at which you want to start. Then you use duh_render() or
duh_sigrenderer_get_samples() to generate the samples. You can call these
functions as many times as you like, and they will generate as many or as
few samples as you require. When you have finished, call
duh_end_sigrenderer().
DUH_SIGRENDERER *duh_start_sigrenderer
(DUH *duh, int sig, int n_channels, long pos);
Starts a DUH_SIGRENDERER off. This is the struct you can use to get
samples from a DUH. This function does not generate any samples; you must
pass the struct to duh_render() or duh_sigrenderer_get_samples() for that.
When you have finished with it, you must pass it to duh_end_sigrenderer().
You can use as many DUH_SIGRENDERER structs as you like at the same time.
Set sig to 0 for now. Currently, n_channels can only be 1 or 2, for
monaural and stereo sound respectively. The debugging library will cause
your program to abort if you pass anything else. Future versions will be
enhanced to support more channels as soon as someone needs them.
When specifying the position, 0 represents the start of the DUH, and 65536
represents one second. Unlike most other music systems, DUMB will always
make sure every note is there right from the start (assuming you aren't
using any broken add-ons). In other words, you can start a DUH at a point
halfway through a long note, and you will still hear the long note.
void duh_sigrenderer_set_analyser_callback(DUH_SIGRENDERER *sigrenderer,
DUH_SIGRENDERER_ANALYSER_CALLBACK callback, void *data);
Installs a callback function which will be called every time the given
sigrenderer is used to generate some samples. This can be used to create
an oscilloscope or spectrum analyser. DUH_SIGRENDERER_ANALYSER_CALLBACK is
defined as follows:
typedef void (*DUH_SIGRENDERER_ANALYSER_CALLBACK)(void *data,
const sample_t *const *samples, int n_channels, long length);
If the above confuses you, see fnptr.txt. As for the 'samples' parameter,
the first 'const' says that the samples are read-only; the second says
that each channel's sample pointer is also read-only. If you don't
understand this, don't worry about it.
Beware: your callback function may occasionally be called with
samples == NULL. This means the main program has decided to skip through
the music without generating any data (see duh_sigrenderer_get_samples()).
You should handle this case elegantly, typically by returning immediately,
but you may wish to make a note of the fact that the music is being
skipped, for whatever reason.
Beware again: if the main program ever calls duh_sigrenderer_get_samples()
on a buffer that isn't all silence, this callback function will be passed
the existing buffer after mixing, and thus it will include the original
data. This will not be an issue if you stick to duh_render(), which always
starts with a buffer filled with silence.
The samples array is two-dimensional. Refer to it as follows:
samples[channel_number][sample_position]
where 0 <= channel_number < n_channels,
and 0 <= sample_position < length.
In addition you can pass any 'data' pointer you like to
duh_sigrenderer_set_analyser_callback(), and this pointer will be relayed
to your callback function each time.
To remove the callback function, pass NULL to
duh_sigrenderer_set_analyser_callback().
int duh_sigrenderer_get_n_channels(DUH_SIGRENDERER *sigrenderer);
Tells you how many channels a DUH_SIGRENDERER is set up to generate, or 0
if it is invalid (perhaps owing to lack of memory). This will be 1 for
monaural sound or 2 for stereo, in this release.
long duh_sigrenderer_get_position(DUH_SIGRENDERER *sigrenderer);
Tells you what position a DUH_SIGRENDERER is up to, or -1 if it is invalid
(perhaps owing to lack of memory). As usual, 65536 is one second.
long duh_sigrenderer_get_samples(DUH_SIGRENDERER *sigrenderer,
float volume, float delta,
long size, sample_t **samples);
Generates some samples in DUMB's internal 32-bit format (see sample_t; see
also duh_render()). The samples buffer is a two-dimensional array, and can
be allocated with create_sample_buffer(); see
duh_sigrenderer_set_analyser_callback() for details.
duh_sigrenderer_get_samples() mixes sample data with what's already in the
buffer, so you have to call dumb_silence() first.
The volume is a float. 1.0f is the pseudo-maximum. If you pass 1.0f, any
properly designed DUH will play nice and loud, but will not clip. You can
pass a greater volume if you like, but be prepared for the possibility of
distortion due to integer overflow. Of course you can pass smaller values
to play the DUH more quietly, and this will also resolve clipping issues
in badly designed DUHs.
Use delta to control the speed of the output signal. If you pass 1.0f, the
resultant signal will be suitable for a 65536-Hz sampling rate (which
isn't a commonly used rate). The most common sampling rates are 11025 Hz,
22050 Hz, 44100 Hz and 48000 Hz. You can work out the required delta value
as follows:
delta = 65536.0f / sampling_rate;
If you then increase this value, the DUH will speed up and increase in
pitch. If you decrease it, the DUH will slow down and decrease in pitch.
This function will attempt to render 'size' samples. In most cases it will
succeed. However, if the end of the DUH is reached, it may render fewer.
The number of samples rendered will be returned. Therefore, if the return
value is less than the value of 'size' passed, you know the DUH has
finished. It is safe to continue calling duh_sigrenderer_get_samples() if
you wish, and it will continually return 0.
If the DUH_SIGRENDERER is a null pointer, this function will generate
precisely 0 samples. If you pass NULL for 'samples', the function will
behave exactly the same as if you provided a sample buffer, except the
samples won't be stored anywhere and the function will execute very
quickly. This can be used to skip ahead in the audio.
long duh_render(DUH_SIGRENDERER *sigrenderer,
int bits, int unsign,
float volume, float delta,
long size, void *sptr);
Generates some samples and converts them to an 8-bit or 16-bit format (see
also duh_sigrenderer_get_samples()). Pass the DUH_SIGRENDERER as returned
by duh_start_sigrenderer(). Pass the number of bits, which should be 8 or
16. If unsign is nonzero, the samples will be unsigned (centred on 0x80 or
0x8000 for 8 bits and 16 bits respectively). If unsign is zero, the
samples will be signed.
Allegro's audio streams always take unsigned samples. 8-bit .wav files
always take unsigned samples. 16-bit .wav files always take signed
samples.
The volume and delta parameters work the same as for
duh_sigrenderer_get_samples().
This function will attempt to render 'size' samples. In most cases it will
succeed. However, if the end of the DUH is reached, it may render fewer.
The number of samples rendered will be returned. Therefore, if the return
value is less than the value of 'size' passed, you know the DUH has
finished. It is safe to continue calling duh_render() if you wish, and it
will continually return 0. However, if you wish to do this, you will
probably have to fill the rest of the buffer with silence, which is 0 for
signed, 0x80 for 8-bit unsigned or 0x8000 for 16-bit unsigned.
The samples will be placed at sptr. Use an array of chars for 8 bits or an
array of shorts for 16 bits. Stereo samples will be interleaved, left
first. Your array should contain at least (size * n_channels) elements of
the appropriate bit resolution.
From an aesthetic standpoint if nothing else, it is wise to use the C
qualifiers 'signed' or 'unsigned' depending on whether the samples are
signed or unsigned. This is also convenient if you wish to process the
samples further yourself.
If the DUH_SIGRENDERER is a null pointer, this function will generate
precisely 0 samples. Unlike with duh_sigrenderer_get_samples(), you must
specify a sample buffer.
void duh_end_sigrenderer(DUH_SIGRENDERER *dr);
Terminates a DUH_SIGRENDERER. Be sure to call this when you've finished
with one. You can safely pass a null pointer.
********************************
*** Allegro Packfile Support ***
********************************
void dumb_register_packfiles(void);
This function registers the Allegro PACKFILE input module for use by
DUMBFILEs. PACKFILE structs and their corresponding functions, as defined
by Allegro's header file allegro.h, will be used internally for all
DUMBFILE input (unless opened with dumbfile_open_ex()).
This must be called before dumbfile_open() is used, or else an alternative
system must be registered (see register_dumbfile_system() and
dumb_register_stdfiles()). Note that you don't have to call this function
in order to load datafiles that contain music.
DUMBFILE *dumbfile_open_packfile(PACKFILE *p);
If you have an Allegro PACKFILE struct representing an open file, you can
call this if you wish to read from it using a DUMBFILE. This is useful
when you need to pass a DUMBFILE struct to a library function, to read an
embedded music file for example. When you close the DUMBFILE, you can
continue using the PACKFILE struct to read what follows the embedded data.
DUMBFILE *dumbfile_from_packfile(PACKFILE *p);
This function is the same as dumbfile_open_packfile(), except it will
check if p is NULL, and arrange for pack_fclose() to be called on the
PACKFILE when you close the DUMBFILE. It can be seen as a function for
converting a PACKFILE to a DUMBFILE, but it will only work for a PACKFILE
you obtained with pack_fopen(), not pack_fopen_chunk(). If this function
fails, which may happen if memory is short, then the PACKFILE will be
closed immediately, so you need not worry about potential memory leaks or
files being left open when this happens.
The following is typical usage, and will open the compressed file foo.bin:
DUMBFILE *f = dumbfile_from_packfile(pack_fopen("foo.bin",
F_READ_PACKED));
This differs from calling dumb_register_packfiles() and dumbfile_open() in
that the latter will only read uncompressed files (and is thus a method
suitable for reading music modules).
***********************************************
*** Allegro Datafile Registration Functions ***
***********************************************
void dumb_register_dat_it(long type);
If you wish to embed an IT file in an Allegro datafile, it is recommended
that you use "IT " for the type. The grabber will have a box for the type
when you insert a new object. The grabber will treat the IT file as binary
data, which means the datafile will contain an exact copy of the IT file
on disk.
You must then call dumb_register_dat_it(DUMB_DAT_IT) in your program
before you load the datafile. Once you've done this, you'll be able to
access the DUH using the usual datafile[n].dat notation. You do not need
to call unload_duh() on this DUH; unload_datafile() will do that for you.
If you are using a different type for whatever reason, you can use
Allegro's DAT_ID() macro for encoding it and passing it to this function.
For example:
dumb_register_dat_it(DAT_ID('B','L','A','H'));
Assuming you used the recommended type, the following example iterates
through all the ITs in disan.dat:
DATAFILE *dat;
int n;
dumb_register_dat_it();
dat = load_datafile("disan.dat");
for (n = 0; dat[n].type != DAT_END; n++) {
if (dat[n].type == DUMB_DAT_IT) {
DUH *duh = dat[n].dat;
/* Insert code here to play 'duh' or whatever you want to do. */
}
}
unload_datafile(dat);
void dumb_register_dat_xm(long type);
Inserting an XM file in an Allegro datafile is the same as inserting an IT
file, except that the recommended type is "XM ", the registration
function is dumb_register_dat_xm(), and the macro DUMB_DAT_XM is provided
for the type. The intuitive process of substituting XM for IT in the above
method will work.
void dumb_register_dat_s3m(long type);
Inserting an S3M file in an Allegro datafile is the same as inserting an
IT file, except that the recommended type is "S3M ", the registration
function is dumb_register_dat_s3m(), and the macro DUMB_DAT_S3M is
provided for the type. The intuitive process of substituting S3M for IT in
the above method will work.
void dumb_register_dat_mod(long type);
Inserting a MOD file in an Allegro datafile is the same as inserting an IT
file, except that the recommended type is "MOD ", the registration
function is dumb_register_dat_mod(), and the macro DUMB_DAT_MOD is
provided for the type. The intuitive process of substituting MOD for IT in
the above method will work.
****************************************
*** Sample Buffer Allocation Helpers ***
****************************************
Many parts of DUMB require sample buffers allocated in a special way. A
pointer to one looks like this:
sample_t **samples;
and it can be indexed as follows:
samples[channel_number][sample_position]
where 0 <= channel_number < n_channels
and 0 <= sample_position < length.
The following helpers will allocate and deallocate such buffers for you.
They will not initialise them, and DUMB always writes into these buffers
by adding to what's already there, so you will generally have to call
dumb_silence() too.
sample_t **create_sample_buffer(int n_channels, long length);
This will allocate a sample buffer to hold the specified number of samples
for the specified number of channels. Don't forget to check the return
value!
You will generally have to initialise the buffer by calling
dumb_silence(); the channels will be stored consecutively in memory, so
the following technique is officially supported:
dumb_silence(samples[0], n_channels * length);
See dumb_silence() for general information on what this function does.
void destroy_sample_buffer(sample_t **samples);
This function does the obvious: it frees up a sample buffer when you've
finished with it. It is safe to pass a null pointer to this function.
************************
*** Silencing Helper ***
************************
void dumb_silence(sample_t *samples, long length);
This function simply stores 'length' samples' worth of silence in the
array. It is typically used straight after allocating a sample buffer with
create_sample_buffer().
**************************
*** Resampling Helpers ***
**************************
Please forgive the odd section name; it has to do with DUMB's internal
structure and the fact that the resampling algorithm is there not just for
use in rendering module files but for use anywhere that a waveform needs
resampling. Unfortunately DUMB's resampling algorithm is not ready to be
documented and used yet. However, one thing can be documented, and that's
the global variable controlling the resampling quality.
(Ironically, even this variable has changed! See deprec.txt for
information on what it used to do.)
int dumb_resampling_quality;
Allows you to control the quality of all resampling that takes place. This
may be set to any DUMB_RQ_* constant (except DUMB_RQ_N_LEVELS). Higher
values will sound better, but lower values will use up less processor
time. You may compare any two DUMB_RQ_* constants or values using the
integer inequalities <, <=, > and >=; higher numbers represent higher-
quality algorithms.
#define DUMB_RQ_ALIASING
| --___ 'Aliasing' has very noticeable and usually unwanted
|__--- __ overtones. It will occasionally produce acceptable
| ___-- results for noisy (impure) samples (or for cheap
speakers!), but usually you will want to pay for
the extra processor time, which isn't much, and go for linear
interpolation.
#define DUMB_RQ_LINEAR
| __ Linear interpolation is a pretty good algorithm in most
| / \ /\ cases. When resampling down a few octaves, however, you
|/ \/ \__ may begin to notice unwanted high frequencies. You can
reduce these by switching to cubic interpolation, but it
will cost you some processor time.
#define DUMB_RQ_CUBIC
Cubic interpolation looks like a smooth curve to the eye, and will
produce good results in most cases. At present this is the highest
quality offered by DUMB, and also the default. While this may seem
extravagant, GCC 3.x and an AthlonXP handle it quite well - and the
general trend is for processors to get better!
#define DUMB_RQ_N_LEVELS
This represents the number of resampling quality levels DUMB provides.
Values of dumb_resampling_quality from 0 to DUMB_RQ_N_LEVELS - 1 are
valid. You can use this constant if you wish to offer the resampling
quality as an option for the user.
*************************************
*** Allegro DUH Playing Functions ***
*************************************
The functions in this section allow you to play back a DUH through
Allegro's sound system. You must call Allegro's install_sound() function
before you use them.
AL_DUH_PLAYER *al_start_duh(DUH *duh, int n_channels, long pos,
float volume, long bufsize, int freq);
Starts playing the specified DUH.
An AL_DUH_PLAYER represents one instance of the DUH playing. If you wish,
you can have two or more AL_DUH_PLAYERs going at the same time, for the
same DUH or for different ones. Each uses one of Allegro's audio streams
and hence one voice. The voice will be given priority 255 initially, so a
build-up of sound effects will not cause your music streams to cut off (as
long as you don't give all your sound effects priority 255!). You can
change the priority of a stream with al_duh_set_priority(). See Allegro's
documentation for more information on how voice priorities work.
At present, n_channels can either be 1 or 2 for monaural or stereo
respectively. If you use the debugging library, your program will abort if
other values are passed; otherwise weird things will happen.
The DUH will start playing from position 'pos'. 0 represents the start of
the DUH, and 65536 represents one second. Unlike other music systems, DUMB
will always make sure every note is there right from the start. In other
words, you can start a DUH at a point halfway through a long note, and you
will still hear the long note.
The volume is a float. 1.0f is the pseudo-maximum. If you pass 1.0f, any
properly designed DUH file will play nice and loud, but will not clip. You
can pass a greater volume if you like, but be prepared for clipping to
occur. Of course you can pass smaller values to play the DUH more quietly,
and this will also resolve clipping issues in badly designed DUH files.
You will need to pass the AL_DUH_PLAYER to other functions when you need
to stop or pause the DUH, change its volume, or otherwise modify the way
it is playing. You will also need to pass it to al_poll_duh() at regular
intervals; if the sound is choppy, try calling al_poll_duh() more often.
'bufsize' is the number of samples that will be rendered at once. 1024 is
a suitable value for most purposes. The greater this is, the less often
you will have to call al_poll_duh() - but when al_poll_duh() decides to
fill the buffer, it will take longer doing so. If your game exhibits
regular brief freezes, try reducing the buffer size. If the sound is
choppy, however, you may have to increase it.
'freq' specifies the sampling frequency at which the DUH should be
rendered. At present there is no (official and portable) way of knowing
the frequency at which Allegro is mixing - but if you do know that
frequency, passing it here will give the highest quality sound. If you
reduce it, the DUH will sound less crisp but use less processor time.
When you have finished, you must pass the AL_DUH_PLAYER to al_stop_duh()
to free up memory. Do not destroy the DUH beforehand.
There is no real need to check the return value from this function. The
other functions can be called safely with null pointers, so if there is a
problem, your music will simply not play.
void al_stop_duh(AL_DUH_PLAYER *dp);
This will stop an AL_DUH_PLAYER. You must call this when you have finished
with it, before destroying the DUH. The pointer will no longer be valid on
return from this function.
void al_pause_duh(AL_DUH_PLAYER *dp);
This will pause an AL_DUH_PLAYER. Use al_resume_duh() when you want it to
continue. You can safely call al_poll_duh() while the music is paused, and
it will do nothing.
void al_resume_duh(AL_DUH_PLAYER *dp);
Causes a paused AL_DUH_PLAYER to resume playing (see al_pause_duh()).
void al_duh_set_priority(AL_DUH_PLAYER *dp, int priority);
This will set the priority of the audio stream underlying an
AL_DUH_PLAYER. The priority is an integer ranging from 0 to 255. When
too many samples play at the same time, those with lower priorities will
be cut. 128 is the usual default with Allegro, but DUMB overrides the
default for all AL_DUH_PLAYER structs: they will be set up initially with
priority 255, so your music won't be cut (unless you play too many other
streams or samples with priority 255). See Allegro's documentation for
more information on priorities.
void al_duh_set_volume(AL_DUH_PLAYER *dp, float volume);
This will set the volume of an AL_DUH_PLAYER. See al_start_duh() for
details on the volume parameter.
int al_poll_duh(AL_DUH_PLAYER *dp);
An AL_DUH_PLAYER is not interrupt-driven. That means it will not play by
itself. You must keep it alive from your main program. Call this function
at regular intervals. If the sound crackles, try calling it more often.
(There is nothing you can do if Windows decides to play with the hard
disk; that will make your sound crackle no matter what you do.)
Normally this function will return zero. However, if it returns nonzero,
that means the AL_DUH_PLAYER will not generate any more sound. Indeed the
underlying audio stream and DUH_SIGRENDERER have been destroyed. When this
happens, you can call al_stop_duh() whenever you wish - but you do not
have to. Note that this function will wait two buffers' worth of samples
before taking this action, allowing Allegro to mix the trailing sound
before the audio stream is destroyed. This is an attempt to make sure your
music does not get cut off prematurely, and it should work when using
Allegro's mixer (the only option on DOS, the default on Linux as far as I
know, but not the default on Windows). That said, if you immediately call
Allegro's remove_sound() or exit your program, the music may get cut off.
If you are using another mixer and experience problems, let me know (but I
don't guarantee to be able to come up with an elegant solution, i.e. it
might not get fixed).
In case you were wondering, it is not safe on all platforms to call
al_poll_duh() from an interrupt context (that means an Allegro timer
handler). Not only is no part of DUMB locked in memory, but many parts of
DUMB allocate and free their memory on a call-by-call basis! Remember that
any disk access that occurs in interrupt context is likely to crash the
machine; this is explained more fully in howto.txt. This limitation only
applies to DOS at present, and is due to the fact that the DOS file access
functions are not re-entrant.
Multitasking systems are generally safe. If you are sure you don't want to
target DOS, you can call al_poll_duh() from inside a timer handler, but I
recommend including a construction like the following!
#ifdef ALLEGRO_DOS
#error calling al_poll_duh() from a timer handler will not work in DOS!
#endif
Furthermore, if you call al_poll_duh() from inside a timer handler, you
must use a semaphore or other threading mechanism to make sure it is not
executing when you call al_stop_duh(). If you don't know what a semaphore
is, for Heaven's sake follow my advice and call al_poll_duh() from your
main loop!
long al_duh_get_position(AL_DUH_PLAYER *dp);
Tells you what position an AL_DUH_PLAYER is up to, or -1 if it is invalid
(perhaps owing to lack of memory). As usual, 65536 is one second. Note
that this is a whole number, whereas a fractional part is stored
internally; the sample will not be continuous if you terminate the
AL_DUH_PLAYER and then reinitiate it with the same position. Furthermore,
note that Allegro will not have mixed in all the sound up to this point;
if you wait for this to reach a certain position and then terminate the
AL_DUH_PLAYER, the sound will cut off too early. Please contact me if you
need to get around this.
AL_DUH_PLAYER *al_duh_encapsulate_sigrenderer
(DUH_SIGRENDERER *sigrenderer, float volume, long bufsize, int freq);
If you have a DUH_SIGRENDERER, and would like to start playing music from
it through an Allegro audio stream, use this function. Beware that it may
return NULL, in which case you will have to call duh_end_sigrenderer()
yourself instead of relying on the encapsulating AL_DUH_PLAYER to do it
for you.
DUH_SIGRENDERER *al_duh_get_sigrenderer(AL_DUH_PLAYER *dp);
This returns the DUH_SIGRENDERER contained in an AL_DUH_PLAYER, useful for
controlling playback, installing callbacks, etc.
DUH_SIGRENDERER *al_duh_decompose_to_sigrenderer(AL_DUH_PLAYER *dp);
This destroys an AL_DUH_PLAYER, but preserves the DUH_SIGRENDERER it
contains, and returns it to you. You can then continue rendering samples
from the DUH_SIGRENDERER and do whatever you like with them.
*********************
*** Thread Safety ***
*********************
The following points should pretty much sum up the essence of DUMB's thread
safety. If I haven't covered the one thing you'd like to do, please don't
hesitate to ask about it.
DOs:
- You may load and use multiple DUHs in separate threads.
- You may change dumb_resampling_quality and dumb_it_max_to_mix while another
thread is generating samples.
DON'Ts:
- You may not generate samples from the same DUH in multiple threads, even if
you are using separate DUH_RENDERERs (separate AL_DUH_PLAYERS).
******************
*** Conclusion ***
******************
"DUMB is the bestest music player in the world because ..."
Complete this sentence in fifteen words or fewer and receive a free copy of
DUMB! (Your Internet Service Provider may issue charges for your connection,
required for download of the Product. Your electricity supplier may issue
charges for the electricity consumed in writing the Product to a Permanent
Storage Device. You may have been charged for a Permanent Storage Device on
which to store the Product.)
Ben Davis
entheh@users.sf.net
IRC EFnet #dumb
See readme.txt for details on using IRC.