mirror of
https://github.com/ZDoom/gzdoom.git
synced 2024-11-18 18:31:43 +00:00
01f59fa85f
heavily customized version of DUMB (Dynamic Universal Music Bibliotheque). It has been slightly modified by me: * Added support for Ogg Vorbis-compressed samples in XM files ala FMOD. * Removed excessive mallocs from the replay core. * Rerolled the loops in resample.c. Unrolling them made the object file ~250k large while providing little benefit. Even at ~100k, I think it's still larger than it ought to be, but I'll live with it for now. Other than that, it's essentially the same thing you'd hear in foobar2000, minus some subsong detection features. Release builds of the library look like they might even be slightly faster than FMOD, which is a plus. - Fixed: Timidity::font_add() did not release the file reader it created. - Fixed: The SF2 loader did not free the sample headers in its destructor. SVN r995 (trunk)
1699 lines
74 KiB
Text
1699 lines
74 KiB
Text
/* _______ ____ __ ___ ___
|
|
* \ _ \ \ / \ / \ \ / / ' ' '
|
|
* | | \ \ | | || | \/ | . .
|
|
* | | | | | | || ||\ /| |
|
|
* | | | | | | || || \/ | | ' ' '
|
|
* | | | | | | || || | | . .
|
|
* | |_/ / \ \__// || | |
|
|
* /_______/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.
|