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