1 /*
2 PLAYWAVE: A test application for the SDL mixer library.
3 Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20 */
21
22 /* $Id$ */
23
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <string.h>
27
28 #ifdef unix
29 #include <unistd.h>
30 #endif
31
32 #include "SDL.h"
33 #include "SDL_mixer.h"
34
35 #ifdef HAVE_SIGNAL_H
36 #include <signal.h>
37 #endif
38
39
40 /*
41 * rcg06132001 various mixer tests. Define the ones you want.
42 */
43 /*#define TEST_MIX_DECODERS*/
44 /*#define TEST_MIX_VERSIONS*/
45 /*#define TEST_MIX_CHANNELFINISHED*/
46 /*#define TEST_MIX_PANNING*/
47 /*#define TEST_MIX_DISTANCE*/
48 /*#define TEST_MIX_POSITION*/
49
50
51 #if (defined TEST_MIX_POSITION)
52
53 #if (defined TEST_MIX_PANNING)
54 #error TEST_MIX_POSITION interferes with TEST_MIX_PANNING.
55 #endif
56
57 #if (defined TEST_MIX_DISTANCE)
58 #error TEST_MIX_POSITION interferes with TEST_MIX_DISTANCE.
59 #endif
60
61 #endif
62
63
64 /* rcg06192001 for debugging purposes. */
output_test_warnings(void)65 static void output_test_warnings(void)
66 {
67 #if (defined TEST_MIX_CHANNELFINISHED)
68 SDL_Log("Warning: TEST_MIX_CHANNELFINISHED is enabled in this binary...\n");
69 #endif
70 #if (defined TEST_MIX_PANNING)
71 SDL_Log("Warning: TEST_MIX_PANNING is enabled in this binary...\n");
72 #endif
73 #if (defined TEST_MIX_VERSIONS)
74 SDL_Log("Warning: TEST_MIX_VERSIONS is enabled in this binary...\n");
75 #endif
76 #if (defined TEST_MIX_DISTANCE)
77 SDL_Log("Warning: TEST_MIX_DISTANCE is enabled in this binary...\n");
78 #endif
79 #if (defined TEST_MIX_POSITION)
80 SDL_Log("Warning: TEST_MIX_POSITION is enabled in this binary...\n");
81 #endif
82 }
83
84
85 static int audio_open = 0;
86 static Mix_Chunk *wave = NULL;
87
88 /* rcg06042009 Report available decoders. */
89 #if (defined TEST_MIX_DECODERS)
report_decoders(void)90 static void report_decoders(void)
91 {
92 int i, total;
93
94 SDL_Log("Supported decoders...\n");
95 total = Mix_GetNumChunkDecoders();
96 for (i = 0; i < total; i++) {
97 SDL_Log(" - chunk decoder: %s\n", Mix_GetChunkDecoder(i));
98 }
99
100 total = Mix_GetNumMusicDecoders();
101 for (i = 0; i < total; i++) {
102 SDL_Log(" - music decoder: %s\n", Mix_GetMusicDecoder(i));
103 }
104 }
105 #endif
106
107 /* rcg06192001 Check new Mixer version API. */
108 #if (defined TEST_MIX_VERSIONS)
output_versions(const char * libname,const SDL_version * compiled,const SDL_version * linked)109 static void output_versions(const char *libname, const SDL_version *compiled,
110 const SDL_version *linked)
111 {
112 SDL_Log("This program was compiled against %s %d.%d.%d,\n"
113 " and is dynamically linked to %d.%d.%d.\n", libname,
114 compiled->major, compiled->minor, compiled->patch,
115 linked->major, linked->minor, linked->patch);
116 }
117
test_versions(void)118 static void test_versions(void)
119 {
120 SDL_version compiled;
121 const SDL_version *linked;
122
123 SDL_VERSION(&compiled);
124 linked = SDL_Linked_Version();
125 output_versions("SDL", &compiled, linked);
126
127 SDL_MIXER_VERSION(&compiled);
128 linked = Mix_Linked_Version();
129 output_versions("SDL_mixer", &compiled, linked);
130 }
131 #endif
132
133
134 #ifdef TEST_MIX_CHANNELFINISHED /* rcg06072001 */
135 static volatile int channel_is_done = 0;
channel_complete_callback(int chan)136 static void SDLCALL channel_complete_callback (int chan)
137 {
138 Mix_Chunk *done_chunk = Mix_GetChunk(chan);
139 SDL_Log("We were just alerted that Mixer channel #%d is done.\n", chan);
140 SDL_Log("Channel's chunk pointer is (%p).\n", done_chunk);
141 SDL_Log(" Which %s correct.\n", (wave == done_chunk) ? "is" : "is NOT");
142 channel_is_done = 1;
143 }
144 #endif
145
146
147 /* rcg06192001 abstract this out for testing purposes. */
still_playing(void)148 static int still_playing(void)
149 {
150 #ifdef TEST_MIX_CHANNELFINISHED
151 return(!channel_is_done);
152 #else
153 return(Mix_Playing(0));
154 #endif
155 }
156
157
158 #if (defined TEST_MIX_PANNING)
do_panning_update(void)159 static void do_panning_update(void)
160 {
161 static Uint8 leftvol = 128;
162 static Uint8 rightvol = 128;
163 static Uint8 leftincr = -1;
164 static Uint8 rightincr = 1;
165 static int panningok = 1;
166 static Uint32 next_panning_update = 0;
167
168 if ((panningok) && (SDL_GetTicks() >= next_panning_update)) {
169 panningok = Mix_SetPanning(0, leftvol, rightvol);
170 if (!panningok) {
171 SDL_Log("Mix_SetPanning(0, %d, %d) failed!\n",
172 (int) leftvol, (int) rightvol);
173 SDL_Log("Reason: [%s].\n", Mix_GetError());
174 }
175
176 if ((leftvol == 255) || (leftvol == 0)) {
177 if (leftvol == 255)
178 SDL_Log("All the way in the left speaker.\n");
179 leftincr *= -1;
180 }
181
182 if ((rightvol == 255) || (rightvol == 0)) {
183 if (rightvol == 255)
184 SDL_Log("All the way in the right speaker.\n");
185 rightincr *= -1;
186 }
187
188 leftvol += leftincr;
189 rightvol += rightincr;
190 next_panning_update = SDL_GetTicks() + 10;
191 }
192 }
193 #endif
194
195
196 #if (defined TEST_MIX_DISTANCE)
do_distance_update(void)197 static void do_distance_update(void)
198 {
199 static Uint8 distance = 1;
200 static Uint8 distincr = 1;
201 static int distanceok = 1;
202 static Uint32 next_distance_update = 0;
203
204 if ((distanceok) && (SDL_GetTicks() >= next_distance_update)) {
205 distanceok = Mix_SetDistance(0, distance);
206 if (!distanceok) {
207 SDL_Log("Mix_SetDistance(0, %d) failed!\n", (int) distance);
208 SDL_Log("Reason: [%s].\n", Mix_GetError());
209 }
210
211 if (distance == 0) {
212 SDL_Log("Distance at nearest point.\n");
213 distincr *= -1;
214 }
215 else if (distance == 255) {
216 SDL_Log("Distance at furthest point.\n");
217 distincr *= -1;
218 }
219
220 distance += distincr;
221 next_distance_update = SDL_GetTicks() + 15;
222 }
223 }
224 #endif
225
226
227 #if (defined TEST_MIX_POSITION)
do_position_update(void)228 static void do_position_update(void)
229 {
230 static Sint16 distance = 1;
231 static Sint8 distincr = 1;
232 static Uint16 angle = 0;
233 static Sint8 angleincr = 1;
234 static int positionok = 1;
235 static Uint32 next_position_update = 0;
236
237 if ((positionok) && (SDL_GetTicks() >= next_position_update)) {
238 positionok = Mix_SetPosition(0, angle, distance);
239 if (!positionok) {
240 SDL_Log("Mix_SetPosition(0, %d, %d) failed!\n",
241 (int) angle, (int) distance);
242 SDL_Log("Reason: [%s].\n", Mix_GetError());
243 }
244
245 if (angle == 0) {
246 SDL_Log("Due north; now rotating clockwise...\n");
247 angleincr = 1;
248 }
249
250 else if (angle == 360) {
251 SDL_Log("Due north; now rotating counter-clockwise...\n");
252 angleincr = -1;
253 }
254
255 distance += distincr;
256
257 if (distance < 0) {
258 distance = 0;
259 distincr = 3;
260 SDL_Log("Distance is very, very near. Stepping away by threes...\n");
261 } else if (distance > 255) {
262 distance = 255;
263 distincr = -3;
264 SDL_Log("Distance is very, very far. Stepping towards by threes...\n");
265 }
266
267 angle += angleincr;
268 next_position_update = SDL_GetTicks() + 30;
269 }
270 }
271 #endif
272
273
CleanUp(int exitcode)274 static void CleanUp(int exitcode)
275 {
276 if (wave) {
277 Mix_FreeChunk(wave);
278 wave = NULL;
279 }
280 if (audio_open) {
281 Mix_CloseAudio();
282 audio_open = 0;
283 }
284 SDL_Quit();
285
286 exit(exitcode);
287 }
288
289
Usage(char * argv0)290 static void Usage(char *argv0)
291 {
292 SDL_Log("Usage: %s [-8] [-f32] [-r rate] [-c channels] [-f] [-F] [-l] [-m] <wavefile>\n", argv0);
293 }
294
295
296 /*
297 * rcg06182001 This is sick, but cool.
298 *
299 * Actually, it's meant to be an example of how to manipulate a voice
300 * without having to use the mixer effects API. This is more processing
301 * up front, but no extra during the mixing process. Also, in a case like
302 * this, when you need to touch the whole sample at once, it's the only
303 * option you've got. And, with the effects API, you are altering a copy of
304 * the original sample for each playback, and thus, your changes aren't
305 * permanent; here, you've got a reversed sample, and that's that until
306 * you either reverse it again, or reload it.
307 */
flip_sample(Mix_Chunk * wave)308 static void flip_sample(Mix_Chunk *wave)
309 {
310 Uint16 format;
311 int channels, i, incr;
312 Uint8 *start = wave->abuf;
313 Uint8 *end = wave->abuf + wave->alen;
314
315 Mix_QuerySpec(NULL, &format, &channels);
316 incr = (format & 0xFF) * channels;
317
318 end -= incr;
319
320 switch (incr) {
321 case 8:
322 for (i = wave->alen / 2; i >= 0; i -= 1) {
323 Uint8 tmp = *start;
324 *start = *end;
325 *end = tmp;
326 start++;
327 end--;
328 }
329 break;
330
331 case 16:
332 for (i = wave->alen / 2; i >= 0; i -= 2) {
333 Uint16 tmp = *start;
334 *((Uint16 *) start) = *((Uint16 *) end);
335 *((Uint16 *) end) = tmp;
336 start += 2;
337 end -= 2;
338 }
339 break;
340
341 case 32:
342 for (i = wave->alen / 2; i >= 0; i -= 4) {
343 Uint32 tmp = *start;
344 *((Uint32 *) start) = *((Uint32 *) end);
345 *((Uint32 *) end) = tmp;
346 start += 4;
347 end -= 4;
348 }
349 break;
350
351 default:
352 SDL_Log("Unhandled format in sample flipping.\n");
353 return;
354 }
355 }
356
357
main(int argc,char * argv[])358 int main(int argc, char *argv[])
359 {
360 int audio_rate;
361 Uint16 audio_format;
362 int audio_channels;
363 int loops = 0;
364 int i;
365 int reverse_stereo = 0;
366 int reverse_sample = 0;
367
368 #ifdef HAVE_SETBUF
369 setbuf(stdout, NULL); /* rcg06132001 for debugging purposes. */
370 setbuf(stderr, NULL); /* rcg06192001 for debugging purposes, too. */
371 #endif
372 output_test_warnings();
373
374 /* Initialize variables */
375 audio_rate = MIX_DEFAULT_FREQUENCY;
376 audio_format = MIX_DEFAULT_FORMAT;
377 audio_channels = 2;
378
379 /* Check command line usage */
380 for (i=1; argv[i] && (*argv[i] == '-'); ++i) {
381 if ((strcmp(argv[i], "-r") == 0) && argv[i+1]) {
382 ++i;
383 audio_rate = atoi(argv[i]);
384 } else
385 if (strcmp(argv[i], "-m") == 0) {
386 audio_channels = 1;
387 } else
388 if ((strcmp(argv[i], "-c") == 0) && argv[i+1]) {
389 ++i;
390 audio_channels = atoi(argv[i]);
391 } else
392 if (strcmp(argv[i], "-l") == 0) {
393 loops = -1;
394 } else
395 if (strcmp(argv[i], "-8") == 0) {
396 audio_format = AUDIO_U8;
397 } else
398 if (strcmp(argv[i], "-f32") == 0) {
399 audio_format = AUDIO_F32;
400 } else
401 if (strcmp(argv[i], "-f") == 0) { /* rcg06122001 flip stereo */
402 reverse_stereo = 1;
403 } else
404 if (strcmp(argv[i], "-F") == 0) { /* rcg06172001 flip sample */
405 reverse_sample = 1;
406 } else {
407 Usage(argv[0]);
408 return(1);
409 }
410 }
411 if (! argv[i]) {
412 Usage(argv[0]);
413 return(1);
414 }
415
416 /* Initialize the SDL library */
417 if (SDL_Init(SDL_INIT_AUDIO) < 0) {
418 SDL_Log("Couldn't initialize SDL: %s\n",SDL_GetError());
419 return(255);
420 }
421 #ifdef HAVE_SIGNAL_H
422 signal(SIGINT, CleanUp);
423 signal(SIGTERM, CleanUp);
424 #endif
425
426 /* Open the audio device */
427 if (Mix_OpenAudio(audio_rate, audio_format, audio_channels, 4096) < 0) {
428 SDL_Log("Couldn't open audio: %s\n", SDL_GetError());
429 CleanUp(2);
430 } else {
431 Mix_QuerySpec(&audio_rate, &audio_format, &audio_channels);
432 SDL_Log("Opened audio at %d Hz %d bit%s %s", audio_rate,
433 (audio_format&0xFF),
434 (SDL_AUDIO_ISFLOAT(audio_format) ? " (float)" : ""),
435 (audio_channels > 2) ? "surround" :
436 (audio_channels > 1) ? "stereo" : "mono");
437 if (loops) {
438 SDL_Log(" (looping)\n");
439 } else {
440 putchar('\n');
441 }
442 }
443 audio_open = 1;
444
445 #if (defined TEST_MIX_VERSIONS)
446 test_versions();
447 #endif
448
449 #if (defined TEST_MIX_DECODERS)
450 report_decoders();
451 #endif
452
453 /* Load the requested wave file */
454 wave = Mix_LoadWAV(argv[i]);
455 if (wave == NULL) {
456 SDL_Log("Couldn't load %s: %s\n",
457 argv[i], SDL_GetError());
458 CleanUp(2);
459 }
460
461 if (reverse_sample) {
462 flip_sample(wave);
463 }
464
465 #ifdef TEST_MIX_CHANNELFINISHED /* rcg06072001 */
466 Mix_ChannelFinished(channel_complete_callback);
467 #endif
468
469 if ((!Mix_SetReverseStereo(MIX_CHANNEL_POST, reverse_stereo)) &&
470 (reverse_stereo))
471 {
472 SDL_Log("Failed to set up reverse stereo effect!\n");
473 SDL_Log("Reason: [%s].\n", Mix_GetError());
474 }
475
476 /* Play and then exit */
477 Mix_PlayChannel(0, wave, loops);
478
479 while (still_playing()) {
480
481 #if (defined TEST_MIX_PANNING) /* rcg06132001 */
482 do_panning_update();
483 #endif
484
485 #if (defined TEST_MIX_DISTANCE) /* rcg06192001 */
486 do_distance_update();
487 #endif
488
489 #if (defined TEST_MIX_POSITION) /* rcg06202001 */
490 do_position_update();
491 #endif
492
493 SDL_Delay(1);
494
495 } /* while still_playing() loop... */
496
497 CleanUp(0);
498
499 /* Not reached, but fixes compiler warnings */
500 return 0;
501 }
502
503 /* end of playwave.c ... */
504
505 /* vi: set ts=4 sw=4 expandtab: */
506