music_mpg.c
author Ozkan Sezer <sezeroz@gmail.com>
Thu, 11 Oct 2018 11:50:10 +0300
branchSDL-1.2
changeset 902 6c862e733898
permissions -rw-r--r--
remove smpeg support completely and backport libmpg123 support instead.
     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 */