playwave.c
author Vitaly Novichkov
Mon, 23 Dec 2019 14:01:02 +0300
changeset 1119 24ca9a03d51c
parent 1046 ff8e95ea57ec
child 1129 888c7be704ce
permissions -rw-r--r--
introduce MetaTags api

In some games or players it would be possible to print a title and
some minor tags about playing music.

There are 5 added functions which can give meta-tags from music if
they are available or supported by a codec:
- Mix_GetMusicTitle() gives song title.
Unlike Mix_GetMusicTitleTag, returns filename if tag is blank.
- Mix_GetMusicTitleTag() gives song title
- Mix_GetMusicArtistTag() gives artist name
- Mix_GetMusicAlbumTag() gives album name
- Mix_GetMusicCopyrightTag() gives copyright tag
     1 /*
     2   PLAYWAVE:  A test application for the SDL mixer library.
     3   Copyright (C) 1997-2019 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     SDL_version linked;
   122 
   123     SDL_VERSION(&compiled);
   124     SDL_GetVersion(&linked);
   125     output_versions("SDL", &compiled, &linked);
   126 
   127     SDL_MIXER_VERSION(&compiled);
   128     SDL_memcpy(&linked, Mix_Linked_Version(), sizeof(SDL_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", (void*)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 Sint8 leftincr = -1;
   164     static Sint8 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             }
   180             leftincr *= -1;
   181         }
   182 
   183         if ((rightvol == 255) || (rightvol == 0)) {
   184             if (rightvol == 255) {
   185                 SDL_Log("All the way in the right speaker.\n");
   186             }
   187             rightincr *= -1;
   188         }
   189 
   190         leftvol += leftincr;
   191         rightvol += rightincr;
   192         next_panning_update = SDL_GetTicks() + 10;
   193     }
   194 }
   195 #endif
   196 
   197 
   198 #if (defined TEST_MIX_DISTANCE)
   199 static void do_distance_update(void)
   200 {
   201     static Uint8 distance = 1;
   202     static Sint8 distincr = 1;
   203     static int distanceok = 1;
   204     static Uint32 next_distance_update = 0;
   205 
   206     if ((distanceok) && (SDL_GetTicks() >= next_distance_update)) {
   207         distanceok = Mix_SetDistance(0, distance);
   208         if (!distanceok) {
   209             SDL_Log("Mix_SetDistance(0, %d) failed!\n", (int) distance);
   210             SDL_Log("Reason: [%s].\n", Mix_GetError());
   211         }
   212 
   213         if (distance == 0) {
   214             SDL_Log("Distance at nearest point.\n");
   215             distincr *= -1;
   216         }
   217         else if (distance == 255) {
   218             SDL_Log("Distance at furthest point.\n");
   219             distincr *= -1;
   220         }
   221 
   222         distance += distincr;
   223         next_distance_update = SDL_GetTicks() + 15;
   224     }
   225 }
   226 #endif
   227 
   228 
   229 #if (defined TEST_MIX_POSITION)
   230 static void do_position_update(void)
   231 {
   232     static Sint16 distance = 1;
   233     static Sint8 distincr = 1;
   234     static Sint16 angle = 0;
   235     static Sint8 angleincr = 1;
   236     static int positionok = 1;
   237     static Uint32 next_position_update = 0;
   238 
   239     if ((positionok) && (SDL_GetTicks() >= next_position_update)) {
   240         positionok = Mix_SetPosition(0, angle, (Uint8)distance);
   241         if (!positionok) {
   242             SDL_Log("Mix_SetPosition(0, %d, %d) failed!\n",
   243                     (int) angle, (int) distance);
   244             SDL_Log("Reason: [%s].\n", Mix_GetError());
   245         }
   246 
   247         if (angle == 0) {
   248             SDL_Log("Due north; now rotating clockwise...\n");
   249             angleincr = 1;
   250         }
   251 
   252         else if (angle == 360) {
   253             SDL_Log("Due north; now rotating counter-clockwise...\n");
   254             angleincr = -1;
   255         }
   256 
   257         distance += distincr;
   258         if (distance < 0) {
   259             distance = 0;
   260             distincr = 3;
   261             SDL_Log("Distance is very, very near. Stepping away by threes...\n");
   262         } else if (distance > 255) {
   263             distance = 255;
   264             distincr = -3;
   265             SDL_Log("Distance is very, very far. Stepping towards by threes...\n");
   266         }
   267 
   268         angle += angleincr;
   269         next_position_update = SDL_GetTicks() + 30;
   270     }
   271 }
   272 #endif
   273 
   274 
   275 static void CleanUp(int exitcode)
   276 {
   277     if (wave) {
   278         Mix_FreeChunk(wave);
   279         wave = NULL;
   280     }
   281     if (audio_open) {
   282         Mix_CloseAudio();
   283         audio_open = 0;
   284     }
   285     SDL_Quit();
   286 
   287     exit(exitcode);
   288 }
   289 
   290 
   291 static void Usage(char *argv0)
   292 {
   293     SDL_Log("Usage: %s [-8] [-f32] [-r rate] [-c channels] [-f] [-F] [-l] [-m] <wavefile>\n", argv0);
   294 }
   295 
   296 
   297 /*
   298  * rcg06182001 This is sick, but cool.
   299  *
   300  *  Actually, it's meant to be an example of how to manipulate a voice
   301  *  without having to use the mixer effects API. This is more processing
   302  *  up front, but no extra during the mixing process. Also, in a case like
   303  *  this, when you need to touch the whole sample at once, it's the only
   304  *  option you've got. And, with the effects API, you are altering a copy of
   305  *  the original sample for each playback, and thus, your changes aren't
   306  *  permanent; here, you've got a reversed sample, and that's that until
   307  *  you either reverse it again, or reload it.
   308  */
   309 static void flip_sample(Mix_Chunk *wave)
   310 {
   311     Uint16 format;
   312     int channels, i, incr;
   313     Uint8 *start = wave->abuf;
   314     Uint8 *end = wave->abuf + wave->alen;
   315 
   316     Mix_QuerySpec(NULL, &format, &channels);
   317     incr = (format & 0xFF) * channels;
   318 
   319     end -= incr;
   320 
   321     switch (incr) {
   322         case 8:
   323             for (i = wave->alen / 2; i >= 0; i -= 1) {
   324                 Uint8 tmp = *start;
   325                 *start = *end;
   326                 *end = tmp;
   327                 start++;
   328                 end--;
   329             }
   330             break;
   331 
   332         case 16:
   333             for (i = wave->alen / 2; i >= 0; i -= 2) {
   334                 Uint16 tmp = *start;
   335                 *((Uint16 *) start) = *((Uint16 *) end);
   336                 *((Uint16 *) end) = tmp;
   337                 start += 2;
   338                 end -= 2;
   339             }
   340             break;
   341 
   342         case 32:
   343             for (i = wave->alen / 2; i >= 0; i -= 4) {
   344                 Uint32 tmp = *start;
   345                 *((Uint32 *) start) = *((Uint32 *) end);
   346                 *((Uint32 *) end) = tmp;
   347                 start += 4;
   348                 end -= 4;
   349             }
   350             break;
   351 
   352         default:
   353             SDL_Log("Unhandled format in sample flipping.\n");
   354             return;
   355     }
   356 }
   357 
   358 
   359 int main(int argc, char *argv[])
   360 {
   361     int audio_rate;
   362     Uint16 audio_format;
   363     int audio_channels;
   364     int loops = 0;
   365     int i;
   366     int reverse_stereo = 0;
   367     int reverse_sample = 0;
   368 
   369     (void) argc;
   370 
   371 #ifdef HAVE_SETBUF
   372     setbuf(stdout, NULL);    /* rcg06132001 for debugging purposes. */
   373     setbuf(stderr, NULL);    /* rcg06192001 for debugging purposes, too. */
   374 #endif
   375     output_test_warnings();
   376 
   377     /* Initialize variables */
   378     audio_rate = MIX_DEFAULT_FREQUENCY;
   379     audio_format = MIX_DEFAULT_FORMAT;
   380     audio_channels = MIX_DEFAULT_CHANNELS;
   381 
   382     /* Check command line usage */
   383     for (i=1; argv[i] && (*argv[i] == '-'); ++i) {
   384         if ((strcmp(argv[i], "-r") == 0) && argv[i+1]) {
   385             ++i;
   386             audio_rate = atoi(argv[i]);
   387         } else
   388         if (strcmp(argv[i], "-m") == 0) {
   389             audio_channels = 1;
   390         } else
   391         if ((strcmp(argv[i], "-c") == 0) && argv[i+1]) {
   392             ++i;
   393             audio_channels = atoi(argv[i]);
   394         } else
   395         if (strcmp(argv[i], "-l") == 0) {
   396             loops = -1;
   397         } else
   398         if (strcmp(argv[i], "-8") == 0) {
   399             audio_format = AUDIO_U8;
   400         } else
   401         if (strcmp(argv[i], "-f32") == 0) {
   402             audio_format = AUDIO_F32;
   403         } else
   404         if (strcmp(argv[i], "-f") == 0) { /* rcg06122001 flip stereo */
   405             reverse_stereo = 1;
   406         } else
   407         if (strcmp(argv[i], "-F") == 0) { /* rcg06172001 flip sample */
   408             reverse_sample = 1;
   409         } else {
   410             Usage(argv[0]);
   411             return(1);
   412         }
   413     }
   414     if (! argv[i]) {
   415         Usage(argv[0]);
   416         return(1);
   417     }
   418 
   419     /* Initialize the SDL library */
   420     if (SDL_Init(SDL_INIT_AUDIO) < 0) {
   421         SDL_Log("Couldn't initialize SDL: %s\n",SDL_GetError());
   422         return(255);
   423     }
   424 #ifdef HAVE_SIGNAL_H
   425     signal(SIGINT, CleanUp);
   426     signal(SIGTERM, CleanUp);
   427 #endif
   428 
   429     /* Open the audio device */
   430     if (Mix_OpenAudio(audio_rate, audio_format, audio_channels, 4096) < 0) {
   431         SDL_Log("Couldn't open audio: %s\n", SDL_GetError());
   432         CleanUp(2);
   433     } else {
   434         Mix_QuerySpec(&audio_rate, &audio_format, &audio_channels);
   435         SDL_Log("Opened audio at %d Hz %d bit%s %s", audio_rate,
   436             (audio_format&0xFF),
   437             (SDL_AUDIO_ISFLOAT(audio_format) ? " (float)" : ""),
   438             (audio_channels > 2) ? "surround" :
   439             (audio_channels > 1) ? "stereo" : "mono");
   440         if (loops) {
   441           SDL_Log(" (looping)\n");
   442         } else {
   443           putchar('\n');
   444         }
   445     }
   446     audio_open = 1;
   447 
   448 #if (defined TEST_MIX_VERSIONS)
   449     test_versions();
   450 #endif
   451 
   452 #if (defined TEST_MIX_DECODERS)
   453     report_decoders();
   454 #endif
   455 
   456     /* Load the requested wave file */
   457     wave = Mix_LoadWAV(argv[i]);
   458     if (wave == NULL) {
   459         SDL_Log("Couldn't load %s: %s\n",
   460                         argv[i], SDL_GetError());
   461         CleanUp(2);
   462     }
   463 
   464     if (reverse_sample) {
   465         flip_sample(wave);
   466     }
   467 
   468 #ifdef TEST_MIX_CHANNELFINISHED  /* rcg06072001 */
   469     Mix_ChannelFinished(channel_complete_callback);
   470 #endif
   471 
   472     if ((!Mix_SetReverseStereo(MIX_CHANNEL_POST, reverse_stereo)) &&
   473          (reverse_stereo))
   474     {
   475         SDL_Log("Failed to set up reverse stereo effect!\n");
   476         SDL_Log("Reason: [%s].\n", Mix_GetError());
   477     }
   478 
   479     /* Play and then exit */
   480     Mix_PlayChannel(0, wave, loops);
   481 
   482     while (still_playing()) {
   483 
   484 #if (defined TEST_MIX_PANNING)  /* rcg06132001 */
   485         do_panning_update();
   486 #endif
   487 
   488 #if (defined TEST_MIX_DISTANCE) /* rcg06192001 */
   489         do_distance_update();
   490 #endif
   491 
   492 #if (defined TEST_MIX_POSITION) /* rcg06202001 */
   493         do_position_update();
   494 #endif
   495 
   496         SDL_Delay(1);
   497 
   498     } /* while still_playing() loop... */
   499 
   500     CleanUp(0);
   501 
   502     /* Not reached, but fixes compiler warnings */
   503     return 0;
   504 }
   505 
   506 /* end of playwave.c ... */
   507 
   508 /* vi: set ts=4 sw=4 expandtab: */