music_mad.c
author Sam Lantinga <slouken@libsdl.org>
Mon, 03 Jun 2013 19:55:47 -0700
changeset 631 a0d34c8077a1
parent 625 1d489d8ec2e0
child 711 f40c5ac95b12
permissions -rw-r--r--
Fixed bug 1887 - libmad support broken
slouken@357
     1
/*
slouken@518
     2
  SDL_mixer:  An audio mixer library based on the SDL library
slouken@601
     3
  Copyright (C) 1997-2013 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@357
    22
#ifdef MP3_MAD_MUSIC
slouken@357
    23
slouken@357
    24
#include <string.h>
slouken@357
    25
slouken@357
    26
#include "music_mad.h"
slouken@357
    27
slouken@357
    28
mad_data *
slouken@625
    29
mad_openFileRW(SDL_RWops *src, SDL_AudioSpec *mixer, int freesrc)
slouken@521
    30
{
slouken@357
    31
  mad_data *mp3_mad;
slouken@357
    32
slouken@561
    33
  mp3_mad = (mad_data *)SDL_malloc(sizeof(mad_data));
slouken@380
    34
  if (mp3_mad) {
slouken@625
    35
    mp3_mad->src = src;
slouken@625
    36
    mp3_mad->freesrc = freesrc;
slouken@617
    37
    mad_stream_init(&mp3_mad->stream);
slouken@617
    38
    mad_frame_init(&mp3_mad->frame);
slouken@617
    39
    mad_synth_init(&mp3_mad->synth);
slouken@617
    40
    mp3_mad->frames_read = 0;
slouken@617
    41
    mad_timer_reset(&mp3_mad->next_frame_start);
slouken@617
    42
    mp3_mad->volume = MIX_MAX_VOLUME;
slouken@617
    43
    mp3_mad->status = 0;
slouken@617
    44
    mp3_mad->output_begin = 0;
slouken@617
    45
    mp3_mad->output_end = 0;
slouken@617
    46
    mp3_mad->mixer = *mixer;
slouken@380
    47
  }
slouken@357
    48
  return mp3_mad;
slouken@357
    49
}
slouken@357
    50
slouken@357
    51
void
slouken@521
    52
mad_closeFile(mad_data *mp3_mad)
slouken@521
    53
{
slouken@357
    54
  mad_stream_finish(&mp3_mad->stream);
slouken@357
    55
  mad_frame_finish(&mp3_mad->frame);
slouken@357
    56
  mad_synth_finish(&mp3_mad->synth);
slouken@357
    57
slouken@625
    58
  if (mp3_mad->freesrc) {
slouken@625
    59
    SDL_RWclose(mp3_mad->src);
slouken@380
    60
  }
slouken@561
    61
  SDL_free(mp3_mad);
slouken@357
    62
}
slouken@357
    63
slouken@357
    64
/* Starts the playback. */
slouken@357
    65
void
slouken@357
    66
mad_start(mad_data *mp3_mad) {
slouken@357
    67
  mp3_mad->status |= MS_playing;
slouken@357
    68
}
slouken@357
    69
slouken@357
    70
/* Stops the playback. */
slouken@617
    71
void
slouken@357
    72
mad_stop(mad_data *mp3_mad) {
slouken@357
    73
  mp3_mad->status &= ~MS_playing;
slouken@357
    74
}
slouken@357
    75
slouken@357
    76
/* Returns true if the playing is engaged, false otherwise. */
slouken@357
    77
int
slouken@357
    78
mad_isPlaying(mad_data *mp3_mad) {
slouken@357
    79
  return ((mp3_mad->status & MS_playing) != 0);
slouken@357
    80
}
slouken@357
    81
slouken@357
    82
/* Reads the next frame from the file.  Returns true on success or
slouken@357
    83
   false on failure. */
slouken@357
    84
static int
slouken@357
    85
read_next_frame(mad_data *mp3_mad) {
slouken@617
    86
  if (mp3_mad->stream.buffer == NULL ||
slouken@617
    87
      mp3_mad->stream.error == MAD_ERROR_BUFLEN) {
slouken@617
    88
    size_t read_size;
slouken@617
    89
    size_t remaining;
slouken@617
    90
    unsigned char *read_start;
slouken@357
    91
slouken@617
    92
    /* There might be some bytes in the buffer left over from last
slouken@617
    93
       time.  If so, move them down and read more bytes following
slouken@617
    94
       them. */
slouken@617
    95
    if (mp3_mad->stream.next_frame != NULL) {
slouken@617
    96
      remaining = mp3_mad->stream.bufend - mp3_mad->stream.next_frame;
slouken@617
    97
      memmove(mp3_mad->input_buffer, mp3_mad->stream.next_frame, remaining);
slouken@617
    98
      read_start = mp3_mad->input_buffer + remaining;
slouken@617
    99
      read_size = MAD_INPUT_BUFFER_SIZE - remaining;
slouken@617
   100
slouken@617
   101
    } else {
slouken@617
   102
      read_size = MAD_INPUT_BUFFER_SIZE;
slouken@617
   103
      read_start = mp3_mad->input_buffer;
slouken@617
   104
      remaining = 0;
slouken@617
   105
    }
slouken@617
   106
slouken@617
   107
    /* Now read additional bytes from the input file. */
slouken@631
   108
    read_size = SDL_RWread(mp3_mad->src, read_start, 1, read_size);
slouken@617
   109
slouken@617
   110
    if (read_size <= 0) {
slouken@617
   111
      if ((mp3_mad->status & (MS_input_eof | MS_input_error)) == 0) {
slouken@617
   112
        if (read_size == 0) {
slouken@617
   113
          mp3_mad->status |= MS_input_eof;
slouken@617
   114
        } else {
slouken@617
   115
          mp3_mad->status |= MS_input_error;
slouken@617
   116
        }
slouken@617
   117
slouken@617
   118
        /* At the end of the file, we must stuff MAD_BUFFER_GUARD
slouken@617
   119
           number of 0 bytes. */
slouken@621
   120
        SDL_memset(read_start + read_size, 0, MAD_BUFFER_GUARD);
slouken@617
   121
        read_size += MAD_BUFFER_GUARD;
slouken@617
   122
      }
slouken@617
   123
    }
slouken@617
   124
slouken@617
   125
    /* Now feed those bytes into the libmad stream. */
slouken@617
   126
    mad_stream_buffer(&mp3_mad->stream, mp3_mad->input_buffer,
slouken@617
   127
                      read_size + remaining);
slouken@617
   128
    mp3_mad->stream.error = MAD_ERROR_NONE;
slouken@357
   129
  }
slouken@617
   130
slouken@357
   131
  /* Now ask libmad to extract a frame from the data we just put in
slouken@617
   132
     its buffer. */
slouken@357
   133
  if (mad_frame_decode(&mp3_mad->frame, &mp3_mad->stream)) {
slouken@617
   134
    if (MAD_RECOVERABLE(mp3_mad->stream.error)) {
slouken@617
   135
      return 0;
slouken@617
   136
slouken@617
   137
    } else if (mp3_mad->stream.error == MAD_ERROR_BUFLEN) {
slouken@617
   138
      return 0;
slouken@617
   139
slouken@617
   140
    } else {
slouken@617
   141
      mp3_mad->status |= MS_decode_error;
slouken@617
   142
      return 0;
slouken@617
   143
    }
slouken@357
   144
  }
slouken@617
   145
slouken@357
   146
  mp3_mad->frames_read++;
slouken@357
   147
  mad_timer_add(&mp3_mad->next_frame_start, mp3_mad->frame.header.duration);
slouken@357
   148
slouken@357
   149
  return 1;
slouken@357
   150
}
slouken@357
   151
slouken@357
   152
/* Scale a MAD sample to 16 bits for output. */
slouken@357
   153
static signed int
slouken@357
   154
scale(mad_fixed_t sample) {
slouken@357
   155
  /* round */
slouken@357
   156
  sample += (1L << (MAD_F_FRACBITS - 16));
slouken@357
   157
slouken@357
   158
  /* clip */
slouken@357
   159
  if (sample >= MAD_F_ONE)
slouken@357
   160
    sample = MAD_F_ONE - 1;
slouken@357
   161
  else if (sample < -MAD_F_ONE)
slouken@357
   162
    sample = -MAD_F_ONE;
slouken@357
   163
slouken@357
   164
  /* quantize */
slouken@357
   165
  return sample >> (MAD_F_FRACBITS + 1 - 16);
slouken@357
   166
}
slouken@357
   167
slouken@357
   168
/* Once the frame has been read, copies its samples into the output
slouken@357
   169
   buffer. */
slouken@357
   170
static void
slouken@357
   171
decode_frame(mad_data *mp3_mad) {
slouken@357
   172
  struct mad_pcm *pcm;
slouken@357
   173
  unsigned int nchannels, nsamples;
slouken@357
   174
  mad_fixed_t const *left_ch, *right_ch;
slouken@357
   175
  unsigned char *out;
slouken@357
   176
  int ret;
slouken@357
   177
slouken@357
   178
  mad_synth_frame(&mp3_mad->synth, &mp3_mad->frame);
slouken@357
   179
  pcm = &mp3_mad->synth.pcm;
slouken@357
   180
  out = mp3_mad->output_buffer + mp3_mad->output_end;
slouken@357
   181
slouken@357
   182
  if ((mp3_mad->status & MS_cvt_decoded) == 0) {
slouken@617
   183
    mp3_mad->status |= MS_cvt_decoded;
slouken@357
   184
slouken@617
   185
    /* The first frame determines some key properties of the stream.
slouken@617
   186
       In particular, it tells us enough to set up the convert
slouken@617
   187
       structure now. */
slouken@617
   188
    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@357
   189
  }
slouken@357
   190
slouken@357
   191
  /* pcm->samplerate contains the sampling frequency */
slouken@357
   192
slouken@357
   193
  nchannels = pcm->channels;
slouken@357
   194
  nsamples  = pcm->length;
slouken@357
   195
  left_ch   = pcm->samples[0];
slouken@357
   196
  right_ch  = pcm->samples[1];
slouken@357
   197
slouken@357
   198
  while (nsamples--) {
slouken@357
   199
    signed int sample;
slouken@357
   200
slouken@357
   201
    /* output sample(s) in 16-bit signed little-endian PCM */
slouken@357
   202
slouken@357
   203
    sample = scale(*left_ch++);
slouken@357
   204
    *out++ = ((sample >> 0) & 0xff);
slouken@357
   205
    *out++ = ((sample >> 8) & 0xff);
slouken@357
   206
slouken@357
   207
    if (nchannels == 2) {
slouken@357
   208
      sample = scale(*right_ch++);
slouken@357
   209
      *out++ = ((sample >> 0) & 0xff);
slouken@357
   210
      *out++ = ((sample >> 8) & 0xff);
slouken@357
   211
    }
slouken@357
   212
  }
slouken@357
   213
slouken@357
   214
  mp3_mad->output_end = out - mp3_mad->output_buffer;
slouken@357
   215
  /*assert(mp3_mad->output_end <= MAD_OUTPUT_BUFFER_SIZE);*/
slouken@357
   216
}
slouken@357
   217
slouken@407
   218
int
slouken@357
   219
mad_getSamples(mad_data *mp3_mad, Uint8 *stream, int len) {
slouken@357
   220
  int bytes_remaining;
slouken@357
   221
  int num_bytes;
slouken@357
   222
  Uint8 *out;
slouken@357
   223
slouken@357
   224
  if ((mp3_mad->status & MS_playing) == 0) {
slouken@617
   225
    /* We're not supposed to be playing, so send silence instead. */
slouken@621
   226
    SDL_memset(stream, 0, len);
slouken@617
   227
    return 0;
slouken@357
   228
  }
slouken@357
   229
slouken@357
   230
  out = stream;
slouken@357
   231
  bytes_remaining = len;
slouken@357
   232
  while (bytes_remaining > 0) {
slouken@617
   233
    if (mp3_mad->output_end == mp3_mad->output_begin) {
slouken@617
   234
      /* We need to get a new frame. */
slouken@617
   235
      mp3_mad->output_begin = 0;
slouken@617
   236
      mp3_mad->output_end = 0;
slouken@617
   237
      if (!read_next_frame(mp3_mad)) {
slouken@617
   238
        if ((mp3_mad->status & MS_error_flags) != 0) {
slouken@617
   239
          /* Couldn't read a frame; either an error condition or
slouken@617
   240
             end-of-file.  Stop. */
slouken@621
   241
          SDL_memset(out, 0, bytes_remaining);
slouken@617
   242
          mp3_mad->status &= ~MS_playing;
slouken@617
   243
          return bytes_remaining;
slouken@617
   244
        }
slouken@617
   245
      } else {
slouken@617
   246
        decode_frame(mp3_mad);
slouken@357
   247
slouken@617
   248
        /* Now convert the frame data to the appropriate format for
slouken@617
   249
           output. */
slouken@617
   250
        mp3_mad->cvt.buf = mp3_mad->output_buffer;
slouken@617
   251
        mp3_mad->cvt.len = mp3_mad->output_end;
slouken@357
   252
slouken@617
   253
        mp3_mad->output_end = (int)(mp3_mad->output_end * mp3_mad->cvt.len_ratio);
slouken@617
   254
        /*assert(mp3_mad->output_end <= MAD_OUTPUT_BUFFER_SIZE);*/
slouken@617
   255
        SDL_ConvertAudio(&mp3_mad->cvt);
slouken@617
   256
      }
slouken@617
   257
    }
slouken@357
   258
slouken@617
   259
    num_bytes = mp3_mad->output_end - mp3_mad->output_begin;
slouken@617
   260
    if (bytes_remaining < num_bytes) {
slouken@617
   261
      num_bytes = bytes_remaining;
slouken@617
   262
    }
slouken@617
   263
slouken@617
   264
    if (mp3_mad->volume == MIX_MAX_VOLUME) {
slouken@621
   265
      SDL_memcpy(out, mp3_mad->output_buffer + mp3_mad->output_begin, num_bytes);
slouken@617
   266
    } else {
slouken@617
   267
      SDL_MixAudio(out, mp3_mad->output_buffer + mp3_mad->output_begin,
slouken@617
   268
                   num_bytes, mp3_mad->volume);
slouken@617
   269
    }
slouken@617
   270
    out += num_bytes;
slouken@617
   271
    mp3_mad->output_begin += num_bytes;
slouken@617
   272
    bytes_remaining -= num_bytes;
slouken@357
   273
  }
slouken@407
   274
  return 0;
slouken@357
   275
}
slouken@357
   276
slouken@357
   277
void
slouken@357
   278
mad_seek(mad_data *mp3_mad, double position) {
slouken@357
   279
  mad_timer_t target;
slouken@357
   280
  int int_part;
slouken@357
   281
slouken@357
   282
  int_part = (int)position;
slouken@617
   283
  mad_timer_set(&target, int_part,
slouken@617
   284
                (int)((position - int_part) * 1000000), 1000000);
slouken@357
   285
slouken@357
   286
  if (mad_timer_compare(mp3_mad->next_frame_start, target) > 0) {
slouken@617
   287
    /* In order to seek backwards in a VBR file, we have to rewind and
slouken@617
   288
       start again from the beginning.  This isn't necessary if the
slouken@617
   289
       file happens to be CBR, of course; in that case we could seek
slouken@617
   290
       directly to the frame we want.  But I leave that little
slouken@617
   291
       optimization for the future developer who discovers she really
slouken@617
   292
       needs it. */
slouken@617
   293
    mp3_mad->frames_read = 0;
slouken@617
   294
    mad_timer_reset(&mp3_mad->next_frame_start);
slouken@617
   295
    mp3_mad->status &= ~MS_error_flags;
slouken@617
   296
    mp3_mad->output_begin = 0;
slouken@617
   297
    mp3_mad->output_end = 0;
slouken@357
   298
slouken@631
   299
    SDL_RWseek(mp3_mad->src, 0, RW_SEEK_SET);
slouken@357
   300
  }
slouken@357
   301
slouken@357
   302
  /* Now we have to skip frames until we come to the right one.
slouken@617
   303
     Again, only truly necessary if the file is VBR. */
slouken@357
   304
  while (mad_timer_compare(mp3_mad->next_frame_start, target) < 0) {
slouken@617
   305
    if (!read_next_frame(mp3_mad)) {
slouken@617
   306
      if ((mp3_mad->status & MS_error_flags) != 0) {
slouken@617
   307
        /* Couldn't read a frame; either an error condition or
slouken@617
   308
           end-of-file.  Stop. */
slouken@617
   309
        mp3_mad->status &= ~MS_playing;
slouken@617
   310
        return;
slouken@617
   311
      }
slouken@617
   312
    }
slouken@357
   313
  }
slouken@357
   314
slouken@357
   315
  /* Here we are, at the beginning of the frame that contains the
slouken@617
   316
     target time.  Ehh, I say that's close enough.  If we wanted to,
slouken@617
   317
     we could get more precise by decoding the frame now and counting
slouken@617
   318
     the appropriate number of samples out of it. */
slouken@357
   319
}
slouken@357
   320
slouken@357
   321
void
slouken@357
   322
mad_setVolume(mad_data *mp3_mad, int volume) {
slouken@357
   323
  mp3_mad->volume = volume;
slouken@357
   324
}
slouken@357
   325
slouken@357
   326
slouken@357
   327
#endif  /* MP3_MAD_MUSIC */