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