summaryrefslogtreecommitdiff
path: root/dumb/dumb-kode54/docs/dumbfull.txt
diff options
context:
space:
mode:
Diffstat (limited to 'dumb/dumb-kode54/docs/dumbfull.txt')
-rw-r--r--dumb/dumb-kode54/docs/dumbfull.txt1717
1 files changed, 1717 insertions, 0 deletions
diff --git a/dumb/dumb-kode54/docs/dumbfull.txt b/dumb/dumb-kode54/docs/dumbfull.txt
new file mode 100644
index 00000000..6a8e192c
--- /dev/null
+++ b/dumb/dumb-kode54/docs/dumbfull.txt
@@ -0,0 +1,1717 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/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 in order to load stand-alone music files.
+
+ Optimised: libdumb.a (-ldumb)
+ Debugging: libdumbd.a (-ldumbd)
+
+
+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. 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 -lalleg -ldumb
+ Debugging: -laldmd -lalld -ldumbd
+
+ libaldmb.a or libaldmd.a must be linked in first, so the symbols can be
+ resolved when linking in the other two libraries.
+
+
+***********************************
+*** 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 passing zero or negative numbers to malloc() or 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().
+
+
+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 datatypes, you can simplify this process by writing a
+ function for each struct.
+
+
+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.
+
+
+********************************
+*** Signal Type Registration ***
+********************************
+
+
+
+
+
+
+ NOTE: SINCE THIS IS NOT TO BE THE INTRODUCTION TO DUMB, SHOULD THE
+ FOLLOWING INFORMATION BE MOVED TO ANOTHER FILE?
+
+
+
+
+
+
+ If you are lazy, then don't bother to read this section. Simply follow
+ these steps:
+
+ 1. Call all the dumb_register_sigtype_*() functions. You must do this
+ after dumb_init(), but before you try to call load_duh(). If you try
+ to load any DUH files beforehand, the library will fail to load
+ them.
+
+ 2. Run your program, and make sure the DUH file was successfully loaded
+ and played.
+
+ 3. Comment out the first dumb_register_sigtype_*() function call.
+
+ 3.1. If the DUH file is still loaded and played, remove this function
+ call.
+ 3.2. If the DUH file was not loaded and played, uncomment and keep this
+ function call.
+
+ 4. Repeat Step 3 for the other function calls.
+
+ Alternatively, the musician might have told you which of the functions you
+ need to call.
+
+ If you are the epitome of laziness, stop after Step 1. However, if you do
+ this, your executable may contain unnecessary code.
+
+ If, on the other hand, you are interested in how all this works, then read
+ on.
+
+ A DUH file comprises a number of 'signals'. A signal may be thought of as
+ a 'black box'; commands go in, sound comes out. A signal may in turn call
+ upon other signals to generate sound, and then do something with this
+ sound to generate its own output.
+
+ For example, here are the contents of a simple DUH file:
+ Signal #0 SEQU
+ Signal #1 SAMP
+ Signal #2 SAMP
+ Signal #3 SAMP
+
+ 'SEQU' and 'SAMP' are the signal types. 'SAMP' designates a sample, so
+ Signals #1, #2 and #3 are simple recordings. For instance, Signal #1 could
+ be a bass drum, Signal #2 a snare drum and Signal #3 a high hat. When
+ invoked, these signals simply output their respective percussion sounds.
+
+ 'SEQU' designates a sequence. Signal #0 will therefore generate its sound
+ by taking the output waveforms from the other signals and mixing them in
+ at appropriate times. For example:
+
+ 0.00 Signal #1
+
+ 0.50 Signal #2
+
+ 1.00 Signal #1
+ 1.25 Signal #3
+ 1.50 Signal #2 Signal #3
+ 1.75 Signal #3
+ 2.00 Signal #1
+ 2.25 Signal #3
+ 2.50 Signal #2 Signal #3
+ 2.75 Signal #3
+ 3.00 Signal #1
+
+ 3.50 Signal #2 Signal #3
+
+ The numbers down the left are times. Those with a good sense of rhythm
+ will be able to see that this represents a rather lame, highly cheesy pop
+ beat. Experienced gurus will also realise how painfully slow the beat is,
+ given that the times are in seconds. But enough of that.
+
+ A DUH is played by taking the output from Signal #0 and sending it through
+ to the sound card via an Allegro audio stream. If you do not know what an
+ audio stream is, suffice it to say that you must call Allegro's
+ install_sound() function, and an audio stream uses one voice (hence one
+ voice per DUH file). If you still don't know what I'm on about, read up on
+ Allegro's digital sound system and then come back here. Alternatively,
+ read porting.txt for details on how to use DUMB without Allegro.
+
+ In reality, DUH files are likely to be much more complicated than the
+ above example. Sequences may call upon other 'sub-sequences' to generate
+ their sound, so the above beat might be used repeatedly by a
+ 'super-sequence' to generate a more complex (but still lame) piece of
+ music.
+
+ The DUH player library is split into two main parts - the core and the
+ basic signal types. The core is very simple. It does not know what 'SAMP'
+ or 'SEQU' mean. In fact it is not familiar with any signal type. It only
+ contains generic code for dealing with signals and generating output. By
+ itself, it cannot load or play any DUH file.
+
+ In addition to the core, the player library comprises a few basic signal
+ types, including 'SAMP' and 'SEQU'. In order to use a signal type, you
+ must 'register' this signal type by calling its corresponding
+ dumb_register_sigtype_*() function. These functions are listed below,
+ along with descriptions of the signals.
+
+ If you do not register a signal type, the code for that signal type will
+ not be linked into your executable file. That means DUMB will not become
+ bloated with age; new features will not add to the size of your executable
+ unless you use them. Dynamically linked libraries will still increase in
+ size.
+
+ If you try to load a DUH file that uses a signal type you haven't
+ registered, the library will not crash. It will simply fail to load the
+ DUH file. In fact, an unrecognised or corrupt signal will prevent the
+ library from reading the rest of the file - but remember, DUH files are
+ always (intended to be) generated from other formats, so you will never
+ lose any data this way.
+
+ If you are unsure which signal types a DUH uses, get in contact with the
+ author of the DUH file, or register them all and remove them one by one to
+ find out which are required (as described in the steps for lazy people at
+ the start of this section).
+
+ The great advantage of DUMB over other music systems is that it enables
+ you to create your own signals. Filters, synthesisers and compression
+ algorithms can all be set up this way. Programming and audio experience
+ are useful, but the editor provides a way to create your own signals even
+ if you don't know C. More information is available in the on-line help in
+ the editor.
+
+ If you are interested in the gory details of how a signal works, or if you
+ wish to brave creating your own without the help of the editor, then see
+ the section entitled Signal Design.
+
+
+void dumb_register_sigtype_sample(void);
+
+ Registers the sample signal type ('SAMP'). This signal deals with samples,
+ or recordings, in the DUH file. All samples are monaural; a combining
+ signal can be used to combine two of these into a stereo sample. The DUMB
+ library will cater for more than two channels to a certain extent, but it
+ is currently limited by Allegro's audio streams.
+
+
+void dumb_register_sigtype_combining(void);
+
+ Registers the combining signal type ('COMB'). This signal takes a set of
+ monaural signals and combines them into one signal with multiple channels.
+ Stereo samples can be set up this way.
+
+
+void dumb_register_sigtype_stereopan(void);
+
+ Registers the stereo pan signal type ('SPAN'). This signal takes a
+ monaural sample and sources it at the stereo position you specify,
+ provided the DUH is played in stereo (or, more precisely, provided stereo
+ output is requested of the signal).
+
+
+void dumb_register_sigtype_sequence(void);
+
+ Registers the sequence signal type ('SEQU'). This signal sequences the
+ sound from other signals. It is what turns a random collection of
+ recordings of single notes into a complete piece of music.
+
+
+**********************
+*** DUH Management ***
+**********************
+
+
+DUH *load_duh(const char *filename);
+
+ Loads a .duh file. Before you call this, make sure you have registered all
+ the necessary signal types (see 'Signal Registration'). The DUH is
+ returned to you; you will need to pass it to various functions. When you
+ have finished with it, call unload_duh() to remove it from memory.
+
+ There is no special need to check that this function succeeds. All other
+ functions can safely be called with a null pointer, which means your music
+ will simply not play if it could not be loaded.
+
+
+DUH *read_duh(DUMBFILE *f);
+
+ Reads a DUH from an already open file, and leaves the file open so you can
+ read subsequent data from the file if you wish. Otherwise this function is
+ identical to load_duh().
+
+
+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
+ simply lifted from the DUH struct, so it may not truly correspond to the
+ time for which the DUH will generate sound. However, if the musician is
+ any good - or if the code that calculated this value is written properly -
+ you can assume it represents the point at which the DUH first loops, or
+ else it allows time for any final flourish to be appreciated. It is used
+ by the Winamp plug-in to decide when to stop.
+
+
+*******************************
+*** Impulse Tracker 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 128 (slightly
+ simplified), and cut samples will sound again as soon as the congestion
+ clears. Samples are prioritised by final volume, after all factors
+ affecting the volume of a sample have been considered.
+
+ If you play two or more IT files 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.
+
+
+DUH *dumb_load_it(const char *filename);
+
+ Loads the specified Impulse Tracker file, encapsulating it in a DUH
+ struct. No signal types need be registered for this to work. The length
+ will be set to the point at which the music first loops (see
+ duh_get_length()).
+
+ Once the file is loaded, it can be treated exactly the same as any other
+ DUH in memory.
+
+
+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().
+
+
+*******************************
+*** DUH Rendering Functions ***
+*******************************
+
+
+ Use these functions to generate samples from a DUH. First you call
+ duh_start_renderer() with the DUH, the number of channels you want and the
+ position at which you want to start. Then you use duh_render() to generate
+ the samples. You can call duh_render() as many times as you like, and it
+ will generate as many or as few samples as you require. When you have
+ finished, call duh_end_renderer().
+
+
+DUH_RENDERER *duh_start_renderer(DUH *duh, int n_channels, long pos);
+
+ Starts a DUH_RENDERER 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() for that. When you have finished with it, you must
+ pass it to duh_end_renderer(). You can use as many DUH_RENDERER structs as
+ you like at the same time.
+
+ 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 (provided any custom
+ signal types are properly designed). In other words, you can start a DUH
+ at a point halfway through a long note, and you will still hear the long
+ note.
+
+
+long duh_render(
+ DUH_RENDERER *dr,
+ int bits, int unsign,
+ float volume, float delta,
+ long size, void *sptr
+);
+
+ Generates some samples. Pass the DUH_RENDERER as returned by
+ duh_start_renderer(). 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 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 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 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_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.
+
+
+long duh_renderer_get_position(DUH_RENDERER *dr);
+
+ Tells you what position a DUH_RENDERER 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
+ DUH_RENDERER and then reinitiate it with the same position.
+
+
+void duh_end_renderer(DUH_RENDERER *dr);
+
+ Terminates a DUH_RENDERER. Be sure to call this when you've finished with
+ one.
+
+
+***********************************
+*** Signal Design Helper Values ***
+***********************************
+
+
+DUMB_SEMITONE_BASE
+
+ When a 'delta' value is required, you can use DUMB_SEMITONE_BASE to
+ control the pitch. Use pow(DUMB_SEMITONE_BASE, n) to transpose up by n
+ semitones. To transpose down, use negative n.
+
+
+DUMB_QUARTERTONE_BASE
+
+ When a 'delta' value is required, you can use DUMB_QUARTERTONE_BASE to
+ control the pitch. Use pow(DUMB_QUARTERTONE_BASE, n) to transpose up by n
+ quartertones. To transpose down, use negative n.
+
+
+DUMB_PITCH_BASE
+
+ When a 'delta' value is required, you can use DUMB_PITCH_BASE to control
+ the pitch accurately. Use pow(DUMB_PITCH_BASE, n) to transpose up by n
+ units, where 256 units represent one semitone. This scale is used for the
+ 'pitch' in a sequence (SEQU).
+
+
+************************************
+*** Signal Design Function Types ***
+************************************
+
+
+ In order to design your own signal type, you will have to write six
+ functions to be linked into your program. They are described below.
+
+ See fnptr.txt for help with function pointer types such as these.
+
+
+typedef void *(*DUH_LOAD_SIGNAL)(DUH *duh, DUMBFILE *file);
+
+ Write a function conforming to this type which loads or creates all the
+ data required by your signal. You may use this, for instance, to load a
+ sample. You will be reading directly from the DUH file, so make sure you
+ read exactly the quantity of data stored in the file for this signal. You
+ should allocate memory (multiple blocks if you like), and return a pointer
+ referencing all these data. Your data will be stored, and provided
+ whenever this signal is handled. Be careful about using global data, since
+ your load_signal function will be called more than once for different
+ signals of the same type (e.g. for signals which are both samples but are
+ different samples).
+
+ On failure you should return NULL. Please take the possibility of failed
+ memory allocation seriously, especially when loading large quantities of
+ data. You should make sure all allocated memory is freed before you return
+ with failure. See the standard signal types for examples of how to do this
+ elegantly using your unload_signal function. On failure, you need not
+ worry about reading the right quantity of data from the file.
+
+ Do not close the file under any circumstances.
+
+ Often you will write this function before you have written any code to
+ write a DUH file using this signal. If this is the case, don't worry; you
+ can write this function free-style, and then the function itself will
+ serve as a file format reference when you come to create the file.
+
+ Note that a null return value will always be interpreted as failure. If
+ this type of signal does not need to load any data, you should not provide
+ a load_signal function at all. The absence of this function will convey
+ the intended message to the library.
+
+
+typedef void *(*DUH_START_SAMPLES)(
+ DUH *duh,
+ void *signal,
+ int n_channels,
+ long pos
+);
+
+ Write a function conforming to this type. Every time your signal is
+ required to produce some output, this will be called first. The 'signal'
+ parameter is the same one you returned from your load_signal function. The
+ 'n_channels' parameter specifies how many channels you will need to render
+ sound in. In general you should support one or two, although sometimes
+ only one is necessary, depending on how your signal is used. If you can
+ write generic code to support more channels, then do so. Store the number
+ of channels for use later, unless you're only permitting one value.
+
+ If your signal does not support more than two channels, you are
+ responsible for making sure it is never invoked with more than two;
+ likewise if your signal only supports one, then make sure it is never
+ invoked with more than one. In general you only need to make sure the DUH
+ file is never rendered with too many channels (see duh_start_renderer()
+ and al_start_duh()). It may be prudent to use Allegro's ASSERT() macro to
+ catch bugs of this kind.
+
+ The 'pos' parameter specifies where to start, 65536 being one second into
+ the signal. Even if you do not intend to start in the middle of the signal
+ yourself, you should support this as it will be used when a DUH is not
+ played from the beginning.
+
+ This function should make the necessary preparations in order to render a
+ string of samples. All your preparations should be stored in allocated
+ memory, to which you return a pointer; this allows several instances of
+ the same signal to play at the same time. You will not be given the 'duh'
+ and 'signal' parameters again, so you must store them if you need them in
+ the render_samples or free_samples functions.
+
+ Once again, on the rare occasions on which you do not need such data (e.g.
+ a white noise signal), you should not provide a start_samples function. A
+ null return code is interpreted as failure, and your music will be lacking
+ notes (which is better than crashing on undetected memory allocation
+ failure).
+
+
+typedef void (*DUH_SET_PARAMETER)(
+ void *sampinfo,
+ unsigned char id, long value
+);
+
+ Some signals can take parameters. For example, a stereo pan signal allows
+ you to specify the position at which to source the sample, and you might
+ want to choose the cut-off frequency for a low-pass filter. Such
+ parameters are set on an instance-by-instance basis, not globally.
+ Therefore, if you have any parameters, you should place default values for
+ them in the data you initialise in start_samples. Then you should write a
+ function conforming to this type, and have it change the parameters when
+ the correct IDs are passed (see below).
+
+ Each of your parameters should be identified by a single byte, which will
+ be passed in the 'id' parameter. When one of your parameters is correctly
+ identified by id, you should change the parameter's value in the data you
+ returned from your start_samples function. These data are available via
+ the 'sampinfo' parameter. If you do not recognise the ID passed, you may
+ wish to log the situation using Allegro's TRACE() macro (which will
+ compile away to nothing unless you define DEBUGMODE), since it signifies a
+ fault in the DUH file.
+
+ Take some care in deciding what should be a parameter and what should be
+ arranged through the use of separate signals (of the same type). For
+ example, if you are doing a low-pass filter, you will need a source signal
+ on which to apply the filter; this signal's index should be loaded by the
+ load_signal function, so you will create separate filter signals for the
+ separate source signals. However, the cut-off frequency for the filter is
+ more suitably stored in a parameter. Here are some guidelines:
+
+ Continuous values, or discrete quantities, can be stored in parameters.
+ Examples: filter cut-off, echo time, stereo pan position.
+
+ Discrete indices or references should be loaded by load_signal.
+ Examples: references to other signals.
+
+ Another way of looking at it is as follows:
+
+ If a value could be changed halfway through playing the signal, then
+ consider storing it in a parameter.
+
+ If a value needs to be known by the start_samples function and cannot
+ change later, then consider loading it in the load_signal function.
+
+ If your signal has no parameters, do not provide a set_parameters
+ function. Attempts to change parameters for this signal will not cause a
+ crash, but will cause the operation to be logged using Allegro's TRACE()
+ macro if the debugging library is used to play the DUH.
+
+
+typedef long (*DUH_RENDER_SAMPLES)(
+ void *sampinfo,
+ float volume, float delta,
+ long size, sample_t **samples
+);
+
+ Write a function conforming to this type. It should render a series of
+ samples into the array of buffers provided (see below). You are passed
+ 'sampinfo' as returned by start_samples. The 'samples' parameter points to
+ an array of buffers, one for each channel. You can get a pointer to the
+ buffer for channel #n with:
+
+ sample_t *buffer = samples[n];
+
+ As you can see, samples are of type sample_t, which is typedeffed as a
+ signed int (32 bits). Your waveform should be centred on 0 and should peak
+ at no more than +/- 32768, or +/- 0x8000, given a volume of 1.0f. If your
+ waveform goes higher, it will be clipped later on (you do not need to test
+ for this yourself). Please do not read any special meaning into the volume
+ parameter; it is simply a factor to be multiplied into each sample. If you
+ wish to adjust the tone, use a parameter (see DUH_SET_PARAMETER).
+
+ In this function, you should render 'size' samples into the buffer for
+ each channel. Return the number of samples actually rendered, which will
+ be fewer than 'size' if you run out of samples (e.g. if you reach the end
+ of a non-looping sample). Never stop short unless the signal ends, and do
+ not overrun under any circumstances. Update the 'sampinfo' data so that
+ the samples generated by the next call to render_samples will continue
+ seamlessly from the samples generated this time.
+
+ The delta parameter governs the speed at which your signal will play back.
+ Higher values of delta should cause the speed and pitch to increase, as
+ with duh_render(). In general, if delta is 1.0f then your waveform should
+ be suitable for playback at a sampling rate of 65536 Hz. If you wish to
+ bend this rule, then be careful - usually it is wiser to adjust your
+ thinking in other parts.
+
+ For example, consider a sample recorded at 44100 Hz, stored in a sample
+ signal. If a delta of 1.0f is passed to this signal's render function, the
+ output will be exactly the same as the original sample - a signal at
+ 44100 Hz. So it breaks the rule.
+
+ Ah, but it doesn't. Instead, it is wiser to consider the sample stored in
+ the DUH file as being sampled at 65536 Hz. Once we consider this, it is
+ clear that the output is suitable for playback at 65536 Hz, so we do not
+ break the rule. Now it is a simple matter of adjusting all the pitches in
+ the sequence to compensate for this. See duhtech.txt for details on how to
+ do this when you are writing the DUH file.
+
+ Note that the position that was passed to the start_samples function is
+ 65536 for one second into the signal as played with delta 1.0f. So, with a
+ sample signal, this position is interpreted as the index of the sample on
+ which to start. If delta is 2.0, only half a second of the resultant
+ output will have been skipped.
+
+ Once again, please do not read any special meaning into the delta
+ parameter. The effect of doubling delta should be virtually the same as
+ the effect of not doubling delta but resampling the output. If you wish to
+ adjust the tone for different pitches, use a parameter.
+
+ This function is compulsory. Silent signals only increase processor and
+ memory usage, and we are not Microsoft-Intel evangelists.
+
+
+typedef void (*DUH_END_SAMPLES)(void *sampinfo);
+
+ Write a function conforming to this type. It will be called when a signal
+ stops playing. The parameter points to the data you returned from the
+ start_samples function, and here you should simply free the memory up.
+
+ This function should be provided, always if, and only if, start_samples is
+ present. The debugging library will check you get this right.
+
+
+typedef void (*DUH_UNLOAD_SIGNAL)(void *signal);
+
+ Write a function conforming to this type. It will be called when the DUH
+ is removed from memory. It should free all memory allocated by your
+ load_signal function. The parameter is the same pointer that you returned
+ from load_signal.
+
+ If you only ever use this signal type with make_duh(), and not with
+ dumb_register_sigtype(), then the following does not apply.
+
+ If load_signal is present, unload_signal should also be present; if you
+ did not provide a load_signal function, then you should not provide an
+ unload_signal function either. The debugging library will check you get
+ this right.
+
+
+**********************************
+*** Signal Design Registration ***
+**********************************
+
+
+void dumb_register_sigtype(DUH_SIGTYPE_DESC *desc);
+
+ When you have written all the functions that represent your signal, you
+ will need to register them with the library before it will be able to load
+ your DUH file. Use this function. The DUH_SIGTYPE_DESC struct contains the
+ following fields:
+
+ long type;
+ DUH_LOAD_SIGNAL load_signal;
+ DUH_START_SAMPLES start_samples;
+ DUH_SET_PARAMETER set_parameter;
+ DUH_RENDER_SAMPLES render_samples;
+ DUH_END_SAMPLES end_samples;
+ DUH_UNLOAD_SIGNAL unload_signal;
+
+ You need to create a DUH_SIGTYPE_DESC struct and pass its pointer to
+ dumb_register_sigtype(). The struct must be in permanent memory. In other
+ words, it must be either global or static, and you should not modify it
+ later. DUMB will not make its own copy.
+
+ 'type' should be a four-character string encoded with DUMB_ID(), for
+ example DUMB_ID('M','E','O','W'). By convention it should be upper case,
+ and padded with spaces if you do not use all four characters. However, you
+ do not have to stick to this.
+
+ If you are not providing a function, specify NULL for the corresponding
+ function pointer.
+
+
+**********************************
+*** Signal Rendering Functions ***
+**********************************
+
+
+ When you are designing your own signals, you will often want to retrieve
+ samples from another signal in the DUH. This signal's index ('sig') should
+ be loaded by your load_samples function. You can use the following
+ functions to obtain the samples.
+
+
+DUH_SIGNAL_SAMPINFO *duh_signal_start_samples(
+ DUH *duh, int sig, int n_channels, long pos
+);
+
+ Specifies where you want to start rendering. This function returns a
+ DUH_SIGNAL_SAMPINFO struct, which you need to pass to the other functions.
+ You can use as many DUH_SIGNAL_SAMPINFOs at once as you like.
+
+ Pass the DUH and the index of the signal whose samples you want to obtain
+ ('sig'). Specify how many channels you want, and where you want to start
+ rendering (65536 represents one second).
+
+ There is no special need to check that this function succeeds. The other
+ functions are safe to call with null pointers. However, checking the
+ return value can make your code more efficient.
+
+ Be sure to call duh_signal_end_samples() when you've finished.
+
+
+void duh_signal_set_parameter(
+ DUH_SIGNAL_SAMPINFO *signal_sampinfo,
+ unsigned char id, long value
+);
+
+ Sets a parameter for the signal whose samples are being rendered by
+ signal_sampinfo.
+
+
+
+
+ Calls the set_parameter function for the instance started by
+ duh_signal_start_samples of the signal whose details you passed to that
+ function. Exactly what this does depends on the signal in question.
+
+
+
+
+
+ THIS IS NOT VERY HELPFUL. REFER TO A FILE?
+
+
+
+
+
+
+
+
+long duh_signal_render_samples(
+ DUH_SIGNAL_SAMPINFO *signal_sampinfo,
+ float volume, float delta,
+ long size, sample_t **samples
+);
+
+ Renders 'size' samples of the signal for which signal_sampinfo was set up.
+ See duh_render() and DUH_RENDER_SAMPLES for details on the 'volume' and
+ 'delta' parameters. This function will return the number of samples
+ generated, which will be fewer than 'size' if the signal ends.
+
+ Sometimes you can pass the array of sample buffers which was passed to
+ your function, and process the data in place. Other times you will have to
+ set up the array of sample buffer pointers yourself, making sure each
+ buffer can hold 'size' samples. Below is some code to do that. Note that
+ we prefix some variable names with sub-, so they don't clash with the
+ parameters to the function that would typically contain this code.
+
+ sample_t **subsamples;
+ int n;
+ long subsize;
+
+ subsamples = malloc(n_channels * sizeof(*subsamples));
+
+ if (!subsamples)
+ return 0;
+
+ subsamples[0] = malloc(size * n_channels * sizeof(*subsamples[0]));
+
+ if (!subsamples[0]) {
+ free(subsamples);
+ return 0;
+ }
+
+ for (n = 1; n < n_channels; n++)
+ subsamples[n] = subsamples[n-1] + size;
+
+ subsize = signal_render_samples(
+ subsampinfo,
+ volume, delta,
+ size, subsamples
+ );
+
+ /* Process the samples here. */
+
+ free(subsamples[0]);
+ free(subsamples);
+
+ return subsize;
+
+
+void duh_signal_end_samples(DUH_SIGNAL_SAMPINFO *signal_sampinfo);
+
+ Call this when you have finished with a DUH_SIGNAL_SAMPINFO struct. It
+ will free all memory used by the struct.
+
+
+**************************
+*** Resampling Helpers ***
+**************************
+
+
+ The DUH player library provides a versatile resampling system. The sample
+ signal type uses it, and it is available for use in any of your signals.
+
+ Be warned that the resampler may overrun the memory you specify by up to
+ DUMB_EXTRA_SAMPLES samples, so you must allocate these samples and set
+ them to appropriate values when you load your signal. Generally, if you
+ are going to loop, set them to reflect the samples at the loop start
+ point; if you are not going to loop, set them to zero.
+
+ DUMB_EXTRA_SAMPLES is defined as follows:
+
+ #define DUMB_EXTRA_SAMPLES 3
+
+
+extern int resampling_quality;
+
+ Allows you to control the quality of all resampling that takes place. This
+ may be set to any value from 0 to 4. Higher values will sound better, but
+ lower values will use up less processor time.
+
+ | --___
+ 0 - Aliasing |__--- __
+ | ___--
+
+ | __
+ 1 - Linear resampling | / \ /\
+ |/ \/ \__
+
+ 2 - Linear resampling / linear average
+
+ 3 - Quadratic / linear average
+
+ 4 - Cubic / linear average
+
+ Level 0 has very noticeable unwanted overtones. It will occasionally
+ produce satisfactory results for noisy signals, but usually you will
+ want to pay for the extra processor time (which isn't much) and go
+ for Level 1.
+
+ Levels 1 and 2 are already pretty good. When resampling down a few
+ octaves, however, you will begin to notice unwanted high frequencies.
+ These can be eliminated by switching to Levels 3 or 4.
+
+ When Level 2 or higher are selected, a linear average function is used for
+ speeding the wave up. Instead of skipping samples, all samples in the
+ interval will be averaged. The interval is also smoothed at the edges.
+ This will be especially beneficial when increasing the pitch by several
+ octaves.
+
+ Levels 3 and 4 are both smooth curves to the eye. They both give
+ extremely good performance, but you may sometimes notice the difference
+ when reducing the pitch of a sample by several octaves, where Level 3
+ may exhibit unwanted high frequencies.
+
+
+
+
+
+
+
+
+ NOTE: WHAT IS THE DEFAULT? (2 at the moment, but might change. Config?)
+
+
+
+
+
+
+
+
+
+
+
+
+
+long dumb_resample(
+ sample_t *src, long *_src_pos, int *_src_subpos,
+ long src_start, long src_end,
+ sample_t *dst, long dst_size,
+ float delta, int *_dir,
+ DUMB_RESAMPLE_PICKUP pickup, void *pickup_data
+);
+
+ This is the resampling function. It takes an array of source samples and
+ fills as much of the destination array as it can for you. Its operation is
+ quite complicated, so pay attention.
+
+ All the parameters prefixed with an underline (_) are pointers to the
+ fields they describe. This means the function can modify the variables you
+ pass, but you have to prefix the variable with an ampersand (&) when
+ passing it. If you get warnings about ints being converted to pointers
+ without casts, you have most likely forgotten an ampersand somewhere.
+
+ 'src' points to the source sample data. It should point to the beginning
+ of the sample, even if you are starting your resampling from somewhere in
+ the middle. Do not break this rule unless you know what you're doing.
+
+ '_src_pos' and '_src_subpos' together represent the current position in
+ the sample. When you first call the function, they represent the starting
+ position. Once the function has done its work, they will represent the
+ point at which the resampling stopped. '_src_pos' is measured in samples.
+ '_src_subpos' represents how far between samples we are, and ranges from
+ 0 to 65535 - so if _src_subpos is 65535, we are very nearly on to the next
+ sample.
+
+ Once _src_pos and _src_subpos have been modified by this function, you can
+ pass them to the function again and the resampling will continue
+ seamlessly. This is important, as you will hardly ever get to render all
+ your samples in one go. Typically you will make one call to this function
+ from within your render_samples function.
+
+ This function does not need to know the size of the source buffer as such.
+ Instead, it knows src_start and src_end, which are start and end points.
+ The end point actually points to the first sample not to use, following
+ the usual start-inclusive end-exclusive convention. In general, the
+ resampling will stop when src_pos tries to pass one of these.
+
+ WARNING: dumb_resample() may read up to DUMB_EXTRA_SAMPLES samples beyond
+ src_end. Make sure the memory belongs to you.
+
+ The _dir parameter should be either 1 or -1, and tells this function
+ whether to go forwards or backwards respectively through the source sample
+ buffer. If _dir is 0, nothing will happen - resampling has stopped
+ permanently. Any other values of _dir will have unpredictable results, and
+ the debugging library will abort if you try to use them.
+
+ If _dir is 1, then _src_pos will only be tested against src_end.
+ If _dir is -1, then _src_pos will only be tested against src_start.
+
+ That means you can set src_start and src_end to your loop points, and
+ start playing from the beginning of the sample. The resampling will
+ proceed unimpeded while _src_pos is outside the loop section, provided it
+ is advancing towards the loop section. The sample signal makes extensive
+ use of this capability.
+
+ The output waveform will be rendered into the buffer pointed to by dst.
+ Up to dst_size samples will be generated. Fewer will be generated if the
+ resampling stops permanently. The number generated will be returned. You
+ can find out if resampling ended by testing the value of your direction
+ variable (pointed to by _dir); if it is 0, then resampling stopped
+ permanently.
+
+ If you pass NULL for dst, no samples will be generated. However, _src_pos,
+ _src_subpos and _dir will be updated as normal. The pick-up function will
+ be called as necessary; you should pass the usual 'src' parameter so it
+ can be passed to your pick-up function. (See below for information on
+ pick-up functions.) The function returns the number of samples that would
+ have been generated, had dst pointed somewhere. The operation of
+ do_resample() when dst is NULL is exactly the same as that when dst points
+ somewhere. However, do_resample() is much faster in this case; you should
+ use it when volume is 0, or when you are culling quieter samples to gain
+ execution speed.
+
+ If delta is 1.0f, the destination is the same speed as the source. Greater
+ values cause the sample to speed up, and lesser values cause the sample to
+ slow down. delta should always be positive.
+
+ We have covered most of the parameters. There are only two left - pickup
+ and pickup_data. The simplest usage is to set these both to NULL. Then, if
+ resampling stops, it stops permanently. Use this if your sample will not
+ loop.
+
+ Alternatively, you may wish to write a pick-up function. Your pick-up
+ function will take control whenever _src_pos tries to pass the start or
+ end points. You can use it for looping, or for a multitude of other tasks.
+ The typedef is as follows:
+
+ typedef int (*DUMB_RESAMPLE_PICKUP)(
+ sample_t *src, long *_src_pos, int *_src_subpos,
+ long *_src_start, long *_src_end,
+ int dir,
+ void *data
+ );
+
+ You are passed the source buffer, in case you want to fill it with new
+ samples. You have the _src_pos, _src_subpos, _src_start and _src_end
+ pointers, should you need to change the values to which they point. You
+ also have dir, but note that it is not a pointer. Instead, you should
+ return the new direction, or 0 to stop the resampling permanently.
+
+ The data parameter is a copy of pickup_data as passed to dumb_resample().
+ This is typically used to give you access to your 'sampinfo' parameter,
+ passed to your render_samples function.
+
+ BEWARE: you must refer to and change src_pos, src_subpos, src_start and
+ src_end through the parameters passed. Do not mistakenly use their
+ equivalents in your struct instead. The equivalents in your struct
+ will eventually be updated, but they will not be accurate during
+ the pick-up function.
+
+ On entry to this function, src_pos and src_subpos will have overrun by a
+ small amount. When changing them, you should preserve this overrun. The
+ following examples will do this for you:
+
+ If you are executing a simple loop, subtract (or add) the difference
+ between the loop points from (or to) src_pos. Do not simply set src_pos
+ to the other loop point.
+
+ *_src_pos -= *_src_end - *_src_start;
+
+ If you are executing a ping-pong loop, you need to reflect the pointer
+ off the loop boundary. To reflect off the loop end point:
+
+ *_src_pos = (*_src_end << 1) - 1 - *_src_pos;
+ *_src_subpos ^= 65535;
+ dir = -1;
+
+ To reflect off the loop start point:
+
+ *_src_pos = (*_src_start << 1) - 1 - *_src_pos;
+ *_src_subpos ^= 65535;
+ dir = 1;
+
+ In each case, don't forget to return the new direction correctly!
+
+ An ideal example of a pick-up function can be found in src/sample.c.
+
+ Of course there is more that can be done with a pick-up function. Enjoy
+ experimenting!
+
+
+************************
+*** DUH Construction ***
+************************
+
+
+DUH *make_duh(
+ long length,
+ int n_signals,
+ DUH_SIGTYPE_DESC *desc[],
+ void *signal[]
+);
+
+ Constructs a DUH from its component parts. Use this function if you are
+ writing a function to load a music file format other than .duh. Indeed,
+ this function is used internally to load IT files.
+
+ Before you call this function, you must do some preparation. Your DUH will
+ contain a fixed number of signals; pass this number as n_signals. Each
+ signal will have a DUH_SIGTYPE_DESC struct, and you pass an array of
+ pointers to these. The DUH_SIGTYPE_DESC struct contains the following
+ fields:
+
+ long type;
+ DUH_LOAD_SIGNAL load_signal;
+ DUH_START_SAMPLES start_samples;
+ DUH_SET_PARAMETER set_parameter;
+ DUH_RENDER_SAMPLES render_samples;
+ DUH_END_SAMPLES end_samples;
+ DUH_UNLOAD_SIGNAL unload_signal;
+
+ The structs must be in permanent memory, i.e. either global or static, and
+ not modified later; however, the array of pointers can be destroyed as
+ soon as the function returns.
+
+ The values of 'type' and 'load_signal' are irrelevant; set them to 0 and
+ NULL respectively, unless you are using the DUH_SIGTYPE_DESC struct
+ elsewhere.
+
+ Because 'load_signal' is never used, you must provide an array of pointers
+ to the data that 'load_signal' would otherwise have returned. This will
+ be passed to the other functions as 'signal'. Once again, the array of
+ pointers can be destroyed as soon as the function returns. As for the
+ pointers themselves:
+
+ If an 'unload_signal' function is provided, then that will be used to
+ deallocate the pointer when you finally destroy the DUH. If the function
+ is not provided, then you are responsible for deallocating any memory
+ referenced by that pointer yourself. This applies individually to each
+ signal.
+
+ If this function fails and returns NULL, then any 'unload_signal'
+ functions you provided will have been called. You do not have any extra
+ work to do if it fails.
+
+
+********************************
+*** 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()).
+
+
+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.
+
+
+***********************************************
+*** Allegro Datafile Registration Functions ***
+***********************************************
+
+
+void register_dat_duh(void);
+
+ If you wish to put a DUH file in an Allegro datafile, you must use "DUH "
+ for the type. The grabber will have a box for the type when you insert a
+ new object. The grabber will treat the DUH file as binary data, which
+ means the datafile will contain an exact copy of the DUH file on disk.
+
+
+
+
+
+ TODO: make it possible to choose the type...
+
+
+
+
+
+ You must then call register_dat_duh() 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 need to check the type of the object for whatever reason, you can
+ use DAT_DUH, defined as follows:
+
+ #define DAT_DUH DAT_ID('D','U','H',' ')
+
+ The following example iterates through all the DUHs in disan.dat:
+
+ DATAFILE *dat;
+ int n;
+
+ register_dat_duh();
+ dat = load_datafile("disan.dat");
+
+ for (n = 0; dat[n].type != DAT_END; n++) {
+ if (dat[n].type == DAT_DUH) {
+ DUH *duh = dat[n].dat;
+ /* Insert code here to play 'duh' or whatever you want to do. */
+ }
+ }
+
+ unload_datafile(dat);
+
+
+void register_dat_it(void);
+
+ Inserting an IT file in an Allegro datafile is the same as inserting a DUH
+ file, except the type has to be "IT ", the registration function is
+ register_dat_it(), and the following definition is available for use
+ instead of DAT_DUH:
+
+ #define DAT_IT DAT_ID('I','T',' ',' ')
+
+ Once the datafile is loaded, the 'dat' field indeed points to a DUH
+ struct. There are no differences other than those listed above.
+
+
+*************************************
+*** 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.
+
+ 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, provided
+ any custom signal types are designed properly. 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.
+
+
+
+
+
+ NOTE: IS THAT TRUE ON ALL SYSTEMS?
+
+
+
+
+
+ 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.
+
+
+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_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_RENDERER 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. In other words, your music will not
+ be cut off prematurely.
+
+ In case you were wondering, it is definitely not safe to call
+ al_poll_duh() from an interrupt context. Not only is no part of DUMB
+ locked in memory, but many signals and many core library functions
+ 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 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. However, even if you do not think
+ you are targeting DOS, it is worth considering that calling this function
+ regularly from your main loop is likely to be much more efficient than
+ relying on task switching to do it for you.)
+
+
+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.
+
+
+
+
+
+
+
+
+ NOTE: HOW TO GET AROUND THIS?
+
+
+
+
+
+
+
+
+DUH_RENDERER *al_duh_get_renderer(AL_DUH_PLAYER *dp);
+
+ This function returns to you the DUH_RENDERER underlying the specified
+ AL_DUH_PLAYER. It has no practical use in this release, but it
+ TODO: Write this. Also see if it should go in howto.txt.
+
+
+******************
+*** Conclusion ***
+******************
+
+
+I conclude that... DUMB is the bestest music player in the world because...
+Complete this sentence in fifteen words or fewer... D'OH!
+
+
+Ben Davis
+entheh@users.sf.net
+IRC EFnet #dumb
+See readme.txt for details on using IRC.