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