music_mad.c
author Sam Lantinga <slouken@libsdl.org>
Mon, 12 Nov 2018 16:54:24 -0800
changeset 925 5945988b4a41
parent 891 c39a11fa853e
child 926 d6c9518fb5ee
permissions -rw-r--r--
Fixed bug 4371 - tvOS Simulator devices not listed

Caleb Cornett

In the Xcode-iOS project, when selecting the libSDL_mixer-tvOS target, no tvOS simulators appear in the available device dropdown.

This is easily fixed with the attached patch.
slouken@357
     1
/*
slouken@518
     2
  SDL_mixer:  An audio mixer library based on the SDL library
slouken@848
     3
  Copyright (C) 1997-2018 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@797
    28
slouken@797
    29
/* NOTE: The dithering functions are GPL, which should be fine if your
slouken@797
    30
         application is GPL (which would need to be true if you enabled
slouken@797
    31
         libmad support in SDL_mixer). If you're using libmad under the
slouken@797
    32
         commercial license, you need to disable this code.
slouken@797
    33
*/
slouken@797
    34
/************************ dithering functions ***************************/
slouken@797
    35
slouken@797
    36
#ifdef MUSIC_MP3_MAD_GPL_DITHERING
slouken@797
    37
slouken@797
    38
/* All dithering done here is taken from the GPL'ed xmms-mad plugin. */
slouken@797
    39
slouken@797
    40
/* Copyright (C) 1997 Makoto Matsumoto and Takuji Nishimura.       */
slouken@797
    41
/* Any feedback is very welcome. For any question, comments,       */
slouken@797
    42
/* see http://www.math.keio.ac.jp/matumoto/emt.html or email       */
slouken@797
    43
/* matumoto@math.keio.ac.jp                                        */
slouken@797
    44
slouken@797
    45
/* Period parameters */
slouken@797
    46
#define MP3_DITH_N 624
slouken@797
    47
#define MP3_DITH_M 397
slouken@797
    48
#define MATRIX_A 0x9908b0df   /* constant vector a */
slouken@797
    49
#define UPPER_MASK 0x80000000 /* most significant w-r bits */
slouken@797
    50
#define LOWER_MASK 0x7fffffff /* least significant r bits */
slouken@797
    51
slouken@797
    52
/* Tempering parameters */
slouken@797
    53
#define TEMPERING_MASK_B 0x9d2c5680
slouken@797
    54
#define TEMPERING_MASK_C 0xefc60000
slouken@797
    55
#define TEMPERING_SHIFT_U(y)  (y >> 11)
slouken@797
    56
#define TEMPERING_SHIFT_S(y)  (y << 7)
slouken@797
    57
#define TEMPERING_SHIFT_T(y)  (y << 15)
slouken@797
    58
#define TEMPERING_SHIFT_L(y)  (y >> 18)
slouken@797
    59
slouken@797
    60
static unsigned long mt[MP3_DITH_N]; /* the array for the state vector  */
slouken@797
    61
static int mti=MP3_DITH_N+1; /* mti==MP3_DITH_N+1 means mt[MP3_DITH_N] is not initialized */
slouken@797
    62
slouken@797
    63
/* initializing the array with a NONZERO seed */
slouken@797
    64
static void sgenrand(unsigned long seed)
slouken@797
    65
{
slouken@797
    66
    /* setting initial seeds to mt[MP3_DITH_N] using         */
slouken@797
    67
    /* the generator Line 25 of Table 1 in          */
slouken@797
    68
    /* [KNUTH 1981, The Art of Computer Programming */
slouken@797
    69
    /*    Vol. 2 (2nd Ed.), pp102]                  */
slouken@797
    70
    mt[0]= seed & 0xffffffff;
slouken@797
    71
    for (mti=1; mti<MP3_DITH_N; mti++)
slouken@797
    72
        mt[mti] = (69069 * mt[mti-1]) & 0xffffffff;
slouken@797
    73
}
slouken@797
    74
slouken@797
    75
static unsigned long genrand(void)
slouken@797
    76
{
slouken@797
    77
    unsigned long y;
slouken@797
    78
    static unsigned long mag01[2]={0x0, MATRIX_A};
slouken@797
    79
    /* mag01[x] = x * MATRIX_A  for x=0,1 */
slouken@797
    80
slouken@797
    81
    if (mti >= MP3_DITH_N) { /* generate MP3_DITH_N words at one time */
slouken@797
    82
        int kk;
slouken@797
    83
slouken@797
    84
        if (mti == MP3_DITH_N+1)   /* if sgenrand() has not been called, */
slouken@797
    85
            sgenrand(4357); /* a default initial seed is used   */
slouken@797
    86
slouken@797
    87
        for (kk=0;kk<MP3_DITH_N-MP3_DITH_M;kk++) {
slouken@797
    88
            y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
slouken@797
    89
            mt[kk] = mt[kk+MP3_DITH_M] ^ (y >> 1) ^ mag01[y & 0x1];
slouken@797
    90
        }
slouken@797
    91
        for (;kk<MP3_DITH_N-1;kk++) {
slouken@797
    92
            y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
slouken@797
    93
            mt[kk] = mt[kk+(MP3_DITH_M-MP3_DITH_N)] ^ (y >> 1) ^ mag01[y & 0x1];
slouken@797
    94
        }
slouken@797
    95
        y = (mt[MP3_DITH_N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK);
slouken@797
    96
        mt[MP3_DITH_N-1] = mt[MP3_DITH_M-1] ^ (y >> 1) ^ mag01[y & 0x1];
slouken@797
    97
slouken@797
    98
        mti = 0;
slouken@797
    99
    }
slouken@797
   100
slouken@797
   101
    y = mt[mti++];
slouken@797
   102
    y ^= TEMPERING_SHIFT_U(y);
slouken@797
   103
    y ^= TEMPERING_SHIFT_S(y) & TEMPERING_MASK_B;
slouken@797
   104
    y ^= TEMPERING_SHIFT_T(y) & TEMPERING_MASK_C;
slouken@797
   105
    y ^= TEMPERING_SHIFT_L(y);
slouken@797
   106
slouken@797
   107
    return y;
slouken@797
   108
}
slouken@797
   109
slouken@797
   110
static long triangular_dither_noise(int nbits) {
slouken@797
   111
    /* parameter nbits : the peak-to-peak amplitude desired (in bits)
slouken@797
   112
     *  use with nbits set to    2 + nber of bits to be trimmed.
slouken@797
   113
     * (because triangular is made from two uniformly distributed processes,
slouken@797
   114
     * it starts at 2 bits peak-to-peak amplitude)
slouken@797
   115
     * see The Theory of Dithered Quantization by Robert Alexander Wannamaker
slouken@797
   116
     * for complete proof of why that's optimal
slouken@797
   117
     */
slouken@797
   118
    long v = (genrand()/2 - genrand()/2); /* in ]-2^31, 2^31[ */
slouken@797
   119
    long P = 1 << (32 - nbits); /* the power of 2 */
slouken@797
   120
    v /= P;
slouken@797
   121
    /* now v in ]-2^(nbits-1), 2^(nbits-1) [ */
slouken@797
   122
slouken@797
   123
    return v;
slouken@797
   124
}
slouken@797
   125
slouken@797
   126
#endif /* MUSIC_MP3_MAD_GPL_DITHERING */
slouken@797
   127
slouken@797
   128
slouken@777
   129
#define MAD_INPUT_BUFFER_SIZE   (5*8192)
slouken@777
   130
slouken@777
   131
enum {
slouken@777
   132
    MS_input_eof      = 0x0001,
slouken@777
   133
    MS_input_error    = 0x0001,
slouken@797
   134
    MS_decode_error   = 0x0002,
slouken@777
   135
    MS_error_flags    = 0x000f,
slouken@777
   136
};
slouken@777
   137
slouken@777
   138
typedef struct {
slouken@797
   139
    int play_count;
slouken@777
   140
    SDL_RWops *src;
slouken@777
   141
    int freesrc;
slouken@777
   142
    struct mad_stream stream;
slouken@777
   143
    struct mad_frame frame;
slouken@777
   144
    struct mad_synth synth;
slouken@777
   145
    mad_timer_t next_frame_start;
slouken@777
   146
    int volume;
slouken@777
   147
    int status;
slouken@797
   148
    SDL_AudioStream *audiostream;
slouken@777
   149
slouken@777
   150
    unsigned char input_buffer[MAD_INPUT_BUFFER_SIZE + MAD_BUFFER_GUARD];
slouken@797
   151
} MAD_Music;
slouken@777
   152
slouken@777
   153
slouken@797
   154
static int MAD_Seek(void *context, double position);
slouken@797
   155
slouken@777
   156
static void *MAD_CreateFromRW(SDL_RWops *src, int freesrc)
slouken@521
   157
{
slouken@797
   158
    MAD_Music *music;
slouken@357
   159
slouken@797
   160
    music = (MAD_Music *)SDL_calloc(1, sizeof(MAD_Music));
slouken@797
   161
    if (!music) {
slouken@797
   162
        SDL_OutOfMemory();
slouken@797
   163
        return NULL;
slouken@777
   164
    }
slouken@797
   165
    music->src = src;
slouken@797
   166
    music->volume = MIX_MAX_VOLUME;
slouken@797
   167
slouken@797
   168
    mad_stream_init(&music->stream);
slouken@797
   169
    mad_frame_init(&music->frame);
slouken@797
   170
    mad_synth_init(&music->synth);
slouken@797
   171
    mad_timer_reset(&music->next_frame_start);
slouken@797
   172
slouken@797
   173
    music->freesrc = freesrc;
slouken@797
   174
    return music;
slouken@357
   175
}
slouken@357
   176
slouken@777
   177
static void MAD_SetVolume(void *context, int volume)
slouken@521
   178
{
slouken@797
   179
    MAD_Music *music = (MAD_Music *)context;
slouken@797
   180
    music->volume = volume;
slouken@357
   181
}
slouken@357
   182
slouken@357
   183
/* Starts the playback. */
slouken@797
   184
static int MAD_Play(void *context, int play_count)
slouken@777
   185
{
slouken@797
   186
    MAD_Music *music = (MAD_Music *)context;
slouken@797
   187
    music->play_count = play_count;
slouken@797
   188
    return MAD_Seek(music, 0.0);
slouken@357
   189
}
slouken@357
   190
sezeroz@891
   191
sezeroz@891
   192
/*************************** TAG HANDLING: ******************************/
sezeroz@891
   193
sezeroz@891
   194
static SDL_INLINE SDL_bool is_id3v1(const Uint8 *data, size_t length)
sezeroz@891
   195
{
sezeroz@891
   196
    /* http://id3.org/ID3v1 :  3 bytes "TAG" identifier and 125 bytes tag data */
sezeroz@891
   197
    if (length < 3 || SDL_memcmp(data,"TAG",3) != 0) {
sezeroz@891
   198
        return SDL_FALSE;
sezeroz@891
   199
    }
sezeroz@891
   200
    return SDL_TRUE;
sezeroz@891
   201
}
sezeroz@891
   202
static SDL_INLINE SDL_bool is_id3v2(const Uint8 *data, size_t length)
sezeroz@891
   203
{
sezeroz@891
   204
    /* ID3v2 header is 10 bytes:  http://id3.org/id3v2.4.0-structure */
sezeroz@891
   205
    /* bytes 0-2: "ID3" identifier */
sezeroz@891
   206
    if (length < 10 || SDL_memcmp(data,"ID3",3) != 0) {
sezeroz@891
   207
        return SDL_FALSE;
sezeroz@891
   208
    }
sezeroz@891
   209
    /* bytes 3-4: version num (major,revision), each byte always less than 0xff. */
sezeroz@891
   210
    if (data[3] == 0xff || data[4] == 0xff) {
sezeroz@891
   211
        return SDL_FALSE;
sezeroz@891
   212
    }
sezeroz@891
   213
    /* bytes 6-9 are the ID3v2 tag size: a 32 bit 'synchsafe' integer, i.e. the
sezeroz@891
   214
     * highest bit 7 in each byte zeroed.  i.e.: 7 bit information in each byte ->
sezeroz@891
   215
     * effectively a 28 bit value.  */
sezeroz@891
   216
    if (data[6] >= 0x80 || data[7] >= 0x80 || data[8] >= 0x80 || data[9] >= 0x80) {
sezeroz@891
   217
        return SDL_FALSE;
sezeroz@891
   218
    }
sezeroz@891
   219
    return SDL_TRUE;
sezeroz@891
   220
}
sezeroz@891
   221
static SDL_INLINE SDL_bool is_apetag(const Uint8 *data, size_t length)
sezeroz@891
   222
{
sezeroz@891
   223
   /* http://wiki.hydrogenaud.io/index.php?title=APEv2_specification
sezeroz@891
   224
    * APEv2 header is 32 bytes: bytes 0-7 ident, bytes 8-11 version,
sezeroz@891
   225
    * bytes 12-17 size.  bytes 24-31 are reserved: must be all zeroes.
sezeroz@891
   226
    * APEv1 has no header, so no luck.  */
sezeroz@891
   227
    Uint32 v;
sezeroz@891
   228
sezeroz@891
   229
    if (length < 32 || SDL_memcmp(data,"APETAGEX",8) != 0) {
sezeroz@891
   230
        return SDL_FALSE;
sezeroz@891
   231
    }
sezeroz@891
   232
    v = (data[11]<<24) | (data[10]<<16) | (data[9]<<8) | data[8]; /* version */
sezeroz@891
   233
    if (v != 2000U) {
sezeroz@891
   234
        return SDL_FALSE;
sezeroz@891
   235
    }
sezeroz@891
   236
    v = 0; /* reserved bits : */
sezeroz@891
   237
    if (SDL_memcmp(&data[24],&v,4) != 0 || SDL_memcmp(&data[28],&v,4) != 0) {
sezeroz@891
   238
        return SDL_FALSE;
sezeroz@891
   239
    }
sezeroz@891
   240
    return SDL_TRUE;
sezeroz@891
   241
}
sezeroz@891
   242
sezeroz@891
   243
static size_t get_tagsize(const Uint8 *data, size_t length)
sezeroz@891
   244
{
sezeroz@891
   245
    size_t size;
sezeroz@891
   246
sezeroz@891
   247
    if (is_id3v1(data, length)) {
sezeroz@891
   248
        return 128; /* fixed length */
sezeroz@891
   249
    }
sezeroz@891
   250
    if (is_id3v2(data, length)) {
sezeroz@891
   251
        /* size is a 'synchsafe' integer (see above) */
sezeroz@891
   252
        size = (data[6]<<21) + (data[7]<<14) + (data[8]<<7) + data[9];
sezeroz@891
   253
        size += 10; /* header size */
sezeroz@891
   254
        /* ID3v2 header[5] is flags (bits 4-7 only, 0-3 are zero).
sezeroz@891
   255
         * bit 4 set: footer is present (a copy of the header but
sezeroz@891
   256
         * with "3DI" as ident.)  */
sezeroz@891
   257
        if (data[5] & 0x10) {
sezeroz@891
   258
            size += 10; /* footer size */
sezeroz@891
   259
        }
sezeroz@891
   260
        /* optional padding (always zeroes) */
sezeroz@891
   261
        while (size < length && data[size] == 0) {
sezeroz@891
   262
            ++size;
sezeroz@891
   263
        }
sezeroz@891
   264
        return size;
sezeroz@891
   265
    }
sezeroz@891
   266
    if (is_apetag(data, length)) {
sezeroz@891
   267
        size = (data[15]<<24) | (data[14]<<16) | (data[13]<<8) | data[12];
sezeroz@891
   268
        size += 32; /* header size */
sezeroz@891
   269
        return size;
sezeroz@891
   270
    }
sezeroz@891
   271
    return 0;
sezeroz@891
   272
}
sezeroz@891
   273
sezeroz@891
   274
static int consume_tag(struct mad_stream *stream)
sezeroz@891
   275
{
sezeroz@891
   276
    /* FIXME: what if the buffer doesn't have the full tag ??? */
sezeroz@891
   277
    size_t remaining = stream->bufend - stream->next_frame;
sezeroz@891
   278
    size_t tagsize = get_tagsize(stream->this_frame, remaining);
sezeroz@891
   279
    if (tagsize != 0) {
sezeroz@891
   280
        mad_stream_skip(stream, tagsize);
sezeroz@891
   281
        return 0;
sezeroz@891
   282
    }
sezeroz@891
   283
    return -1;
sezeroz@891
   284
}
sezeroz@891
   285
slouken@797
   286
/* Reads the next frame from the file.
slouken@797
   287
   Returns true on success or false on failure.
slouken@797
   288
 */
slouken@797
   289
static SDL_bool read_next_frame(MAD_Music *music)
slouken@777
   290
{
slouken@797
   291
    if (music->stream.buffer == NULL ||
slouken@797
   292
        music->stream.error == MAD_ERROR_BUFLEN) {
slouken@777
   293
        size_t read_size;
slouken@777
   294
        size_t remaining;
slouken@777
   295
        unsigned char *read_start;
slouken@357
   296
slouken@777
   297
        /* There might be some bytes in the buffer left over from last
slouken@777
   298
           time.    If so, move them down and read more bytes following
slouken@777
   299
           them. */
slouken@797
   300
        if (music->stream.next_frame != NULL) {
slouken@797
   301
            remaining = music->stream.bufend - music->stream.next_frame;
slouken@797
   302
            memmove(music->input_buffer, music->stream.next_frame, remaining);
slouken@797
   303
            read_start = music->input_buffer + remaining;
slouken@777
   304
            read_size = MAD_INPUT_BUFFER_SIZE - remaining;
slouken@617
   305
slouken@777
   306
        } else {
slouken@777
   307
            read_size = MAD_INPUT_BUFFER_SIZE;
slouken@797
   308
            read_start = music->input_buffer;
slouken@777
   309
            remaining = 0;
slouken@777
   310
        }
slouken@777
   311
slouken@777
   312
        /* Now read additional bytes from the input file. */
slouken@797
   313
        read_size = SDL_RWread(music->src, read_start, 1, read_size);
slouken@777
   314
slouken@777
   315
        if (read_size == 0) {
slouken@797
   316
            if ((music->status & (MS_input_eof | MS_input_error)) == 0) {
slouken@777
   317
                /* FIXME: how to detect error? */
slouken@797
   318
                music->status |= MS_input_eof;
slouken@777
   319
slouken@777
   320
                /* At the end of the file, we must stuff MAD_BUFFER_GUARD
slouken@777
   321
                   number of 0 bytes. */
slouken@777
   322
                SDL_memset(read_start + read_size, 0, MAD_BUFFER_GUARD);
slouken@777
   323
                read_size += MAD_BUFFER_GUARD;
slouken@777
   324
            }
slouken@777
   325
        }
slouken@777
   326
slouken@777
   327
        /* Now feed those bytes into the libmad stream. */
slouken@797
   328
        mad_stream_buffer(&music->stream, music->input_buffer,
slouken@777
   329
                                            read_size + remaining);
slouken@797
   330
        music->stream.error = MAD_ERROR_NONE;
slouken@617
   331
    }
slouken@617
   332
slouken@777
   333
    /* Now ask libmad to extract a frame from the data we just put in
slouken@777
   334
       its buffer. */
slouken@797
   335
    if (mad_frame_decode(&music->frame, &music->stream)) {
slouken@797
   336
        if (MAD_RECOVERABLE(music->stream.error)) {
sezeroz@891
   337
            consume_tag(&music->stream); /* consume any ID3 tags */
sezeroz@891
   338
            mad_stream_sync(&music->stream); /* to frame seek mode */
slouken@797
   339
            return SDL_FALSE;
slouken@617
   340
slouken@797
   341
        } else if (music->stream.error == MAD_ERROR_BUFLEN) {
slouken@797
   342
            return SDL_FALSE;
slouken@617
   343
slouken@777
   344
        } else {
slouken@797
   345
            Mix_SetError("mad_frame_decode() failed, corrupt stream?");
slouken@797
   346
            music->status |= MS_decode_error;
slouken@797
   347
            return SDL_FALSE;
slouken@777
   348
        }
slouken@617
   349
    }
slouken@617
   350
slouken@797
   351
    mad_timer_add(&music->next_frame_start, music->frame.header.duration);
slouken@617
   352
slouken@797
   353
    return SDL_TRUE;
slouken@357
   354
}
slouken@357
   355
slouken@357
   356
/* Scale a MAD sample to 16 bits for output. */
slouken@797
   357
static Sint16 scale(mad_fixed_t sample)
slouken@797
   358
{
slouken@797
   359
    const int n_bits_to_loose = MAD_F_FRACBITS + 1 - 16;
slouken@797
   360
slouken@777
   361
    /* round */
slouken@797
   362
    sample += (1L << (n_bits_to_loose - 1));
slouken@797
   363
slouken@797
   364
#ifdef MUSIC_MP3_MAD_GPL_DITHERING
slouken@797
   365
    sample += triangular_dither_noise(n_bits_to_loose + 1);
slouken@797
   366
#endif
slouken@357
   367
slouken@777
   368
    /* clip */
slouken@777
   369
    if (sample >= MAD_F_ONE)
slouken@777
   370
        sample = MAD_F_ONE - 1;
slouken@777
   371
    else if (sample < -MAD_F_ONE)
slouken@777
   372
        sample = -MAD_F_ONE;
slouken@357
   373
slouken@777
   374
    /* quantize */
slouken@797
   375
    return (Sint16)(sample >> n_bits_to_loose);
slouken@357
   376
}
slouken@357
   377
slouken@777
   378
/* Once the frame has been read, copies its samples into the output buffer. */
slouken@797
   379
static SDL_bool decode_frame(MAD_Music *music)
slouken@797
   380
{
slouken@777
   381
    struct mad_pcm *pcm;
slouken@797
   382
    unsigned int i, nchannels, nsamples;
slouken@777
   383
    mad_fixed_t const *left_ch, *right_ch;
slouken@797
   384
    Sint16 *buffer, *dst;
slouken@797
   385
    int result;
slouken@357
   386
slouken@797
   387
    mad_synth_frame(&music->synth, &music->frame);
slouken@797
   388
    pcm = &music->synth.pcm;
slouken@357
   389
slouken@797
   390
    if (!music->audiostream) {
slouken@797
   391
        music->audiostream = SDL_NewAudioStream(AUDIO_S16, pcm->channels, pcm->samplerate, music_spec.format, music_spec.channels, music_spec.freq);
slouken@797
   392
        if (!music->audiostream) {
slouken@797
   393
            return SDL_FALSE;
slouken@777
   394
        }
slouken@357
   395
    }
slouken@357
   396
slouken@797
   397
    nchannels = pcm->channels;
slouken@797
   398
    nsamples = pcm->length;
slouken@797
   399
    left_ch = pcm->samples[0];
slouken@797
   400
    right_ch = pcm->samples[1];
slouken@797
   401
    buffer = SDL_stack_alloc(Sint16, nsamples*nchannels);
slouken@797
   402
    if (!buffer) {
slouken@797
   403
        SDL_OutOfMemory();
slouken@797
   404
        return SDL_FALSE;
slouken@797
   405
    }
slouken@797
   406
slouken@797
   407
    dst = buffer;
slouken@797
   408
    if (nchannels == 1) {
slouken@797
   409
        for (i = nsamples; i--;) {
slouken@797
   410
            *dst++ = scale(*left_ch++);
slouken@797
   411
        }
slouken@797
   412
    } else {
slouken@797
   413
        for (i = nsamples; i--;) {
slouken@797
   414
            *dst++ = scale(*left_ch++);
slouken@797
   415
            *dst++ = scale(*right_ch++);
slouken@797
   416
        }
slouken@797
   417
    }
slouken@797
   418
slouken@797
   419
    result = SDL_AudioStreamPut(music->audiostream, buffer, (nsamples * nchannels * sizeof(Sint16)));
slouken@797
   420
    SDL_stack_free(buffer);
slouken@797
   421
slouken@797
   422
    if (result < 0) {
slouken@797
   423
        return SDL_FALSE;
slouken@797
   424
    }
slouken@797
   425
    return SDL_TRUE;
slouken@357
   426
}
slouken@357
   427
slouken@797
   428
static int MAD_GetSome(void *context, void *data, int bytes, SDL_bool *done)
slouken@777
   429
{
slouken@797
   430
    MAD_Music *music = (MAD_Music *)context;
slouken@797
   431
    int filled;
slouken@357
   432
slouken@797
   433
    if (music->audiostream) {
slouken@797
   434
        filled = SDL_AudioStreamGet(music->audiostream, data, bytes);
slouken@797
   435
        if (filled != 0) {
slouken@797
   436
            return filled;
slouken@797
   437
        }
slouken@797
   438
    }
slouken@797
   439
slouken@797
   440
    if (!music->play_count) {
slouken@797
   441
        /* All done */
slouken@797
   442
        *done = SDL_TRUE;
slouken@777
   443
        return 0;
slouken@617
   444
    }
slouken@357
   445
slouken@797
   446
    if (read_next_frame(music)) {
slouken@797
   447
        if (!decode_frame(music)) {
slouken@797
   448
            return -1;
slouken@797
   449
        }
slouken@815
   450
    } else if (music->status & MS_input_eof) {
slouken@797
   451
        int play_count = -1;
slouken@797
   452
        if (music->play_count > 0) {
slouken@797
   453
            play_count = (music->play_count - 1);
slouken@777
   454
        }
slouken@797
   455
        if (MAD_Play(music, play_count) < 0) {
slouken@797
   456
            return -1;
slouken@777
   457
        }
slouken@815
   458
    } else if (music->status & MS_decode_error) {
slouken@815
   459
        return -1;
slouken@777
   460
    }
slouken@777
   461
    return 0;
slouken@777
   462
}
slouken@797
   463
static int MAD_GetAudio(void *context, void *data, int bytes)
slouken@797
   464
{
slouken@797
   465
    MAD_Music *music = (MAD_Music *)context;
slouken@797
   466
    return music_pcm_getaudio(context, data, bytes, music->volume, MAD_GetSome);
slouken@797
   467
}
slouken@777
   468
slouken@777
   469
static int MAD_Seek(void *context, double position)
slouken@777
   470
{
slouken@797
   471
    MAD_Music *music = (MAD_Music *)context;
slouken@777
   472
    mad_timer_t target;
slouken@777
   473
    int int_part;
slouken@777
   474
slouken@777
   475
    int_part = (int)position;
slouken@777
   476
    mad_timer_set(&target, int_part, (int)((position - int_part) * 1000000), 1000000);
slouken@777
   477
slouken@797
   478
    if (mad_timer_compare(music->next_frame_start, target) > 0) {
slouken@777
   479
        /* In order to seek backwards in a VBR file, we have to rewind and
slouken@777
   480
           start again from the beginning.    This isn't necessary if the
slouken@777
   481
           file happens to be CBR, of course; in that case we could seek
slouken@777
   482
           directly to the frame we want.    But I leave that little
slouken@777
   483
           optimization for the future developer who discovers she really
slouken@777
   484
           needs it. */
slouken@797
   485
        mad_timer_reset(&music->next_frame_start);
slouken@797
   486
        music->status &= ~MS_error_flags;
slouken@777
   487
slouken@797
   488
        SDL_RWseek(music->src, 0, RW_SEEK_SET);
slouken@617
   489
    }
slouken@617
   490
slouken@777
   491
    /* Now we have to skip frames until we come to the right one.
slouken@777
   492
       Again, only truly necessary if the file is VBR. */
slouken@797
   493
    while (mad_timer_compare(music->next_frame_start, target) < 0) {
slouken@797
   494
        if (!read_next_frame(music)) {
slouken@797
   495
            if ((music->status & MS_error_flags) != 0) {
slouken@777
   496
                /* Couldn't read a frame; either an error condition or
slouken@777
   497
                     end-of-file.    Stop. */
slouken@777
   498
                return Mix_SetError("Seek position out of range");
slouken@777
   499
            }
slouken@777
   500
        }
slouken@617
   501
    }
slouken@777
   502
slouken@777
   503
    /* Here we are, at the beginning of the frame that contains the
slouken@777
   504
       target time.    Ehh, I say that's close enough.    If we wanted to,
slouken@777
   505
       we could get more precise by decoding the frame now and counting
slouken@777
   506
       the appropriate number of samples out of it. */
slouken@777
   507
    return 0;
slouken@357
   508
}
slouken@357
   509
slouken@777
   510
static void MAD_Delete(void *context)
slouken@777
   511
{
slouken@797
   512
    MAD_Music *music = (MAD_Music *)context;
slouken@777
   513
slouken@797
   514
    mad_stream_finish(&music->stream);
slouken@797
   515
    mad_frame_finish(&music->frame);
slouken@797
   516
    mad_synth_finish(&music->synth);
slouken@797
   517
slouken@797
   518
    if (music->audiostream) {
slouken@797
   519
        SDL_FreeAudioStream(music->audiostream);
slouken@777
   520
    }
slouken@797
   521
    if (music->freesrc) {
slouken@797
   522
        SDL_RWclose(music->src);
slouken@797
   523
    }
slouken@797
   524
    SDL_free(music);
slouken@357
   525
}
slouken@357
   526
slouken@777
   527
Mix_MusicInterface Mix_MusicInterface_MAD =
slouken@777
   528
{
slouken@777
   529
    "MAD",
slouken@777
   530
    MIX_MUSIC_MAD,
slouken@777
   531
    MUS_MP3,
slouken@777
   532
    SDL_FALSE,
slouken@777
   533
    SDL_FALSE,
slouken@357
   534
slouken@797
   535
    NULL,   /* Load */
slouken@797
   536
    NULL,   /* Open */
slouken@777
   537
    MAD_CreateFromRW,
slouken@797
   538
    NULL,   /* CreateFromFile */
slouken@777
   539
    MAD_SetVolume,
slouken@777
   540
    MAD_Play,
slouken@797
   541
    NULL,   /* IsPlaying */
slouken@777
   542
    MAD_GetAudio,
slouken@777
   543
    MAD_Seek,
slouken@797
   544
    NULL,   /* Pause */
slouken@797
   545
    NULL,   /* Resume */
slouken@797
   546
    NULL,   /* Stop */
slouken@777
   547
    MAD_Delete,
slouken@797
   548
    NULL,   /* Close */
slouken@797
   549
    NULL,   /* Unload */
slouken@777
   550
};
slouken@777
   551
slouken@777
   552
#endif /* MUSIC_MP3_MAD */
slouken@777
   553
slouken@777
   554
/* vi: set ts=4 sw=4 expandtab: */