diff options
| author | Michiel Van Der Kolk <not.valid@email.address> | 2005-03-17 20:50:03 +0000 |
|---|---|---|
| committer | Michiel Van Der Kolk <not.valid@email.address> | 2005-03-17 20:50:03 +0000 |
| commit | 27be5bc72855a0fbbdae230bc144624c9eb85f5e (patch) | |
| tree | b553f1321df924c4b744ffcab48dce5f4f081f7d /apps/codecs/dumb/docs | |
| parent | 7e7662bb716917ca431204f0113d400c1014f2e8 (diff) | |
| download | rockbox-27be5bc72855a0fbbdae230bc144624c9eb85f5e.zip rockbox-27be5bc72855a0fbbdae230bc144624c9eb85f5e.tar.gz rockbox-27be5bc72855a0fbbdae230bc144624c9eb85f5e.tar.bz2 rockbox-27be5bc72855a0fbbdae230bc144624c9eb85f5e.tar.xz | |
Initial check in dumb 0.9.2 - has a few usages of floating point that should
be rewritten to fixed point. seems to compile cleanly for iriver.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@6197 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/codecs/dumb/docs')
| -rw-r--r-- | apps/codecs/dumb/docs/deprec.txt | 281 | ||||
| -rw-r--r-- | apps/codecs/dumb/docs/dumb.txt | 1699 | ||||
| -rw-r--r-- | apps/codecs/dumb/docs/faq.txt | 263 | ||||
| -rw-r--r-- | apps/codecs/dumb/docs/fnptr.txt | 113 | ||||
| -rw-r--r-- | apps/codecs/dumb/docs/howto.txt | 845 | ||||
| -rw-r--r-- | apps/codecs/dumb/docs/modplug.txt | 137 | ||||
| -rw-r--r-- | apps/codecs/dumb/docs/ptr.txt | 129 |
7 files changed, 3467 insertions, 0 deletions
diff --git a/apps/codecs/dumb/docs/deprec.txt b/apps/codecs/dumb/docs/deprec.txt new file mode 100644 index 0000000..88ca2e9 --- /dev/null +++ b/apps/codecs/dumb/docs/deprec.txt @@ -0,0 +1,281 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * deprec.txt - Deprecated functions, why they / / \ \ + * were deprecated, and what to do | < / \_ + * instead. | \/ /\ / + * \_ / > / + * | \ / / + * | ' / + * \__/ + */ + + +********************************************** +*** How the functions have been deprecated *** +********************************************** + + + GCC 3.1 and later provide a very useful attribute. The following: + + __attribute__((__deprecated__)) + + when written alongside a function prototype, variable declaration or type + definition, will result in a warning from GCC if any such part of the API + is used. The warning will even tell you where the declaration is, and I + have inserted comments by all the deprecated declarations, telling you + what to do. + + Unfortunately, GCC 2.x and 3.0.x and MSVC do not have any means to + deprecate things. The approach I have taken with these compilers is to + avoid prototyping the declared functions. This means you will get + warnings and errors, and they won't be very helpful. If your program + compiles, you may get strange crashes when you run it, since the compiler + needs the declarations in order to make sure function calls are carried + out correctly. + + If you would like the deprecated parts of the API to be declared, you can + compile with the -DDUMB_DECLARE_DEPRECATED switch for GCC, or the + -D"DUMB_DECLARE_DEPRECATED" switch for MSVC. This will be accepted by + GCC 3.x but is unnecessary. Use this switch with other people's projects + if necessary, but please make the effort to update your own projects to + use the new API, as the deprecated parts may be removed in the future. + + The rest of this file explains why some parts of the API were deprecated, + and how to adapt your code. + + +************************************** +*** What happened to DUH_RENDERER? *** +************************************** + + + The DUH_RENDERER struct was designed for rendering audio to an end-user + format - 8-bit or 16-bit, signed or unsigned, with stereo samples + interleaved. In order for it to do this, it was built on top of the + hitherto undocumented DUH_SIGRENDERER struct, which rendered audio in + DUMB's internal 32-bit signed format with channels (left/right) stored + separately. The DUH_RENDERER struct contained a pointer to a + DUH_SIGRENDERER struct, along with some other data like the position and + number of channels. + + There were then some developments in the API. The DUH_SIGRENDERER struct + also stored the position and the number of channels, so I decided to write + functions for returning these. Suddenly there was no need to store them in + the DUH_RENDERER struct. Before long, the DUH_RENDERER struct contained + nothing but a pointer to a DUH_SIGRENDERER. + + I decided it would be a good idea to unify the structs. After all, there + really is no difference between the data stored in each, and it would be + easy to make duh_render(DUH_RENDERER *dr, ...) and + duh_render_signal(DUH_SIGRENDERER *sr, ...) work on the same type of + struct. (Note that duh_render_signal() is now deprecated too; see the next + section.) It took some deliberation, but I decided I didn't want functions + to be #defined (it prevents you from using these names for member + functions in C++ classes), and that meant they had to be defined + somewhere. Defining redundant functions is a source of bloat, inefficiency + and general inelegance. After weighing things up, I decided it was better + to deprecate the redundant functions and have people begin to use the more + efficient versions, and eventually the redundant functions will be able to + be removed. + + So why did I choose to keep the more complicated name, DUH_SIGRENDERER? + The reason has to do with what DUMB will become in the future. Signals are + an inherent part of the DUH struct and how .duh files will be constructed. + It will be possible to have multiple signals in a single DUH struct, and + you will be able to choose which one you want to play (this is the 'sig' + parameter passed to duh_start_sigrenderer()). But don't hold your breath; + we still have a long way to go before .duh files will start to appear... + + +typedef DUH_SIGRENDERER DUH_RENDERER; + + Wherever you are using DUH_RENDERER in your program, simply replace it + with DUH_SIGRENDERER. An automated (case-sensitive!) search and replace + operation should get this done. + + +DUH_RENDERER *duh_start_renderer(DUH *duh, int n_channels, long pos); + + Use duh_start_sigrenderer() instead. It takes an extra parameter, 'sig', + which comes after 'duh' and before 'n_channels'; pass 0 for this. So an + example would be, replace: + + sr = duh_start_renderer(duh, 2, 0); + + with: + + sr = duh_start_sigrenderer(duh, 0, 2, 0); + + +int duh_renderer_get_n_channels(DUH_RENDERER *dr); +long duh_renderer_get_position(DUH_RENDERER *dr); +void duh_end_renderer(DUH_RENDERER *dr); + + These are easy enough to fix; all you have to do is replace 'renderer' + with 'sigrenderer'. So the new functions are: + + int duh_sigrenderer_get_n_channels(DUH_SIGRENDERER *sigrenderer); + long duh_sigrenderer_get_position(DUH_SIGRENDERER *sigrenderer); + void duh_end_sigrenderer(DUH_SIGRENDERER *sigrenderer); + + +Note that duh_render() has NOT been deprecated. It now uses DUH_SIGRENDERER +instead of DUH_RENDERER, but its functionality is unchanged. You do not have +to change calls to this function in any way. + + +DUH_RENDERER *duh_renderer_encapsulate_sigrenderer(DUH_SIGRENDERER *sr); +DUH_SIGRENDERER *duh_renderer_get_sigrenderer(DUH_RENDERER *dr); +DUH_SIGRENDERER *duh_renderer_decompose_to_sigrenderer(DUH_RENDERER *dr); + + These functions did not exist in the last release of DUMB, so you are + probably not using them, but they are included here for completeness. All + you have to do here is unwrap the function, since the structs have been + unified. So, for instance, replace: + + duh_renderer_encapsulate_sigrenderer(my_sigrenderer) + + with: + + my_sigrenderer + + Simple! + + +AL_DUH_PLAYER *al_duh_encapsulate_renderer(DUH_RENDERER *dr, + float volume, long bufsize, int freq); +DUH_RENDERER *al_duh_get_renderer(AL_DUH_PLAYER *dp); +DUH_RENDERER *al_duh_decompose_to_renderer(AL_DUH_PLAYER *dp); + + Again, these functions were not in the last release, so you probably + aren't using them. Nevertheless, the fix is simple as always: simply + replace 'renderer' with 'sigrenderer'. So the new functions are: + + AL_DUH_PLAYER *al_duh_encapsulate_sigrenderer(DUH_SIGRENDERER *sr, + float volume, long bufsize, int freq); + DUH_SIGRENDERER *al_duh_get_sigrenderer(AL_DUH_PLAYER *dp); + DUH_SIGRENDERER *al_duh_decompose_to_sigrenderer(AL_DUH_PLAYER *dp); + + +********************* +*** Miscellaneous *** +********************* + + +long duh_render_signal(DUH_SIGRENDERER *sigrenderer, + float volume, float delta, + long size, sample_t **samples); + + This function used to return samples in DUMB's internal format. This + format consisted of 32-bit integers whose 'normal range' was -0x8000 to + 0x7FFF (any samples outside this range would have to be clipped when sent + to the sound card). + + DUMB's internal format has changed. DUMB still uses 32-bit integers, but + now the normal range is -0x800000 to 0x7FFFFF. The lowest eight bits are + discarded at the final stage by duh_render() when you ask for 16-bit + output. A new function, duh_sigrenderer_get_samples(), will return samples + in DUMB's new internal format. It takes exactly the same parameters, so + all you have to do to the call itself is change the name; however, you + will most likely have to change your code to account for the new + normalised range. + + duh_render_signal() will still be able to give you the samples in DUMB's + old internal format, but it is inefficient. You should change your code as + soon as possible. + + +typedef void (*DUH_SIGRENDERER_CALLBACK)(void *data, sample_t **samples, + int n_channels, long length); + +void duh_sigrenderer_set_callback(DUH_SIGRENDERER *sigrenderer, + DUH_SIGRENDERER_CALLBACK callback, void *data); + + This callback was intended to allow you to analyse the output. It was by + no means intended to let you modify the output. For this reason, the names + have been changed to DUH_SIGRENDERER_ANALYSER_CALLBACK and + duh_sigrenderer_set_analyser_callback, and the 'samples' parameter to your + callback should now be specified as follows: + + const sample_t *const *samples + + The first 'const' indicates that you must not modify the samples. The + second indicates that you must not modify the pointers to each channel. + + There is a second reason why this change was necessary, and it is the one + described further up for duh_render_signal()'s entry: the format in which + the samples themselves are stored has changed. They are 256 times as + large, with a normal range from -0x800000 to 0x7FFFFF. You will most + likely need to change your code to account for this. + + If you try to call the old function, it will print a message to stderr + directing you to this file, and it will not install the callback. You + shouldn't be able to get this far without a compiler warning (or, if you + don't have GCC 3.1 or later, some compiler errors). + + If you wanted to use this callback to apply a DSP effect, don't worry; + there is a better way of doing this. It is undocumented, so contact me + and I shall try to help. Contact details are at the bottom of this file. + + For reference, here are the new definitions: + + typedef void (*DUH_SIGRENDERER_ANALYSER_CALLBACK)(void *data, + const sample_t *const *samples, int n_channels, long length); + + void duh_sigrenderer_set_analyser_callback(DUH_SIGRENDERER *sigrenderer, + DUH_SIGRENDERER_ANALYSER_CALLBACK callback, void *data); + + +int dumb_resampling_quality; + + This variable has changed meaning. It used to hold a value from 0 to 4, + whose meaning was as follows: + + 0 - aliasing + 1,2 - linear interpolation + 3 - quadratic interpolation + 4 - cubic interpolation + + 0,1 - always use a straightforward interpolation algorithm + 2,3,4 - when decimating (increasing the pitch), use a linear average + algorithm designed to reduce frequencies that would otherwise + reflect off the Nyquist + + Now the variable only holds values from 0 to 2, and these values have + preprocessor constants associated with them. The somewhat inappropriate + quadratic interpolation has been removed. The linear average algorithm has + also been removed, and may or may not come back; there are probably more + efficient ways of achieving the same effect, which I shall be + investigating in the future. + + This change will have hardly any noticeable effect on existing programs. + Levels 2, 3 and 4 used considerably more processor time because of the + linear average algorithm. Likewise, Level 2 in the new scheme (cubic) uses + considerably more processor time than Levels 1 and 0, and Levels 3 and 4 + will behave identically to Level 2. + + +****************** +*** Conclusion *** +****************** + + +"I conclude that... DUMB is the bestest music player in the world because... +Complete this sentence in fifteen words or fewer... D'OH!" + +The preceding conclusion formerly appeared in dumb.txt, and is deprecated +because it's lame. + + +Ben Davis +entheh@users.sf.net +IRC EFnet #dumb +See readme.txt for details on using IRC. diff --git a/apps/codecs/dumb/docs/dumb.txt b/apps/codecs/dumb/docs/dumb.txt new file mode 100644 index 0000000..86b2cc3 --- /dev/null +++ b/apps/codecs/dumb/docs/dumb.txt @@ -0,0 +1,1699 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/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. diff --git a/apps/codecs/dumb/docs/faq.txt b/apps/codecs/dumb/docs/faq.txt new file mode 100644 index 0000000..48b5ef3 --- /dev/null +++ b/apps/codecs/dumb/docs/faq.txt @@ -0,0 +1,263 @@ +TO DO: add question regarding set_close_button_callback vs set_window_close_hook + +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * faq.txt - Frequently Asked Questions. / / \ \ + * | < / \_ + * This file covers some of the common problems | \/ /\ / + * and misconceptions people have with DUMB. If \_ / > / + * your problem is not covered here, please | \ / / + * contact me. I'll do my best to help - but | ' / + * don't be offended if I just direct you to the \__/ + * manual! + */ + + +***************************************************************************** +* I get a lot of strange warnings and errors when I compile my projects * +* with this release of DUMB. They work with older versions! What happened? * +***************************************************************************** + + Some parts of DUMB's API have been deprecated. See docs/deprec.txt for + full details, including an explanation as to why your compiler warnings + and errors are so unfriendly, and information on how to fix each warning + or error. + + +***************************************************************************** +* When I try to compile DUMB with Allegro, it complains that it cannot find * +* 'internal/alconfig.h'! What's wrong? * +***************************************************************************** + + In Allegro 4.0.1, and quite likely some other versions of Allegro, the + msvcmake batch file does not install Allegro properly. I believe this was + fixed in Allegro 4.0.2, but don't take my word for it. Some include files + are neglected, including alconfig.h. The fix is quite easy; you need to + copy all of Allegro's include files to your compiler's directory. The + following should do this for you (alter it accordingly depending on where + MSVC and Allegro are installed): + + cd\progra~1\msvc\include + xcopy/s \allegro\include\*.* + + You can safely tell it to overwrite all files. + + +***************************************************************************** +* When I build a project that uses DUMB, I get an error that it doesn't * +* find -laldmbd! What's wrong? * +***************************************************************************** + + See the notes for DUMB v0.8 in release.txt; the existence of libaldmbd.a + in DUMB v0.7 was due to a mistake in the makefiles. It should be + libaldmd.a, in order to maintain DOS compatibility. All subsequent + releases get it right, but you will have to change your project files to + allow for the change. If this is someone else's project, please let them + know that it needs changing. + + +***************************************************************************** +* When I build a project that uses DUMB, I get some linker errors about * +* _free, _malloc, etc. already being defined in LIBC.lib! What's wrong? * +***************************************************************************** + + MSVC offers three different implementations of the standard libraries. + When you link statically with a library, you have to use the same + implementation that the library uses. You need the multithreaded DLL + implementation, which you can select by passing /MD when you compile (not + when you link). See howto.txt for details. + + +***************************************************************************** +* I created an IT file with Impulse Tracker, but DUMB won't play it! Why? * +***************************************************************************** + + You probably created some patterns but didn't give any information on the + order in which they should be played. Impulse Tracker will also fail to + play your music if you press F5. Press F11 and you will have an + opportunity to create an order list, required for playback. + + +***************************************************************************** +* I created an IT file with ModPlug Tracker and I have it fading out at the * +* end. Why won't it loop when I play it with DUMB? * +***************************************************************************** + + It loops at zero volume. This is what Impulse Tracker itself does. Fix the + IT file by setting the global volume explicitly (Vxx in the effects + column), either at the start, or right at the end before looping. Also see + the next two questions. + + +***************************************************************************** +* My module plays too loud and distorts badly with DUMB! What can I do? * +***************************************************************************** + + This problem is most often caused by ModPlug Tracker, which has a complete + lack of regard for the playback volume of the original tracker. See the + next question for DUMB's official position with regard to ModPlug Tracker. + If you wrote your module with ModPlug Tracker, please try loading it with + the original tracker and see if it distorts there too. If it does, reduce + the volume. If not, then it's a problem with DUMB; please let me know. + + If for whatever reason you cannot modify the module file itself, you can + make it sound better by reducing the volume passed to al_start_duh(). + + +***************************************************************************** +* I created a music module with ModPlug Tracker, and DUMB doesn't play it * +* right! * +***************************************************************************** + + DUMB cannot and will not support ModPlug Tracker. Please see + docs/modplug.txt for details. The original trackers, which DUMB is + designed to mimic as closely as possible, are listed in readme.txt. + If you find DUMB plays your module differently from the original tracker, + then please contact me. + + +***************************************************************************** +* My program crashes as soon as I try to load anything with DUMB! * +***************************************************************************** + + Please take my advice and use the debugging build of DUMB, not the + optimised build. Then you'll probably find it aborts instead of crashing. + In this case you probably forgot to register a DUMBFILE system; this is + necessary for loading stand-alone files, though not for loading Allegro + datafiles with embedded music. Follow the instructions in docs/howto.txt + carefully and you shouldn't have this problem. + + If DUMB crashes with a specific music module, please let me know. + + +***************************************************************************** +* I want to use the stdio file access functions to load stand-alone music * +* files, but I also want to load datafiles containing music files. The docs * +* say I shouldn't call both dumb_register_stdfiles() and * +* dumb_register_packfiles(). What shall I do? * +***************************************************************************** + + When you register a DUMBFILE system, it only applies to files opened with + dumbfile_open(), i.e. separate files. When a file is embedded in a + datafile, dumbfile_open_ex() is used to read it, enabling it to use + PACKFILEs regardless of which DUMBFILE system is registered. In short, you + do not need to call dumb_register_packfiles() in order to load datafiles + with embedded music. See the section on "Sequential File Input" in + docs/dumb.txt if you're interested in how all this works. + + +***************************************************************************** +* I want to read a specific object in a datafile using Allegro's * +* "demo.dat#MY_MUSIC" syntax. Why won't it work? * +***************************************************************************** + + Did you call dumb_register_packfiles(), or did you call + dumb_register_stdfiles()? It will only work if you use the former. + + +***************************************************************************** +* My program runs, but no music plays! What am I doing wrong? * +***************************************************************************** + + There are a number of possible causes for this. The most likely reason is + that you aren't calling al_poll_duh(); see docs/howto.txt for further + information. + + Other possible causes are as follows: + + - The speakers are turned down (duh) + - The volume of some system mixer is turned down + - Another program is using the sound card (not a problem for most modern + systems) + - You didn't initialise Allegro's sound system; see install_sound() in + Allegro's docs + - Allegro's drivers don't work on your system and chosen platform + + In order to narrow down the cause, consider the following: + + - Do you get any other sound from your program? + - Do other Allegro+DUMB programs generate sound? + - Do other Allegro programs generate sound? + - Do other non-Allegro programs generate sound? + - Does your program fail only on a specific platform (e.g. DOS but not + Windows)? + + This problem is highly system-specific; please try hard to solve it by + yourself before contacting me. However, if you think this problem could + affect other people, please let me know what the problem is and how you + fixed it, if you did. Be as specific as possible. + + +***************************************************************************** +* The music stutters! What can I do? * +***************************************************************************** + + If you have an older computer, it may not be able to cope with the load. + Try reducing quality options; look up dumb_resampling_quality and + dumb_it_max_to_mix in docs/dumb.txt, and consider changing the frequency + you pass to al_start_duh(). + + Stuttering may not be caused by excessive load. To find out, try + increasing the buffer size passed to al_start_duh(). Beware of making it + too big though; older systems will freeze periodically if it's too big, + because they render larger chunks less frequently. The timing of callbacks + will also be less accurate, if you are using those. + + If you're using the 'dumbplay' example, you can control these parameters + by editing dumb.ini. + + +***************************************************************************** +* Why does DUMB use so much processor time compared with other players? * +***************************************************************************** + + This should be less so in this release than in previous releases; the + resampling and filtering algorithms have been optimised. + + By default, DUMB uses the most expensive resampling quality option. I've + found on an AthlonXP 1800+ and on a Pentium 233 that it typically uses + about twice as much processor time as the least expensive option. + + Try setting dumb_resampling_quality to DUMB_RQ_ALIASING or DUMB_RQ_LINEAR. + See dumb.txt for more information. If you're using the example programs, + you can control this variable by editing dumb.ini. + + DUMB uses 32-bit ints for mixing. Some players use 16-bit ints, and are + therefore marginally faster (not much!) and lower quality. So you can't + expect DUMB to beat these players. Furthermore, DUMB is currently written + entirely in C. GCC does an impressive job on the C code, but that's not to + say some custom-written assembly language couldn't beat it ... + + +***************************************************************************** +* Why does DUMB generate so much background noise? * +***************************************************************************** + + You're probably using the DOS build on a system with bad Sound Blaster + compatibility (most Windows XP systems fall in this category). This would + mean DUMB could only access an 8-bit driver. The Windows build will almost + certainly give better results. Your DOS binary will still give good + results on systems with better compatibility (like my Windows 98 system). + + +***************************************************************************** +* I e-mailed you and you replied with "RTFM"! What does that mean? * +***************************************************************************** + + Read The Manual. If it's a specific problem, I'll probably be kind and + tell you where to look in the manual. However, if I get the impression you + haven't even looked for a solution in the manual, expect no mercy ... + + +Ben Davis +entheh@users.sf.net +IRC EFnet #dumb +See readme.txt for details on using IRC. diff --git a/apps/codecs/dumb/docs/fnptr.txt b/apps/codecs/dumb/docs/fnptr.txt new file mode 100644 index 0000000..a5fb216 --- /dev/null +++ b/apps/codecs/dumb/docs/fnptr.txt @@ -0,0 +1,113 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * fnptr.txt - Function pointer explanation. / / \ \ + * | < / \_ + * | \/ /\ / + * \_ / > / + * | \ / / + * | ' / + * \__/ + */ + + +C allows you to create and use function pointers. A function pointer is a +variable that points to a function, and you can use it to call that function. +Why is this useful? + +Function pointers can be passed as parameters. As an example, here's a +function from Allegro: + + void create_light_table(COLOR_MAP *table, const PALETTE pal, int r, g, b, + void (*callback)(int pos)); + +Don't worry about the syntax just yet, but the last parameter, 'callback', is +a pointer to a function that takes an int parameter. create_light_table() can +take some time to complete its work, and you may want to display a progress +indicator. So you write a function to draw the progress indicator, and then, +for 'callback', you specify a pointer to your function. This will enable +create_light_table() to call your function at intervals during its +processing. (If you don't want to use the callback, you can pass NULL, but +this only works because create_light_table() checks actively for NULL. You +can't always specify NULL when you want nothing to happen.) + +There are many other uses. In addition to using function pointers as +parameters, Allegro has some global function pointers you can set to point to +your functions. Function pointers can also be used in structs, and this is +where DUMB makes the most use of them. + +So how are they used? + + void bar(void) { ... } /* Here's a function */ + void (*foo)(void) = &bar; /* Take a pointer */ + (*foo)(); /* Call the function */ + + char *baz(float a) { ... } /* Here's another function */ + char *(*foobarbaz)(float a) = &baz; /* Take a pointer */ + char *rv = (*foobarbaz)(0.1); /* Call the function */ + +In both these cases, note how the statement for calling the pointed-to +function (third line) resembles the definition of the function pointer +(second line). This is true of any variable in C, and can lead to some truly +obfuscated definitions if you are that way inclined. Such definitions can be +clarified with typedefs, but before you use those, it is important you +understand how the above statements work. I speak from experience: function +pointer notation looks random and scary, until you understand why it's the +way it is; then it makes perfect sense. + +(It is actually permissible to omit the & when taking a pointer and to write +e.g. foobarbaz(0.1) instead of (*foobarbaz)(0.1). However, I recommend not +doing this, since the syntax for using the pointer no longer resembles the +definition. Writing e.g. (*foobarbaz)(0.1) also makes a clear distinction +between function pointer calls and ordinary function calls, which makes code +more readable.) + +Note that function pointers have the return value and parameter list +specified. A function pointer can only point to a function with a matching +return value and matching parameters. (You can break this rule by casting the +pointer explicitly, but there is no situation where doing so is portable to +all computers, and I strongly advise against it unless you're writing system +code. If you're not sure whether you're writing system code or not, then +you're not.) + +The parameter names need not match (although the types must). If you wish to +rename a parameter in your function, you do not have to change the function +pointer accordingly. In fact, when you define a function pointer, you don't +even have to specify the names of parameters if you don't want to. I normally +do so for clarity. + +It is possible to typedef a function pointer. In order to typedef a function +pointer, you start by declaring the pointer as a variable: + + void (*myfunc)(void); + +Then you write 'typedef' before it and replace the variable name, which is +myfunc, with the type name (this rule can be applied to any variable when you +want to use typedef): + + typedef void (*MYTYPE)(void); + +Now 'MYTYPE' represents a pointer to a function with no parameters and no +return value. The following two lines are completely equivalent: + + MYTYPE myfunc; + void (*myfunc)(void); + +Note that we use MYTYPE without an asterisk (*), since it is already a +pointer. + +That's it. If you feel anything should be explained better here, or if you +feel something should be added, please don't hesitate to let me know! + + +Ben Davis +entheh@users.sf.net +IRC EFnet #dumb +See readme.txt for details on using IRC. diff --git a/apps/codecs/dumb/docs/howto.txt b/apps/codecs/dumb/docs/howto.txt new file mode 100644 index 0000000..0e7057d --- /dev/null +++ b/apps/codecs/dumb/docs/howto.txt @@ -0,0 +1,845 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * howto.txt - How To Use DUMB. / / \ \ + * | < / \_ + * See readme.txt for general information on | \/ /\ / + * DUMB and how to set it up. \_ / > / + * | \ / / + * | ' / + * \__/ + */ + + +******************** +*** Introduction *** +******************** + + +Welcome to the DUMB How-To! It is assumed here that you have already set DUMB +up on your system, with or without Allegro. If not, please see readme.txt. + + +********************************* +*** Adding music to your game *** +********************************* + + +These instructions will help you add a piece of music to your game, assuming +your music is stored in a stand-alone IT, XM, S3M or MOD file. If you wish to +use a different method (such as putting the music file in an Allegro +datafile), please follow these instructions first, test your program, and +then follow the instructions further down for adapting your code. + + +1. You need to include DUMB's header file. If you have Allegro, add the + following line to the top of your source file (or at the top of each file + where you wish to use DUMB): + + #include <aldumb.h> + + If you do not have Allegro or do not wish to use it, use dumb.h instead. + + +2. You need to link with DUMB's library file or files. If you are compiling + with GCC from a command line on any platform, you need to add the + following to the command line: + + If you are using Allegro: -laldmd -ldumbd + If you are not using Allegro: -ldumbd + + If you are using MSVC from the command line: + + If you are using Allegro: /link aldmd.lib dumbd.lib + If you are not using Allegro: /link dumbd.lib + + With MSVC, you must also add /MD to the command line when compiling (not + when linking). + + Note that -laldmd or aldmd.lib must PRECEDE alleg.lib, -lalleg_s, + `allegro-config --libs`, or whatever you are already using to link with + Allegro. For MSVC users, the /MD flag selects the multithreaded DLL + implementation of the standard libraries; since DUMB is statically linked, + you have to use the same library DUMB uses. You would also need this flag + to link statically with Allegro; if you already have it, there's no need + to put it twice. + + (If anyone would like to contribute instructions for doing the above using + MSVC's IDE, please contact me. Contact details are at the end of this + file.) + + If you are using RHIDE, go to Options -> Libraries. You will need to type + 'aldmd' and 'dumbd' in two boxes, making sure 'aldmd' comes above whatever + you are using to link with Allegro (or just put 'dumbd' if you are not + using Allegro). Make sure the box next to each of these libraries is + checked. + + The above are the debugging libraries. It is VERY HIGHLY RECOMMENDED that + you use the debugging libraries at first. The reason is as follows. + Although DUMB is supposedly robust against corrupt music files and things + like lack of memory, it will NOT tolerate programmer error. If you write + faulty code, DUMB will probably crash rather than returning an error code + for you. However, the debugging libraries will abort in many cases, + enabling you to find out what the cause is. + + Once your program is up and running reliably, you can replace 'aldmd' with + 'aldmb' and 'dumbd' with 'dumb'. Don't forget to do this, or DUMB will be + a lot slower than it should be! + + +3. As you use DUMB, it may claim system resources (memory in particular). You + will need to arrange for these resources to be freed at the end. Doing so + is very easy. Simply write the following line at the top of your main + function, but below allegro_init() if you are using Allegro: + + atexit(&dumb_exit); + + This arranges for the function dumb_exit() to be called when your program + exits; you do not need to call dumb_exit() yourself. This method is + preferable to calling dumb_exit() manually, as it will free resources even + if your program aborts unexpectedly. + + If you are happy with this, please skip ahead to Step 4. If you are + interested in alternative methods, read on, but read on carefully. + + In fact it mostly doesn't matter where you put the above atexit() line, + provided it gets called only once, and before you do anything with DUMB. + If you are using DUMB with Allegro, it is recommended that you write the + functions in this order: + + allegro_init(); + atexit(&dumb_exit); + + And then you must NOT call allegro_exit() yourself (because it has to be + called after dumb_exit()). Alternatively, if you prefer not to use + atexit() (or you cannot), you will have to do the following before + exiting: + + dumb_exit(); + allegro_exit(); + + +4. DUMB does not automatically do any of its own file input. You have to tell + it how to read files. Don't worry, it's easy. Simply call the following + function near the beginning of your program, after your atexit() call: + + dumb_register_stdfiles(); + + This tells DUMB to use ordinary stdio FILE structs for reading and writing + files. If you are using Allegro and would rather DUMB used PACKFILEs, call + the following function INSTEAD: + + dumb_register_packfiles(); + + In the latter case, DUMB will be affected by any password you set with + packfile_password() in the same way that other PACKFILEs are. + + Note that the procedure for loading datafiles with embedded music is + independent of these two functions; even if you will be loading datafiles, + you can use either of these functions. If you are loading datafiles, your + executable might be slightly smaller if you use dumb_register_packfiles(). + On the other hand, dumb_register_stdfiles() will probably be faster. If + you are only ever going to load datafiles and never stand-alone files, you + can actually leave this step out; but I would recommend you put this in, + test your code with a stand-alone file, then follow the instructions in + the next section in order to adapt your code to use the datafile (you will + be reminded that you can remove the function call). + + +5. If you are using Allegro, you'll have to initialise Allegro's sound + system. In most cases the following line will do the job: + + install_sound(DIGI_AUTODETECT, MIDI_NONE, NULL); + + You may like to initialise a MIDI driver though; see Allegro's docs for + details. Put this line after allegro_init(). + + +6. All pieces of music are stored in memory in DUH structs. To handle these, + you must define pointers to them. Such pointers look like this: + + DUH *myduh; + + You can of course replace 'myduh' with anything you like. If you are + unfamiliar with pointers, please see ptr.txt. It is very important that + you understand these if you wish to use DUMB correctly. + + You do not have direct access to the contents of a DUH struct, so do not + try. DUMB's functions provide everything you need; if you disagree, please + let me know and I shall see what I can do. Contact details are at the end + of this file. + + Given the above definition, you can load a piece of music using one of the + following lines, depending on what file format you want to load: + + myduh = dumb_load_it("a_one.it"); + myduh = dumb_load_xm("a_two.xm"); + myduh = dumb_load_s3m("a_one_two.s3m"); + myduh = dumb_load_mod("three_four.mod"); + + Obviously you can use relative or absolute paths as normal. You should + always use forward slash (/), not backslash (\), when coding in C and + similar languages. + + Every piece of music you load must be unloaded when you've finished with + it. When you type the above line in, it is good practice to type the + following line in at the same time, but put it at the end of the program: + + unload_duh(myduh); + + You will now be able to use the DUH struct anywhere in between the two + lines you just added. There is no need to check the return value; if the + DUH failed to load for one reason or another (this could be due to lack of + memory as well as the file not being there), then DUMB will do nothing - + safely. + + +7. From this step onwards, it will be assumed you're using Allegro. If not, + please read these steps anyway, and then see the section entitled + "Rendering music into a buffer". You will have to write your own playback + code using whatever sound output system is available. Alternatively you + may like to write data to a file (especially if you have a file that + consumes a lot of processor time), but beware that any streaming audio + format is likely to be substantially larger than the module file you + generate it from, and formats like MP3 will be lower quality. You might + not be able to hear the difference between the MP3 and the original, but + many people can and don't like it, so please consider them. I'm one of + them. If you really want to use a lossy compression format, I highly + recommend Ogg Vorbis: + + http://www.vorbis.com/ + + But I digress. + + In order to play the DUH you loaded, you need to define a pointer to an + AL_DUH_PLAYER struct: + + AL_DUH_PLAYER *dp; + + Two of the functions you will need are prototyped as follows: + + AL_DUH_PLAYER *al_start_duh(DUH *duh, int n_channels, long pos, + float volume, long bufsize, int freq); + + void al_stop_duh(AL_DUH_PLAYER *dp); + + As you can see, al_start_duh() returns a pointer to an AL_DUH_PLAYER + struct when you call it. You then pass this pointer to all the other + functions. Again, if it is a NULL pointer for whatever reason (usually + lack of memory), DUMB will safely do nothing. When you call al_stop_duh(), + the pointer becomes invalid and you should not use it again; if there's + any risk of the pointer being used again, it is wise to set it to NULL at + this point. You can reassign the variable with a new call to + al_start_duh() of course. + + Set 'n_channels' to 1 or 2 for mono or stereo respectively. Note that this + parameter has nothing to do with the number of samples that can play at + once in a music module. Set 'pos' to 0 to play from the beginning; each + time you add 65536, you will have advanced one second into the piece. As a + general rule, set the volume to 1.0f and adjust it later if the music is + too loud or too quiet - but see Allegro's set_volume_per_voice() function + first. + + 'bufsize' can generally be set to 4096. If your music stutters, try + increasing it; if your game freezes periodically, try reducing it. Find a + happy medium. Set 'freq' to 48000 for the best quality, though 44100 will + do in most cases. 22050 will be fine for a lot of music, though 11025 may + sound muffled. You can choose any other value, higher, lower or in + between. If your music stutters, and increasing 'bufsize' doesn't fix it, + try reducing this value. + + Once you have put in a call to al_start_duh(), it is good practice to + insert the call to al_stop_duh() at the same time. You must call + al_stop_duh() before the DUH is unloaded (unload_duh(), Step 6 above). + + Don't get impetuous, your program is not ready yet! Proceed to Step 8. + + +8. DUMB does not play music in the background for you; if you were expecting + it to do so, please see the explanation at the end of this step. For your + music to be played, you have to call another function at regular + intervals. Here is its prototype: + + int al_poll_duh(AL_DUH_PLAYER *dp); + + Do NOT call this function from inside a timer function unless you really + know what you are doing. The reasons why this is bad are explained + further down. You should call it from your main program. + + Simply writing the following line will be sufficient in general, if you + have a variable 'dp' that points to your AL_DUH_PLAYER struct. + + al_poll_duh(dp); + + As a general rule, calling this once for each logic update will do the + trick. If, however, you are executing time-consuming algorithms such as + software 3D rendering, you may wish to insert calls to this function in + the middle of those algorithms. You cannot call this function too often + (within reason); if it has nothing to do it will return immediately. + + Exactly how often you need to call the function depends on the values for + 'bufsize' and 'freq' that you passed to al_start_duh(): + + n = freq / bufsize; + + You have to call al_poll_duh() at least n times a second. Do not hesitate + to call it more often for safety; if the sound stutters, you may need to + do just that. (Or you may need to increase the buffer size or reduce the + quality settings; the only way to find out is to try.) + + For now, don't worry about al_poll_duh()'s return value. As soon as you + need it, it will be explained. + + If you are happy, please skip to Step 9. If you were expecting DUMB to + play your music in the background, please read on. + + The natural way to play music in the background on most operating systems + nowadays is to use threads. DOS was not built with multithreading in mind, + and its system operations (notably disk access) assume they will only be + used from a single thread. + + Interrupts are the next best thing to threads. A DOS hardware interrupt + could be triggered at any moment, and a handler function will be called. + This is how Allegro's timer functions work. Unfortunately, what you can do + inside an interrupt handler is very limited. For one thing, all code and + data used by the handler must be locked in memory; if not, it could get + written to disk (virtual memory). If the main program was accessing the + disk when it got interrupted, the system would then die a horrible death. + This precludes the possibility of allocating extra memory inside the + handler, and DUMB does a lot of that in al_poll_duh(). + + Given DUMB's architecture, which cannot change for reasons which will + become apparent in future versions, this renders it impossible to come up + with a portable solution for making DUMB play music in the background. + Having said that, if you wish to write your own wrapper for al_poll_duh() + and use it in a thread, there is nothing stopping you. If you do do this, + you will have to be very careful when stopping the music; see the + description of al_poll_duh() in dumb.txt for more information. + + So why not kill DOS? It is all too common a practice among programmers to + quote the phrase, "DOS is as dead as the dodo." Despite being a decidedly + derisible demonstation of the dreary device of alliteration, it shows a + distinct lack of experience. Many embedded systems still use DOS because + it provides hardware access capabilities and real-time possibilities + unparalleled by any current multitasking operating system. For an argument + closer to home, I used to use RHIDE for DOS before I switched to Linux, + and I have not found a single Freeware Windows IDE that measures up to + RHIDE. I'm sure many people are in the same boat, and really appreciate + DUMB's DOS port. + + We will not be removing DOS support from DUMB. Any blind suggestions to do + so will be met with fiery flames. You have been warned. + + +9. Test your program! + + If you have trouble, check through the above steps to make sure you didn't + miss one out. Refer to faq.txt to see if your problem is addressed there. + If you still have trouble, contact me; details are at the end of this + file. + + +********************************** +*** Controlling music playback *** +********************************** + + +Here I describe some common operations you may wish to perform. The method +for doing so will seem a bit strange sometimes, as will the names of the +structs. However, there is a reason behind everything. If you would like to +do more exotic things, or better understand some of the methods used here, +then see dumb.txt, which covers everything from the ground up. + + +To control playback quality: + + #define DUMB_RQ_ALIASING + #define DUMB_RQ_LINEAR + #define DUMB_RQ_CUBIC + #define DUMB_RQ_N_LEVELS + extern int dumb_resampling_quality; + extern int dumb_it_max_to_mix; + + Please note that dumb_resampling_quality has changed in DUMB v0.9.2. See + deprec.txt for more details on the change. + + dumb_resampling_quality can be set to any of the DUMB_RQ_* constants + (except DUMB_RQ_N_LEVELS; see below). Resampling is the term given to the + process of adjusting a sample's pitch (in this context). + dumb_resampling_quality defaults to DUMB_RQ_CUBIC, which sounds nice but + takes a lot of processor power. Try reducing it if you have an older + computer or if you are trying to mix an insane number of samples (or + both!). See dumb.txt for details on what the different values actually do. + + If you wish to give this option to your user, you can use + DUMB_RQ_N_LEVELS. All the values from 0 to DUMB_RQ_N_LEVELS - 1 will be + valid resampling levels. If a value outside this range is chosen, it is + not the end of the world; DUMB will behave as if you had chosen the value + at whichever extreme you went beyond. + + dumb_it_max_to_mix, defaulting to 64, is the maximum number of samples + DUMB will ever mix together when playing an IT, XM, S3M or MOD file. + Unlike many other music systems, DUMB will still keep track of all samples + (up to a fixed maximum of 256 of them, roughly speaking), and then will + just render as many of them as this variable permits, starting with the + loudest ones. When samples are cut or come back in, the exact timings will + not generally be predictable - but nor will they be important. + + dumb_it_max_to_mix applies to each currently playing module file + independently. So if you set it to 64, but render two modules + simultaneously, DUMB could end up mixing up to 128 samples. + + +To pause and resume playback, set the volume, get the current playback +position, or get the length of time a DUH will play for before either looping +or freezing (effect F00 in XM and MOD files, which means no new notes will be +played but any existing notes will continue): + + void al_pause_duh(AL_DUH_PLAYER *dp); + void al_resume_duh(AL_DUH_PLAYER *dp); + void al_duh_set_volume(AL_DUH_PLAYER *dp, float volume); + long al_duh_get_position(AL_DUH_PLAYER *dp); + + long duh_get_length(DUH *duh); + + These functions are pretty self-explanatory. The volume passed to + al_duh_set_volume() and the position returned by al_duh_get_position() are + in the same units as those you passed to al_start_duh(). The length + returned by duh_get_length() is in the same units as the aforementioned + position; see dumb.txt for more information on this function. Be careful + with al_duh_get_position(); it will return a position slightly ahead of + what you can hear, because the system has to keep ahead slightly to avoid + stuttering. + + +To prevent the music from looping and/or freezing: + + DUH_SIGRENDERER *al_duh_get_sigrenderer(AL_DUH_PLAYER *dp); + DUMB_IT_SIGRENDERER *duh_get_it_sigrenderer(DUH_SIGRENDERER *sigrenderer); + + void dumb_it_set_loop_callback(DUMB_IT_SIGRENDERER *sigrenderer, + int (*callback)(void *data), void *data); + void dumb_it_set_xm_speed_zero_callback(DUMB_IT_SIGRENDERER *sigrenderer, + int (*callback)(void *data), void *data); + + int dumb_it_callback_terminate(void *data); + + If you are unfamiliar with function pointers, please see fnptr.txt. + + Note that these functions apply to IT, XM, S3M and MOD files - not just to + IT files. This holds true throughout DUMB, for all functions with "it" in + the name. The xm_speed_zero event can only occur with XM and MOD files. + + The first two functions will return a pointer to a struct contained by the + struct you pass. This system is necessary to ensure that these operations + are possible when not using Allegro. Typically you would write the + following code: + + { + DUH_SIGRENDERER *sr = al_duh_get_sigrenderer(dp); + DUMB_IT_SIGRENDERER *itsr = duh_get_it_sigrenderer(sigrenderer); + dumb_it_set_loop_callback(itsr, &dumb_it_callback_terminate, NULL); + dumb_it_set_xm_speed_zero_callback + (itsr, &dumb_it_callback_terminate, NULL); + } + + Once you have done this, the return value of al_poll_duh() becomes + significant. It will be 0 as long as the music is playing. When the music + stops, al_poll_duh() will return nonzero. You can call al_stop_duh() and + do something else as soon as you wish, but calling al_poll_duh() some more + will not do any harm. + + al_poll_duh() will also return 1 if the music could not be loaded, or if + memory was short when trying to play it, or if it was a quirky music file + with no music in it (technically one with an empty order list). This + happens regardless of whether or not you execute the above code to disable + looping. Normally you shouldn't need to worry about this. + + To undo the above and make DUMB loop or freeze again, pass NULL instead of + &dumb_it_callback_terminate. If you would like to fade on looping, or loop + a finite number of times, or display a message when looping, or whatever, + you will have to write your own callback function. In this case, please + see dumb.txt. + + Note that the above code can safely be applied for a DUH that doesn't + contain a music module but contains some other kind of music. + duh_get_it_sigrenderer() will return NULL, and the code will do nothing. + + +To analyse the audio as it's generated: + + typedef int sample_t; + + typedef void (*DUH_SIGRENDERER_ANALYSER_CALLBACK)(void *data, + const sample_t *const *samples, int n_channels, long length); + + void duh_sigrenderer_set_analyser_callback(DUH_SIGRENDERER *sigrenderer, + DUH_SIGRENDERER_ANALYSER_CALLBACK callback, void *data); + + If the above confuses you, see fnptr.txt. These functions, along with + al_duh_get_sigrenderer() from the last section, enable you to register a + callback function. Every time some samples are generated, they will be + passed to this function. This enables you to display an oscilloscope or + spectrum analyser, for example. + + 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. 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(). + + +Everything below this point assumes some knowledge of how a music module is +constructed. If you do not have this knowledge, talk to whoever is writing +music for you, or download a tracking program and play with it (see +readme.txt). + + +To start playing an IT, XM, S3M or MOD from an arbitrary order number (the +default being 0, the beginning of the song), use the following: + + DUH_SIGRENDERER *dumb_it_start_at_order + (DUH *duh, int n_channels, int startorder); + AL_DUH_PLAYER *al_duh_encapsulate_sigrenderer + (DUH_SIGRENDERER *sigrenderer, float volume, long bufsize, int freq); + + The usage of these functions is as follows: + + { + DUH_SIGRENDERER *sr = dumb_it_start_at_order + (duh, n_channels, startorder); + dp = al_duh_encapsulate_sigrenderer(sr, volume, bufsize, freq); + } + + Replace 'dp' with whatever your AL_DUH_PLAYER pointer is. You also need + to insert suitable values for n_channels, startorder, volume, bufsize and + freq. These have the same meaning as those passed to al_start_duh(). + + WARNING: after passing a pointer to an "encapsulate" function, do not use + that pointer again. (More specifically, do not use it again if + the function returns NULL, because the function will have + destroyed the pointer if this happens, to help prevent memory + leaks.) There will be a "get" function with which you can obtain + the original pointer if it is still valid, or NULL otherwise. + + The above functions will fail (safely) if you try to use them with a DUH + that contains a different type of music. + + Notice that there is no 'pos' parameter. If you would like to skip through + the music, you can use this function: + + long duh_sigrenderer_get_samples( + DUH_SIGRENDERER *sigrenderer, + float volume, float delta, + long size, sample_t **samples + ); + + Pass 0 for volume and NULL for samples, and this function will skip + through the music nice and quickly. So insert the following between the + two above statements: + + duh_sigrenderer_get_samples(sr, 0, 65536.0f / freq, pos, NULL); + + Substitute for 'freq' and 'pos'. An explanation of the 'delta' parameter + can be found further down in this file. + + Finally, note that duh_get_length() is only meaningful when you start + playing music from order 0. + + +If an IT file contains Zxx effects, DUMB will generate MIDI messages, which +will control the low-pass resonant filters unless the IT file actively +specifies something else. In rare cases this may not be what the Zxx effects +were intended to do; if this is the case, you can block the MIDI messages as +follows. Note that this does NOT mean filters are disabled; if an instrument +specifies initial cut-off and resonance values, or has a filter envelope, +then filters will be applied. It only makes sense to use this procedure at +the beginning of playback. + + void dumb_it_set_midi_callback(DUMB_IT_SIGRENDERER *sigrenderer, + int (*callback)(void *data, int channel, unsigned char byte), + void *data); + + int dumb_it_callback_midi_block(void *data, int channel, + unsigned char byte); + + Using some functions described in the previous section, we arrive at the + following code: + + { + DUH_SIGRENDERER *sr = al_duh_get_sigrenderer(dp); + DUMB_IT_SIGRENDERER *itsr = duh_get_it_sigrenderer(sigrenderer); + dumb_it_set_midi_callback(itsr, &dumb_it_callback_midi_block, NULL); + } + +DUMB offers no way of disabling filters completely. Disabling filters is not +recommended as a means to reduce processor usage, as it will completely +damage any piece of music that uses the filters. If you want lower processor +consumption, use a piece of music that does not use filters. + + +Finally, DUMB offers a myriad of functions for querying and adjusting +module playback. Those beginning with "dumb_it_sd" operate on the +DUMB_IT_SIGDATA struct, which represents the piece of music before it starts +to play. Those beginning with "dumb_it_sr" operate on the DUMB_IT_SIGRENDERER +struct, which represents a currently playing instance of the music. Note that +duh_get_length(), described above, becomes meaningless after some of these +functions are used. + +The method for getting a DUMB_IT_SIGRENDERER struct has already been given, +but the function prototypes are repeated here for convenience: + + DUH_SIGRENDERER *al_duh_get_sigrenderer(AL_DUH_PLAYER *dp); + DUMB_IT_SIGRENDERER *duh_get_it_sigrenderer(DUH_SIGRENDERER *sigrenderer); + +Getting a DUMB_IT_SIGDATA struct is simpler: + + DUMB_IT_SIGDATA *duh_get_it_sigdata(DUH *duh); + +For a list of dumb_it_sd_*() and dumb_it_sr_*() functions, please see +dumb.txt. These functions are new, and may not provide exactly what you need; +if not, please let me know. + + +************************************************** +*** Embedding music files in Allegro datafiles *** +************************************************** + + +In this section it is assumed you are already reasonably familiar with how +Allegro datafiles are used. If not, please refer to Allegro's documentation. +At the time of writing, the documentation you need is off the beaten track, +so to speak, in allegro/tools/grabber.txt. + +To add a piece of music to a datafile, you need to create an object of type +"IT ", "XM ", "S3M " or "MOD " (note the spaces used as padding, although +you do not need to type these into the grabber). Then grab the piece of music +in. The grabber will treat it as a binary object. Save the datafile as usual. + + +To use a piece of music you added to the datafile, follow these steps: + + +1. Before loading the datafile, call one or more of these functions, + depending on which music format or formats you'd like to support: + + dumb_register_dat_it(DUMB_DAT_IT); + dumb_register_dat_xm(DUMB_DAT_XM); + dumb_register_dat_s3m(DUMB_DAT_S3M); + dumb_register_dat_mod(DUMB_DAT_MOD); + + Remember, do not call multiple functions unless you want to support + multiple formats. Calling more functions will add unused code to your + executable. + + It is important that you make call these before loading the datafile, + since they tell Allegro how to load the respective files straight from + datafiles in the future. They will not help Allegro interpret any module + files that have already been loaded as binary objects (but if you really + need to interpret a module that has been loaded in this fashion, have a + look at dumbfile_open_memory() in dumb.txt). + + If for whatever reason your music objects are identified by a different + type in the datafile, you can tell DUMB what that type is by changing the + parameter to the registration function above. Use Allegro's DAT_ID() + macro, e.g. DAT_ID('B','L','A','H'). This is not really recommended + though, since it would prevent a hypothetical grabber plug-in from being + able to play your music files. Use the above types if possible. + + +2. Whenever you need a pointer to a DUH struct, simply use the 'dat' field. + Do this in the same way you would for a pointer to a BITMAP struct or + anything else. If it makes you feel more comfortable, you can extract the + pointer in advance: + + DATAFILE *dat = load_datafile("smurf.dat"); + if (!dat) abort(); /* There are much nicer ways of handling failure! */ + DUH *myduh = (DUH *)dat[GAME_MUSIC].dat; + + Note that the explicit (DUH *) cast is only necessary for C++, not for C. + However, it does no harm. + + Be sure that you do NOT call unload_duh() for anything stored in the + datafile. These DUHs will be freed when you call unload_datafile(), and + freeing them twice is practically guaranteed to crash your program. + + +3. If you only ever load music as part of a datafile, and you never load any + stand-alone music files, you do not need to register a file input system + for DUMB to use. If you followed the instructions for the first section + you will have one of these two lines in your program: + + dumb_register_stdfiles(); + dumb_register_packfiles(); + + You can safely delete this line - but only if you never load any + stand-alone music files. The debugging library will bale you out if you + delete it when you shouldn't; the optimised library won't. + + +************************************* +*** Rendering music into a buffer *** +************************************* + + +NOTE: much of the API formerly described in this section has been deprecated, + and you will need to alter your code. See deprec.txt for details. If + you are reading this section for the first time, you can ignore this + note. + +Rendering to a buffer is similar to playing using an AL_DUH_PLAYER. However, +you must use a DUH_SIGRENDERER struct instead. Here are the functions: + + DUH_SIGRENDERER *duh_start_sigrenderer + (DUH *duh, int sig, int n_channels, long pos); + + int duh_sigrenderer_get_n_channels(DUH_SIGRENDERER *sigrenderer); + long duh_sigrenderer_get_position(DUH_SIGRENDERER *sigrenderer); + + long duh_sigrenderer_get_samples(DUH_SIGRENDERER *sigrenderer, + float volume, float delta, long size, sample_t **samples); + + long duh_render(DUH_SIGRENDERER *sigrenderer, + int bits, int unsign, float volume, float delta, long size, void *sptr); + + void duh_end_sigrenderer(DUH_SIGRENDERER *sigrenderer); + +The parameters to duh_start_sigrenderer() have the same meanings as those to +al_start_duh(). However, note that the volume is not set at this stage. You +pass the desired volume each time you want to render a block. The 'sig' +parameter should be set to 0 for now. + +Notice that there are two rendering functions. duh_sigrenderer_get_samples() +will generate samples in the internal 32-bit format, with a normal range from +-0x800000 to 0x7FFFFF and with each channel in a separate array; duh_render() +will convert to 8 or 16 bits, signed or unsigned, with stereo samples +interleaved, left first. + +When you call duh_render(), pass 8 or 16 for 'bits'. If you pass 8, 'sptr' is +expected to be an array of chars. If you pass 16, 'sptr' is expected to be an +array of shorts. Endianness therefore depends on the platform, and you should +not try to interpret 16-bit wave data as an array of chars (unless you're +writing highly system-specific code anyway). Because DUMB renders internally +with 32 bits, there is no significant speed increase in rendering an 8-bit +stream. + +If you are rendering in stereo, make sure your 'sptr' array is twice as big! + +If you set 'unsign' to a nonzero value, then the samples generated will be +centred on 0x80 or 0x8000, suitably stored in an array of unsigned chars or +unsigned shorts. If 'unsign' is zero, the samples will be centred on 0, +suitably stored in an array of signed chars or signed shorts. Note that 8-bit +WAV files are unsigned while 16-bit WAV files are signed. This convention was +used by the SoundBlaster 16 when receiving samples to be sent to the +speakers. If you wish to write 16-bit sample data to a WAV file, don't use +fwrite(); instead, take the shorts one at a time, split them up into chars as +follows, and write the chars to the file. + + short sptr[n]; + char lsb = (char)sptr[n]; + char msb = (char)(sptr[n] >> 8); + +For a 16-bit WAV file, write the LSB (less significant byte) first. + +The following applies equally to duh_render() and +duh_sigrenderer_get_samples(), except where otherwise stated. + +If you set 'delta' to 1.0f, the sound generated will be suitable for playback +at 65536 Hz. Increasing 'delta' causes the wave to speed up, given a constant +sampling rate for playback. Supposing you want to vary the playback sampling +rate but keep the pitch constant, here's the equation for 'delta': + + delta = 65536.0f / sampling_rate; + +'size' is the number of samples you want rendered. For duh_render(), they +will be rendered into an array which you pass as 'sptr'. Note that stereo +samples count as one; so if you set n_channels to 2, your array must contain +(2 * size) elements. + +For duh_sigrenderer_get_samples() you will have to use the following +functions: + + sample_t **create_sample_buffer(int n_channels, long length); + void destroy_sample_buffer(sample_t **samples); + + void dumb_silence(sample_t *samples, long length); + +create_sample_buffer() allocates the channels sequentially in memory, so the +following technique is valid: + + sample_t **samples = create_sample_buffer(n_channels, length); + dumb_silence(samples[0], n_channels * length); + +It is necessary to fill the buffer with silence like this because +duh_sigrenderer_get_samples() mixes what it renders with the existing +contents of the buffer. + +The return values from duh_render() and duh_sigrenderer_get_samples() tell +you how many samples were actually generated. In most cases, this will be the +same as the 'size' parameter. However, if you reach the end of the DUH (which +will happen if you disable looping or freezing as described further up), this +function will return less. When that happens, you can assume the stream has +finished. In the case of duh_render(), the remainder of the array will not +have been initialised, so you either have to initialise it yourself or avoid +using it. + +If for whatever reason duh_start_sigrenderer() returns NULL, then +duh_render() and duh_sigrenderer_get_samples() will generate exactly 0 +samples, duh_sigrenderer_get_n_channels() will return 0, +duh_sigrenderer_get_position() will return -1, and duh_end_sigrenderer() will +safely do nothing. + + +********************* +*** Miscellaneous *** +********************* + + +Please see dumb.txt for an API reference and for information on thread safety +with DUMB. The API reference has been stripped down, since some functions and +variables are subject to change. If something does not appear in dumb.txt, +please do not use it. + + +****************** +*** Conclusion *** +****************** + + +If you have any difficulties, or if you use DUMB successfully, please don't +hesitate to contact me (see below). + +Enjoy! + + +Ben Davis +entheh@users.sf.net +IRC EFnet #dumb +See readme.txt for details on using IRC. diff --git a/apps/codecs/dumb/docs/modplug.txt b/apps/codecs/dumb/docs/modplug.txt new file mode 100644 index 0000000..a02ddd8 --- /dev/null +++ b/apps/codecs/dumb/docs/modplug.txt @@ -0,0 +1,137 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * modplug.txt - Our official position regarding / / \ \ + * compatibility with ModPlug | < / \_ + * Tracker. | \/ /\ / + * \_ / > / + * | \ / / + * | ' / + * \__/ + */ + + +******************** +*** Introduction *** +******************** + +ModPlug Tracker is a very popular tracker for Windows. Its popularity is due +to the intuitive interface and its many advanced features. The author has +done a good job with this piece of software, but sadly in doing so he has +desecrated the IT file format. + +I am not against ModPlug Tracker being used to write music modules. As +already stated, it has some very advanced and convenient features; I use it +myself. However, I believe its users should be aware of the entire situation +before using it for any serious work. + + ModPlug Tracker - http://www.modplug.com/ + + +************************* +*** Incompatibilities *** +************************* + +There are a few situations in which ModPlug Tracker misinterprets the +original module formats. I shall list the five I am most aware of, from least +to most annoying: + +5. Create a multisample instrument, for example a piano. Play a low note. + Then go up the scale, but in the pattern data, make sure the instrument + column is blank; put in only the notes. Play this with ModPlug Tracker, + and play it with Impulse Tracker. Impulse Tracker changes sample as you go + up the scale; ModPlug Tracker does not. + +4. Arpeggio and Retrigger Note effects behave badly when combined with + Portamento, which can appear in the volume column. While Retrigger Note + isn't too bad, Arpeggio sounds completely wrong. Try it and see what + happens. Then repeat the experiment in Impulse Tracker. + +3. The filter algorithm is incorrect, in more ways than one. When Jeffrey Lim + programmed the low-pass resonant filters into Impulse Tracker, he used a + standard filter algorithm with a slight modification to achieve greater + resonance. ModPlug Tracker does not incorporate this modification. + Furthermore, ModPlug Tracker uses integer arithmetic with nowhere near + enough precision; the wave output is really poor in some cases. I don't + doubt it damages the acoustic properties of the filters in subtle ways. + +2. When looping, ModPlug Tracker resets all variables. The original trackers + do not do this. + +1. Worst of all, ModPlug Tracker has no regard for playback volume, and + generally has a much lower output level than the original trackers. + +Cases 3, 2 and 1 lead people to write IT files that play badly in the +original trackers. If some of these problems could be fixed, I'd be all for +it - but these problems have been reported to the author and he had no +motivation to fix them. ModPlug Tracker has been around long enough that +fixing 3, 2 and 1 would be detrimental to too many people's music. + + +****************** +*** Extensions *** +****************** + +Worse than the incompatibilities are the extensions ModPlug Tracker makes, +mostly to the IT format. DUMB currently supports one of these extensions, +namely stereo samples, but supporting the others is not high on my list of +priorities. + +Other extensions ModPlug Tracker has provided mostly take the form of extra +effects. For instance, S98 and S99 can be used to enable or disable reverb. I +believe the latest versions of ModPlug Tracker offer alternative types of +filter, such as high-pass and band-pass. As soon as an IT file uses any of +these features, it will play incorrectly with Impulse Tracker. + +By far the most evil extension provided by ModPlug Tracker is the effect +plug-ins. These enable IT files to use VST effects. I recently downloaded an +IT file that uses some effects from a collection named "DirectX Media Audio +Effects". When can we expect these effects to be ported to Linux? + + +****************** +*** Conclusion *** +****************** + +ModPlug Tracker is trying to be two things at once. It wants to be an editor +for the existing formats, but at the same time it wants to be proprietary, +with all its own features and extensions. Unfortunately it is succeeding; +there are many IT files out there that only play right in ModPlug Tracker. In +my opinion, ModPlug Tracker should have come out with its own file format, in +which all these extensions would have found a home. + +If you are going to use ModPlug Tracker's extensions, I recommend you +ultimately convert your music to a streamed format such as Ogg Vorbis. (If +you were thinking of using MP3, then don't - consider using Ogg Vorbis +instead.) If you release IT files that use ModPlug Tracker's extensions, +please state prominently that the files are designed to be played with +ModPlug Tracker. Finally, don't ask me to support ModPlug Tracker's +extensions; ModPlug Tracker's playback code is available for use in your +games, so use that instead. + + Ogg Vorbis - http://www.vorbis.com/ + +Despite all the above problems, don't forget that ModPlug Tracker does have a +lot of very useful features for editing files. These include a function for +removing unused patterns, samples and instruments, drag-and-drop sample and +instrument ripping, drop-down menus for selecting the effects by name without +having to memorise the codes or refer to help, and lots of other nice things. +I do recommend it as an editor, provided you make sure you are aware of the +situation and do not use ModPlug Tracker's extensions or incompatibilities +inadvertently. + +Oh, and by the way, save your final version with Impulse Tracker. Then the +samples will be compressed for you! + + +Ben Davis +entheh@users.sf.net +IRC EFnet #dumb +See readme.txt for details on using IRC. diff --git a/apps/codecs/dumb/docs/ptr.txt b/apps/codecs/dumb/docs/ptr.txt new file mode 100644 index 0000000..0eb42cc --- /dev/null +++ b/apps/codecs/dumb/docs/ptr.txt @@ -0,0 +1,129 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * ptr.txt - Pointer explanation. / / \ \ + * | < / \_ + * | \/ /\ / + * \_ / > / + * | \ / / + * | ' / + * \__/ + */ + + +A pointer is a small variable (often the same size as an int BUT NOT ALWAYS) +that holds the address of something in memory. You create a pointer by adding +a * to a variable, as follows: + + int x, *y; + + x = 5; + y = &x; + +The & means 'address of', so &x gives us a pointer to x. We are storing it in +y. + + (*y)++; + +The * here means 'value at'. It's known as the 'dereferencing' operator. When +written before a pointer, as it is here, it allows you to treat the value +like a normal variable. In this case we are incrementing the value. If we +look at x, we'll find that it now contains 6, not 5. + + y++; + +Here we are incrementing the pointer itself. This is useful for traversing +through an array, but in this particular example it is not much use. + + *y++; + +Beware; this will increment the pointer, not the value stored there. It will +return the value stored at the pointer (before incrementing the pointer), so +you can use this in a bigger expression. This is why we needed brackets in +the first example. + +Note that you will not need these three examples when working with DUMB; they +are simply to help illustrate the idea of pointers. + +Also be aware that when defining pointers you attach the * to the variable, +not to the type. The following example will create a pointer and an int, not +two pointers: + + int *a, b; + +That is why I believe it's a good idea to put a space before the * and not +after it, although programmers are divided on this. + + y = 0; + y = NULL; + +These two statements are equivalent. 0, or NULL, is a special value that is +guaranteed to have a different value from any valid pointer. This is most +often used to indicate that something doesn't point anywhere. DUMB's +functions may return it on occasion. However, in simple usage of DUMB, you +will not actually need to check for it. + +Some of DUMB's functions return pointers to structs. (A struct is an +aggregration of other variables, such as ints, pointers, or other structs. +You can generally treat a struct as a single unit.) Here's an example of such +a function: + + DUH *dumb_load_it(const char *filename); + +You do not know what the DUH struct actually contains; dumb.h and aldumb.h +only give the compiler enough information to deal with pointers to them. DUMB +will take charge of everything that happens inside a DUH struct. + +The above function will create a DUH struct for you. First it allocates +the memory it needs, then it fills the struct with data, then it returns a +pointer. This DUH struct will contain the data necessary to play an IT file. +You can define a suitable variable and store the pointer in it as follows: + + DUH *duh = dumb_load_it("music.it"); + +Or this can be split up: + + DUH *duh; + duh = dumb_load_it("music.it"); + +In order to use this DUH struct later, you must pass its pointer to other +functions. To pass the pointer to a function, simply write 'duh' for the +appropriate parameter. + +When you've finished with a DUH struct (this applies equally to the other +structs DUMB deals with), you must pass it to an appropriate function for +freeing up the memory: + + unload_duh(duh); + +After you've done this, the memory will no longer be allocated, and the +pointer will have no meaning. You may wish to set it to NULL at this point +for safety. Alternatively just be sure not to use the present value of the +pointer any more. You can of course assign a new value to the pointer, e.g. +by calling dumb_load_it() again. + +Note the following: + + DUH *duh2 = duh; + +This only duplicates the pointer, not the DUH itself. You still only have one +copy of the DUH. There is no way of duplicating a DUH, short of loading it +twice. This is not a problem, because DUMB can play it 'twice at the same +time' anyway. + +That should be all you need to know about pointers in order to use DUMB. If +there's anything you feel should be explained better here, or anything else +that should be added, please don't hesitate to let me know! + + +Ben Davis +entheh@users.sf.net +IRC EFnet #dumb +See readme.txt for details on using IRC. |