1 /* _______ ____ __ ___ ___
2 * \ _ \ \ / \ / \ \ / / ' ' '
3 * | | \ \ | | || | \/ | . .
4 * | | | | | | || ||\ /| |
5 * | | | | | | || || \/ | | ' ' '
6 * | | | | | | || || | | . .
7 * | |_/ / \ \__// || | |
8 * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
9 * / \
10 * / . \
11 * dumbplay.c - Not-so-simple program to play / / \ \
12 * music. It used to be simpler! | < / \_
13 * | \/ /\ /
14 * By entheh. \_ / > /
15 * | \ / /
16 * If this example does not explain everything you | ' /
17 * need to know, or you write your own code and it \__/
18 * doesn't work, please refer to docs/howto.txt for
19 * step-by-step instructions or docs/dumb.txt for a detailed description of
20 * each of DUMB's functions.
21 */
22
23 #include <stdlib.h>
24 #include <allegro.h>
25
26 #ifndef ALLEGRO_DOS
27 #include <string.h>
28 #endif
29
30 /* Note that your own programs should use <aldumb.h> not "aldumb.h". <> tells
31 * the compiler to look in the compiler's default header directory, which is
32 * where DUMB should be installed before you use it (make install does this).
33 * Use "" when it is your own header file. This example uses "" because DUMB
34 * might not have been installed yet when the makefile builds it.
35 */
36 #include "aldumb.h"
37
38
39
40 /* Hook function to be called when the close button is clicked. If the
41 * variable is set, the program will exit. Not needed in DOS.
42 */
43 #ifndef ALLEGRO_DOS
44 static volatile int closed = 0;
closehook(void)45 static void closehook(void) { closed = 1; }
46 #else
47 # define closed 0
48 #endif
49
50
51 /* Platform-specific code. We use GDI in Windows since there's no point in
52 * using an accelerated graphics driver. If we can, we define our own YIELD()
53 * function that allows the system to sleep when it has nothing to do. This
54 * may now be part of Allegro, but I haven't kept up with it. You can ignore
55 * this stuff.
56 */
57 #ifdef ALLEGRO_WINDOWS
58 # define GFX_DUMB_MODE GFX_GDI
59 # include <winalleg.h>
60 # define YIELD() Sleep(1)
61 #else
62 # define GFX_DUMB_MODE GFX_AUTODETECT_WINDOWED
63 # ifdef ALLEGRO_UNIX
64 # include <sys/time.h>
YIELD(void)65 static void YIELD(void)
66 {
67 struct timeval tv;
68 tv.tv_sec = 0;
69 tv.tv_usec = 1;
70 select(0, NULL, NULL, NULL, &tv);
71 }
72 # else
73 # define YIELD() yield_timeslice()
74 # endif
75 #endif
76
77
78
79 /* Playback flow callback functions. In DOS we don't set a graphics mode, so
80 * we can print a message to the console when a callback is invoked. On other
81 * platforms, we add a message to the window the program has created.
82 */
83 #ifdef ALLEGRO_DOS
loop_callback(void * data)84 static int loop_callback(void *data)
85 {
86 (void)data;
87 printf("Music has looped.\n");
88 return 0;
89 }
90
xm_speed_zero_callback(void * data)91 static int xm_speed_zero_callback(void *data)
92 {
93 (void)data;
94 printf("Music has stopped.\n");
95 return 0;
96 }
97 #else
98 static int gfx_half_width;
99
loop_callback(void * data)100 static int loop_callback(void *data)
101 {
102 (void)data;
103 if (gfx_half_width) {
104 acquire_screen();
105 textout_centre(screen, font, "Music has looped.", gfx_half_width, 36, 10);
106 release_screen();
107 }
108 return 0;
109 }
110
xm_speed_zero_callback(void * data)111 static int xm_speed_zero_callback(void *data)
112 {
113 (void)data;
114 if (gfx_half_width) {
115 text_mode(0); /* In case this is overwriting "Music has looped." */
116 acquire_screen();
117 textout_centre(screen, font, "Music has stopped.", gfx_half_width, 36, 10);
118 release_screen();
119 }
120 return 0;
121 }
122 #endif
123
124
125
usage(const char * exename)126 static void usage(const char *exename)
127 {
128 allegro_message(
129 #ifdef ALLEGRO_WINDOWS
130 "Usage:\n"
131 " At the command line: %s file\n"
132 " In Windows Explorer: drag a file on to this program's icon.\n"
133 #else
134 "Usage: %s file\n"
135 #endif
136 "This will play the music file specified.\n"
137 "File formats supported: IT XM S3M MOD.\n"
138 "You can control playback quality by editing dumb.ini.\n", exename);
139
140 exit(1);
141 }
142
143
144
main(int argc,const char * const * argv)145 int main(int argc, const char *const *argv) /* I'm const-crazy! */
146 {
147 DUH *duh; /* Encapsulates the music file. */
148 AL_DUH_PLAYER *dp; /* Holds the current playback state. */
149
150 /* Initialise Allegro */
151 if (allegro_init())
152 return EXIT_FAILURE;
153
154 /* Check that we have one argument (plus the executable name). */
155 if (argc != 2)
156 usage(argv[0]);
157
158 /* Tell Allegro where to find configuration data. This means you can
159 * put any settings for Allegro in dumb.ini. See Allegro's
160 * documentation for more information.
161 */
162 set_config_file("dumb.ini");
163
164 /* Initialise Allegro's keyboard input. */
165 if (install_keyboard()) {
166 allegro_message("Failed to initialise keyboard driver!\n");
167 return EXIT_FAILURE;
168 }
169
170 /* This function call is appropriate for a program that will play one
171 * sample or one audio stream at a time. If you have sound effects
172 * too, you may want to increase the parameter. See Allegro's
173 * documentation for details on what the parameter means. Note that
174 * newer versions of Allegro act as if set_volume_per_voice() was
175 * called with parameter 1 initially, while older versions behave as
176 * if -1 was passed, so you should call the function if you want
177 * consistent behaviour.
178 */
179 set_volume_per_voice(0);
180
181 /* Initialise Allegro's sound output system. */
182 if (install_sound(DIGI_AUTODETECT, MIDI_NONE, NULL)) {
183 allegro_message("Failed to initialise sound driver!\n%s\n", allegro_error);
184 return EXIT_FAILURE;
185 }
186
187 /* dumb_exit() is a function defined by DUMB. This operation arranges
188 * for dumb_exit() to be called last thing before the program exits.
189 * dumb_exit() does a bit of cleaning up for you. atexit() is
190 * declared in stdlib.h.
191 */
192 atexit(&dumb_exit);
193
194 /* DUMB defines its own wrappers for file input. There is a struct
195 * called DUMBFILE that holds function pointers for the various file
196 * operations needed by DUMB. You can decide whether to use stdio
197 * FILE objects, Allegro's PACKFILEs or something else entirely. No
198 * wrapper is installed initially, so you must call this or
199 * dumb_register_stdfiles() or set up your own before trying to load
200 * modules by file name. (If you are using another method, such as
201 * loading an Allegro datafile with modules embedded in it, then DUMB
202 * never opens a file by file name so this doesn't apply.)
203 */
204 dumb_register_packfiles();
205
206 /* Load the module file into a DUH object. Quick and dirty: try the
207 * loader for each format until one succeeds. Note that 15-sample
208 * mods have no identifying features, so dumb_load_mod() may succeed
209 * on files that aren't mods at all. We therefore try that one last.
210 */
211 duh = dumb_load_it(argv[1]);
212 if (!duh) {
213 duh = dumb_load_xm(argv[1]);
214 if (!duh) {
215 duh = dumb_load_s3m(argv[1]);
216 if (!duh) {
217 duh = dumb_load_mod(argv[1]);
218 if (!duh) {
219 allegro_message("Failed to load %s!\n", argv[1]);
220 return EXIT_FAILURE;
221 }
222 }
223 }
224 }
225
226 /* Read the quality values from the config file we told Allegro to
227 * use. You may want to hardcode these or provide a more elaborate
228 * interface via which the user can control them.
229 */
230 dumb_resampling_quality = get_config_int("sound", "dumb_resampling_quality", 4);
231 dumb_it_max_to_mix = get_config_int("sound", "dumb_it_max_to_mix", 128);
232
233 /* If we're not in DOS, show a window and register our close hook
234 * function.
235 */
236 # ifndef ALLEGRO_DOS
237 {
238 const char *fn = get_filename(argv[1]);
239 gfx_half_width = strlen(fn);
240 if (gfx_half_width < 22) gfx_half_width = 22;
241 gfx_half_width = (gfx_half_width + 2) * 4;
242
243 /* set_window_title() is not const-correct (yet). */
244 set_window_title((char *)"DUMB Music Player");
245
246 if (set_gfx_mode(GFX_DUMB_MODE, gfx_half_width*2, 80, 0, 0) == 0) {
247 acquire_screen();
248 textout_centre(screen, font, fn, gfx_half_width, 20, 14);
249 textout_centre(screen, font, "Press any key to exit.", gfx_half_width, 52, 11);
250 release_screen();
251 } else
252 gfx_half_width = 0;
253 }
254
255 /* Silly check to get around the fact that someone stupidly removed
256 * an old function from Allegro instead of deprecating it. The old
257 * function was put back a version later, but we may as well use the
258 * new one if it's there!
259 */
260 # if ALLEGRO_VERSION*10000 + ALLEGRO_SUB_VERSION*100 + ALLEGRO_WIP_VERSION >= 40105
261 set_close_button_callback(&closehook);
262 # else
263 set_window_close_hook(&closehook);
264 # endif
265
266 # endif
267
268 /* We want to continue running if the user switches to another
269 * application.
270 */
271 set_display_switch_mode(SWITCH_BACKGROUND);
272
273 /* We have the music loaded, but it isn't playing yet. This starts it
274 * playing. We construct a second object, the AL_DUH_PLAYER, to
275 * represent the playing music. This means you can play the music
276 * twice at the same time should you want to!
277 *
278 * Specify the number of channels (2 for stereo), which 'signal' to
279 * play (always 0 for modules), the volume (1.0f for default), the
280 * buffer size (4096 generally works well) and the sampling frequency
281 * (ideally match the final output frequency Allegro is using). An
282 * Allegro audio stream will be started.
283 */
284 dp = al_start_duh(duh, 2, 0, 1.0f,
285 get_config_int("sound", "buffer_size", 4096),
286 get_config_int("sound", "sound_freq", 44100));
287
288 /* Register our callback functions so that they are called when the
289 * music loops or stops. See docs/howto.txt for more information.
290 * There is no threading issue: DUMB will only process playback
291 * in al_poll_duh(), which we call below.
292 */
293 {
294 DUH_SIGRENDERER *sr = al_duh_get_sigrenderer(dp);
295 DUMB_IT_SIGRENDERER *itsr = duh_get_it_sigrenderer(sr);
296 dumb_it_set_loop_callback(itsr, &loop_callback, NULL);
297 dumb_it_set_xm_speed_zero_callback(itsr, &xm_speed_zero_callback, NULL);
298 }
299
300 /* Main loop. */
301 for (;;) {
302 /* Check for keys in the buffer. If we get one, discard it
303 * and exit the main loop.
304 */
305 if (keypressed()) {
306 readkey();
307 break;
308 }
309
310 /* Poll the music. We exit the loop if al_poll_duh() has
311 * returned nonzero (music finished) or the window has been
312 * closed. al_poll_duh() might return nonzero if you have set
313 * up a callback that tells the music to stop.
314 */
315 if (al_poll_duh(dp) || closed)
316 break;
317
318 /* Give other threads a look-in, or allow the processor to
319 * sleep for a bit. YIELD() is defined further up in this
320 * file.
321 */
322 YIELD();
323 }
324
325 /* Remove the audio stream and deallocate the memory being used for
326 * the playback state. We set dp to NULL to emphasise that the object
327 * has gone.
328 */
329 al_stop_duh(dp);
330 dp = NULL;
331
332 /* Free the DUH object containing the actual music data. */
333 unload_duh(duh);
334 duh = NULL;
335
336 /* All done! */
337 return EXIT_SUCCESS;
338 }
339 END_OF_MAIN();
340