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