playwave.c
author Ozkan Sezer <sezero@users.sourceforge.net>
Sat, 21 Oct 2017 17:00:00 +0300
changeset 817 aef7a1784ffd
parent 802 98ee7bc3833a
child 848 3907db698eb5
permissions -rw-r--r--
Specify SDLCALL as the calling convention for API callbacks.
This is in parallel with bug #3745.
     1 /*
     2   PLAYWAVE:  A test application for the SDL mixer library.
     3   Copyright (C) 1997-2017 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. */
    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)
    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)
   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 
   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;
   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. */
   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)
   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)
   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)
   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 
   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 
   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  */
   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 
   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: */