summaryrefslogtreecommitdiff
path: root/plugins/dumb/dumb-kode54/docs
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/dumb/dumb-kode54/docs')
-rw-r--r--plugins/dumb/dumb-kode54/docs/Makefile.am11
-rw-r--r--plugins/dumb/dumb-kode54/docs/deprec.txt281
-rw-r--r--plugins/dumb/dumb-kode54/docs/duhspecs.txt296
-rw-r--r--plugins/dumb/dumb-kode54/docs/dumb.txt1699
-rw-r--r--plugins/dumb/dumb-kode54/docs/dumbfull.txt1717
-rw-r--r--plugins/dumb/dumb-kode54/docs/faq.txt264
-rw-r--r--plugins/dumb/dumb-kode54/docs/fnptr.txt113
-rw-r--r--plugins/dumb/dumb-kode54/docs/howto.txt845
-rw-r--r--plugins/dumb/dumb-kode54/docs/modplug.txt137
-rw-r--r--plugins/dumb/dumb-kode54/docs/ptr.txt129
-rw-r--r--plugins/dumb/dumb-kode54/docs/tutorial.txt94
11 files changed, 5586 insertions, 0 deletions
diff --git a/plugins/dumb/dumb-kode54/docs/Makefile.am b/plugins/dumb/dumb-kode54/docs/Makefile.am
new file mode 100644
index 00000000..86ccd9b8
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/docs/Makefile.am
@@ -0,0 +1,11 @@
+EXTRA_DIST = \
+ deprec.txt \
+ duhspecs.txt \
+ dumb.txt \
+ dumbfull.txt \
+ faq.txt \
+ fnptr.txt \
+ howto.txt \
+ modplug.txt \
+ ptr.txt \
+ tutorial.txt
diff --git a/plugins/dumb/dumb-kode54/docs/deprec.txt b/plugins/dumb/dumb-kode54/docs/deprec.txt
new file mode 100644
index 00000000..6ab7b60a
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/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 deprecated parts of the API. 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/plugins/dumb/dumb-kode54/docs/duhspecs.txt b/plugins/dumb/dumb-kode54/docs/duhspecs.txt
new file mode 100644
index 00000000..43b3d39c
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/docs/duhspecs.txt
@@ -0,0 +1,296 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * duhspecs.txt - DUH File Specifications. / / \ \
+ * | < / \_
+ * Written by entheh, one of the few programmers | \/ /\ /
+ * in existance who can spell correctly. \_ / > /
+ * | \ / /
+ * | ' /
+ * \__/
+ */
+
+Technical Details
+=================
+
+WARNING: until this warning disappears, the DUH file format could change at
+any moment. This should not be of great concern, since DUH files are not
+designed to be edited directly, but will always be generated from some other
+format. However, it is our intention that this warning be removed before the
+first release.
+
+This document is written chiefly in the context of writing a DUH file, since
+the library already contains the necessary functionality to read and play a
+DUH file.
+
+DUH files are currently saved using Allegro's file compression routines. See
+Allegro's documentation and source code for details on this system. If you
+wish to port DUMB away from Allegro and wish to preserve the file compression
+capabilities, you will have to borrow the packfile source code from Allegro.
+
+If you are happy to do away with file compression, please store the following
+four-byte signature before the rest of the file: "slh." Alternatively, write
+your DUH file writer with Allegro, and open the file with F_WRITE_NOPACK.
+This will enable versions of the library using Allegro's file compression
+routines to load the file. If you are reading a DUH file and you detect the
+signature "slh!", then the file is compressed (and is not necessarily a DUH
+file).
+
+All numbers are little-endian unless specified otherwise. Allegro's
+pack_iget*() and pack_iput*() functions can be used to read and write data in
+this format. However, the four-byte signatures can be encoded into long ints
+with AL_ID() and read and written with pack_m*().
+
+
+Overall Structure
+=================
+
+Size Type Value Example C code to save to PACKFILE *f
+
+ 4 ID "DUH!" pack_mputl(AL_ID('D','U','H','!'), f);
+ 4 Int Number of signals pack_iputl(n_signals, f);
+
+For each signal { for (i = 0; i < n_signals; i++) {
+ 4 ID Signal type pack_mputl(AL_ID('S','E','Q','U'), f);
+ * - Signal data write_sequence(f);
+} }
+
+* The size of the data for any signal must either be constant or somehow
+ encoded in the data themselves. The library contains functions to read
+ various standard signal types, including "SAMP" and "SEQU" (sample and
+ sequence respectively), and the formats for these types are laid out
+ further down. If you wish to create your own signals, you must provide your
+ own loading function for the signal. This will be described in more detail
+ in a separate file.
+
+In order to play a DUH file, we simply play the first signal. Signals can
+construct their sound from the samples of other signals, and they in turn can
+use other signals. Thus a recursive structure is built up. Recursive cycles
+are not permitted.
+
+
+Signal: SAMP (Sample)
+=====================
+
+Size Type Value Example C code to save to PACKFILE *f
+
+ 4 Int Size pack_iputl(size, f);
+ 1 Bits Flags pack_putc(flags, f);
+ 1 ID Compression type pack_putc(compress, f); /* NOT IMPLEMENTED YET */
+
+The flags are stored in a bit-field. Bit 0 indicates whether 16-bit samples
+(set) or 8-bit samples (clear) are stored in the file. In both cases, the
+samples are signed. NOTE: this bit might be replaced with a system allowing
+for various sample compression algorithms, or altered so there are different
+signal types for the purpose.
+
+If Bit 1 is set, the sample is a looping sample, and loops indefinitely. In
+this case the loop start point will be saved. The loop end point is not
+saved, and is assumed to be the end of the sample. (When creating DUH files
+from other formats which allow for the loop end to be earlier, you should
+truncate the sample.)
+
+If Bit 1 is not set, then Bit 2 may be set to indicate that the sample is
+looping but only loops a finite number of times before continuing to play
+normally. In this mode, both loop points (start and end) are saved in the
+file. The number of times to loop will be specified on an instance-by-
+instance basis using signal parameter #0, which should be set immediately
+(before any samples are rendered) if it is to be set at all. It defaults to 0
+(so the sample just plays through normally). In fact this parameter's value
+is added to the loop count, but this is immaterial since there is no reason
+to specify it more than once.
+
+If Bit 1 is set, you should make sure Bit 2 is clear to allow for the
+possibility of future expansion.
+
+If Bit 3 is set, a ping-pong loop is used. When the sample reaches the loop
+end point, it starts to play backwards until it reaches the loop start point,
+at which time it will resume forward playback. When using a finite loop,
+every change of direction counts as one iteration. That means an odd loop
+count will cause the sample to proceed backwards when the looping ends.
+
+If neither Bit 1 nor Bit 2 is set, then neither loop point will be saved. In
+this case, you should also make sure Bit 3 is clear for the same reason as
+above.
+
+You may find the following definitions useful:
+
+#define SAMPFLAG_16BIT 1
+#define SAMPFLAG_LOOP 2
+#define SAMPFLAG_XLOOP 4
+#define SAMPFLAG_PINGPONG 8
+
+#define SAMPPARAM_N_LOOPS 0
+
+Size Type Value Example C code to save to PACKFILE *f
+
+ 4 Int Loop start pack_iputl(loop_start, f);
+ 4 Int Loop end pack_iputl(loop_end, f);
+
+For a 16-bit sample: if (flags & SAMPFLAG_16BIT)
+ for (n = 0; n < size; n++)
+ x*2 Int Sample data pack_iputw(sample[n], f);
+For an 8-bit sample: else
+ for (n = 0; n < size; n++)
+ x*1 Int Sample data pack_putc(sample[n], f);
+
+/*
+Compression type is 0 for uncompressed PCM.
+*/
+
+
+Signal: SEQU (Sequence)
+=======================
+
+Size Type Value Example C code to save to PACKFILE *f
+
+ 4 Int Size size = pack_igetl(f);
+ x - Sequencing data pack_fwrite(data, size, f);
+
+The sequence signal provides a medium in which other signals can be played at
+specific times for specific lengths. You can control the pitch, volume and
+other parameters for a signal, and these can change during the signal.
+
+A sequence consists of a series of commands. Each command is preceded by a
+time, which measures how long to wait before executing this command. A time
+of zero indicates that this command is simultaneous with the previous. A time
+of -1 indicates the end of the sequence. Note that signals do not stop
+playing when the end is reached.
+
+All times are measured in units such that 65536 corresponds to one second.
+The timing in DUMB is accurate to the nearest sample, and cannot be offset in
+the way it can with much mixing software, so you can rely on timing to
+achieve certain effects. Resampling should be accurate enough to satisfy the
+most acute musician's ear, but juggling pitches at this level of accuracy
+requires knowledge of temperaments such as many musicians do not have. The
+vast majority of people are satisfied with the even temperament. More on this
+later.
+
+Size Type Value Example C code to save to PACKFILE *f
+
+ 4 Int Time pack_iputl(time, f);
+ 1 ID Command pack_putc(SEQUENCE_START_SIGNAL, f);
+
+/********************************
+ Proposed change:
+ Time is a short, encoded in 2 bytes.
+ The value of 'time' is actually an unsigned offset from the time of the
+ previous command. 0 means at the same time as the last command.
+ If the time in between this signal and the previous one is larger than
+ 65534 ticks, then the value 65535 is written, followed by 4 more bytes (uint)
+ indicating the time offset.
+**********************************/
+
+Here are definitions for the various commands:
+
+#define SEQUENCE_START_SIGNAL 0
+#define SEQUENCE_SET_VOLUME 1
+#define SEQUENCE_SET_PITCH 2
+#define SEQUENCE_SET_PARAMETER 3
+#define SEQUENCE_STOP_SIGNAL 4
+
+Below are the details of what to write after each command code. The various
+fields are explained afterwards.
+
+Size Type Value Example C code to save to PACKFILE *f
+
+SEQUENCE_START_SIGNAL:
+ 1 ID Reference pack_putc(ref, f);
+ 4 Int Signal pack_iputl(signal, f); /* --> Can we drop this to 2 bytes? (65536 signals) */
+ 4 Int Starting position pack_iputl(pos, f);
+ 2 Int Volume pack_iputw(volume, f);
+ 2 Int Pitch pack_iputw(pitch, f);
+
+SEQUENCE_SET_VOLUME:
+ 1 ID Reference pack_putc(ref, f);
+ 2 Int Volume pack_iputw(volume, f);
+
+SEQUENCE_SET_PITCH:
+ 1 ID Reference pack_putc(ref, f);
+ 2 Int Pitch pack_iputw(pitch, f);
+
+SEQUENCE_SET_PARAMETER:
+ 1 ID Reference pack_putc(ref, f);
+ 1 ID Parameter ID pack_putc(id, f);
+ 4 Int Value pack_iputl(value, f);
+
+SEQUENCE_STOP_SIGNAL:
+ 1 ID Reference pack_putc(ref, f);
+
+When you initiate a signal, you must choose a reference number. If you want
+to modify the signal's volume, pitch or parameters, or stop the signal later,
+you must use this reference number to do so. Need more than 256 reference
+numbers? Use two sequences, and get your brain seen to.
+
+If you initiate a new signal with the same reference number, the reference
+will belong to the new signal. The old signal becomes anonymous, and will
+either continue to play indefinitely or stop of its own accord. Even if the
+new signal stops, the old one remains anonymous. DUMB will safely ignore
+operations on reference numbers not used by any signal, or which were used by
+a signal which has now stopped.
+
+Of course all signals will stop if the sequence itself is stopped.
+
+To initiate a signal, you must index the signal. The index is 0-based, so to
+initiate the fifth signal in the file you must specify 4. Out-of-range values
+will be handled safely, as will the case where a signal tries to generate
+itself directly or indirectly from its own samples (a recursive cycle).
+
+When you initiate a signal, you can specify a starting position. This will be
+passed directly to the appropriate signal's start_samples function, so for a
+SAMP (sample) signal it represents the sample on which to start, after any
+loops have been expanded (so you can start on the backwards-playing part of
+a ping-pong loop for example by careful choice of the starting position).
+
+Volume is probably the simplest parameter. It is on a linear scale ranging
+from 0 to 65535. Note that most music sounds more dramatic if the volume
+rises and falls exponentially or on a greater curve. Linear fades are more
+suitable for fading in and out, and do not sound dramatic in the least.
+
+Pitch is specified on what is perceived as a linear scale. It is in fact
+logarithmic, but you will not need to worry about this for most purposes.
+Pitch 0 represents that the sample will be played at 65536 Hz. (This is not
+strictly true, and will be explained further later.) In the likely case that
+your sample is not recorded at 65536 Hz, you will first need to calculate the
+central pitch. Use the following formula:
+
+pitch_centre = 12 * 256 * log(sampling_frequency / 65536.0) / log(2);
+
+If your programming language does not have a log function, look for ln, or
+any function that calculates the logarithm (to any base) of the number you
+give it. If you are lucky enough to find a logarithm to base 2, you can omit
+the final division since the divisor evaluates to 1.
+
+Once you have calculated pitch_centre, you can use it to play the sample at
+the frequency at which it was recorded. Each time you add or subtract 256,
+the sample will increase or decrease respectively in pitch by one semitone in
+the even temperament. (The even temperament was noted further up as being
+suitable for most musical applications.) One octave is represented by an
+interval of 12 * 256.
+
+If you wish to use another temperament, you can calculate the appropriate
+intervals in pitch as follows:
+
+pitch_interval = 12 * 256 * log(ratio) / log(2);
+
+where, for example, ratio = 1.5 for a perfect fifth. An octave is, of course,
+still represented by 12 * 256.
+
+The SEQUENCE_SET_PARAMETER command needs little explanation. Quite simply,
+the parameter ID and value you specify are passed on to the set_parameter
+function of the signal to which this reference belongs. Exactly what this
+does depends on the signal in question.
+
+Remember, a sequence is a signal in itself. Like all signals, it is subject
+to changes in pitch. Increasing the pitch of a sequence will also speed it
+up. This capability is used to allow DUH files to be rendered at different
+sampling frequencies, and it is also available for use by the musician. This
+means that samples are only played at 65536 Hz if the pitch of the sequence
+itself has not been adjusted.
diff --git a/plugins/dumb/dumb-kode54/docs/dumb.txt b/plugins/dumb/dumb-kode54/docs/dumb.txt
new file mode 100644
index 00000000..4f6cc69b
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/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/plugins/dumb/dumb-kode54/docs/dumbfull.txt b/plugins/dumb/dumb-kode54/docs/dumbfull.txt
new file mode 100644
index 00000000..6a8e192c
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/docs/dumbfull.txt
@@ -0,0 +1,1717 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * dumb.txt - DUMB library reference. / / \ \
+ * | < / \_
+ * See readme.txt for general information on | \/ /\ /
+ * DUMB and how to set it up. \_ / > /
+ * | \ / /
+ * If you are new to DUMB, see howto.txt. | ' /
+ * \__/
+ */
+
+
+***********************************
+*** Include Files and Libraries ***
+***********************************
+
+
+dumb.h
+
+ Include this if you only want the core DUMB library functions. You will
+ be able to load music files and render them into memory buffers at your
+ own pace. The core library is completely portable, and as such does not
+ access hardware; you must relay the sound data to the sound card yourself.
+ A stdio file input module is available, but you must actively register it
+ if you wish to use it (see dumb_register_stdfiles()); if you do not
+ register it, it will not be linked into your executable. You must register
+ it in order to load stand-alone music files.
+
+ Optimised: libdumb.a (-ldumb)
+ Debugging: libdumbd.a (-ldumbd)
+
+
+aldumb.h
+
+ Include this if you wish to use DUMB with Allegro. This will provide you
+ with functions to play DUHs back through Allegro's audio streams and embed
+ music files in Allegro datafiles. A file input module using Allegro's
+ packfiles is provided; you have a choice between this and the stdio
+ module. You will be able to load datafiles containing music files no
+ matter which file input module you register, or even if you register no
+ file input module. However, you must register a file input module in order
+ to load stand-alone files.
+
+ Optimised: -laldmb -lalleg -ldumb
+ Debugging: -laldmd -lalld -ldumbd
+
+ libaldmb.a or libaldmd.a must be linked in first, so the symbols can be
+ resolved when linking in the other two libraries.
+
+
+***********************************
+*** Library Clean-up Management ***
+***********************************
+
+
+int dumb_atexit(void (*proc)(void));
+
+ Registers a function to be called at the end of your program. You can
+ register multiple functions to be called, and the one you register last
+ will be called first. If you try to register the same function twice, the
+ second attempt will have no effect.
+
+ See fnptr.txt for help with function pointers.
+
+ You must call dumb_exit() before exiting your program for this to work
+ properly. The library itself registers functions with dumb_atexit(), so it
+ is important to call dumb_exit() even if you do not use dumb_atexit()
+ yourself.
+
+ This function will return zero on success. It will return zero when
+ trying to install the same function twice. If it fails through lack of
+ memory, it will return nonzero. Generally you can ignore the return code;
+ in the worst case some memory will not be freed at the end. If it is
+ crucial that your function be called (e.g. to shut down some hardware or
+ save critical data), then you should call your function manually at the
+ end of the program instead of registering it here - or use the stdlib
+ function atexit(), guaranteed under ANSI C to succeed for at least 32
+ functions.
+
+
+void dumb_exit(void);
+
+ You should call this before exiting your program if you have used any part
+ of DUMB in the program. Some parts of DUMB will allocate memory, and this
+ function will free it all up.
+
+ More specifically, this function will call any functions that have been
+ registered with dumb_atexit(). If a part of DUMB needs shutting down, the
+ shutdown procedure will have been registered in this way.
+
+ dumb_exit() will, of course, also call any functions you registered with
+ dumb_atexit() yourself.
+
+ After a call to dumb_exit(), the list of functions is erased. If you are
+ not ready to exit your program, you can start using DUMB anew as if your
+ program had just started. (Note that not everything will be reset in
+ practice - dumb_resampling_quality will retain whatever you set it to, for
+ example, though you should not assume it will.)
+
+ If you only need to call dumb_exit() once at the end of the program, you
+ can use the following to register dumb_exit() with stdlib.h atexit():
+
+ #include <stdlib.h>
+
+ atexit(&dumb_exit);
+
+ Then dumb_exit() will be called for you when your program exits. This is
+ the recommended method, since it will ensure clean-up even if your program
+ aborts. You should only call dumb_exit() manually if you need to shut DUMB
+ down prematurely, or if atexit() is unavailable for one reason or another.
+
+
+*****************************
+*** Sequential File Input ***
+*****************************
+
+
+ DUMB provides a strictly sequential file input system which uses the
+ DUMBFILE struct. "Strictly sequential" means you cannot seek backwards.
+ However, the system will keep track of how many bytes you have read,
+ enabling you to seek forwards. DUMBFILEs provide a convenient error
+ detection system, so you do not have to check the return value from every
+ function call in the way you do with the ANSI C functions.
+
+ Note that DUMBFILEs cannot be used for output, nor can they be used
+ portably for text files.
+
+ If an error occurs when reading data from a DUMBFILE, the DUMBFILE will
+ become inoperative. All subsequent activities on the DUMBFILE will return
+ error codes without attempting to read from the file. The position in the
+ file will also be forgotten. You can find out if this has happened at any
+ stage with the dumbfile_error() function. You are still required to close
+ the DUMBFILE, and the return value from dumbfile_close() will tell you if
+ an error has occurred.
+
+ This system allows you to input large chunks of your file, neither
+ checking every return value nor wasting time accessing a file that has
+ already experienced an error. However, before you allocate an amount of
+ memory or read in a quantity of data depending on previous input from the
+ file, you should always check that such input was valid. In particular you
+ should passing zero or negative numbers to malloc() or dumbfile_getnc().
+
+ DUMBFILEs can be hooked. In other words, you can specify your own
+ functions to do the work of reading from a file. While DUMB contains two
+ modules for this purpose, it does not set them up for you automatically.
+ In most cases you must register one of these modules yourself, or provide
+ your own module. See register_dumbfile_system(), dumb_register_stdfiles()
+ and dumb_register_packfiles().
+
+
+void register_dumbfile_system(DUMBFILE_SYSTEM *dfs);
+
+ Use this function to register a set of functions for use by the DUMBFILEs
+ (a DUMBFILE system). The DUMBFILE_SYSTEM struct contains the following
+ fields:
+
+ void *(*open)(const char *filename);
+ int (*skip)(void *f, long n);
+ int (*getc)(void *f);
+ long (*getnc)(char *ptr, long n, void *f);
+ void (*close)(void *f);
+
+ See fnptr.txt for help with function pointers such as these.
+
+ Your 'open' function should open the file specified and return a pointer
+ to a struct representing the open file. This pointer will be passed to
+ your other functions as 'f'. Your 'close' function should close the file
+ and free all memory pointed to by 'f'. Note that the 'close' operation
+ should never be able to fail; if you are calling a function with a return
+ value, you can generally ignore it.
+
+ Your 'getc' function should read one byte from the file and return its
+ value in the range 0 to 255. If an error occurs, you should return -1. Do
+ not worry about remembering that an error has occurred; DUMB will do that
+ for you.
+
+ 'skip' is for skipping parts of the file, and should skip n bytes,
+ returning 0 on success or any other number on failure. 'getnc' should read
+ n bytes from the file, store them at 'ptr', and return the number of bytes
+ read (n on success, fewer on failure). However, these two functions are
+ optional, and you should only provide them if the operations can be done
+ more efficiently than with repeated calls to your 'getc' function. If this
+ is not the case, specify NULL for 'skip', 'getnc' or both, and DUMB will
+ use your 'getc' function to do the work.
+
+ Once you have written all your functions, you need to create a
+ DUMBFILE_SYSTEM struct to hold them, and pass its pointer to
+ register_dumbfile_system().
+
+ The DUMBFILE_SYSTEM struct must be permanent. In other words, it must be
+ either global or static, and you should not modify it later. DUMB will not
+ make its own copy.
+
+ You will most likely create your own struct to represent the open file,
+ but do not be tempted to specify that struct in the function prototypes
+ and pacify the compiler warnings by casting your function pointers. There
+ exist computer systems where a (void *) pointer and a (MY_STRUCT *)
+ pointer are represented differently in memory, and a cast of such a
+ pointer causes a tangible conversion to take place. If you cast the
+ function pointers, the computer cannot know when such a conversion is
+ necessary. Instead, use the following structure:
+
+ int myskip(void *f, long n)
+ {
+ FILE *file = f;
+ /* Do some stuff with 'file' */
+ return something;
+ }
+
+ If you need examples, have a look at the two existing DUMBFILE systems in
+ dumb/src/core/stdfile.c and dumb/src/allegro/packfile.c.
+
+
+DUMBFILE *dumbfile_open(const char *filename);
+
+ Open the specified file for input. You must pass the DUMBFILE pointer
+ whenever you wish to operate on this file. When you have finished with the
+ file, you must pass it to dumbfile_close().
+
+ Before you use this function, make sure you have registered a DUMBFILE
+ system. See register_dumbfile_system(), dumb_register_stdfiles() and
+ dumb_register_packfiles().
+
+
+DUMBFILE *dumbfile_open_ex(void *file, DUMBFILE_SYSTEM *dfs);
+
+ This function is provided for more specialised use. You should create a
+ DUMBFILE_SYSTEM specially for the purpose. Its 'open' field is irrelevant;
+ for neatness, set it to NULL, unless you are using this DUMBFILE_SYSTEM
+ with register_dumbfile_system() as well.
+
+ When you have called this function, the DUMBFILE struct it returned can be
+ used as normal. The specified DUMBFILE_SYSTEM will be used for all input,
+ with 'file' passed to your 'skip', 'getc' and 'getnc' functions as 'f'.
+ This can be used, for example, to read from an already open file.
+
+ Note that the position will always be initialised to 0 for this DUMBFILE.
+ This means for example that offsets in the file do not need adjusting when
+ embedding data in a larger file.
+
+ There are two ways to use this function. If you want 'file' to persist
+ after using a DUMBFILE returned by this function, you should make sure the
+ 'close' field in the DUMBFILE is set to NULL. When the DUMBFILE is closed,
+ 'file' will be left alone, and you can and should deal with it yourself
+ when the DUMBFILE has been closed.
+
+ Alternatively, you can provide a 'close' function to get rid of 'file' for
+ you when the DUMBFILE is closed. If you do this, you should not otherwise
+ use 'file' after a call to this function.
+
+ If dumbfile_open_ex() has to return NULL, owing to lack of memory, then
+ your 'close' function will be called if provided. In other words, if you
+ have provided a 'close' function, then you no longer need to worry about
+ 'file' whether this function succeeds or not.
+
+ See dumb/src/helpers/stdfile.c and dumb/src/allegro/packfile.c for
+ examples of how to use this function. Neither provides a 'close' function,
+ so I hope my explanation here will suffice. If not, please feel free to
+ contact me so I can make the explanation clearer and help you do what you
+ want to do. Contact details are at the end of this file.
+
+
+long dumbfile_pos(DUMBFILE *f);
+
+ Returns the number of bytes read from the DUMBFILE (or skipped) since it
+ was opened, or -1 if an error has occurred while reading.
+
+
+int dumbfile_skip(DUMBFILE *f, long n);
+
+ Skips n bytes of the specified DUMBFILE. Returns zero on success.
+
+
+int dumbfile_getc(DUMBFILE *f);
+
+ Reads one byte from the DUMBFILE and returns it in unsigned format (from 0
+ to 255). If an error occurs, or occurred before, this function returns -1.
+
+
+int dumbfile_igetw(DUMBFILE *f);
+
+ Reads two bytes from the DUMBFILE and combines them into a word ranging
+ from 0 to 65535. The first byte read is the least significant byte, as
+ with Intel processors. This function returns -1 on error.
+
+
+int dumbfile_mgetw(DUMBFILE *f);
+
+ Reads two bytes from the DUMBFILE and combines them into a word ranging
+ from 0 to 65535. The first byte read is the most significant byte, as
+ with the Apple Macintosh. This function returns -1 on error.
+
+
+long dumbfile_igetl(DUMBFILE *f);
+
+ Reads four bytes from the DUMBFILE and combines them into a long integer
+ ranging from -2147483648 to 2147483647. The first byte read is the least
+ significant byte, as with Intel processors. This function returns -1 on
+ error, but -1 is also a valid return value. After a call to this function,
+ you can use dumbfile_error() to find out if an error occurred.
+
+
+long dumbfile_mgetl(DUMBFILE *f);
+
+ Reads four bytes from the DUMBFILE and combines them into a long integer
+ ranging from -2147483648 to 2147483647. The first byte read is the most
+ significant byte, as with the Apple Macintosh. This function returns -1 on
+ error, but -1 is also a valid return value. After a call to this function,
+ you can use dumbfile_error() to find out if an error occurred.
+
+
+unsigned long dumbfile_cgetul(DUMBFILE *f);
+
+ Reads an unsigned (nonnegative) integer from the DUMBFILE. The integer is
+ stored in a condensed format where smaller numbers use less space:
+
+ 0 to 127 1 byte
+ 128 to 16383 2 bytes
+ 16384 to 2097151 3 bytes
+ 2097152 to 268435455 4 bytes
+ 268435456 to 4294967295 5 bytes
+
+ This format is the same as that used for the times between notes in MIDI
+ files.
+
+ If an error occurs, this function returns (unsigned long)(-1), but that
+ may be a valid return value. After a call to this function, you can use
+ dumbfile_error() to find out if an error occurred.
+
+
+signed long dumbfile_cgetsl(DUMBFILE *f);
+
+ Reads a signed integer from the DUMBFILE. The integer is stored in a
+ condensed format where numbers closer to zero use less space:
+
+ -64 to 63 1 byte
+ -8192 to 8191 2 bytes
+ -1048576 to 1048575 3 bytes
+ -134217728 to 134217727 4 bytes
+ -2147483648 to 2147483647 5 bytes
+
+ If an error occurs, this function returns -1, but -1 is also a valid
+ return value. After a call to this function, you can use dumbfile_error()
+ to find out if an error occurred.
+
+
+long dumbfile_getnc(char *ptr, long n, DUMBFILE *f);
+
+ Reads n bytes from the DUMBFILE and stores them at 'ptr'. Note that the
+ pointer is to a series of chars. You may also use this function to read in
+ a series of signed chars or unsigned chars (which are both officially
+ distinct types from char), but do not use this to read ints, structs or
+ any other data type from the file. Integers must be read one at a time
+ using dumbfile_igetl(), dumbfile_cgetul(), etc. To load a struct in, you
+ must read each field separately using an appropriate function for each
+ one. For complicated datatypes, you can simplify this process by writing a
+ function for each struct.
+
+
+int dumbfile_error(DUMBFILE *f);
+
+ This function returns -1 if an error has occurred with the specified
+ DUMBFILE, or 0 if all is well.
+
+
+int dumbfile_close(DUMBFILE *f);
+
+ This function closes the DUMBFILE, after which the pointer will be
+ invalid. dumbfile_close() returns the value that dumbfile_error() would
+ have returned, which is -1 if an error occurred while reading or 0
+ otherwise. Regardless of the return value, the file will always be closed
+ properly.
+
+
+*******************************
+*** stdio File Input Module ***
+*******************************
+
+
+void dumb_register_stdfiles(void);
+
+ This function registers the stdio file input module for use by DUMBFILEs.
+ FILE structs and their corresponding functions, as defined by the ANSI C
+ header stdio.h, will be used internally for all DUMBFILE input (unless
+ opened with dumbfile_open_ex()).
+
+ This must be called before dumbfile_open() is used, or else an alternative
+ system must be registered (see register_dumbfile_system() and
+ dumb_register_packfiles()).
+
+
+DUMBFILE *dumbfile_open_stdfile(FILE *p);
+
+ If you have a stdio FILE struct representing an open file, you can call
+ this if you wish to read from it using a DUMBFILE. This is useful when you
+ need to pass a DUMBFILE struct to a library function, to read an embedded
+ music file for example. When you close the DUMBFILE, you can continue
+ using the FILE struct to read what follows the embedded data.
+
+
+********************************
+*** Signal Type Registration ***
+********************************
+
+
+
+
+
+
+ NOTE: SINCE THIS IS NOT TO BE THE INTRODUCTION TO DUMB, SHOULD THE
+ FOLLOWING INFORMATION BE MOVED TO ANOTHER FILE?
+
+
+
+
+
+
+ If you are lazy, then don't bother to read this section. Simply follow
+ these steps:
+
+ 1. Call all the dumb_register_sigtype_*() functions. You must do this
+ after dumb_init(), but before you try to call load_duh(). If you try
+ to load any DUH files beforehand, the library will fail to load
+ them.
+
+ 2. Run your program, and make sure the DUH file was successfully loaded
+ and played.
+
+ 3. Comment out the first dumb_register_sigtype_*() function call.
+
+ 3.1. If the DUH file is still loaded and played, remove this function
+ call.
+ 3.2. If the DUH file was not loaded and played, uncomment and keep this
+ function call.
+
+ 4. Repeat Step 3 for the other function calls.
+
+ Alternatively, the musician might have told you which of the functions you
+ need to call.
+
+ If you are the epitome of laziness, stop after Step 1. However, if you do
+ this, your executable may contain unnecessary code.
+
+ If, on the other hand, you are interested in how all this works, then read
+ on.
+
+ A DUH file comprises a number of 'signals'. A signal may be thought of as
+ a 'black box'; commands go in, sound comes out. A signal may in turn call
+ upon other signals to generate sound, and then do something with this
+ sound to generate its own output.
+
+ For example, here are the contents of a simple DUH file:
+ Signal #0 SEQU
+ Signal #1 SAMP
+ Signal #2 SAMP
+ Signal #3 SAMP
+
+ 'SEQU' and 'SAMP' are the signal types. 'SAMP' designates a sample, so
+ Signals #1, #2 and #3 are simple recordings. For instance, Signal #1 could
+ be a bass drum, Signal #2 a snare drum and Signal #3 a high hat. When
+ invoked, these signals simply output their respective percussion sounds.
+
+ 'SEQU' designates a sequence. Signal #0 will therefore generate its sound
+ by taking the output waveforms from the other signals and mixing them in
+ at appropriate times. For example:
+
+ 0.00 Signal #1
+
+ 0.50 Signal #2
+
+ 1.00 Signal #1
+ 1.25 Signal #3
+ 1.50 Signal #2 Signal #3
+ 1.75 Signal #3
+ 2.00 Signal #1
+ 2.25 Signal #3
+ 2.50 Signal #2 Signal #3
+ 2.75 Signal #3
+ 3.00 Signal #1
+
+ 3.50 Signal #2 Signal #3
+
+ The numbers down the left are times. Those with a good sense of rhythm
+ will be able to see that this represents a rather lame, highly cheesy pop
+ beat. Experienced gurus will also realise how painfully slow the beat is,
+ given that the times are in seconds. But enough of that.
+
+ A DUH is played by taking the output from Signal #0 and sending it through
+ to the sound card via an Allegro audio stream. If you do not know what an
+ audio stream is, suffice it to say that you must call Allegro's
+ install_sound() function, and an audio stream uses one voice (hence one
+ voice per DUH file). If you still don't know what I'm on about, read up on
+ Allegro's digital sound system and then come back here. Alternatively,
+ read porting.txt for details on how to use DUMB without Allegro.
+
+ In reality, DUH files are likely to be much more complicated than the
+ above example. Sequences may call upon other 'sub-sequences' to generate
+ their sound, so the above beat might be used repeatedly by a
+ 'super-sequence' to generate a more complex (but still lame) piece of
+ music.
+
+ The DUH player library is split into two main parts - the core and the
+ basic signal types. The core is very simple. It does not know what 'SAMP'
+ or 'SEQU' mean. In fact it is not familiar with any signal type. It only
+ contains generic code for dealing with signals and generating output. By
+ itself, it cannot load or play any DUH file.
+
+ In addition to the core, the player library comprises a few basic signal
+ types, including 'SAMP' and 'SEQU'. In order to use a signal type, you
+ must 'register' this signal type by calling its corresponding
+ dumb_register_sigtype_*() function. These functions are listed below,
+ along with descriptions of the signals.
+
+ If you do not register a signal type, the code for that signal type will
+ not be linked into your executable file. That means DUMB will not become
+ bloated with age; new features will not add to the size of your executable
+ unless you use them. Dynamically linked libraries will still increase in
+ size.
+
+ If you try to load a DUH file that uses a signal type you haven't
+ registered, the library will not crash. It will simply fail to load the
+ DUH file. In fact, an unrecognised or corrupt signal will prevent the
+ library from reading the rest of the file - but remember, DUH files are
+ always (intended to be) generated from other formats, so you will never
+ lose any data this way.
+
+ If you are unsure which signal types a DUH uses, get in contact with the
+ author of the DUH file, or register them all and remove them one by one to
+ find out which are required (as described in the steps for lazy people at
+ the start of this section).
+
+ The great advantage of DUMB over other music systems is that it enables
+ you to create your own signals. Filters, synthesisers and compression
+ algorithms can all be set up this way. Programming and audio experience
+ are useful, but the editor provides a way to create your own signals even
+ if you don't know C. More information is available in the on-line help in
+ the editor.
+
+ If you are interested in the gory details of how a signal works, or if you
+ wish to brave creating your own without the help of the editor, then see
+ the section entitled Signal Design.
+
+
+void dumb_register_sigtype_sample(void);
+
+ Registers the sample signal type ('SAMP'). This signal deals with samples,
+ or recordings, in the DUH file. All samples are monaural; a combining
+ signal can be used to combine two of these into a stereo sample. The DUMB
+ library will cater for more than two channels to a certain extent, but it
+ is currently limited by Allegro's audio streams.
+
+
+void dumb_register_sigtype_combining(void);
+
+ Registers the combining signal type ('COMB'). This signal takes a set of
+ monaural signals and combines them into one signal with multiple channels.
+ Stereo samples can be set up this way.
+
+
+void dumb_register_sigtype_stereopan(void);
+
+ Registers the stereo pan signal type ('SPAN'). This signal takes a
+ monaural sample and sources it at the stereo position you specify,
+ provided the DUH is played in stereo (or, more precisely, provided stereo
+ output is requested of the signal).
+
+
+void dumb_register_sigtype_sequence(void);
+
+ Registers the sequence signal type ('SEQU'). This signal sequences the
+ sound from other signals. It is what turns a random collection of
+ recordings of single notes into a complete piece of music.
+
+
+**********************
+*** DUH Management ***
+**********************
+
+
+DUH *load_duh(const char *filename);
+
+ Loads a .duh file. Before you call this, make sure you have registered all
+ the necessary signal types (see 'Signal Registration'). The DUH is
+ returned to you; you will need to pass it to various functions. When you
+ have finished with it, call unload_duh() to remove it from memory.
+
+ There is no special need to check that this function succeeds. All other
+ functions can safely be called with a null pointer, which means your music
+ will simply not play if it could not be loaded.
+
+
+DUH *read_duh(DUMBFILE *f);
+
+ Reads a DUH from an already open file, and leaves the file open so you can
+ read subsequent data from the file if you wish. Otherwise this function is
+ identical to load_duh().
+
+
+void unload_duh(DUH *duh);
+
+ Removes a DUH from memory. You must call this for all DUHs you load,
+ making sure they're not playing at the time.
+
+
+long duh_get_length(DUH *duh);
+
+ Returns the length of a DUH; 65536 represents one second. This value is
+ simply lifted from the DUH struct, so it may not truly correspond to the
+ time for which the DUH will generate sound. However, if the musician is
+ any good - or if the code that calculated this value is written properly -
+ you can assume it represents the point at which the DUH first loops, or
+ else it allows time for any final flourish to be appreciated. It is used
+ by the Winamp plug-in to decide when to stop.
+
+
+*******************************
+*** Impulse Tracker Support ***
+*******************************
+
+
+int dumb_it_max_to_mix;
+
+ Specifies the maximum number of samples DUMB will mix at any one time.
+ The default number is 64. Regardless of this value, all samples will
+ continue to be processed up to an internal maximum of 128 (slightly
+ simplified), and cut samples will sound again as soon as the congestion
+ clears. Samples are prioritised by final volume, after all factors
+ affecting the volume of a sample have been considered.
+
+ If you play two or more IT files at once, this value represents the
+ maximum number of samples for each one. You will have to reduce it further
+ if your computer cannot keep up.
+
+
+DUH *dumb_load_it(const char *filename);
+
+ Loads the specified Impulse Tracker file, encapsulating it in a DUH
+ struct. No signal types need be registered for this to work. The length
+ will be set to the point at which the music first loops (see
+ duh_get_length()).
+
+ Once the file is loaded, it can be treated exactly the same as any other
+ DUH in memory.
+
+
+DUH *dumb_read_it(DUMBFILE *f);
+
+ Reads an Impulse Tracker file from an already open DUMBFILE. This leaves
+ the DUMBFILE open, but the DUMBFILE may not be positioned at the end of
+ the IT data. If you are embedding an IT in another file, you are advised
+ to store the size of the IT file and make up for it at the end using
+ dumbfile_pos().
+
+ Otherwise, this function is identical to dumb_load_it().
+
+
+*******************************
+*** DUH Rendering Functions ***
+*******************************
+
+
+ Use these functions to generate samples from a DUH. First you call
+ duh_start_renderer() with the DUH, the number of channels you want and the
+ position at which you want to start. Then you use duh_render() to generate
+ the samples. You can call duh_render() as many times as you like, and it
+ will generate as many or as few samples as you require. When you have
+ finished, call duh_end_renderer().
+
+
+DUH_RENDERER *duh_start_renderer(DUH *duh, int n_channels, long pos);
+
+ Starts a DUH_RENDERER off. This is the struct you can use to get samples
+ from a DUH. This function does not generate any samples; you must pass the
+ struct to duh_render() for that. When you have finished with it, you must
+ pass it to duh_end_renderer(). You can use as many DUH_RENDERER structs as
+ you like at the same time.
+
+ Currently, n_channels can only be 1 or 2, for monaural and stereo sound
+ respectively. The debugging library will cause your program to abort if
+ you pass anything else. Future versions will be enhanced to support more
+ channels as soon as someone needs them.
+
+ When specifying the position, 0 represents the start of the DUH, and 65536
+ represents one second. Unlike most other music systems, DUMB will always
+ make sure every note is there right from the start (provided any custom
+ signal types are properly designed). In other words, you can start a DUH
+ at a point halfway through a long note, and you will still hear the long
+ note.
+
+
+long duh_render(
+ DUH_RENDERER *dr,
+ int bits, int unsign,
+ float volume, float delta,
+ long size, void *sptr
+);
+
+ Generates some samples. Pass the DUH_RENDERER as returned by
+ duh_start_renderer(). Pass the number of bits, which should be 8 or 16. If
+ unsign is nonzero, the samples will be unsigned (centred on 0x80 or 0x8000
+ for 8 bits and 16 bits respectively). If unsign is zero, the samples will
+ be signed.
+
+ Allegro's audio streams always take unsigned samples. 8-bit .wav files
+ always take unsigned samples. 16-bit .wav files always take signed
+ samples.
+
+ The volume is a float. 1.0f is the pseudo-maximum. If you pass 1.0f, any
+ properly designed DUH will play nice and loud, but will not clip. You can
+ pass a greater volume if you like, but be prepared for clipping to occur.
+ Of course you can pass smaller values to play the DUH more quietly, and
+ this will also resolve clipping issues in badly designed DUHs.
+
+ Use delta to control the speed of the output signal. If you pass 1.0f, the
+ resultant signal will be suitable for a 65536-Hz sampling rate (which
+ isn't a commonly used rate). The most common sampling rates are 11025 Hz,
+ 22050 Hz, 44100 Hz and 48000 Hz. You can work out the required delta value
+ as follows:
+
+ delta = 65536.0f / sampling_rate;
+
+ If you then increase this value, the DUH will speed up and increase in
+ pitch. If you decrease it, the DUH will slow down and decrease in pitch.
+
+ This function will attempt to render 'size' samples. In most cases it will
+ succeed. However, if the end of the DUH is reached, it may render fewer.
+ The number of samples rendered will be returned. Therefore, if the return
+ value is less than the value of 'size' passed, you know the DUH has
+ finished. It is safe to continue calling duh_render() if you wish, and it
+ will continually return 0. However, if you wish to do this, you will
+ probably have to fill the rest of the buffer with silence, which is 0 for
+ signed, 0x80 for 8-bit unsigned or 0x8000 for 16-bit unsigned.
+
+ The samples will be placed at sptr. Use an array of chars for 8 bits or an
+ array of shorts for 16 bits. Stereo samples will be interleaved, left
+ first. Your array should contain at least (size * n_channels) elements of
+ the appropriate bit resolution.
+
+ From an aesthetic standpoint if nothing else, it is wise to use the C
+ qualifiers 'signed' or 'unsigned' depending on whether the samples are
+ signed or unsigned. This is also convenient if you wish to process the
+ samples further yourself.
+
+
+long duh_renderer_get_position(DUH_RENDERER *dr);
+
+ Tells you what position a DUH_RENDERER is up to, or -1 if it is invalid
+ (perhaps owing to lack of memory). As usual, 65536 is one second. Note
+ that this is a whole number, whereas a fractional part is stored
+ internally; the sample will not be continuous if you terminate the
+ DUH_RENDERER and then reinitiate it with the same position.
+
+
+void duh_end_renderer(DUH_RENDERER *dr);
+
+ Terminates a DUH_RENDERER. Be sure to call this when you've finished with
+ one.
+
+
+***********************************
+*** Signal Design Helper Values ***
+***********************************
+
+
+DUMB_SEMITONE_BASE
+
+ When a 'delta' value is required, you can use DUMB_SEMITONE_BASE to
+ control the pitch. Use pow(DUMB_SEMITONE_BASE, n) to transpose up by n
+ semitones. To transpose down, use negative n.
+
+
+DUMB_QUARTERTONE_BASE
+
+ When a 'delta' value is required, you can use DUMB_QUARTERTONE_BASE to
+ control the pitch. Use pow(DUMB_QUARTERTONE_BASE, n) to transpose up by n
+ quartertones. To transpose down, use negative n.
+
+
+DUMB_PITCH_BASE
+
+ When a 'delta' value is required, you can use DUMB_PITCH_BASE to control
+ the pitch accurately. Use pow(DUMB_PITCH_BASE, n) to transpose up by n
+ units, where 256 units represent one semitone. This scale is used for the
+ 'pitch' in a sequence (SEQU).
+
+
+************************************
+*** Signal Design Function Types ***
+************************************
+
+
+ In order to design your own signal type, you will have to write six
+ functions to be linked into your program. They are described below.
+
+ See fnptr.txt for help with function pointer types such as these.
+
+
+typedef void *(*DUH_LOAD_SIGNAL)(DUH *duh, DUMBFILE *file);
+
+ Write a function conforming to this type which loads or creates all the
+ data required by your signal. You may use this, for instance, to load a
+ sample. You will be reading directly from the DUH file, so make sure you
+ read exactly the quantity of data stored in the file for this signal. You
+ should allocate memory (multiple blocks if you like), and return a pointer
+ referencing all these data. Your data will be stored, and provided
+ whenever this signal is handled. Be careful about using global data, since
+ your load_signal function will be called more than once for different
+ signals of the same type (e.g. for signals which are both samples but are
+ different samples).
+
+ On failure you should return NULL. Please take the possibility of failed
+ memory allocation seriously, especially when loading large quantities of
+ data. You should make sure all allocated memory is freed before you return
+ with failure. See the standard signal types for examples of how to do this
+ elegantly using your unload_signal function. On failure, you need not
+ worry about reading the right quantity of data from the file.
+
+ Do not close the file under any circumstances.
+
+ Often you will write this function before you have written any code to
+ write a DUH file using this signal. If this is the case, don't worry; you
+ can write this function free-style, and then the function itself will
+ serve as a file format reference when you come to create the file.
+
+ Note that a null return value will always be interpreted as failure. If
+ this type of signal does not need to load any data, you should not provide
+ a load_signal function at all. The absence of this function will convey
+ the intended message to the library.
+
+
+typedef void *(*DUH_START_SAMPLES)(
+ DUH *duh,
+ void *signal,
+ int n_channels,
+ long pos
+);
+
+ Write a function conforming to this type. Every time your signal is
+ required to produce some output, this will be called first. The 'signal'
+ parameter is the same one you returned from your load_signal function. The
+ 'n_channels' parameter specifies how many channels you will need to render
+ sound in. In general you should support one or two, although sometimes
+ only one is necessary, depending on how your signal is used. If you can
+ write generic code to support more channels, then do so. Store the number
+ of channels for use later, unless you're only permitting one value.
+
+ If your signal does not support more than two channels, you are
+ responsible for making sure it is never invoked with more than two;
+ likewise if your signal only supports one, then make sure it is never
+ invoked with more than one. In general you only need to make sure the DUH
+ file is never rendered with too many channels (see duh_start_renderer()
+ and al_start_duh()). It may be prudent to use Allegro's ASSERT() macro to
+ catch bugs of this kind.
+
+ The 'pos' parameter specifies where to start, 65536 being one second into
+ the signal. Even if you do not intend to start in the middle of the signal
+ yourself, you should support this as it will be used when a DUH is not
+ played from the beginning.
+
+ This function should make the necessary preparations in order to render a
+ string of samples. All your preparations should be stored in allocated
+ memory, to which you return a pointer; this allows several instances of
+ the same signal to play at the same time. You will not be given the 'duh'
+ and 'signal' parameters again, so you must store them if you need them in
+ the render_samples or free_samples functions.
+
+ Once again, on the rare occasions on which you do not need such data (e.g.
+ a white noise signal), you should not provide a start_samples function. A
+ null return code is interpreted as failure, and your music will be lacking
+ notes (which is better than crashing on undetected memory allocation
+ failure).
+
+
+typedef void (*DUH_SET_PARAMETER)(
+ void *sampinfo,
+ unsigned char id, long value
+);
+
+ Some signals can take parameters. For example, a stereo pan signal allows
+ you to specify the position at which to source the sample, and you might
+ want to choose the cut-off frequency for a low-pass filter. Such
+ parameters are set on an instance-by-instance basis, not globally.
+ Therefore, if you have any parameters, you should place default values for
+ them in the data you initialise in start_samples. Then you should write a
+ function conforming to this type, and have it change the parameters when
+ the correct IDs are passed (see below).
+
+ Each of your parameters should be identified by a single byte, which will
+ be passed in the 'id' parameter. When one of your parameters is correctly
+ identified by id, you should change the parameter's value in the data you
+ returned from your start_samples function. These data are available via
+ the 'sampinfo' parameter. If you do not recognise the ID passed, you may
+ wish to log the situation using Allegro's TRACE() macro (which will
+ compile away to nothing unless you define DEBUGMODE), since it signifies a
+ fault in the DUH file.
+
+ Take some care in deciding what should be a parameter and what should be
+ arranged through the use of separate signals (of the same type). For
+ example, if you are doing a low-pass filter, you will need a source signal
+ on which to apply the filter; this signal's index should be loaded by the
+ load_signal function, so you will create separate filter signals for the
+ separate source signals. However, the cut-off frequency for the filter is
+ more suitably stored in a parameter. Here are some guidelines:
+
+ Continuous values, or discrete quantities, can be stored in parameters.
+ Examples: filter cut-off, echo time, stereo pan position.
+
+ Discrete indices or references should be loaded by load_signal.
+ Examples: references to other signals.
+
+ Another way of looking at it is as follows:
+
+ If a value could be changed halfway through playing the signal, then
+ consider storing it in a parameter.
+
+ If a value needs to be known by the start_samples function and cannot
+ change later, then consider loading it in the load_signal function.
+
+ If your signal has no parameters, do not provide a set_parameters
+ function. Attempts to change parameters for this signal will not cause a
+ crash, but will cause the operation to be logged using Allegro's TRACE()
+ macro if the debugging library is used to play the DUH.
+
+
+typedef long (*DUH_RENDER_SAMPLES)(
+ void *sampinfo,
+ float volume, float delta,
+ long size, sample_t **samples
+);
+
+ Write a function conforming to this type. It should render a series of
+ samples into the array of buffers provided (see below). You are passed
+ 'sampinfo' as returned by start_samples. The 'samples' parameter points to
+ an array of buffers, one for each channel. You can get a pointer to the
+ buffer for channel #n with:
+
+ sample_t *buffer = samples[n];
+
+ As you can see, samples are of type sample_t, which is typedeffed as a
+ signed int (32 bits). Your waveform should be centred on 0 and should peak
+ at no more than +/- 32768, or +/- 0x8000, given a volume of 1.0f. If your
+ waveform goes higher, it will be clipped later on (you do not need to test
+ for this yourself). Please do not read any special meaning into the volume
+ parameter; it is simply a factor to be multiplied into each sample. If you
+ wish to adjust the tone, use a parameter (see DUH_SET_PARAMETER).
+
+ In this function, you should render 'size' samples into the buffer for
+ each channel. Return the number of samples actually rendered, which will
+ be fewer than 'size' if you run out of samples (e.g. if you reach the end
+ of a non-looping sample). Never stop short unless the signal ends, and do
+ not overrun under any circumstances. Update the 'sampinfo' data so that
+ the samples generated by the next call to render_samples will continue
+ seamlessly from the samples generated this time.
+
+ The delta parameter governs the speed at which your signal will play back.
+ Higher values of delta should cause the speed and pitch to increase, as
+ with duh_render(). In general, if delta is 1.0f then your waveform should
+ be suitable for playback at a sampling rate of 65536 Hz. If you wish to
+ bend this rule, then be careful - usually it is wiser to adjust your
+ thinking in other parts.
+
+ For example, consider a sample recorded at 44100 Hz, stored in a sample
+ signal. If a delta of 1.0f is passed to this signal's render function, the
+ output will be exactly the same as the original sample - a signal at
+ 44100 Hz. So it breaks the rule.
+
+ Ah, but it doesn't. Instead, it is wiser to consider the sample stored in
+ the DUH file as being sampled at 65536 Hz. Once we consider this, it is
+ clear that the output is suitable for playback at 65536 Hz, so we do not
+ break the rule. Now it is a simple matter of adjusting all the pitches in
+ the sequence to compensate for this. See duhtech.txt for details on how to
+ do this when you are writing the DUH file.
+
+ Note that the position that was passed to the start_samples function is
+ 65536 for one second into the signal as played with delta 1.0f. So, with a
+ sample signal, this position is interpreted as the index of the sample on
+ which to start. If delta is 2.0, only half a second of the resultant
+ output will have been skipped.
+
+ Once again, please do not read any special meaning into the delta
+ parameter. The effect of doubling delta should be virtually the same as
+ the effect of not doubling delta but resampling the output. If you wish to
+ adjust the tone for different pitches, use a parameter.
+
+ This function is compulsory. Silent signals only increase processor and
+ memory usage, and we are not Microsoft-Intel evangelists.
+
+
+typedef void (*DUH_END_SAMPLES)(void *sampinfo);
+
+ Write a function conforming to this type. It will be called when a signal
+ stops playing. The parameter points to the data you returned from the
+ start_samples function, and here you should simply free the memory up.
+
+ This function should be provided, always if, and only if, start_samples is
+ present. The debugging library will check you get this right.
+
+
+typedef void (*DUH_UNLOAD_SIGNAL)(void *signal);
+
+ Write a function conforming to this type. It will be called when the DUH
+ is removed from memory. It should free all memory allocated by your
+ load_signal function. The parameter is the same pointer that you returned
+ from load_signal.
+
+ If you only ever use this signal type with make_duh(), and not with
+ dumb_register_sigtype(), then the following does not apply.
+
+ If load_signal is present, unload_signal should also be present; if you
+ did not provide a load_signal function, then you should not provide an
+ unload_signal function either. The debugging library will check you get
+ this right.
+
+
+**********************************
+*** Signal Design Registration ***
+**********************************
+
+
+void dumb_register_sigtype(DUH_SIGTYPE_DESC *desc);
+
+ When you have written all the functions that represent your signal, you
+ will need to register them with the library before it will be able to load
+ your DUH file. Use this function. The DUH_SIGTYPE_DESC struct contains the
+ following fields:
+
+ long type;
+ DUH_LOAD_SIGNAL load_signal;
+ DUH_START_SAMPLES start_samples;
+ DUH_SET_PARAMETER set_parameter;
+ DUH_RENDER_SAMPLES render_samples;
+ DUH_END_SAMPLES end_samples;
+ DUH_UNLOAD_SIGNAL unload_signal;
+
+ You need to create a DUH_SIGTYPE_DESC struct and pass its pointer to
+ dumb_register_sigtype(). The struct must be in permanent memory. In other
+ words, it must be either global or static, and you should not modify it
+ later. DUMB will not make its own copy.
+
+ 'type' should be a four-character string encoded with DUMB_ID(), for
+ example DUMB_ID('M','E','O','W'). By convention it should be upper case,
+ and padded with spaces if you do not use all four characters. However, you
+ do not have to stick to this.
+
+ If you are not providing a function, specify NULL for the corresponding
+ function pointer.
+
+
+**********************************
+*** Signal Rendering Functions ***
+**********************************
+
+
+ When you are designing your own signals, you will often want to retrieve
+ samples from another signal in the DUH. This signal's index ('sig') should
+ be loaded by your load_samples function. You can use the following
+ functions to obtain the samples.
+
+
+DUH_SIGNAL_SAMPINFO *duh_signal_start_samples(
+ DUH *duh, int sig, int n_channels, long pos
+);
+
+ Specifies where you want to start rendering. This function returns a
+ DUH_SIGNAL_SAMPINFO struct, which you need to pass to the other functions.
+ You can use as many DUH_SIGNAL_SAMPINFOs at once as you like.
+
+ Pass the DUH and the index of the signal whose samples you want to obtain
+ ('sig'). Specify how many channels you want, and where you want to start
+ rendering (65536 represents one second).
+
+ There is no special need to check that this function succeeds. The other
+ functions are safe to call with null pointers. However, checking the
+ return value can make your code more efficient.
+
+ Be sure to call duh_signal_end_samples() when you've finished.
+
+
+void duh_signal_set_parameter(
+ DUH_SIGNAL_SAMPINFO *signal_sampinfo,
+ unsigned char id, long value
+);
+
+ Sets a parameter for the signal whose samples are being rendered by
+ signal_sampinfo.
+
+
+
+
+ Calls the set_parameter function for the instance started by
+ duh_signal_start_samples of the signal whose details you passed to that
+ function. Exactly what this does depends on the signal in question.
+
+
+
+
+
+ THIS IS NOT VERY HELPFUL. REFER TO A FILE?
+
+
+
+
+
+
+
+
+long duh_signal_render_samples(
+ DUH_SIGNAL_SAMPINFO *signal_sampinfo,
+ float volume, float delta,
+ long size, sample_t **samples
+);
+
+ Renders 'size' samples of the signal for which signal_sampinfo was set up.
+ See duh_render() and DUH_RENDER_SAMPLES for details on the 'volume' and
+ 'delta' parameters. This function will return the number of samples
+ generated, which will be fewer than 'size' if the signal ends.
+
+ Sometimes you can pass the array of sample buffers which was passed to
+ your function, and process the data in place. Other times you will have to
+ set up the array of sample buffer pointers yourself, making sure each
+ buffer can hold 'size' samples. Below is some code to do that. Note that
+ we prefix some variable names with sub-, so they don't clash with the
+ parameters to the function that would typically contain this code.
+
+ sample_t **subsamples;
+ int n;
+ long subsize;
+
+ subsamples = malloc(n_channels * sizeof(*subsamples));
+
+ if (!subsamples)
+ return 0;
+
+ subsamples[0] = malloc(size * n_channels * sizeof(*subsamples[0]));
+
+ if (!subsamples[0]) {
+ free(subsamples);
+ return 0;
+ }
+
+ for (n = 1; n < n_channels; n++)
+ subsamples[n] = subsamples[n-1] + size;
+
+ subsize = signal_render_samples(
+ subsampinfo,
+ volume, delta,
+ size, subsamples
+ );
+
+ /* Process the samples here. */
+
+ free(subsamples[0]);
+ free(subsamples);
+
+ return subsize;
+
+
+void duh_signal_end_samples(DUH_SIGNAL_SAMPINFO *signal_sampinfo);
+
+ Call this when you have finished with a DUH_SIGNAL_SAMPINFO struct. It
+ will free all memory used by the struct.
+
+
+**************************
+*** Resampling Helpers ***
+**************************
+
+
+ The DUH player library provides a versatile resampling system. The sample
+ signal type uses it, and it is available for use in any of your signals.
+
+ Be warned that the resampler may overrun the memory you specify by up to
+ DUMB_EXTRA_SAMPLES samples, so you must allocate these samples and set
+ them to appropriate values when you load your signal. Generally, if you
+ are going to loop, set them to reflect the samples at the loop start
+ point; if you are not going to loop, set them to zero.
+
+ DUMB_EXTRA_SAMPLES is defined as follows:
+
+ #define DUMB_EXTRA_SAMPLES 3
+
+
+extern int resampling_quality;
+
+ Allows you to control the quality of all resampling that takes place. This
+ may be set to any value from 0 to 4. Higher values will sound better, but
+ lower values will use up less processor time.
+
+ | --___
+ 0 - Aliasing |__--- __
+ | ___--
+
+ | __
+ 1 - Linear resampling | / \ /\
+ |/ \/ \__
+
+ 2 - Linear resampling / linear average
+
+ 3 - Quadratic / linear average
+
+ 4 - Cubic / linear average
+
+ Level 0 has very noticeable unwanted overtones. It will occasionally
+ produce satisfactory results for noisy signals, but usually you will
+ want to pay for the extra processor time (which isn't much) and go
+ for Level 1.
+
+ Levels 1 and 2 are already pretty good. When resampling down a few
+ octaves, however, you will begin to notice unwanted high frequencies.
+ These can be eliminated by switching to Levels 3 or 4.
+
+ When Level 2 or higher are selected, a linear average function is used for
+ speeding the wave up. Instead of skipping samples, all samples in the
+ interval will be averaged. The interval is also smoothed at the edges.
+ This will be especially beneficial when increasing the pitch by several
+ octaves.
+
+ Levels 3 and 4 are both smooth curves to the eye. They both give
+ extremely good performance, but you may sometimes notice the difference
+ when reducing the pitch of a sample by several octaves, where Level 3
+ may exhibit unwanted high frequencies.
+
+
+
+
+
+
+
+
+ NOTE: WHAT IS THE DEFAULT? (2 at the moment, but might change. Config?)
+
+
+
+
+
+
+
+
+
+
+
+
+
+long dumb_resample(
+ sample_t *src, long *_src_pos, int *_src_subpos,
+ long src_start, long src_end,
+ sample_t *dst, long dst_size,
+ float delta, int *_dir,
+ DUMB_RESAMPLE_PICKUP pickup, void *pickup_data
+);
+
+ This is the resampling function. It takes an array of source samples and
+ fills as much of the destination array as it can for you. Its operation is
+ quite complicated, so pay attention.
+
+ All the parameters prefixed with an underline (_) are pointers to the
+ fields they describe. This means the function can modify the variables you
+ pass, but you have to prefix the variable with an ampersand (&) when
+ passing it. If you get warnings about ints being converted to pointers
+ without casts, you have most likely forgotten an ampersand somewhere.
+
+ 'src' points to the source sample data. It should point to the beginning
+ of the sample, even if you are starting your resampling from somewhere in
+ the middle. Do not break this rule unless you know what you're doing.
+
+ '_src_pos' and '_src_subpos' together represent the current position in
+ the sample. When you first call the function, they represent the starting
+ position. Once the function has done its work, they will represent the
+ point at which the resampling stopped. '_src_pos' is measured in samples.
+ '_src_subpos' represents how far between samples we are, and ranges from
+ 0 to 65535 - so if _src_subpos is 65535, we are very nearly on to the next
+ sample.
+
+ Once _src_pos and _src_subpos have been modified by this function, you can
+ pass them to the function again and the resampling will continue
+ seamlessly. This is important, as you will hardly ever get to render all
+ your samples in one go. Typically you will make one call to this function
+ from within your render_samples function.
+
+ This function does not need to know the size of the source buffer as such.
+ Instead, it knows src_start and src_end, which are start and end points.
+ The end point actually points to the first sample not to use, following
+ the usual start-inclusive end-exclusive convention. In general, the
+ resampling will stop when src_pos tries to pass one of these.
+
+ WARNING: dumb_resample() may read up to DUMB_EXTRA_SAMPLES samples beyond
+ src_end. Make sure the memory belongs to you.
+
+ The _dir parameter should be either 1 or -1, and tells this function
+ whether to go forwards or backwards respectively through the source sample
+ buffer. If _dir is 0, nothing will happen - resampling has stopped
+ permanently. Any other values of _dir will have unpredictable results, and
+ the debugging library will abort if you try to use them.
+
+ If _dir is 1, then _src_pos will only be tested against src_end.
+ If _dir is -1, then _src_pos will only be tested against src_start.
+
+ That means you can set src_start and src_end to your loop points, and
+ start playing from the beginning of the sample. The resampling will
+ proceed unimpeded while _src_pos is outside the loop section, provided it
+ is advancing towards the loop section. The sample signal makes extensive
+ use of this capability.
+
+ The output waveform will be rendered into the buffer pointed to by dst.
+ Up to dst_size samples will be generated. Fewer will be generated if the
+ resampling stops permanently. The number generated will be returned. You
+ can find out if resampling ended by testing the value of your direction
+ variable (pointed to by _dir); if it is 0, then resampling stopped
+ permanently.
+
+ If you pass NULL for dst, no samples will be generated. However, _src_pos,
+ _src_subpos and _dir will be updated as normal. The pick-up function will
+ be called as necessary; you should pass the usual 'src' parameter so it
+ can be passed to your pick-up function. (See below for information on
+ pick-up functions.) The function returns the number of samples that would
+ have been generated, had dst pointed somewhere. The operation of
+ do_resample() when dst is NULL is exactly the same as that when dst points
+ somewhere. However, do_resample() is much faster in this case; you should
+ use it when volume is 0, or when you are culling quieter samples to gain
+ execution speed.
+
+ If delta is 1.0f, the destination is the same speed as the source. Greater
+ values cause the sample to speed up, and lesser values cause the sample to
+ slow down. delta should always be positive.
+
+ We have covered most of the parameters. There are only two left - pickup
+ and pickup_data. The simplest usage is to set these both to NULL. Then, if
+ resampling stops, it stops permanently. Use this if your sample will not
+ loop.
+
+ Alternatively, you may wish to write a pick-up function. Your pick-up
+ function will take control whenever _src_pos tries to pass the start or
+ end points. You can use it for looping, or for a multitude of other tasks.
+ The typedef is as follows:
+
+ typedef int (*DUMB_RESAMPLE_PICKUP)(
+ sample_t *src, long *_src_pos, int *_src_subpos,
+ long *_src_start, long *_src_end,
+ int dir,
+ void *data
+ );
+
+ You are passed the source buffer, in case you want to fill it with new
+ samples. You have the _src_pos, _src_subpos, _src_start and _src_end
+ pointers, should you need to change the values to which they point. You
+ also have dir, but note that it is not a pointer. Instead, you should
+ return the new direction, or 0 to stop the resampling permanently.
+
+ The data parameter is a copy of pickup_data as passed to dumb_resample().
+ This is typically used to give you access to your 'sampinfo' parameter,
+ passed to your render_samples function.
+
+ BEWARE: you must refer to and change src_pos, src_subpos, src_start and
+ src_end through the parameters passed. Do not mistakenly use their
+ equivalents in your struct instead. The equivalents in your struct
+ will eventually be updated, but they will not be accurate during
+ the pick-up function.
+
+ On entry to this function, src_pos and src_subpos will have overrun by a
+ small amount. When changing them, you should preserve this overrun. The
+ following examples will do this for you:
+
+ If you are executing a simple loop, subtract (or add) the difference
+ between the loop points from (or to) src_pos. Do not simply set src_pos
+ to the other loop point.
+
+ *_src_pos -= *_src_end - *_src_start;
+
+ If you are executing a ping-pong loop, you need to reflect the pointer
+ off the loop boundary. To reflect off the loop end point:
+
+ *_src_pos = (*_src_end << 1) - 1 - *_src_pos;
+ *_src_subpos ^= 65535;
+ dir = -1;
+
+ To reflect off the loop start point:
+
+ *_src_pos = (*_src_start << 1) - 1 - *_src_pos;
+ *_src_subpos ^= 65535;
+ dir = 1;
+
+ In each case, don't forget to return the new direction correctly!
+
+ An ideal example of a pick-up function can be found in src/sample.c.
+
+ Of course there is more that can be done with a pick-up function. Enjoy
+ experimenting!
+
+
+************************
+*** DUH Construction ***
+************************
+
+
+DUH *make_duh(
+ long length,
+ int n_signals,
+ DUH_SIGTYPE_DESC *desc[],
+ void *signal[]
+);
+
+ Constructs a DUH from its component parts. Use this function if you are
+ writing a function to load a music file format other than .duh. Indeed,
+ this function is used internally to load IT files.
+
+ Before you call this function, you must do some preparation. Your DUH will
+ contain a fixed number of signals; pass this number as n_signals. Each
+ signal will have a DUH_SIGTYPE_DESC struct, and you pass an array of
+ pointers to these. The DUH_SIGTYPE_DESC struct contains the following
+ fields:
+
+ long type;
+ DUH_LOAD_SIGNAL load_signal;
+ DUH_START_SAMPLES start_samples;
+ DUH_SET_PARAMETER set_parameter;
+ DUH_RENDER_SAMPLES render_samples;
+ DUH_END_SAMPLES end_samples;
+ DUH_UNLOAD_SIGNAL unload_signal;
+
+ The structs must be in permanent memory, i.e. either global or static, and
+ not modified later; however, the array of pointers can be destroyed as
+ soon as the function returns.
+
+ The values of 'type' and 'load_signal' are irrelevant; set them to 0 and
+ NULL respectively, unless you are using the DUH_SIGTYPE_DESC struct
+ elsewhere.
+
+ Because 'load_signal' is never used, you must provide an array of pointers
+ to the data that 'load_signal' would otherwise have returned. This will
+ be passed to the other functions as 'signal'. Once again, the array of
+ pointers can be destroyed as soon as the function returns. As for the
+ pointers themselves:
+
+ If an 'unload_signal' function is provided, then that will be used to
+ deallocate the pointer when you finally destroy the DUH. If the function
+ is not provided, then you are responsible for deallocating any memory
+ referenced by that pointer yourself. This applies individually to each
+ signal.
+
+ If this function fails and returns NULL, then any 'unload_signal'
+ functions you provided will have been called. You do not have any extra
+ work to do if it fails.
+
+
+********************************
+*** Allegro Packfile Support ***
+********************************
+
+
+void dumb_register_packfiles(void);
+
+ This function registers the Allegro PACKFILE input module for use by
+ DUMBFILEs. PACKFILE structs and their corresponding functions, as defined
+ by Allegro's header file allegro.h, will be used internally for all
+ DUMBFILE input (unless opened with dumbfile_open_ex()).
+
+ This must be called before dumbfile_open() is used, or else an alternative
+ system must be registered (see register_dumbfile_system() and
+ dumb_register_stdfiles()).
+
+
+DUMBFILE *dumbfile_open_packfile(PACKFILE *p);
+
+ If you have an Allegro PACKFILE struct representing an open file, you can
+ call this if you wish to read from it using a DUMBFILE. This is useful
+ when you need to pass a DUMBFILE struct to a library function, to read an
+ embedded music file for example. When you close the DUMBFILE, you can
+ continue using the PACKFILE struct to read what follows the embedded data.
+
+
+***********************************************
+*** Allegro Datafile Registration Functions ***
+***********************************************
+
+
+void register_dat_duh(void);
+
+ If you wish to put a DUH file in an Allegro datafile, you must use "DUH "
+ for the type. The grabber will have a box for the type when you insert a
+ new object. The grabber will treat the DUH file as binary data, which
+ means the datafile will contain an exact copy of the DUH file on disk.
+
+
+
+
+
+ TODO: make it possible to choose the type...
+
+
+
+
+
+ You must then call register_dat_duh() in your program before you load the
+ datafile. Once you've done this, you'll be able to access the DUH using
+ the usual datafile[n].dat notation. You do not need to call unload_duh()
+ on this DUH; unload_datafile() will do that for you.
+
+ If you need to check the type of the object for whatever reason, you can
+ use DAT_DUH, defined as follows:
+
+ #define DAT_DUH DAT_ID('D','U','H',' ')
+
+ The following example iterates through all the DUHs in disan.dat:
+
+ DATAFILE *dat;
+ int n;
+
+ register_dat_duh();
+ dat = load_datafile("disan.dat");
+
+ for (n = 0; dat[n].type != DAT_END; n++) {
+ if (dat[n].type == DAT_DUH) {
+ DUH *duh = dat[n].dat;
+ /* Insert code here to play 'duh' or whatever you want to do. */
+ }
+ }
+
+ unload_datafile(dat);
+
+
+void register_dat_it(void);
+
+ Inserting an IT file in an Allegro datafile is the same as inserting a DUH
+ file, except the type has to be "IT ", the registration function is
+ register_dat_it(), and the following definition is available for use
+ instead of DAT_DUH:
+
+ #define DAT_IT DAT_ID('I','T',' ',' ')
+
+ Once the datafile is loaded, the 'dat' field indeed points to a DUH
+ struct. There are no differences other than those listed above.
+
+
+*************************************
+*** Allegro DUH Playing Functions ***
+*************************************
+
+
+ The functions in this section allow you to play back a DUH through
+ Allegro's sound system. You must call Allegro's install_sound() function
+ before you use them.
+
+
+AL_DUH_PLAYER *al_start_duh(
+ DUH *duh, int n_channels, long pos, float volume, long bufsize, int freq
+);
+
+ Starts playing the specified DUH.
+
+ An AL_DUH_PLAYER represents one instance of the DUH playing. If you wish,
+ you can have two or more AL_DUH_PLAYERs going at the same time, for the
+ same DUH or for different ones. Each uses one of Allegro's audio streams
+ and hence one voice.
+
+ At present, n_channels can either be 1 or 2 for monaural or stereo
+ respectively. If you use the debugging library, your program will abort if
+ other values are passed; otherwise weird things will happen.
+
+ The DUH will start playing from position 'pos'. 0 represents the start of
+ the DUH, and 65536 represents one second. Unlike other music systems, DUMB
+ will always make sure every note is there right from the start, provided
+ any custom signal types are designed properly. In other words, you can
+ start a DUH at a point halfway through a long note, and you will still
+ hear the long note.
+
+ The volume is a float. 1.0f is the pseudo-maximum. If you pass 1.0f, any
+ properly designed DUH file will play nice and loud, but will not clip. You
+ can pass a greater volume if you like, but be prepared for clipping to
+ occur. Of course you can pass smaller values to play the DUH more quietly,
+ and this will also resolve clipping issues in badly designed DUH files.
+
+ You will need to pass the AL_DUH_PLAYER to other functions when you need
+ to stop or pause the DUH, change its volume, or otherwise modify the way
+ it is playing. You will also need to pass it to al_poll_duh() at regular
+ intervals; if the sound is choppy, try calling al_poll_duh() more often.
+
+ 'bufsize' is the number of samples that will be rendered at once. 1024 is
+ a suitable value for most purposes.
+
+
+
+
+
+ NOTE: IS THAT TRUE ON ALL SYSTEMS?
+
+
+
+
+
+ The greater this is, the less often you will have to call al_poll_duh() -
+ but when al_poll_duh() decides to fill the buffer, it will take longer
+ doing so. If your game exhibits regular brief freezes, try reducing the
+ buffer size. If the sound is choppy, however, you may have to increase it.
+
+ 'freq' specifies the sampling frequency at which the DUH should be
+ rendered. At present there is no (official and portable) way of knowing
+ the frequency at which Allegro is mixing - but if you do know that
+ frequency, passing it here will give the highest quality sound. If you
+ reduce it, the DUH will sound less crisp but use less processor time.
+
+ When you have finished, you must pass the AL_DUH_PLAYER to al_stop_duh()
+ to free up memory. Do not destroy the DUH beforehand.
+
+ There is no real need to check the return value from this function. The
+ other functions can be called safely with null pointers, so if there is a
+ problem, your music will simply not play.
+
+
+void al_stop_duh(AL_DUH_PLAYER *dp);
+
+ This will stop an AL_DUH_PLAYER. You must call this when you have finished
+ with it, before destroying the DUH. The pointer will no longer be valid on
+ return from this function.
+
+
+void al_pause_duh(AL_DUH_PLAYER *dp);
+
+ This will pause an AL_DUH_PLAYER. Use al_resume_duh() when you want it to
+ continue.
+
+
+void al_resume_duh(AL_DUH_PLAYER *dp);
+
+ Causes a paused AL_DUH_PLAYER to resume playing (see al_pause_duh()).
+
+
+void al_duh_set_volume(AL_DUH_PLAYER *dp, float volume);
+
+ This will set the volume of an AL_DUH_PLAYER. See al_start_duh() for
+ details on the volume parameter.
+
+
+int al_poll_duh(AL_DUH_PLAYER *dp);
+
+ An AL_DUH_PLAYER is not interrupt-driven. That means it will not play by
+ itself. You must keep it alive from your main program. Call this function
+ at regular intervals. If the sound crackles, try calling it more often.
+ (There is nothing you can do if Windows decides to play with the hard
+ disk; that will make your sound crackle no matter what you do.)
+
+ Normally this function will return zero. However, if it returns nonzero,
+ that means the AL_DUH_PLAYER will not generate any more sound. Indeed the
+ underlying audio stream and DUH_RENDERER have been destroyed. When this
+ happens, you can call al_stop_duh() whenever you wish - but you do not
+ have to. Note that this function will wait two buffers' worth of samples
+ before taking this action, allowing Allegro to mix the trailing sound
+ before the audio stream is destroyed. In other words, your music will not
+ be cut off prematurely.
+
+ In case you were wondering, it is definitely not safe to call
+ al_poll_duh() from an interrupt context. Not only is no part of DUMB
+ locked in memory, but many signals and many core library functions
+ allocate and free their memory on a call-by-call basis! Remember that any
+ disk access that occurs in interrupt context is likely to crash the
+ machine. (This limitation only applies to DOS at present, and is due to
+ the fact that the DOS file access functions are not re-entrant.
+ Multitasking systems are generally safe. However, even if you do not think
+ you are targeting DOS, it is worth considering that calling this function
+ regularly from your main loop is likely to be much more efficient than
+ relying on task switching to do it for you.)
+
+
+long al_duh_get_position(AL_DUH_PLAYER *dp);
+
+ Tells you what position an AL_DUH_PLAYER is up to, or -1 if it is invalid
+ (perhaps owing to lack of memory). As usual, 65536 is one second. Note
+ that this is a whole number, whereas a fractional part is stored
+ internally; the sample will not be continuous if you terminate the
+ AL_DUH_PLAYER and then reinitiate it with the same position. Furthermore,
+ note that Allegro will not have mixed in all the sound up to this point;
+ if you wait for this to reach a certain position and then terminate the
+ AL_DUH_PLAYER, the sound will cut off too early.
+
+
+
+
+
+
+
+
+ NOTE: HOW TO GET AROUND THIS?
+
+
+
+
+
+
+
+
+DUH_RENDERER *al_duh_get_renderer(AL_DUH_PLAYER *dp);
+
+ This function returns to you the DUH_RENDERER underlying the specified
+ AL_DUH_PLAYER. It has no practical use in this release, but it
+ TODO: Write this. Also see if it should go in howto.txt.
+
+
+******************
+*** Conclusion ***
+******************
+
+
+I conclude that... DUMB is the bestest music player in the world because...
+Complete this sentence in fifteen words or fewer... D'OH!
+
+
+Ben Davis
+entheh@users.sf.net
+IRC EFnet #dumb
+See readme.txt for details on using IRC.
diff --git a/plugins/dumb/dumb-kode54/docs/faq.txt b/plugins/dumb/dumb-kode54/docs/faq.txt
new file mode 100644
index 00000000..63913f76
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/docs/faq.txt
@@ -0,0 +1,264 @@
+TO DO: add question regarding set_close_button_callback vs set_window_close_hook
+TO DO: add question regarding mixing of DJGPP and MinGW object files
+
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/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/plugins/dumb/dumb-kode54/docs/fnptr.txt b/plugins/dumb/dumb-kode54/docs/fnptr.txt
new file mode 100644
index 00000000..9a0d87b6
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/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/plugins/dumb/dumb-kode54/docs/howto.txt b/plugins/dumb/dumb-kode54/docs/howto.txt
new file mode 100644
index 00000000..1d9a439f
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/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/plugins/dumb/dumb-kode54/docs/modplug.txt b/plugins/dumb/dumb-kode54/docs/modplug.txt
new file mode 100644
index 00000000..65eabe8a
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/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/plugins/dumb/dumb-kode54/docs/ptr.txt b/plugins/dumb/dumb-kode54/docs/ptr.txt
new file mode 100644
index 00000000..bf7534d4
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/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.
diff --git a/plugins/dumb/dumb-kode54/docs/tutorial.txt b/plugins/dumb/dumb-kode54/docs/tutorial.txt
new file mode 100644
index 00000000..a4309655
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/docs/tutorial.txt
@@ -0,0 +1,94 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * tutorial.txt - DUMB Programmers' Tutorial. / / \ \
+ * | < / \_
+ * See readme.txt for general information on | \/ /\ /
+ * DUMB and how to set it up. \_ / > /
+ * | \ / /
+ * | ' /
+ * \__/
+ */
+
+
+********************
+*** Introduction ***
+********************
+
+
+Welcome to the DUMB Programmers' Tutorial!
+
+In order to follow this tutorial, you should already have set Allegro and
+DUMB up on your system, including DUMB's support for Allegro and the example
+programs. If you have not done so, see readme.txt for details.
+
+You will also need some IT files. If you would like to compose your own, then
+first I must offer a word of warning: not everyone is capable of composing
+music. Do not assume you will be able to learn the art. By all means have a
+go; if you can learn to play tunes on the computer keyboard, you're well on
+the way to being a composer!
+
+The best program for the job is Impulse Tracker itself, available from:
+
+http://www.noisemusic.org/it/
+
+This is a DOS program. Users of DOS-incapable operating systems may like to
+try ModPlug Tracker, but should be aware that it does not support all
+combinations of effects correctly, and some IT files will sound wrong. If you
+use a different operating system, or if you know of a better IT editor for
+Windows, please give me some links so I can put them here!
+
+ModPlug Tracker is available from: http://www.modplug.com/
+
+If you would like to download IT files composed by other people, check the
+following sites:
+
+http://www.modplug.com/
+http://www.traxinspace.com/
+
+Once again, if you know of more sites where IT files are available for
+download, please let me know.
+
+Once you've got some IT files, we're ready to begin!
+
+Note that support for S3M files has very recently been added to DUMB, but it
+is bound to be faulty. I recommend you use IT files in preference until later
+releases of DUMB.
+
+
+******************
+*** Try It Out ***
+******************
+
+
+We'll start simply by running one of the example programs.
+
+Find playit.exe, in the dumb/examples folder. Choose an IT file, and pass it
+to playit.exe. In DOS, you can do this by typing 'playit' followed by the
+name of the IT file. In Windows, you can drag the IT file and drop it on
+playit.exe.
+
+You should now hear the music play back. If not, make sure your speakers are
+on and volume is not turned down. Try one of Allegro's example programs. If
+you are using DJGPP under Windows, consider using a Windows compiler instead.
+If you cannot get it working, see readme.txt for details on seeking help with
+DUMB.
+
+When you are satisfied, press any key to stop the music and return to the
+operating system.
+
+
+*************************
+*** How Does It Work? ***
+*************************
+
+
+Now load playit.c into your favourite editor. Here is an explanation of what
+it does: