diff options
Diffstat (limited to 'apps/codecs/dumb/docs/howto.txt')
-rw-r--r-- | apps/codecs/dumb/docs/howto.txt | 845 |
1 files changed, 845 insertions, 0 deletions
diff --git a/apps/codecs/dumb/docs/howto.txt b/apps/codecs/dumb/docs/howto.txt new file mode 100644 index 0000000000..0e7057da2c --- /dev/null +++ b/apps/codecs/dumb/docs/howto.txt | |||
@@ -0,0 +1,845 @@ | |||
1 | /* _______ ____ __ ___ ___ | ||
2 | * \ _ \ \ / \ / \ \ / / ' ' ' | ||
3 | * | | \ \ | | || | \/ | . . | ||
4 | * | | | | | | || ||\ /| | | ||
5 | * | | | | | | || || \/ | | ' ' ' | ||
6 | * | | | | | | || || | | . . | ||
7 | * | |_/ / \ \__// || | | | ||
8 | * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque | ||
9 | * / \ | ||
10 | * / . \ | ||
11 | * howto.txt - How To Use DUMB. / / \ \ | ||
12 | * | < / \_ | ||
13 | * See readme.txt for general information on | \/ /\ / | ||
14 | * DUMB and how to set it up. \_ / > / | ||
15 | * | \ / / | ||
16 | * | ' / | ||
17 | * \__/ | ||
18 | */ | ||
19 | |||
20 | |||
21 | ******************** | ||
22 | *** Introduction *** | ||
23 | ******************** | ||
24 | |||
25 | |||
26 | Welcome to the DUMB How-To! It is assumed here that you have already set DUMB | ||
27 | up on your system, with or without Allegro. If not, please see readme.txt. | ||
28 | |||
29 | |||
30 | ********************************* | ||
31 | *** Adding music to your game *** | ||
32 | ********************************* | ||
33 | |||
34 | |||
35 | These instructions will help you add a piece of music to your game, assuming | ||
36 | your music is stored in a stand-alone IT, XM, S3M or MOD file. If you wish to | ||
37 | use a different method (such as putting the music file in an Allegro | ||
38 | datafile), please follow these instructions first, test your program, and | ||
39 | then follow the instructions further down for adapting your code. | ||
40 | |||
41 | |||
42 | 1. You need to include DUMB's header file. If you have Allegro, add the | ||
43 | following line to the top of your source file (or at the top of each file | ||
44 | where you wish to use DUMB): | ||
45 | |||
46 | #include <aldumb.h> | ||
47 | |||
48 | If you do not have Allegro or do not wish to use it, use dumb.h instead. | ||
49 | |||
50 | |||
51 | 2. You need to link with DUMB's library file or files. If you are compiling | ||
52 | with GCC from a command line on any platform, you need to add the | ||
53 | following to the command line: | ||
54 | |||
55 | If you are using Allegro: -laldmd -ldumbd | ||
56 | If you are not using Allegro: -ldumbd | ||
57 | |||
58 | If you are using MSVC from the command line: | ||
59 | |||
60 | If you are using Allegro: /link aldmd.lib dumbd.lib | ||
61 | If you are not using Allegro: /link dumbd.lib | ||
62 | |||
63 | With MSVC, you must also add /MD to the command line when compiling (not | ||
64 | when linking). | ||
65 | |||
66 | Note that -laldmd or aldmd.lib must PRECEDE alleg.lib, -lalleg_s, | ||
67 | `allegro-config --libs`, or whatever you are already using to link with | ||
68 | Allegro. For MSVC users, the /MD flag selects the multithreaded DLL | ||
69 | implementation of the standard libraries; since DUMB is statically linked, | ||
70 | you have to use the same library DUMB uses. You would also need this flag | ||
71 | to link statically with Allegro; if you already have it, there's no need | ||
72 | to put it twice. | ||
73 | |||
74 | (If anyone would like to contribute instructions for doing the above using | ||
75 | MSVC's IDE, please contact me. Contact details are at the end of this | ||
76 | file.) | ||
77 | |||
78 | If you are using RHIDE, go to Options -> Libraries. You will need to type | ||
79 | 'aldmd' and 'dumbd' in two boxes, making sure 'aldmd' comes above whatever | ||
80 | you are using to link with Allegro (or just put 'dumbd' if you are not | ||
81 | using Allegro). Make sure the box next to each of these libraries is | ||
82 | checked. | ||
83 | |||
84 | The above are the debugging libraries. It is VERY HIGHLY RECOMMENDED that | ||
85 | you use the debugging libraries at first. The reason is as follows. | ||
86 | Although DUMB is supposedly robust against corrupt music files and things | ||
87 | like lack of memory, it will NOT tolerate programmer error. If you write | ||
88 | faulty code, DUMB will probably crash rather than returning an error code | ||
89 | for you. However, the debugging libraries will abort in many cases, | ||
90 | enabling you to find out what the cause is. | ||
91 | |||
92 | Once your program is up and running reliably, you can replace 'aldmd' with | ||
93 | 'aldmb' and 'dumbd' with 'dumb'. Don't forget to do this, or DUMB will be | ||
94 | a lot slower than it should be! | ||
95 | |||
96 | |||
97 | 3. As you use DUMB, it may claim system resources (memory in particular). You | ||
98 | will need to arrange for these resources to be freed at the end. Doing so | ||
99 | is very easy. Simply write the following line at the top of your main | ||
100 | function, but below allegro_init() if you are using Allegro: | ||
101 | |||
102 | atexit(&dumb_exit); | ||
103 | |||
104 | This arranges for the function dumb_exit() to be called when your program | ||
105 | exits; you do not need to call dumb_exit() yourself. This method is | ||
106 | preferable to calling dumb_exit() manually, as it will free resources even | ||
107 | if your program aborts unexpectedly. | ||
108 | |||
109 | If you are happy with this, please skip ahead to Step 4. If you are | ||
110 | interested in alternative methods, read on, but read on carefully. | ||
111 | |||
112 | In fact it mostly doesn't matter where you put the above atexit() line, | ||
113 | provided it gets called only once, and before you do anything with DUMB. | ||
114 | If you are using DUMB with Allegro, it is recommended that you write the | ||
115 | functions in this order: | ||
116 | |||
117 | allegro_init(); | ||
118 | atexit(&dumb_exit); | ||
119 | |||
120 | And then you must NOT call allegro_exit() yourself (because it has to be | ||
121 | called after dumb_exit()). Alternatively, if you prefer not to use | ||
122 | atexit() (or you cannot), you will have to do the following before | ||
123 | exiting: | ||
124 | |||
125 | dumb_exit(); | ||
126 | allegro_exit(); | ||
127 | |||
128 | |||
129 | 4. DUMB does not automatically do any of its own file input. You have to tell | ||
130 | it how to read files. Don't worry, it's easy. Simply call the following | ||
131 | function near the beginning of your program, after your atexit() call: | ||
132 | |||
133 | dumb_register_stdfiles(); | ||
134 | |||
135 | This tells DUMB to use ordinary stdio FILE structs for reading and writing | ||
136 | files. If you are using Allegro and would rather DUMB used PACKFILEs, call | ||
137 | the following function INSTEAD: | ||
138 | |||
139 | dumb_register_packfiles(); | ||
140 | |||
141 | In the latter case, DUMB will be affected by any password you set with | ||
142 | packfile_password() in the same way that other PACKFILEs are. | ||
143 | |||
144 | Note that the procedure for loading datafiles with embedded music is | ||
145 | independent of these two functions; even if you will be loading datafiles, | ||
146 | you can use either of these functions. If you are loading datafiles, your | ||
147 | executable might be slightly smaller if you use dumb_register_packfiles(). | ||
148 | On the other hand, dumb_register_stdfiles() will probably be faster. If | ||
149 | you are only ever going to load datafiles and never stand-alone files, you | ||
150 | can actually leave this step out; but I would recommend you put this in, | ||
151 | test your code with a stand-alone file, then follow the instructions in | ||
152 | the next section in order to adapt your code to use the datafile (you will | ||
153 | be reminded that you can remove the function call). | ||
154 | |||
155 | |||
156 | 5. If you are using Allegro, you'll have to initialise Allegro's sound | ||
157 | system. In most cases the following line will do the job: | ||
158 | |||
159 | install_sound(DIGI_AUTODETECT, MIDI_NONE, NULL); | ||
160 | |||
161 | You may like to initialise a MIDI driver though; see Allegro's docs for | ||
162 | details. Put this line after allegro_init(). | ||
163 | |||
164 | |||
165 | 6. All pieces of music are stored in memory in DUH structs. To handle these, | ||
166 | you must define pointers to them. Such pointers look like this: | ||
167 | |||
168 | DUH *myduh; | ||
169 | |||
170 | You can of course replace 'myduh' with anything you like. If you are | ||
171 | unfamiliar with pointers, please see ptr.txt. It is very important that | ||
172 | you understand these if you wish to use DUMB correctly. | ||
173 | |||
174 | You do not have direct access to the contents of a DUH struct, so do not | ||
175 | try. DUMB's functions provide everything you need; if you disagree, please | ||
176 | let me know and I shall see what I can do. Contact details are at the end | ||
177 | of this file. | ||
178 | |||
179 | Given the above definition, you can load a piece of music using one of the | ||
180 | following lines, depending on what file format you want to load: | ||
181 | |||
182 | myduh = dumb_load_it("a_one.it"); | ||
183 | myduh = dumb_load_xm("a_two.xm"); | ||
184 | myduh = dumb_load_s3m("a_one_two.s3m"); | ||
185 | myduh = dumb_load_mod("three_four.mod"); | ||
186 | |||
187 | Obviously you can use relative or absolute paths as normal. You should | ||
188 | always use forward slash (/), not backslash (\), when coding in C and | ||
189 | similar languages. | ||
190 | |||
191 | Every piece of music you load must be unloaded when you've finished with | ||
192 | it. When you type the above line in, it is good practice to type the | ||
193 | following line in at the same time, but put it at the end of the program: | ||
194 | |||
195 | unload_duh(myduh); | ||
196 | |||
197 | You will now be able to use the DUH struct anywhere in between the two | ||
198 | lines you just added. There is no need to check the return value; if the | ||
199 | DUH failed to load for one reason or another (this could be due to lack of | ||
200 | memory as well as the file not being there), then DUMB will do nothing - | ||
201 | safely. | ||
202 | |||
203 | |||
204 | 7. From this step onwards, it will be assumed you're using Allegro. If not, | ||
205 | please read these steps anyway, and then see the section entitled | ||
206 | "Rendering music into a buffer". You will have to write your own playback | ||
207 | code using whatever sound output system is available. Alternatively you | ||
208 | may like to write data to a file (especially if you have a file that | ||
209 | consumes a lot of processor time), but beware that any streaming audio | ||
210 | format is likely to be substantially larger than the module file you | ||
211 | generate it from, and formats like MP3 will be lower quality. You might | ||
212 | not be able to hear the difference between the MP3 and the original, but | ||
213 | many people can and don't like it, so please consider them. I'm one of | ||
214 | them. If you really want to use a lossy compression format, I highly | ||
215 | recommend Ogg Vorbis: | ||
216 | |||
217 | http://www.vorbis.com/ | ||
218 | |||
219 | But I digress. | ||
220 | |||
221 | In order to play the DUH you loaded, you need to define a pointer to an | ||
222 | AL_DUH_PLAYER struct: | ||
223 | |||
224 | AL_DUH_PLAYER *dp; | ||
225 | |||
226 | Two of the functions you will need are prototyped as follows: | ||
227 | |||
228 | AL_DUH_PLAYER *al_start_duh(DUH *duh, int n_channels, long pos, | ||
229 | float volume, long bufsize, int freq); | ||
230 | |||
231 | void al_stop_duh(AL_DUH_PLAYER *dp); | ||
232 | |||
233 | As you can see, al_start_duh() returns a pointer to an AL_DUH_PLAYER | ||
234 | struct when you call it. You then pass this pointer to all the other | ||
235 | functions. Again, if it is a NULL pointer for whatever reason (usually | ||
236 | lack of memory), DUMB will safely do nothing. When you call al_stop_duh(), | ||
237 | the pointer becomes invalid and you should not use it again; if there's | ||
238 | any risk of the pointer being used again, it is wise to set it to NULL at | ||
239 | this point. You can reassign the variable with a new call to | ||
240 | al_start_duh() of course. | ||
241 | |||
242 | Set 'n_channels' to 1 or 2 for mono or stereo respectively. Note that this | ||
243 | parameter has nothing to do with the number of samples that can play at | ||
244 | once in a music module. Set 'pos' to 0 to play from the beginning; each | ||
245 | time you add 65536, you will have advanced one second into the piece. As a | ||
246 | general rule, set the volume to 1.0f and adjust it later if the music is | ||
247 | too loud or too quiet - but see Allegro's set_volume_per_voice() function | ||
248 | first. | ||
249 | |||
250 | 'bufsize' can generally be set to 4096. If your music stutters, try | ||
251 | increasing it; if your game freezes periodically, try reducing it. Find a | ||
252 | happy medium. Set 'freq' to 48000 for the best quality, though 44100 will | ||
253 | do in most cases. 22050 will be fine for a lot of music, though 11025 may | ||
254 | sound muffled. You can choose any other value, higher, lower or in | ||
255 | between. If your music stutters, and increasing 'bufsize' doesn't fix it, | ||
256 | try reducing this value. | ||
257 | |||
258 | Once you have put in a call to al_start_duh(), it is good practice to | ||
259 | insert the call to al_stop_duh() at the same time. You must call | ||
260 | al_stop_duh() before the DUH is unloaded (unload_duh(), Step 6 above). | ||
261 | |||
262 | Don't get impetuous, your program is not ready yet! Proceed to Step 8. | ||
263 | |||
264 | |||
265 | 8. DUMB does not play music in the background for you; if you were expecting | ||
266 | it to do so, please see the explanation at the end of this step. For your | ||
267 | music to be played, you have to call another function at regular | ||
268 | intervals. Here is its prototype: | ||
269 | |||
270 | int al_poll_duh(AL_DUH_PLAYER *dp); | ||
271 | |||
272 | Do NOT call this function from inside a timer function unless you really | ||
273 | know what you are doing. The reasons why this is bad are explained | ||
274 | further down. You should call it from your main program. | ||
275 | |||
276 | Simply writing the following line will be sufficient in general, if you | ||
277 | have a variable 'dp' that points to your AL_DUH_PLAYER struct. | ||
278 | |||
279 | al_poll_duh(dp); | ||
280 | |||
281 | As a general rule, calling this once for each logic update will do the | ||
282 | trick. If, however, you are executing time-consuming algorithms such as | ||
283 | software 3D rendering, you may wish to insert calls to this function in | ||
284 | the middle of those algorithms. You cannot call this function too often | ||
285 | (within reason); if it has nothing to do it will return immediately. | ||
286 | |||
287 | Exactly how often you need to call the function depends on the values for | ||
288 | 'bufsize' and 'freq' that you passed to al_start_duh(): | ||
289 | |||
290 | n = freq / bufsize; | ||
291 | |||
292 | You have to call al_poll_duh() at least n times a second. Do not hesitate | ||
293 | to call it more often for safety; if the sound stutters, you may need to | ||
294 | do just that. (Or you may need to increase the buffer size or reduce the | ||
295 | quality settings; the only way to find out is to try.) | ||
296 | |||
297 | For now, don't worry about al_poll_duh()'s return value. As soon as you | ||
298 | need it, it will be explained. | ||
299 | |||
300 | If you are happy, please skip to Step 9. If you were expecting DUMB to | ||
301 | play your music in the background, please read on. | ||
302 | |||
303 | The natural way to play music in the background on most operating systems | ||
304 | nowadays is to use threads. DOS was not built with multithreading in mind, | ||
305 | and its system operations (notably disk access) assume they will only be | ||
306 | used from a single thread. | ||
307 | |||
308 | Interrupts are the next best thing to threads. A DOS hardware interrupt | ||
309 | could be triggered at any moment, and a handler function will be called. | ||
310 | This is how Allegro's timer functions work. Unfortunately, what you can do | ||
311 | inside an interrupt handler is very limited. For one thing, all code and | ||
312 | data used by the handler must be locked in memory; if not, it could get | ||
313 | written to disk (virtual memory). If the main program was accessing the | ||
314 | disk when it got interrupted, the system would then die a horrible death. | ||
315 | This precludes the possibility of allocating extra memory inside the | ||
316 | handler, and DUMB does a lot of that in al_poll_duh(). | ||
317 | |||
318 | Given DUMB's architecture, which cannot change for reasons which will | ||
319 | become apparent in future versions, this renders it impossible to come up | ||
320 | with a portable solution for making DUMB play music in the background. | ||
321 | Having said that, if you wish to write your own wrapper for al_poll_duh() | ||
322 | and use it in a thread, there is nothing stopping you. If you do do this, | ||
323 | you will have to be very careful when stopping the music; see the | ||
324 | description of al_poll_duh() in dumb.txt for more information. | ||
325 | |||
326 | So why not kill DOS? It is all too common a practice among programmers to | ||
327 | quote the phrase, "DOS is as dead as the dodo." Despite being a decidedly | ||
328 | derisible demonstation of the dreary device of alliteration, it shows a | ||
329 | distinct lack of experience. Many embedded systems still use DOS because | ||
330 | it provides hardware access capabilities and real-time possibilities | ||
331 | unparalleled by any current multitasking operating system. For an argument | ||
332 | closer to home, I used to use RHIDE for DOS before I switched to Linux, | ||
333 | and I have not found a single Freeware Windows IDE that measures up to | ||
334 | RHIDE. I'm sure many people are in the same boat, and really appreciate | ||
335 | DUMB's DOS port. | ||
336 | |||
337 | We will not be removing DOS support from DUMB. Any blind suggestions to do | ||
338 | so will be met with fiery flames. You have been warned. | ||
339 | |||
340 | |||
341 | 9. Test your program! | ||
342 | |||
343 | If you have trouble, check through the above steps to make sure you didn't | ||
344 | miss one out. Refer to faq.txt to see if your problem is addressed there. | ||
345 | If you still have trouble, contact me; details are at the end of this | ||
346 | file. | ||
347 | |||
348 | |||
349 | ********************************** | ||
350 | *** Controlling music playback *** | ||
351 | ********************************** | ||
352 | |||
353 | |||
354 | Here I describe some common operations you may wish to perform. The method | ||
355 | for doing so will seem a bit strange sometimes, as will the names of the | ||
356 | structs. However, there is a reason behind everything. If you would like to | ||
357 | do more exotic things, or better understand some of the methods used here, | ||
358 | then see dumb.txt, which covers everything from the ground up. | ||
359 | |||
360 | |||
361 | To control playback quality: | ||
362 | |||
363 | #define DUMB_RQ_ALIASING | ||
364 | #define DUMB_RQ_LINEAR | ||
365 | #define DUMB_RQ_CUBIC | ||
366 | #define DUMB_RQ_N_LEVELS | ||
367 | extern int dumb_resampling_quality; | ||
368 | extern int dumb_it_max_to_mix; | ||
369 | |||
370 | Please note that dumb_resampling_quality has changed in DUMB v0.9.2. See | ||
371 | deprec.txt for more details on the change. | ||
372 | |||
373 | dumb_resampling_quality can be set to any of the DUMB_RQ_* constants | ||
374 | (except DUMB_RQ_N_LEVELS; see below). Resampling is the term given to the | ||
375 | process of adjusting a sample's pitch (in this context). | ||
376 | dumb_resampling_quality defaults to DUMB_RQ_CUBIC, which sounds nice but | ||
377 | takes a lot of processor power. Try reducing it if you have an older | ||
378 | computer or if you are trying to mix an insane number of samples (or | ||
379 | both!). See dumb.txt for details on what the different values actually do. | ||
380 | |||
381 | If you wish to give this option to your user, you can use | ||
382 | DUMB_RQ_N_LEVELS. All the values from 0 to DUMB_RQ_N_LEVELS - 1 will be | ||
383 | valid resampling levels. If a value outside this range is chosen, it is | ||
384 | not the end of the world; DUMB will behave as if you had chosen the value | ||
385 | at whichever extreme you went beyond. | ||
386 | |||
387 | dumb_it_max_to_mix, defaulting to 64, is the maximum number of samples | ||
388 | DUMB will ever mix together when playing an IT, XM, S3M or MOD file. | ||
389 | Unlike many other music systems, DUMB will still keep track of all samples | ||
390 | (up to a fixed maximum of 256 of them, roughly speaking), and then will | ||
391 | just render as many of them as this variable permits, starting with the | ||
392 | loudest ones. When samples are cut or come back in, the exact timings will | ||
393 | not generally be predictable - but nor will they be important. | ||
394 | |||
395 | dumb_it_max_to_mix applies to each currently playing module file | ||
396 | independently. So if you set it to 64, but render two modules | ||
397 | simultaneously, DUMB could end up mixing up to 128 samples. | ||
398 | |||
399 | |||
400 | To pause and resume playback, set the volume, get the current playback | ||
401 | position, or get the length of time a DUH will play for before either looping | ||
402 | or freezing (effect F00 in XM and MOD files, which means no new notes will be | ||
403 | played but any existing notes will continue): | ||
404 | |||
405 | void al_pause_duh(AL_DUH_PLAYER *dp); | ||
406 | void al_resume_duh(AL_DUH_PLAYER *dp); | ||
407 | void al_duh_set_volume(AL_DUH_PLAYER *dp, float volume); | ||
408 | long al_duh_get_position(AL_DUH_PLAYER *dp); | ||
409 | |||
410 | long duh_get_length(DUH *duh); | ||
411 | |||
412 | These functions are pretty self-explanatory. The volume passed to | ||
413 | al_duh_set_volume() and the position returned by al_duh_get_position() are | ||
414 | in the same units as those you passed to al_start_duh(). The length | ||
415 | returned by duh_get_length() is in the same units as the aforementioned | ||
416 | position; see dumb.txt for more information on this function. Be careful | ||
417 | with al_duh_get_position(); it will return a position slightly ahead of | ||
418 | what you can hear, because the system has to keep ahead slightly to avoid | ||
419 | stuttering. | ||
420 | |||
421 | |||
422 | To prevent the music from looping and/or freezing: | ||
423 | |||
424 | DUH_SIGRENDERER *al_duh_get_sigrenderer(AL_DUH_PLAYER *dp); | ||
425 | DUMB_IT_SIGRENDERER *duh_get_it_sigrenderer(DUH_SIGRENDERER *sigrenderer); | ||
426 | |||
427 | void dumb_it_set_loop_callback(DUMB_IT_SIGRENDERER *sigrenderer, | ||
428 | int (*callback)(void *data), void *data); | ||
429 | void dumb_it_set_xm_speed_zero_callback(DUMB_IT_SIGRENDERER *sigrenderer, | ||
430 | int (*callback)(void *data), void *data); | ||
431 | |||
432 | int dumb_it_callback_terminate(void *data); | ||
433 | |||
434 | If you are unfamiliar with function pointers, please see fnptr.txt. | ||
435 | |||
436 | Note that these functions apply to IT, XM, S3M and MOD files - not just to | ||
437 | IT files. This holds true throughout DUMB, for all functions with "it" in | ||
438 | the name. The xm_speed_zero event can only occur with XM and MOD files. | ||
439 | |||
440 | The first two functions will return a pointer to a struct contained by the | ||
441 | struct you pass. This system is necessary to ensure that these operations | ||
442 | are possible when not using Allegro. Typically you would write the | ||
443 | following code: | ||
444 | |||
445 | { | ||
446 | DUH_SIGRENDERER *sr = al_duh_get_sigrenderer(dp); | ||
447 | DUMB_IT_SIGRENDERER *itsr = duh_get_it_sigrenderer(sigrenderer); | ||
448 | dumb_it_set_loop_callback(itsr, &dumb_it_callback_terminate, NULL); | ||
449 | dumb_it_set_xm_speed_zero_callback | ||
450 | (itsr, &dumb_it_callback_terminate, NULL); | ||
451 | } | ||
452 | |||
453 | Once you have done this, the return value of al_poll_duh() becomes | ||
454 | significant. It will be 0 as long as the music is playing. When the music | ||
455 | stops, al_poll_duh() will return nonzero. You can call al_stop_duh() and | ||
456 | do something else as soon as you wish, but calling al_poll_duh() some more | ||
457 | will not do any harm. | ||
458 | |||
459 | al_poll_duh() will also return 1 if the music could not be loaded, or if | ||
460 | memory was short when trying to play it, or if it was a quirky music file | ||
461 | with no music in it (technically one with an empty order list). This | ||
462 | happens regardless of whether or not you execute the above code to disable | ||
463 | looping. Normally you shouldn't need to worry about this. | ||
464 | |||
465 | To undo the above and make DUMB loop or freeze again, pass NULL instead of | ||
466 | &dumb_it_callback_terminate. If you would like to fade on looping, or loop | ||
467 | a finite number of times, or display a message when looping, or whatever, | ||
468 | you will have to write your own callback function. In this case, please | ||
469 | see dumb.txt. | ||
470 | |||
471 | Note that the above code can safely be applied for a DUH that doesn't | ||
472 | contain a music module but contains some other kind of music. | ||
473 | duh_get_it_sigrenderer() will return NULL, and the code will do nothing. | ||
474 | |||
475 | |||
476 | To analyse the audio as it's generated: | ||
477 | |||
478 | typedef int sample_t; | ||
479 | |||
480 | typedef void (*DUH_SIGRENDERER_ANALYSER_CALLBACK)(void *data, | ||
481 | const sample_t *const *samples, int n_channels, long length); | ||
482 | |||
483 | void duh_sigrenderer_set_analyser_callback(DUH_SIGRENDERER *sigrenderer, | ||
484 | DUH_SIGRENDERER_ANALYSER_CALLBACK callback, void *data); | ||
485 | |||
486 | If the above confuses you, see fnptr.txt. These functions, along with | ||
487 | al_duh_get_sigrenderer() from the last section, enable you to register a | ||
488 | callback function. Every time some samples are generated, they will be | ||
489 | passed to this function. This enables you to display an oscilloscope or | ||
490 | spectrum analyser, for example. | ||
491 | |||
492 | Beware: your callback function may occasionally be called with | ||
493 | samples == NULL. This means the main program has decided to skip through | ||
494 | the music without generating any data. You should handle this case | ||
495 | elegantly, typically by returning immediately, but you may wish to make a | ||
496 | note of the fact that the music is being skipped, for whatever reason. | ||
497 | |||
498 | Beware again: if the main program ever calls duh_sigrenderer_get_samples() | ||
499 | on a buffer that isn't all silence, this callback function will be passed | ||
500 | the existing buffer after mixing, and thus it will include the original | ||
501 | data. This will not be an issue if you stick to duh_render(), which always | ||
502 | starts with a buffer filled with silence. | ||
503 | |||
504 | The samples array is two-dimensional. Refer to it as follows: | ||
505 | |||
506 | samples[channel_number][sample_position] | ||
507 | |||
508 | where 0 <= channel_number < n_channels, | ||
509 | and 0 <= sample_position < length. | ||
510 | |||
511 | In addition you can pass any 'data' pointer you like to | ||
512 | duh_sigrenderer_set_analyser_callback(), and this pointer will be relayed | ||
513 | to your callback function each time. | ||
514 | |||
515 | To remove the callback function, pass NULL to | ||
516 | duh_sigrenderer_set_analyser_callback(). | ||
517 | |||
518 | |||
519 | Everything below this point assumes some knowledge of how a music module is | ||
520 | constructed. If you do not have this knowledge, talk to whoever is writing | ||
521 | music for you, or download a tracking program and play with it (see | ||
522 | readme.txt). | ||
523 | |||
524 | |||
525 | To start playing an IT, XM, S3M or MOD from an arbitrary order number (the | ||
526 | default being 0, the beginning of the song), use the following: | ||
527 | |||
528 | DUH_SIGRENDERER *dumb_it_start_at_order | ||
529 | (DUH *duh, int n_channels, int startorder); | ||
530 | AL_DUH_PLAYER *al_duh_encapsulate_sigrenderer | ||
531 | (DUH_SIGRENDERER *sigrenderer, float volume, long bufsize, int freq); | ||
532 | |||
533 | The usage of these functions is as follows: | ||
534 | |||
535 | { | ||
536 | DUH_SIGRENDERER *sr = dumb_it_start_at_order | ||
537 | (duh, n_channels, startorder); | ||
538 | dp = al_duh_encapsulate_sigrenderer(sr, volume, bufsize, freq); | ||
539 | } | ||
540 | |||
541 | Replace 'dp' with whatever your AL_DUH_PLAYER pointer is. You also need | ||
542 | to insert suitable values for n_channels, startorder, volume, bufsize and | ||
543 | freq. These have the same meaning as those passed to al_start_duh(). | ||
544 | |||
545 | WARNING: after passing a pointer to an "encapsulate" function, do not use | ||
546 | that pointer again. (More specifically, do not use it again if | ||
547 | the function returns NULL, because the function will have | ||
548 | destroyed the pointer if this happens, to help prevent memory | ||
549 | leaks.) There will be a "get" function with which you can obtain | ||
550 | the original pointer if it is still valid, or NULL otherwise. | ||
551 | |||
552 | The above functions will fail (safely) if you try to use them with a DUH | ||
553 | that contains a different type of music. | ||
554 | |||
555 | Notice that there is no 'pos' parameter. If you would like to skip through | ||
556 | the music, you can use this function: | ||
557 | |||
558 | long duh_sigrenderer_get_samples( | ||
559 | DUH_SIGRENDERER *sigrenderer, | ||
560 | float volume, float delta, | ||
561 | long size, sample_t **samples | ||
562 | ); | ||
563 | |||
564 | Pass 0 for volume and NULL for samples, and this function will skip | ||
565 | through the music nice and quickly. So insert the following between the | ||
566 | two above statements: | ||
567 | |||
568 | duh_sigrenderer_get_samples(sr, 0, 65536.0f / freq, pos, NULL); | ||
569 | |||
570 | Substitute for 'freq' and 'pos'. An explanation of the 'delta' parameter | ||
571 | can be found further down in this file. | ||
572 | |||
573 | Finally, note that duh_get_length() is only meaningful when you start | ||
574 | playing music from order 0. | ||
575 | |||
576 | |||
577 | If an IT file contains Zxx effects, DUMB will generate MIDI messages, which | ||
578 | will control the low-pass resonant filters unless the IT file actively | ||
579 | specifies something else. In rare cases this may not be what the Zxx effects | ||
580 | were intended to do; if this is the case, you can block the MIDI messages as | ||
581 | follows. Note that this does NOT mean filters are disabled; if an instrument | ||
582 | specifies initial cut-off and resonance values, or has a filter envelope, | ||
583 | then filters will be applied. It only makes sense to use this procedure at | ||
584 | the beginning of playback. | ||
585 | |||
586 | void dumb_it_set_midi_callback(DUMB_IT_SIGRENDERER *sigrenderer, | ||
587 | int (*callback)(void *data, int channel, unsigned char byte), | ||
588 | void *data); | ||
589 | |||
590 | int dumb_it_callback_midi_block(void *data, int channel, | ||
591 | unsigned char byte); | ||
592 | |||
593 | Using some functions described in the previous section, we arrive at the | ||
594 | following code: | ||
595 | |||
596 | { | ||
597 | DUH_SIGRENDERER *sr = al_duh_get_sigrenderer(dp); | ||
598 | DUMB_IT_SIGRENDERER *itsr = duh_get_it_sigrenderer(sigrenderer); | ||
599 | dumb_it_set_midi_callback(itsr, &dumb_it_callback_midi_block, NULL); | ||
600 | } | ||
601 | |||
602 | DUMB offers no way of disabling filters completely. Disabling filters is not | ||
603 | recommended as a means to reduce processor usage, as it will completely | ||
604 | damage any piece of music that uses the filters. If you want lower processor | ||
605 | consumption, use a piece of music that does not use filters. | ||
606 | |||
607 | |||
608 | Finally, DUMB offers a myriad of functions for querying and adjusting | ||
609 | module playback. Those beginning with "dumb_it_sd" operate on the | ||
610 | DUMB_IT_SIGDATA struct, which represents the piece of music before it starts | ||
611 | to play. Those beginning with "dumb_it_sr" operate on the DUMB_IT_SIGRENDERER | ||
612 | struct, which represents a currently playing instance of the music. Note that | ||
613 | duh_get_length(), described above, becomes meaningless after some of these | ||
614 | functions are used. | ||
615 | |||
616 | The method for getting a DUMB_IT_SIGRENDERER struct has already been given, | ||
617 | but the function prototypes are repeated here for convenience: | ||
618 | |||
619 | DUH_SIGRENDERER *al_duh_get_sigrenderer(AL_DUH_PLAYER *dp); | ||
620 | DUMB_IT_SIGRENDERER *duh_get_it_sigrenderer(DUH_SIGRENDERER *sigrenderer); | ||
621 | |||
622 | Getting a DUMB_IT_SIGDATA struct is simpler: | ||
623 | |||
624 | DUMB_IT_SIGDATA *duh_get_it_sigdata(DUH *duh); | ||
625 | |||
626 | For a list of dumb_it_sd_*() and dumb_it_sr_*() functions, please see | ||
627 | dumb.txt. These functions are new, and may not provide exactly what you need; | ||
628 | if not, please let me know. | ||
629 | |||
630 | |||
631 | ************************************************** | ||
632 | *** Embedding music files in Allegro datafiles *** | ||
633 | ************************************************** | ||
634 | |||
635 | |||
636 | In this section it is assumed you are already reasonably familiar with how | ||
637 | Allegro datafiles are used. If not, please refer to Allegro's documentation. | ||
638 | At the time of writing, the documentation you need is off the beaten track, | ||
639 | so to speak, in allegro/tools/grabber.txt. | ||
640 | |||
641 | To add a piece of music to a datafile, you need to create an object of type | ||
642 | "IT ", "XM ", "S3M " or "MOD " (note the spaces used as padding, although | ||
643 | you do not need to type these into the grabber). Then grab the piece of music | ||
644 | in. The grabber will treat it as a binary object. Save the datafile as usual. | ||
645 | |||
646 | |||
647 | To use a piece of music you added to the datafile, follow these steps: | ||
648 | |||
649 | |||
650 | 1. Before loading the datafile, call one or more of these functions, | ||
651 | depending on which music format or formats you'd like to support: | ||
652 | |||
653 | dumb_register_dat_it(DUMB_DAT_IT); | ||
654 | dumb_register_dat_xm(DUMB_DAT_XM); | ||
655 | dumb_register_dat_s3m(DUMB_DAT_S3M); | ||
656 | dumb_register_dat_mod(DUMB_DAT_MOD); | ||
657 | |||
658 | Remember, do not call multiple functions unless you want to support | ||
659 | multiple formats. Calling more functions will add unused code to your | ||
660 | executable. | ||
661 | |||
662 | It is important that you make call these before loading the datafile, | ||
663 | since they tell Allegro how to load the respective files straight from | ||
664 | datafiles in the future. They will not help Allegro interpret any module | ||
665 | files that have already been loaded as binary objects (but if you really | ||
666 | need to interpret a module that has been loaded in this fashion, have a | ||
667 | look at dumbfile_open_memory() in dumb.txt). | ||
668 | |||
669 | If for whatever reason your music objects are identified by a different | ||
670 | type in the datafile, you can tell DUMB what that type is by changing the | ||
671 | parameter to the registration function above. Use Allegro's DAT_ID() | ||
672 | macro, e.g. DAT_ID('B','L','A','H'). This is not really recommended | ||
673 | though, since it would prevent a hypothetical grabber plug-in from being | ||
674 | able to play your music files. Use the above types if possible. | ||
675 | |||
676 | |||
677 | 2. Whenever you need a pointer to a DUH struct, simply use the 'dat' field. | ||
678 | Do this in the same way you would for a pointer to a BITMAP struct or | ||
679 | anything else. If it makes you feel more comfortable, you can extract the | ||
680 | pointer in advance: | ||
681 | |||
682 | DATAFILE *dat = load_datafile("smurf.dat"); | ||
683 | if (!dat) abort(); /* There are much nicer ways of handling failure! */ | ||
684 | DUH *myduh = (DUH *)dat[GAME_MUSIC].dat; | ||
685 | |||
686 | Note that the explicit (DUH *) cast is only necessary for C++, not for C. | ||
687 | However, it does no harm. | ||
688 | |||
689 | Be sure that you do NOT call unload_duh() for anything stored in the | ||
690 | datafile. These DUHs will be freed when you call unload_datafile(), and | ||
691 | freeing them twice is practically guaranteed to crash your program. | ||
692 | |||
693 | |||
694 | 3. If you only ever load music as part of a datafile, and you never load any | ||
695 | stand-alone music files, you do not need to register a file input system | ||
696 | for DUMB to use. If you followed the instructions for the first section | ||
697 | you will have one of these two lines in your program: | ||
698 | |||
699 | dumb_register_stdfiles(); | ||
700 | dumb_register_packfiles(); | ||
701 | |||
702 | You can safely delete this line - but only if you never load any | ||
703 | stand-alone music files. The debugging library will bale you out if you | ||
704 | delete it when you shouldn't; the optimised library won't. | ||
705 | |||
706 | |||
707 | ************************************* | ||
708 | *** Rendering music into a buffer *** | ||
709 | ************************************* | ||
710 | |||
711 | |||
712 | NOTE: much of the API formerly described in this section has been deprecated, | ||
713 | and you will need to alter your code. See deprec.txt for details. If | ||
714 | you are reading this section for the first time, you can ignore this | ||
715 | note. | ||
716 | |||
717 | Rendering to a buffer is similar to playing using an AL_DUH_PLAYER. However, | ||
718 | you must use a DUH_SIGRENDERER struct instead. Here are the functions: | ||
719 | |||
720 | DUH_SIGRENDERER *duh_start_sigrenderer | ||
721 | (DUH *duh, int sig, int n_channels, long pos); | ||
722 | |||
723 | int duh_sigrenderer_get_n_channels(DUH_SIGRENDERER *sigrenderer); | ||
724 | long duh_sigrenderer_get_position(DUH_SIGRENDERER *sigrenderer); | ||
725 | |||
726 | long duh_sigrenderer_get_samples(DUH_SIGRENDERER *sigrenderer, | ||
727 | float volume, float delta, long size, sample_t **samples); | ||
728 | |||
729 | long duh_render(DUH_SIGRENDERER *sigrenderer, | ||
730 | int bits, int unsign, float volume, float delta, long size, void *sptr); | ||
731 | |||
732 | void duh_end_sigrenderer(DUH_SIGRENDERER *sigrenderer); | ||
733 | |||
734 | The parameters to duh_start_sigrenderer() have the same meanings as those to | ||
735 | al_start_duh(). However, note that the volume is not set at this stage. You | ||
736 | pass the desired volume each time you want to render a block. The 'sig' | ||
737 | parameter should be set to 0 for now. | ||
738 | |||
739 | Notice that there are two rendering functions. duh_sigrenderer_get_samples() | ||
740 | will generate samples in the internal 32-bit format, with a normal range from | ||
741 | -0x800000 to 0x7FFFFF and with each channel in a separate array; duh_render() | ||
742 | will convert to 8 or 16 bits, signed or unsigned, with stereo samples | ||
743 | interleaved, left first. | ||
744 | |||
745 | When you call duh_render(), pass 8 or 16 for 'bits'. If you pass 8, 'sptr' is | ||
746 | expected to be an array of chars. If you pass 16, 'sptr' is expected to be an | ||
747 | array of shorts. Endianness therefore depends on the platform, and you should | ||
748 | not try to interpret 16-bit wave data as an array of chars (unless you're | ||
749 | writing highly system-specific code anyway). Because DUMB renders internally | ||
750 | with 32 bits, there is no significant speed increase in rendering an 8-bit | ||
751 | stream. | ||
752 | |||
753 | If you are rendering in stereo, make sure your 'sptr' array is twice as big! | ||
754 | |||
755 | If you set 'unsign' to a nonzero value, then the samples generated will be | ||
756 | centred on 0x80 or 0x8000, suitably stored in an array of unsigned chars or | ||
757 | unsigned shorts. If 'unsign' is zero, the samples will be centred on 0, | ||
758 | suitably stored in an array of signed chars or signed shorts. Note that 8-bit | ||
759 | WAV files are unsigned while 16-bit WAV files are signed. This convention was | ||
760 | used by the SoundBlaster 16 when receiving samples to be sent to the | ||
761 | speakers. If you wish to write 16-bit sample data to a WAV file, don't use | ||
762 | fwrite(); instead, take the shorts one at a time, split them up into chars as | ||
763 | follows, and write the chars to the file. | ||
764 | |||
765 | short sptr[n]; | ||
766 | char lsb = (char)sptr[n]; | ||
767 | char msb = (char)(sptr[n] >> 8); | ||
768 | |||
769 | For a 16-bit WAV file, write the LSB (less significant byte) first. | ||
770 | |||
771 | The following applies equally to duh_render() and | ||
772 | duh_sigrenderer_get_samples(), except where otherwise stated. | ||
773 | |||
774 | If you set 'delta' to 1.0f, the sound generated will be suitable for playback | ||
775 | at 65536 Hz. Increasing 'delta' causes the wave to speed up, given a constant | ||
776 | sampling rate for playback. Supposing you want to vary the playback sampling | ||
777 | rate but keep the pitch constant, here's the equation for 'delta': | ||
778 | |||
779 | delta = 65536.0f / sampling_rate; | ||
780 | |||
781 | 'size' is the number of samples you want rendered. For duh_render(), they | ||
782 | will be rendered into an array which you pass as 'sptr'. Note that stereo | ||
783 | samples count as one; so if you set n_channels to 2, your array must contain | ||
784 | (2 * size) elements. | ||
785 | |||
786 | For duh_sigrenderer_get_samples() you will have to use the following | ||
787 | functions: | ||
788 | |||
789 | sample_t **create_sample_buffer(int n_channels, long length); | ||
790 | void destroy_sample_buffer(sample_t **samples); | ||
791 | |||
792 | void dumb_silence(sample_t *samples, long length); | ||
793 | |||
794 | create_sample_buffer() allocates the channels sequentially in memory, so the | ||
795 | following technique is valid: | ||
796 | |||
797 | sample_t **samples = create_sample_buffer(n_channels, length); | ||
798 | dumb_silence(samples[0], n_channels * length); | ||
799 | |||
800 | It is necessary to fill the buffer with silence like this because | ||
801 | duh_sigrenderer_get_samples() mixes what it renders with the existing | ||
802 | contents of the buffer. | ||
803 | |||
804 | The return values from duh_render() and duh_sigrenderer_get_samples() tell | ||
805 | you how many samples were actually generated. In most cases, this will be the | ||
806 | same as the 'size' parameter. However, if you reach the end of the DUH (which | ||
807 | will happen if you disable looping or freezing as described further up), this | ||
808 | function will return less. When that happens, you can assume the stream has | ||
809 | finished. In the case of duh_render(), the remainder of the array will not | ||
810 | have been initialised, so you either have to initialise it yourself or avoid | ||
811 | using it. | ||
812 | |||
813 | If for whatever reason duh_start_sigrenderer() returns NULL, then | ||
814 | duh_render() and duh_sigrenderer_get_samples() will generate exactly 0 | ||
815 | samples, duh_sigrenderer_get_n_channels() will return 0, | ||
816 | duh_sigrenderer_get_position() will return -1, and duh_end_sigrenderer() will | ||
817 | safely do nothing. | ||
818 | |||
819 | |||
820 | ********************* | ||
821 | *** Miscellaneous *** | ||
822 | ********************* | ||
823 | |||
824 | |||
825 | Please see dumb.txt for an API reference and for information on thread safety | ||
826 | with DUMB. The API reference has been stripped down, since some functions and | ||
827 | variables are subject to change. If something does not appear in dumb.txt, | ||
828 | please do not use it. | ||
829 | |||
830 | |||
831 | ****************** | ||
832 | *** Conclusion *** | ||
833 | ****************** | ||
834 | |||
835 | |||
836 | If you have any difficulties, or if you use DUMB successfully, please don't | ||
837 | hesitate to contact me (see below). | ||
838 | |||
839 | Enjoy! | ||
840 | |||
841 | |||
842 | Ben Davis | ||
843 | entheh@users.sf.net | ||
844 | IRC EFnet #dumb | ||
845 | See readme.txt for details on using IRC. | ||