music_mpg.c
branchSDL-1.2
changeset 902 6c862e733898
equal deleted inserted replaced
898:b5d14516f1df 902:6c862e733898
       
     1 /*
       
     2   SDL_mixer:    An audio mixer library based on the SDL 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 #ifdef MP3_MUSIC
       
    23 
       
    24 #include "SDL_mixer.h"
       
    25 #include "music_mpg.h"
       
    26 #include "dynamic_mp3.h"
       
    27 
       
    28 static int
       
    29 snd_format_to_mpg123(uint16_t sdl_fmt)
       
    30 {
       
    31     switch (sdl_fmt)
       
    32     {
       
    33         case AUDIO_U8:     return MPG123_ENC_UNSIGNED_8;
       
    34         case AUDIO_U16SYS: return MPG123_ENC_UNSIGNED_16;
       
    35         case AUDIO_S8:     return MPG123_ENC_SIGNED_8;
       
    36         case AUDIO_S16SYS: return MPG123_ENC_SIGNED_16;
       
    37     }
       
    38     return -1;
       
    39 }
       
    40 
       
    41 static Uint16
       
    42 mpg123_format_to_sdl(int fmt)
       
    43 {
       
    44     switch (fmt)
       
    45     {
       
    46         case MPG123_ENC_UNSIGNED_8:  return AUDIO_U8;
       
    47         case MPG123_ENC_UNSIGNED_16: return AUDIO_U16SYS;
       
    48         case MPG123_ENC_SIGNED_8:    return AUDIO_S8;
       
    49         case MPG123_ENC_SIGNED_16:   return AUDIO_S16SYS;
       
    50     }
       
    51     return -1;
       
    52 }
       
    53 
       
    54 static char const*
       
    55 mpg123_format_str(int fmt)
       
    56 {
       
    57     switch (fmt)
       
    58     {
       
    59 #define f(x) case x: return #x;
       
    60         f(MPG123_ENC_UNSIGNED_8)
       
    61         f(MPG123_ENC_UNSIGNED_16)
       
    62         f(MPG123_ENC_SIGNED_8)
       
    63         f(MPG123_ENC_SIGNED_16)
       
    64 #undef f
       
    65     }
       
    66     return "unknown";
       
    67 }
       
    68 
       
    69 static char const*
       
    70 mpg_err(mpg123_handle* mpg, int code)
       
    71 {
       
    72     char const* err = "unknown error";
       
    73 
       
    74     if (mpg && code == MPG123_ERR) {
       
    75         err = mpg123.mpg123_strerror(mpg);
       
    76     } else {
       
    77         err = mpg123.mpg123_plain_strerror(code);
       
    78     }
       
    79 
       
    80     return err;
       
    81 }
       
    82 
       
    83 /* we're gonna override mpg123's I/O with these wrappers for RWops */
       
    84 static
       
    85 ssize_t rwops_read(void* p, void* dst, size_t n) {
       
    86     return (ssize_t)SDL_RWread((SDL_RWops*)p, dst, 1, n);
       
    87 }
       
    88 
       
    89 static
       
    90 off_t rwops_seek(void* p, off_t offset, int whence) {
       
    91     return (off_t)SDL_RWseek((SDL_RWops*)p, (Sint64)offset, whence);
       
    92 }
       
    93 
       
    94 static
       
    95 void rwops_cleanup(void* p) {
       
    96     (void)p;
       
    97     /* do nothing, we will free the file later */
       
    98 }
       
    99 
       
   100 static int getsome(mpg_data* m);
       
   101 
       
   102 mpg_data*
       
   103 mpg_new_rw(SDL_RWops *src, SDL_AudioSpec* mixer, int freesrc)
       
   104 {
       
   105     int fmt, result;
       
   106     mpg_data* m = NULL;
       
   107 
       
   108     if (!Mix_Init(MIX_INIT_MP3)) {
       
   109         goto fail;
       
   110     }
       
   111 
       
   112     m = (mpg_data*)SDL_malloc(sizeof(mpg_data));
       
   113     if (!m) {
       
   114         SDL_OutOfMemory();
       
   115         goto fail;
       
   116     }
       
   117 
       
   118     SDL_memset(m, 0, sizeof(mpg_data));
       
   119 
       
   120     m->src = src;
       
   121     m->freesrc = freesrc;
       
   122 
       
   123     m->handle = mpg123.mpg123_new(0, &result);
       
   124     if (result != MPG123_OK) {
       
   125         goto fail;
       
   126     }
       
   127 
       
   128     result = mpg123.mpg123_replace_reader_handle(
       
   129         m->handle,
       
   130         rwops_read, rwops_seek, rwops_cleanup
       
   131     );
       
   132     if (result != MPG123_OK) {
       
   133         goto fail;
       
   134     }
       
   135 
       
   136     result = mpg123.mpg123_format_none(m->handle);
       
   137     if (result != MPG123_OK) {
       
   138         goto fail;
       
   139     }
       
   140 
       
   141     fmt = snd_format_to_mpg123(mixer->format);
       
   142     if (fmt == -1) {
       
   143         goto fail;
       
   144     }
       
   145 
       
   146     result = mpg123.mpg123_format(m->handle, mixer->freq, mixer->channels, fmt);
       
   147     if (result != MPG123_OK) {
       
   148         goto fail;
       
   149     }
       
   150 
       
   151     result = mpg123.mpg123_open_handle(m->handle, m->src);
       
   152     if (result != MPG123_OK) {
       
   153         goto fail;
       
   154     }
       
   155 
       
   156     m->volume = MIX_MAX_VOLUME;
       
   157     m->mixer = *mixer;
       
   158 
       
   159     /* hacky: read until we can figure out the format then rewind */
       
   160     while (!m->gotformat)
       
   161     {
       
   162         if (!getsome(m)) {
       
   163             goto fail;
       
   164         }
       
   165     }
       
   166 
       
   167     /* rewind */
       
   168     mpg123.mpg123_seek(m->handle, 0, SEEK_SET);
       
   169 
       
   170     m->len_available = 0;
       
   171     m->snd_available = m->cvt.buf;
       
   172 
       
   173     return m;
       
   174 
       
   175     fail:
       
   176     if (!m && freesrc) {
       
   177         SDL_RWclose(src);
       
   178     }
       
   179     mpg_delete(m);
       
   180     return NULL;
       
   181 }
       
   182 
       
   183 void
       
   184 mpg_delete(mpg_data* m)
       
   185 {
       
   186     if (!m) {
       
   187         return;
       
   188     }
       
   189 
       
   190     if (m->freesrc) {
       
   191         SDL_RWclose(m->src);
       
   192     }
       
   193 
       
   194     if (m->cvt.buf) {
       
   195         SDL_free(m->cvt.buf);
       
   196     }
       
   197 
       
   198     mpg123.mpg123_close(m->handle);
       
   199     mpg123.mpg123_delete(m->handle);
       
   200     SDL_free(m);
       
   201 }
       
   202 
       
   203 void
       
   204 mpg_start(mpg_data* m) {
       
   205     m->playing = 1;
       
   206 }
       
   207 
       
   208 void
       
   209 mpg_stop(mpg_data* m) {
       
   210     m->playing = 0;
       
   211 }
       
   212 
       
   213 int
       
   214 mpg_playing(mpg_data* m) {
       
   215     return m->playing;
       
   216 }
       
   217 
       
   218 /*
       
   219     updates the convert struct and buffer to match the format queried from
       
   220     mpg123.
       
   221 */
       
   222 static int
       
   223 update_format(mpg_data* m)
       
   224 {
       
   225     int code;
       
   226     long rate;
       
   227     int channels, encoding;
       
   228     Uint16 sdlfmt;
       
   229     size_t bufsize;
       
   230 
       
   231     m->gotformat = 1;
       
   232 
       
   233     code =
       
   234         mpg123.mpg123_getformat(
       
   235             m->handle,
       
   236             &rate, &channels, &encoding
       
   237         );
       
   238 
       
   239     if (code != MPG123_OK) {
       
   240         Mix_SetError("mpg123_getformat: %s", mpg_err(m->handle, code));
       
   241         return 0;
       
   242     }
       
   243 
       
   244     sdlfmt = mpg123_format_to_sdl(encoding);
       
   245     if (sdlfmt == (Uint16)-1)
       
   246     {
       
   247         Mix_SetError(
       
   248             "Format %s is not supported by SDL",
       
   249             mpg123_format_str(encoding)
       
   250         );
       
   251         return 0;
       
   252     }
       
   253 
       
   254     SDL_BuildAudioCVT(
       
   255         &m->cvt,
       
   256         sdlfmt, channels, rate,
       
   257         m->mixer.format,
       
   258         m->mixer.channels,
       
   259         m->mixer.freq
       
   260     );
       
   261 
       
   262     if (m->cvt.buf) {
       
   263         SDL_free(m->cvt.buf);
       
   264     }
       
   265 
       
   266     bufsize = sizeof(m->buf) * m->cvt.len_mult;
       
   267     m->cvt.buf = SDL_malloc(bufsize);
       
   268 
       
   269     if (!m->cvt.buf)
       
   270     {
       
   271         Mix_SetError("Out of memory");
       
   272         mpg_stop(m);
       
   273         return 0;
       
   274     }
       
   275 
       
   276     return 1;
       
   277 }
       
   278 
       
   279 /* read some mp3 stream data and convert it for output */
       
   280 static int
       
   281 getsome(mpg_data* m)
       
   282 {
       
   283     int code;
       
   284     size_t len = 0;
       
   285     Uint8* data = m->buf;
       
   286     const size_t cbdata = sizeof(m->buf);
       
   287     SDL_AudioCVT* cvt = &m->cvt;
       
   288 
       
   289     code = mpg123.mpg123_read(m->handle, data, cbdata, &len);
       
   290     switch (code)
       
   291     {
       
   292     case MPG123_NEW_FORMAT:
       
   293         if (!update_format(m)) {
       
   294             return 0;
       
   295         }
       
   296         break;
       
   297 
       
   298     case MPG123_DONE:
       
   299         mpg_stop(m);
       
   300     case MPG123_OK:
       
   301         break;
       
   302 
       
   303     default:
       
   304         Mix_SetError("mpg123_read: %s", mpg_err(m->handle, code));
       
   305         return 0;
       
   306     }
       
   307 
       
   308     SDL_memcpy(cvt->buf, data, cbdata);
       
   309 
       
   310     if (cvt->needed) {
       
   311         /* we need to convert the audio to SDL's format */
       
   312         cvt->len = len;
       
   313         SDL_ConvertAudio(cvt);
       
   314     }
       
   315     else {
       
   316         /* no conversion needed, just copy */
       
   317         cvt->len_cvt = len;
       
   318     }
       
   319 
       
   320     m->len_available = cvt->len_cvt;
       
   321     m->snd_available = cvt->buf;
       
   322 
       
   323     return 1;
       
   324 }
       
   325 
       
   326 int
       
   327 mpg_get_samples(mpg_data* m, Uint8 *stream, int len)
       
   328 {
       
   329     int mixable;
       
   330 
       
   331     while (len > 0 && m->playing)
       
   332     {
       
   333         if (!m->len_available)
       
   334         {
       
   335             if (!getsome(m)) {
       
   336                 m->playing = 0;
       
   337                 return len;
       
   338             }
       
   339         }
       
   340 
       
   341         mixable = len;
       
   342 
       
   343         if (mixable > m->len_available) {
       
   344             mixable = m->len_available;
       
   345         }
       
   346 
       
   347         if (m->volume == MIX_MAX_VOLUME) {
       
   348             SDL_memcpy(stream, m->snd_available, mixable);
       
   349         }
       
   350         else {
       
   351             SDL_MixAudio(
       
   352                 stream,
       
   353                 m->snd_available,
       
   354                 mixable,
       
   355                 m->volume
       
   356             );
       
   357         }
       
   358 
       
   359         m->len_available -= mixable;
       
   360         m->snd_available += mixable;
       
   361         len -= mixable;
       
   362         stream += mixable;
       
   363     }
       
   364 
       
   365     return len;
       
   366 }
       
   367 
       
   368 void
       
   369 mpg_seek(mpg_data* m, double secs)
       
   370 {
       
   371     off_t offset = m->mixer.freq * secs;
       
   372 
       
   373     if ((offset = mpg123.mpg123_seek(m->handle, offset, SEEK_SET)) < 0) {
       
   374         Mix_SetError("mpg123_seek: %s", mpg_err(m->handle, -offset));
       
   375     }
       
   376 }
       
   377 
       
   378 void
       
   379 mpg_volume(mpg_data* m, int volume) {
       
   380     m->volume = volume;
       
   381 }
       
   382 
       
   383 #endif    /* MP3_MUSIC */