music_mad.c
author Sam Lantinga <slouken@libsdl.org>
Tue, 17 Oct 2017 02:33:47 -0700
changeset 777 92882ef2ab81
parent 769 1dfabfb5ee76
child 797 b4b6adff699a
permissions -rw-r--r--
Rewrote music.c to support any number of decode libraries using a compiled-in plugin interface
Mix_LoadWAV_RW() can now load sound formats that were previously available only as music.

This is still work in progress. Testing and project updates need to happen on other platforms.
slouken@357
     1
/*
slouken@518
     2
  SDL_mixer:  An audio mixer library based on the SDL library
slouken@725
     3
  Copyright (C) 1997-2017 Sam Lantinga <slouken@libsdl.org>
slouken@357
     4
slouken@518
     5
  This software is provided 'as-is', without any express or implied
slouken@518
     6
  warranty.  In no event will the authors be held liable for any damages
slouken@518
     7
  arising from the use of this software.
slouken@357
     8
slouken@518
     9
  Permission is granted to anyone to use this software for any purpose,
slouken@518
    10
  including commercial applications, and to alter it and redistribute it
slouken@518
    11
  freely, subject to the following restrictions:
slouken@357
    12
slouken@518
    13
  1. The origin of this software must not be misrepresented; you must not
slouken@518
    14
     claim that you wrote the original software. If you use this software
slouken@518
    15
     in a product, an acknowledgment in the product documentation would be
slouken@518
    16
     appreciated but is not required.
slouken@518
    17
  2. Altered source versions must be plainly marked as such, and must not be
slouken@518
    18
     misrepresented as being the original software.
slouken@518
    19
  3. This notice may not be removed or altered from any source distribution.
slouken@357
    20
*/
slouken@357
    21
slouken@777
    22
#ifdef MUSIC_MP3_MAD
slouken@357
    23
slouken@357
    24
#include "music_mad.h"
slouken@357
    25
slouken@777
    26
#include "mad.h"
slouken@777
    27
slouken@777
    28
#define MAD_INPUT_BUFFER_SIZE   (5*8192)
slouken@777
    29
#define MAD_OUTPUT_BUFFER_SIZE  8192
slouken@777
    30
slouken@777
    31
enum {
slouken@777
    32
    MS_input_eof      = 0x0001,
slouken@777
    33
    MS_input_error    = 0x0001,
slouken@777
    34
    MS_decode_eof     = 0x0002,
slouken@777
    35
    MS_decode_error   = 0x0004,
slouken@777
    36
    MS_error_flags    = 0x000f,
slouken@777
    37
slouken@777
    38
    MS_playing        = 0x0100,
slouken@777
    39
    MS_cvt_decoded    = 0x0200,
slouken@777
    40
};
slouken@777
    41
slouken@777
    42
typedef struct {
slouken@777
    43
    SDL_RWops *src;
slouken@777
    44
    int freesrc;
slouken@777
    45
    struct mad_stream stream;
slouken@777
    46
    struct mad_frame frame;
slouken@777
    47
    struct mad_synth synth;
slouken@777
    48
    int frames_read;
slouken@777
    49
    mad_timer_t next_frame_start;
slouken@777
    50
    int volume;
slouken@777
    51
    int status;
slouken@777
    52
    int output_begin, output_end;
slouken@777
    53
    SDL_AudioSpec mixer;
slouken@777
    54
    SDL_AudioCVT cvt;
slouken@777
    55
slouken@777
    56
    unsigned char input_buffer[MAD_INPUT_BUFFER_SIZE + MAD_BUFFER_GUARD];
slouken@777
    57
    unsigned char *output_buffer;
slouken@777
    58
} mad_data;
slouken@777
    59
slouken@777
    60
slouken@777
    61
static void *MAD_CreateFromRW(SDL_RWops *src, int freesrc)
slouken@521
    62
{
slouken@777
    63
    mad_data *mp3_mad;
slouken@357
    64
slouken@777
    65
    mp3_mad = (mad_data *)SDL_calloc(1, sizeof(mad_data));
slouken@777
    66
    if (mp3_mad) {
slouken@777
    67
        mp3_mad->src = src;
slouken@777
    68
        mp3_mad->freesrc = freesrc;
slouken@777
    69
        mad_stream_init(&mp3_mad->stream);
slouken@777
    70
        mad_frame_init(&mp3_mad->frame);
slouken@777
    71
        mad_synth_init(&mp3_mad->synth);
slouken@777
    72
        mad_timer_reset(&mp3_mad->next_frame_start);
slouken@777
    73
        mp3_mad->volume = MIX_MAX_VOLUME;
slouken@777
    74
        mp3_mad->mixer = music_spec;
slouken@777
    75
    }
slouken@777
    76
    return mp3_mad;
slouken@357
    77
}
slouken@357
    78
slouken@777
    79
static void MAD_SetVolume(void *context, int volume)
slouken@521
    80
{
slouken@777
    81
    mad_data *mp3_mad = (mad_data *)context;
slouken@777
    82
    mp3_mad->volume = volume;
slouken@357
    83
}
slouken@357
    84
slouken@357
    85
/* Starts the playback. */
slouken@777
    86
static int MAD_Play(void *context)
slouken@777
    87
{
slouken@777
    88
    mad_data *mp3_mad = (mad_data *)context;
slouken@777
    89
    mp3_mad->status |= MS_playing;
slouken@777
    90
    return 0;
slouken@357
    91
}
slouken@357
    92
slouken@357
    93
/* Returns true if the playing is engaged, false otherwise. */
slouken@777
    94
static SDL_bool MAD_IsPlaying(void *context)
slouken@777
    95
{
slouken@777
    96
    mad_data *mp3_mad = (mad_data *)context;
slouken@777
    97
    return ((mp3_mad->status & MS_playing) != 0);
slouken@357
    98
}
slouken@357
    99
slouken@777
   100
/* Reads the next frame from the file.    Returns true on success or
slouken@357
   101
   false on failure. */
slouken@357
   102
static int
slouken@357
   103
read_next_frame(mad_data *mp3_mad) {
slouken@777
   104
    if (mp3_mad->stream.buffer == NULL ||
slouken@777
   105
            mp3_mad->stream.error == MAD_ERROR_BUFLEN) {
slouken@777
   106
        size_t read_size;
slouken@777
   107
        size_t remaining;
slouken@777
   108
        unsigned char *read_start;
slouken@357
   109
slouken@777
   110
        /* There might be some bytes in the buffer left over from last
slouken@777
   111
           time.    If so, move them down and read more bytes following
slouken@777
   112
           them. */
slouken@777
   113
        if (mp3_mad->stream.next_frame != NULL) {
slouken@777
   114
            remaining = mp3_mad->stream.bufend - mp3_mad->stream.next_frame;
slouken@777
   115
            memmove(mp3_mad->input_buffer, mp3_mad->stream.next_frame, remaining);
slouken@777
   116
            read_start = mp3_mad->input_buffer + remaining;
slouken@777
   117
            read_size = MAD_INPUT_BUFFER_SIZE - remaining;
slouken@617
   118
slouken@777
   119
        } else {
slouken@777
   120
            read_size = MAD_INPUT_BUFFER_SIZE;
slouken@777
   121
            read_start = mp3_mad->input_buffer;
slouken@777
   122
            remaining = 0;
slouken@777
   123
        }
slouken@777
   124
slouken@777
   125
        /* Now read additional bytes from the input file. */
slouken@777
   126
        read_size = SDL_RWread(mp3_mad->src, read_start, 1, read_size);
slouken@777
   127
slouken@777
   128
        if (read_size == 0) {
slouken@777
   129
            if ((mp3_mad->status & (MS_input_eof | MS_input_error)) == 0) {
slouken@777
   130
                /* FIXME: how to detect error? */
slouken@777
   131
                mp3_mad->status |= MS_input_eof;
slouken@777
   132
slouken@777
   133
                /* At the end of the file, we must stuff MAD_BUFFER_GUARD
slouken@777
   134
                   number of 0 bytes. */
slouken@777
   135
                SDL_memset(read_start + read_size, 0, MAD_BUFFER_GUARD);
slouken@777
   136
                read_size += MAD_BUFFER_GUARD;
slouken@777
   137
            }
slouken@777
   138
        }
slouken@777
   139
slouken@777
   140
        /* Now feed those bytes into the libmad stream. */
slouken@777
   141
        mad_stream_buffer(&mp3_mad->stream, mp3_mad->input_buffer,
slouken@777
   142
                                            read_size + remaining);
slouken@777
   143
        mp3_mad->stream.error = MAD_ERROR_NONE;
slouken@617
   144
    }
slouken@617
   145
slouken@777
   146
    /* Now ask libmad to extract a frame from the data we just put in
slouken@777
   147
       its buffer. */
slouken@777
   148
    if (mad_frame_decode(&mp3_mad->frame, &mp3_mad->stream)) {
slouken@777
   149
        if (MAD_RECOVERABLE(mp3_mad->stream.error)) {
slouken@777
   150
            return 0;
slouken@617
   151
slouken@777
   152
        } else if (mp3_mad->stream.error == MAD_ERROR_BUFLEN) {
slouken@777
   153
            return 0;
slouken@617
   154
slouken@777
   155
        } else {
slouken@777
   156
            mp3_mad->status |= MS_decode_error;
slouken@777
   157
            return 0;
slouken@777
   158
        }
slouken@617
   159
    }
slouken@617
   160
slouken@777
   161
    mp3_mad->frames_read++;
slouken@777
   162
    mad_timer_add(&mp3_mad->next_frame_start, mp3_mad->frame.header.duration);
slouken@617
   163
slouken@777
   164
    return 1;
slouken@357
   165
}
slouken@357
   166
slouken@357
   167
/* Scale a MAD sample to 16 bits for output. */
slouken@357
   168
static signed int
slouken@357
   169
scale(mad_fixed_t sample) {
slouken@777
   170
    /* round */
slouken@777
   171
    sample += (1L << (MAD_F_FRACBITS - 16));
slouken@357
   172
slouken@777
   173
    /* clip */
slouken@777
   174
    if (sample >= MAD_F_ONE)
slouken@777
   175
        sample = MAD_F_ONE - 1;
slouken@777
   176
    else if (sample < -MAD_F_ONE)
slouken@777
   177
        sample = -MAD_F_ONE;
slouken@357
   178
slouken@777
   179
    /* quantize */
slouken@777
   180
    return sample >> (MAD_F_FRACBITS + 1 - 16);
slouken@357
   181
}
slouken@357
   182
slouken@777
   183
/* Once the frame has been read, copies its samples into the output buffer. */
slouken@357
   184
static void
slouken@357
   185
decode_frame(mad_data *mp3_mad) {
slouken@777
   186
    struct mad_pcm *pcm;
slouken@777
   187
    unsigned int nchannels, nsamples;
slouken@777
   188
    mad_fixed_t const *left_ch, *right_ch;
slouken@777
   189
    unsigned char *out;
slouken@777
   190
    int ret;
slouken@357
   191
slouken@777
   192
    mad_synth_frame(&mp3_mad->synth, &mp3_mad->frame);
slouken@777
   193
    pcm = &mp3_mad->synth.pcm;
slouken@357
   194
slouken@777
   195
    if ((mp3_mad->status & MS_cvt_decoded) == 0) {
slouken@777
   196
        mp3_mad->status |= MS_cvt_decoded;
slouken@357
   197
slouken@777
   198
        /* The first frame determines some key properties of the stream.
slouken@777
   199
           In particular, it tells us enough to set up the convert
slouken@777
   200
           structure now. */
slouken@777
   201
        SDL_BuildAudioCVT(&mp3_mad->cvt, AUDIO_S16, pcm->channels, mp3_mad->frame.header.samplerate, mp3_mad->mixer.format, mp3_mad->mixer.channels, mp3_mad->mixer.freq);
slouken@777
   202
    }
slouken@357
   203
slouken@777
   204
    if (!mp3_mad->output_buffer) {
slouken@777
   205
        size_t sz = MAD_OUTPUT_BUFFER_SIZE;
slouken@777
   206
        if (mp3_mad->cvt.len_mult > 1) {
slouken@777
   207
                sz *= mp3_mad->cvt.len_mult;
slouken@777
   208
        }
slouken@777
   209
        mp3_mad->output_buffer = (unsigned char *) SDL_malloc(sz);
slouken@767
   210
    }
slouken@777
   211
    out = mp3_mad->output_buffer + mp3_mad->output_end;
slouken@767
   212
slouken@777
   213
    /* pcm->samplerate contains the sampling frequency */
slouken@357
   214
slouken@777
   215
    nchannels = pcm->channels;
slouken@777
   216
    nsamples    = pcm->length;
slouken@777
   217
    left_ch     = pcm->samples[0];
slouken@777
   218
    right_ch    = pcm->samples[1];
slouken@357
   219
slouken@777
   220
    while (nsamples--) {
slouken@777
   221
        signed int sample;
slouken@357
   222
slouken@777
   223
        /* output sample(s) in 16-bit signed little-endian PCM */
slouken@357
   224
slouken@777
   225
        sample = scale(*left_ch++);
slouken@777
   226
        *out++ = ((sample >> 0) & 0xff);
slouken@777
   227
        *out++ = ((sample >> 8) & 0xff);
slouken@357
   228
slouken@777
   229
        if (nchannels == 2) {
slouken@777
   230
            sample = scale(*right_ch++);
slouken@777
   231
            *out++ = ((sample >> 0) & 0xff);
slouken@777
   232
            *out++ = ((sample >> 8) & 0xff);
slouken@777
   233
        }
slouken@357
   234
    }
slouken@357
   235
slouken@777
   236
    mp3_mad->output_end = out - mp3_mad->output_buffer;
slouken@777
   237
    /*assert(mp3_mad->output_end <= MAD_OUTPUT_BUFFER_SIZE);*/
slouken@357
   238
}
slouken@357
   239
slouken@777
   240
static int MAD_GetAudio(void *context, void *data, int bytes)
slouken@777
   241
{
slouken@777
   242
    mad_data *mp3_mad = (mad_data *)context;
slouken@777
   243
    Uint8 *stream = (Uint8 *)data;
slouken@777
   244
    int len = bytes;
slouken@777
   245
    int bytes_remaining;
slouken@777
   246
    int num_bytes;
slouken@777
   247
    Uint8 *out;
slouken@357
   248
slouken@777
   249
    if ((mp3_mad->status & MS_playing) == 0) {
slouken@777
   250
        /* We're not supposed to be playing, so send silence instead. */
slouken@777
   251
        SDL_memset(stream, 0, len);
slouken@777
   252
        return 0;
slouken@617
   253
    }
slouken@357
   254
slouken@777
   255
    out = stream;
slouken@777
   256
    bytes_remaining = len;
slouken@777
   257
    while (bytes_remaining > 0) {
slouken@777
   258
        if (mp3_mad->output_end == mp3_mad->output_begin) {
slouken@777
   259
            /* We need to get a new frame. */
slouken@777
   260
            mp3_mad->output_begin = 0;
slouken@777
   261
            mp3_mad->output_end = 0;
slouken@777
   262
            if (!read_next_frame(mp3_mad)) {
slouken@777
   263
                if ((mp3_mad->status & MS_error_flags) != 0) {
slouken@777
   264
                    /* Couldn't read a frame; either an error condition or
slouken@777
   265
                       end-of-file.    Stop. */
slouken@777
   266
                    SDL_memset(out, 0, bytes_remaining);
slouken@777
   267
                    mp3_mad->status &= ~MS_playing;
slouken@777
   268
                    return bytes_remaining;
slouken@777
   269
                }
slouken@777
   270
            } else {
slouken@777
   271
                decode_frame(mp3_mad);
slouken@777
   272
slouken@777
   273
                /* Now convert the frame data to the appropriate format for
slouken@777
   274
                   output. */
slouken@777
   275
                mp3_mad->cvt.buf = mp3_mad->output_buffer;
slouken@777
   276
                mp3_mad->cvt.len = mp3_mad->output_end;
slouken@777
   277
slouken@777
   278
                mp3_mad->output_end = (int)(mp3_mad->output_end * mp3_mad->cvt.len_ratio);
slouken@777
   279
                /*assert(mp3_mad->output_end <= MAD_OUTPUT_BUFFER_SIZE);*/
slouken@777
   280
                SDL_ConvertAudio(&mp3_mad->cvt);
slouken@777
   281
            }
slouken@777
   282
        }
slouken@777
   283
slouken@777
   284
        num_bytes = mp3_mad->output_end - mp3_mad->output_begin;
slouken@777
   285
        if (bytes_remaining < num_bytes) {
slouken@777
   286
            num_bytes = bytes_remaining;
slouken@777
   287
        }
slouken@777
   288
slouken@777
   289
        if (mp3_mad->volume == MIX_MAX_VOLUME) {
slouken@777
   290
            SDL_memcpy(out, mp3_mad->output_buffer + mp3_mad->output_begin, num_bytes);
slouken@777
   291
        } else {
slouken@777
   292
            SDL_MixAudioFormat(out, mp3_mad->output_buffer + mp3_mad->output_begin,
slouken@777
   293
                               mp3_mad->mixer.format, num_bytes, mp3_mad->volume);
slouken@777
   294
        }
slouken@777
   295
        out += num_bytes;
slouken@777
   296
        mp3_mad->output_begin += num_bytes;
slouken@777
   297
        bytes_remaining -= num_bytes;
slouken@777
   298
    }
slouken@777
   299
    return 0;
slouken@777
   300
}
slouken@777
   301
slouken@777
   302
static int MAD_Seek(void *context, double position)
slouken@777
   303
{
slouken@777
   304
    mad_data *mp3_mad = (mad_data *)context;
slouken@777
   305
    mad_timer_t target;
slouken@777
   306
    int int_part;
slouken@777
   307
slouken@777
   308
    int_part = (int)position;
slouken@777
   309
    mad_timer_set(&target, int_part, (int)((position - int_part) * 1000000), 1000000);
slouken@777
   310
slouken@777
   311
    if (mad_timer_compare(mp3_mad->next_frame_start, target) > 0) {
slouken@777
   312
        /* In order to seek backwards in a VBR file, we have to rewind and
slouken@777
   313
           start again from the beginning.    This isn't necessary if the
slouken@777
   314
           file happens to be CBR, of course; in that case we could seek
slouken@777
   315
           directly to the frame we want.    But I leave that little
slouken@777
   316
           optimization for the future developer who discovers she really
slouken@777
   317
           needs it. */
slouken@777
   318
        mp3_mad->frames_read = 0;
slouken@777
   319
        mad_timer_reset(&mp3_mad->next_frame_start);
slouken@777
   320
        mp3_mad->status &= ~MS_error_flags;
slouken@777
   321
        mp3_mad->output_begin = 0;
slouken@777
   322
        mp3_mad->output_end = 0;
slouken@777
   323
slouken@777
   324
        SDL_RWseek(mp3_mad->src, 0, RW_SEEK_SET);
slouken@617
   325
    }
slouken@617
   326
slouken@777
   327
    /* Now we have to skip frames until we come to the right one.
slouken@777
   328
       Again, only truly necessary if the file is VBR. */
slouken@777
   329
    while (mad_timer_compare(mp3_mad->next_frame_start, target) < 0) {
slouken@777
   330
        if (!read_next_frame(mp3_mad)) {
slouken@777
   331
            if ((mp3_mad->status & MS_error_flags) != 0) {
slouken@777
   332
                /* Couldn't read a frame; either an error condition or
slouken@777
   333
                     end-of-file.    Stop. */
slouken@777
   334
                mp3_mad->status &= ~MS_playing;
slouken@777
   335
                return Mix_SetError("Seek position out of range");
slouken@777
   336
            }
slouken@777
   337
        }
slouken@617
   338
    }
slouken@777
   339
slouken@777
   340
    /* Here we are, at the beginning of the frame that contains the
slouken@777
   341
       target time.    Ehh, I say that's close enough.    If we wanted to,
slouken@777
   342
       we could get more precise by decoding the frame now and counting
slouken@777
   343
       the appropriate number of samples out of it. */
slouken@777
   344
    return 0;
slouken@357
   345
}
slouken@357
   346
slouken@777
   347
/* Stops the playback. */
slouken@777
   348
static void MAD_Stop(void *context)
slouken@777
   349
{
slouken@777
   350
    mad_data *mp3_mad = (mad_data *)context;
slouken@777
   351
    mp3_mad->status &= ~MS_playing;
slouken@357
   352
}
slouken@357
   353
slouken@777
   354
static void MAD_Delete(void *context)
slouken@777
   355
{
slouken@777
   356
    mad_data *mp3_mad = (mad_data *)context;
slouken@777
   357
    mad_stream_finish(&mp3_mad->stream);
slouken@777
   358
    mad_frame_finish(&mp3_mad->frame);
slouken@777
   359
    mad_synth_finish(&mp3_mad->synth);
slouken@777
   360
slouken@777
   361
    if (mp3_mad->freesrc) {
slouken@777
   362
        SDL_RWclose(mp3_mad->src);
slouken@777
   363
    }
slouken@777
   364
    SDL_free(mp3_mad->output_buffer);
slouken@777
   365
    SDL_free(mp3_mad);
slouken@357
   366
}
slouken@357
   367
slouken@777
   368
Mix_MusicInterface Mix_MusicInterface_MAD =
slouken@777
   369
{
slouken@777
   370
    "MAD",
slouken@777
   371
    MIX_MUSIC_MAD,
slouken@777
   372
    MUS_MP3,
slouken@777
   373
    SDL_FALSE,
slouken@777
   374
    SDL_FALSE,
slouken@357
   375
slouken@777
   376
    NULL,     /* Load */
slouken@777
   377
    NULL,     /* Open */
slouken@777
   378
    MAD_CreateFromRW,
slouken@777
   379
    NULL,     /* CreateFromFile */
slouken@777
   380
    MAD_SetVolume,
slouken@777
   381
    MAD_Play,
slouken@777
   382
    MAD_IsPlaying,
slouken@777
   383
    MAD_GetAudio,
slouken@777
   384
    MAD_Seek,
slouken@777
   385
    NULL,     /* Pause */
slouken@777
   386
    NULL,     /* Resume */
slouken@777
   387
    MAD_Stop,
slouken@777
   388
    MAD_Delete,
slouken@777
   389
    NULL,     /* Close */
slouken@777
   390
    NULL,     /* Unload */
slouken@777
   391
};
slouken@777
   392
slouken@777
   393
#endif /* MUSIC_MP3_MAD */
slouken@777
   394
slouken@777
   395
/* vi: set ts=4 sw=4 expandtab: */