music_mad.c
author Sam Lantinga <slouken@libsdl.org>
Fri, 13 Jan 2012 03:15:19 -0500
changeset 561 87bdb4c81c0b
parent 542 3de4970b36d4
child 600 413430798384
permissions -rw-r--r--
Fixed memory crash loading Ogg Vorbis files on Windows
The pointer to the audio data could come from SDL_LoadWAV_RW() or from our other loaders, and we need to use the correct free() to release the memory. So we'll just use the SDL memory functions for consistency.
This pretty much only matters on Windows where we can't guarantee a certain C runtime so we provide our own malloc() and friends.
slouken@357
     1
/*
slouken@518
     2
  SDL_mixer:  An audio mixer library based on the SDL library
slouken@518
     3
  Copyright (C) 1997-2012 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@521
    29
mad_openFileRW(SDL_RWops *rw, SDL_AudioSpec *mixer, int freerw)
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@380
    35
	mp3_mad->rw = rw;
slouken@521
    36
	mp3_mad->freerw = freerw;
slouken@380
    37
	mad_stream_init(&mp3_mad->stream);
slouken@380
    38
	mad_frame_init(&mp3_mad->frame);
slouken@380
    39
	mad_synth_init(&mp3_mad->synth);
slouken@380
    40
	mp3_mad->frames_read = 0;
slouken@380
    41
	mad_timer_reset(&mp3_mad->next_frame_start);
slouken@380
    42
	mp3_mad->volume = MIX_MAX_VOLUME;
slouken@380
    43
	mp3_mad->status = 0;
slouken@380
    44
	mp3_mad->output_begin = 0;
slouken@380
    45
	mp3_mad->output_end = 0;
slouken@380
    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@380
    58
  if (mp3_mad->freerw) {
slouken@521
    59
	SDL_RWclose(mp3_mad->rw);
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@357
    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@357
    86
  if (mp3_mad->stream.buffer == NULL || 
slouken@357
    87
	  mp3_mad->stream.error == MAD_ERROR_BUFLEN) {
slouken@357
    88
	size_t read_size;
slouken@357
    89
	size_t remaining;
slouken@357
    90
	unsigned char *read_start;
slouken@357
    91
	
slouken@357
    92
	/* There might be some bytes in the buffer left over from last
slouken@357
    93
	   time.  If so, move them down and read more bytes following
slouken@357
    94
	   them. */
slouken@357
    95
	if (mp3_mad->stream.next_frame != NULL) {
slouken@357
    96
	  remaining = mp3_mad->stream.bufend - mp3_mad->stream.next_frame;
slouken@357
    97
	  memmove(mp3_mad->input_buffer, mp3_mad->stream.next_frame, remaining);
slouken@357
    98
	  read_start = mp3_mad->input_buffer + remaining;
slouken@357
    99
	  read_size = MAD_INPUT_BUFFER_SIZE - remaining;
slouken@357
   100
	  
slouken@357
   101
	} else {
slouken@357
   102
	  read_size = MAD_INPUT_BUFFER_SIZE;
slouken@357
   103
	  read_start = mp3_mad->input_buffer;
slouken@357
   104
	  remaining = 0;
slouken@357
   105
	}
slouken@357
   106
slouken@357
   107
	/* Now read additional bytes from the input file. */
slouken@357
   108
	read_size = SDL_RWread(mp3_mad->rw, read_start, 1, read_size);
slouken@357
   109
	
slouken@357
   110
	if (read_size <= 0) {
slouken@357
   111
	  if ((mp3_mad->status & (MS_input_eof | MS_input_error)) == 0) {
slouken@357
   112
		if (read_size == 0) {
slouken@357
   113
		  mp3_mad->status |= MS_input_eof;
slouken@357
   114
		} else {
slouken@357
   115
		  mp3_mad->status |= MS_input_error;
slouken@357
   116
		}
slouken@357
   117
		
slouken@357
   118
		/* At the end of the file, we must stuff MAD_BUFFER_GUARD
slouken@357
   119
		   number of 0 bytes. */
slouken@357
   120
		memset(read_start + read_size, 0, MAD_BUFFER_GUARD);
slouken@357
   121
		read_size += MAD_BUFFER_GUARD;
slouken@357
   122
	  }
slouken@357
   123
	}
slouken@357
   124
	
slouken@357
   125
	/* Now feed those bytes into the libmad stream. */
slouken@357
   126
	mad_stream_buffer(&mp3_mad->stream, mp3_mad->input_buffer,
slouken@357
   127
					  read_size + remaining);
slouken@357
   128
	mp3_mad->stream.error = MAD_ERROR_NONE;
slouken@357
   129
  }
slouken@357
   130
  
slouken@357
   131
  /* Now ask libmad to extract a frame from the data we just put in
slouken@357
   132
	 its buffer. */
slouken@357
   133
  if (mad_frame_decode(&mp3_mad->frame, &mp3_mad->stream)) {
slouken@357
   134
	if (MAD_RECOVERABLE(mp3_mad->stream.error)) {
slouken@357
   135
	  return 0;
slouken@357
   136
	  
slouken@357
   137
	} else if (mp3_mad->stream.error == MAD_ERROR_BUFLEN) {
slouken@357
   138
	  return 0;
slouken@357
   139
	  
slouken@357
   140
	} else {
slouken@357
   141
	  mp3_mad->status |= MS_decode_error;
slouken@357
   142
	  return 0;
slouken@357
   143
	}
slouken@357
   144
  }
slouken@357
   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@357
   183
	mp3_mad->status |= MS_cvt_decoded;
slouken@357
   184
slouken@357
   185
	/* The first frame determines some key properties of the stream.
slouken@357
   186
	   In particular, it tells us enough to set up the convert
slouken@357
   187
	   structure now. */
slouken@357
   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@357
   225
	/* We're not supposed to be playing, so send silence instead. */
slouken@357
   226
	memset(stream, 0, len);
slouken@357
   227
	return;
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@357
   233
	if (mp3_mad->output_end == mp3_mad->output_begin) {
slouken@357
   234
	  /* We need to get a new frame. */
slouken@357
   235
	  mp3_mad->output_begin = 0;
slouken@357
   236
	  mp3_mad->output_end = 0;
slouken@357
   237
	  if (!read_next_frame(mp3_mad)) {
slouken@357
   238
		if ((mp3_mad->status & MS_error_flags) != 0) {
slouken@357
   239
		  /* Couldn't read a frame; either an error condition or
slouken@357
   240
			 end-of-file.  Stop. */
slouken@357
   241
		  memset(out, 0, bytes_remaining);
slouken@357
   242
		  mp3_mad->status &= ~MS_playing;
slouken@407
   243
		  return bytes_remaining;
slouken@357
   244
		}
slouken@357
   245
	  } else {
slouken@357
   246
		decode_frame(mp3_mad);
slouken@357
   247
slouken@357
   248
		/* Now convert the frame data to the appropriate format for
slouken@357
   249
		   output. */
slouken@357
   250
		mp3_mad->cvt.buf = mp3_mad->output_buffer;
slouken@357
   251
		mp3_mad->cvt.len = mp3_mad->output_end;
slouken@357
   252
		
slouken@357
   253
		mp3_mad->output_end = (int)(mp3_mad->output_end * mp3_mad->cvt.len_ratio);
slouken@357
   254
		/*assert(mp3_mad->output_end <= MAD_OUTPUT_BUFFER_SIZE);*/
slouken@357
   255
		SDL_ConvertAudio(&mp3_mad->cvt);
slouken@357
   256
	  }
slouken@357
   257
	}
slouken@357
   258
slouken@357
   259
	num_bytes = mp3_mad->output_end - mp3_mad->output_begin;
slouken@357
   260
	if (bytes_remaining < num_bytes) {
slouken@357
   261
	  num_bytes = bytes_remaining;
slouken@357
   262
	}
slouken@357
   263
slouken@357
   264
	if (mp3_mad->volume == MIX_MAX_VOLUME) {
slouken@357
   265
	  memcpy(out, mp3_mad->output_buffer + mp3_mad->output_begin, num_bytes);
slouken@357
   266
	} else {
slouken@357
   267
	  SDL_MixAudio(out, mp3_mad->output_buffer + mp3_mad->output_begin,
slouken@357
   268
				   num_bytes, mp3_mad->volume);
slouken@357
   269
	}
slouken@357
   270
	out += num_bytes;
slouken@357
   271
	mp3_mad->output_begin += num_bytes;
slouken@357
   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@357
   283
  mad_timer_set(&target, int_part, 
slouken@357
   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@357
   287
	/* In order to seek backwards in a VBR file, we have to rewind and
slouken@357
   288
	   start again from the beginning.  This isn't necessary if the
slouken@357
   289
	   file happens to be CBR, of course; in that case we could seek
slouken@357
   290
	   directly to the frame we want.  But I leave that little
slouken@357
   291
	   optimization for the future developer who discovers she really
slouken@357
   292
	   needs it. */
slouken@357
   293
	mp3_mad->frames_read = 0;
slouken@357
   294
	mad_timer_reset(&mp3_mad->next_frame_start);
slouken@357
   295
	mp3_mad->status &= ~MS_error_flags;
slouken@357
   296
	mp3_mad->output_begin = 0;
slouken@357
   297
	mp3_mad->output_end = 0;
slouken@357
   298
slouken@473
   299
	SDL_RWseek(mp3_mad->rw, 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@357
   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@357
   305
	if (!read_next_frame(mp3_mad)) {
slouken@357
   306
	  if ((mp3_mad->status & MS_error_flags) != 0) {
slouken@357
   307
		/* Couldn't read a frame; either an error condition or
slouken@357
   308
		   end-of-file.  Stop. */
slouken@357
   309
		mp3_mad->status &= ~MS_playing;
slouken@357
   310
		return;
slouken@357
   311
	  }
slouken@357
   312
	}
slouken@357
   313
  }
slouken@357
   314
slouken@357
   315
  /* Here we are, at the beginning of the frame that contains the
slouken@357
   316
	 target time.  Ehh, I say that's close enough.  If we wanted to,
slouken@357
   317
	 we could get more precise by decoding the frame now and counting
slouken@357
   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 */