load_flac.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 12 Feb 2012 17:58:22 -0500
branchSDL-1.2
changeset 584 ea53a85853dd
parent 561 87bdb4c81c0b
child 601 05123263dab3
child 947 41422d16ee01
permissions -rw-r--r--
Fixed 1418 - crash on double free if loading WAV file failed
slouken@382
     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@382
     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@382
     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@382
    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@382
    20
slouken@518
    21
  This is the source needed to decode a FLAC into a waveform.
slouken@518
    22
  	~ Austen Dicken (admin@cvpcs.org).
slouken@382
    23
*/
slouken@382
    24
slouken@382
    25
#ifdef FLAC_MUSIC
slouken@382
    26
slouken@382
    27
#include <stdio.h>
slouken@382
    28
#include <stdlib.h>
slouken@382
    29
#include <string.h>
slouken@382
    30
slouken@382
    31
#include "SDL_mutex.h"
slouken@382
    32
#include "SDL_endian.h"
slouken@382
    33
#include "SDL_timer.h"
slouken@382
    34
slouken@382
    35
#include "SDL_mixer.h"
slouken@382
    36
#include "dynamic_flac.h"
slouken@382
    37
#include "load_flac.h"
slouken@382
    38
slouken@382
    39
#include <FLAC/stream_decoder.h>
slouken@382
    40
slouken@382
    41
typedef struct {
slouken@382
    42
	SDL_RWops* sdl_src;
slouken@382
    43
	SDL_AudioSpec* sdl_spec;
slouken@382
    44
	Uint8** sdl_audio_buf;
slouken@382
    45
	Uint32* sdl_audio_len;
slouken@382
    46
	int sdl_audio_read;
slouken@382
    47
	FLAC__uint64 flac_total_samples;
slouken@382
    48
	unsigned flac_bps;
slouken@382
    49
} FLAC_SDL_Data;
slouken@382
    50
slouken@382
    51
static FLAC__StreamDecoderReadStatus flac_read_load_cb(
slouken@382
    52
									const FLAC__StreamDecoder *decoder,
slouken@382
    53
									FLAC__byte buffer[],
slouken@382
    54
									size_t *bytes,
slouken@436
    55
									void *client_data)
slouken@436
    56
{
slouken@382
    57
	// make sure there is something to be reading
slouken@382
    58
	if (*bytes > 0) {
slouken@382
    59
		FLAC_SDL_Data *data = (FLAC_SDL_Data *)client_data;
slouken@382
    60
slouken@382
    61
		*bytes = SDL_RWread (data->sdl_src, buffer, sizeof (FLAC__byte),
slouken@382
    62
								*bytes);
slouken@382
    63
slouken@382
    64
		if(*bytes < 0) { // error in read
slouken@382
    65
			return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
slouken@382
    66
		}
slouken@382
    67
		else if(*bytes == 0) { // no data was read (EOF)
slouken@382
    68
			return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
slouken@382
    69
		}
slouken@382
    70
		else { // data was read, continue
slouken@382
    71
			return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
slouken@382
    72
		}
slouken@382
    73
	}
slouken@382
    74
	else {
slouken@382
    75
		return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
slouken@382
    76
	}
slouken@382
    77
}
slouken@382
    78
slouken@382
    79
static FLAC__StreamDecoderSeekStatus flac_seek_load_cb(
slouken@382
    80
									const FLAC__StreamDecoder *decoder,
slouken@382
    81
									FLAC__uint64 absolute_byte_offset,
slouken@436
    82
									void *client_data)
slouken@436
    83
{
slouken@382
    84
	FLAC_SDL_Data *data = (FLAC_SDL_Data *)client_data;
slouken@382
    85
slouken@473
    86
	if (SDL_RWseek (data->sdl_src, absolute_byte_offset, RW_SEEK_SET) < 0) {
slouken@382
    87
		return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR;
slouken@382
    88
	}
slouken@382
    89
	else {
slouken@382
    90
		return FLAC__STREAM_DECODER_SEEK_STATUS_OK;
slouken@382
    91
	}
slouken@382
    92
}
slouken@382
    93
slouken@382
    94
static FLAC__StreamDecoderTellStatus flac_tell_load_cb(
slouken@382
    95
									const FLAC__StreamDecoder *decoder,
slouken@382
    96
									FLAC__uint64 *absolute_byte_offset,
slouken@436
    97
									void *client_data)
slouken@436
    98
{
slouken@382
    99
	FLAC_SDL_Data *data = (FLAC_SDL_Data *)client_data;
slouken@382
   100
slouken@382
   101
	int pos = SDL_RWtell (data->sdl_src);
slouken@382
   102
slouken@382
   103
	if (pos < 0) {
slouken@382
   104
		return FLAC__STREAM_DECODER_TELL_STATUS_ERROR;
slouken@382
   105
	}
slouken@382
   106
	else {
slouken@382
   107
		*absolute_byte_offset = (FLAC__uint64)pos;
slouken@382
   108
		return FLAC__STREAM_DECODER_TELL_STATUS_OK;
slouken@382
   109
	}
slouken@382
   110
}
slouken@382
   111
slouken@382
   112
static FLAC__StreamDecoderLengthStatus flac_length_load_cb(
slouken@382
   113
									const FLAC__StreamDecoder *decoder,
slouken@382
   114
									FLAC__uint64 *stream_length,
slouken@436
   115
									void *client_data)
slouken@436
   116
{
slouken@382
   117
	FLAC_SDL_Data *data = (FLAC_SDL_Data *)client_data;
slouken@382
   118
slouken@382
   119
	int pos = SDL_RWtell (data->sdl_src);
slouken@473
   120
	int length = SDL_RWseek (data->sdl_src, 0, RW_SEEK_END);
slouken@382
   121
slouken@473
   122
	if (SDL_RWseek (data->sdl_src, pos, RW_SEEK_SET) != pos || length < 0) {
slouken@382
   123
		/* there was an error attempting to return the stream to the original
slouken@382
   124
		 * position, or the length was invalid. */
slouken@382
   125
		return FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR;
slouken@382
   126
	}
slouken@382
   127
	else {
slouken@382
   128
		*stream_length = (FLAC__uint64)length;
slouken@382
   129
		return FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
slouken@382
   130
	}
slouken@382
   131
}
slouken@382
   132
slouken@382
   133
static FLAC__bool flac_eof_load_cb(const FLAC__StreamDecoder *decoder,
slouken@436
   134
									void *client_data)
slouken@436
   135
{
slouken@382
   136
	FLAC_SDL_Data *data = (FLAC_SDL_Data *)client_data;
slouken@382
   137
slouken@382
   138
	int pos = SDL_RWtell (data->sdl_src);
slouken@473
   139
	int end = SDL_RWseek (data->sdl_src, 0, RW_SEEK_END);
slouken@382
   140
slouken@382
   141
	// was the original position equal to the end (a.k.a. the seek didn't move)?
slouken@382
   142
	if (pos == end) {
slouken@382
   143
		// must be EOF
slouken@382
   144
		return true;
slouken@382
   145
	}
slouken@382
   146
	else {
slouken@382
   147
		// not EOF, return to the original position
slouken@473
   148
		SDL_RWseek (data->sdl_src, pos, RW_SEEK_SET);
slouken@382
   149
slouken@382
   150
		return false;
slouken@382
   151
	}
slouken@382
   152
}
slouken@382
   153
slouken@382
   154
static FLAC__StreamDecoderWriteStatus flac_write_load_cb(
slouken@382
   155
									const FLAC__StreamDecoder *decoder,
slouken@382
   156
									const FLAC__Frame *frame,
slouken@382
   157
									const FLAC__int32 *const buffer[],
slouken@436
   158
									void *client_data)
slouken@436
   159
{
slouken@382
   160
	FLAC_SDL_Data *data = (FLAC_SDL_Data *)client_data;
slouken@382
   161
	size_t i;
slouken@436
   162
	Uint8 *buf;
slouken@382
   163
slouken@382
   164
	if (data->flac_total_samples == 0) {
slouken@382
   165
		SDL_SetError ("Given FLAC file does not specify its sample count.");
slouken@382
   166
		return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
slouken@382
   167
	}
slouken@382
   168
slouken@382
   169
	if (data->sdl_spec->channels != 2 || data->flac_bps != 16) {
slouken@382
   170
		SDL_SetError ("Current FLAC support is only for 16 bit Stereo files.");
slouken@382
   171
		return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
slouken@382
   172
	}
slouken@382
   173
slouken@382
   174
	// check if it is the first audio frame so we can initialize the output
slouken@382
   175
	// buffer
slouken@382
   176
	if (frame->header.number.sample_number == 0) {
slouken@382
   177
		*(data->sdl_audio_len) = data->sdl_spec->size;
slouken@382
   178
		data->sdl_audio_read = 0;
slouken@561
   179
    	*(data->sdl_audio_buf) = SDL_malloc (*(data->sdl_audio_len));
slouken@382
   180
slouken@382
   181
    	if (*(data->sdl_audio_buf) == NULL) {
slouken@382
   182
    		SDL_SetError
slouken@382
   183
					("Unable to allocate memory to store the FLAC stream.");
slouken@382
   184
    		return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
slouken@382
   185
    	}
slouken@382
   186
	}
slouken@382
   187
slouken@436
   188
	buf = *(data->sdl_audio_buf);
slouken@382
   189
slouken@382
   190
	for (i = 0; i < frame->header.blocksize; i++) {
slouken@382
   191
		FLAC__int16 i16;
slouken@382
   192
		FLAC__uint16 ui16;
slouken@382
   193
slouken@382
   194
		i16 = (FLAC__int16)buffer[0][i];
slouken@382
   195
		ui16 = (FLAC__uint16)i16;
slouken@382
   196
slouken@382
   197
		*(buf + (data->sdl_audio_read++)) = (char)(ui16);
slouken@382
   198
		*(buf + (data->sdl_audio_read++)) = (char)(ui16 >> 8);
slouken@382
   199
slouken@382
   200
		i16 = (FLAC__int16)buffer[1][i];
slouken@382
   201
		ui16 = (FLAC__uint16)i16;
slouken@382
   202
slouken@382
   203
		*(buf + (data->sdl_audio_read++)) = (char)(ui16);
slouken@382
   204
		*(buf + (data->sdl_audio_read++)) = (char)(ui16 >> 8);
slouken@382
   205
	}
slouken@382
   206
slouken@382
   207
	return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
slouken@382
   208
}
slouken@382
   209
slouken@382
   210
static void flac_metadata_load_cb(
slouken@382
   211
					const FLAC__StreamDecoder *decoder,
slouken@382
   212
					const FLAC__StreamMetadata *metadata,
slouken@470
   213
					void *client_data)
slouken@470
   214
{
slouken@382
   215
	FLAC_SDL_Data *data = (FLAC_SDL_Data *)client_data;
slouken@382
   216
	FLAC__uint64 total_samples;
slouken@382
   217
	unsigned bps;
slouken@382
   218
slouken@382
   219
	if (metadata->type == FLAC__METADATA_TYPE_STREAMINFO) {
slouken@382
   220
		// save the metadata right now for use later on
slouken@382
   221
		*(data->sdl_audio_buf) = NULL;
slouken@382
   222
		*(data->sdl_audio_len) = 0;
slouken@382
   223
		memset (data->sdl_spec, '\0', sizeof (SDL_AudioSpec));
slouken@382
   224
slouken@382
   225
		data->sdl_spec->format = AUDIO_S16;
slouken@382
   226
		data->sdl_spec->freq = (int)(metadata->data.stream_info.sample_rate);
slouken@382
   227
		data->sdl_spec->channels = (Uint8)(metadata->data.stream_info.channels);
slouken@382
   228
		data->sdl_spec->samples = 8192; /* buffer size */
slouken@382
   229
slouken@382
   230
		total_samples = metadata->data.stream_info.total_samples;
slouken@382
   231
		bps = metadata->data.stream_info.bits_per_sample;
slouken@382
   232
slouken@382
   233
		data->sdl_spec->size = total_samples * data->sdl_spec->channels *
slouken@382
   234
								(bps / 8);
slouken@382
   235
		data->flac_total_samples = total_samples;
slouken@382
   236
		data->flac_bps = bps;
slouken@382
   237
	}
slouken@382
   238
}
slouken@382
   239
slouken@382
   240
static void flac_error_load_cb(
slouken@382
   241
				const FLAC__StreamDecoder *decoder,
slouken@382
   242
				FLAC__StreamDecoderErrorStatus status,
slouken@470
   243
				void *client_data)
slouken@470
   244
{
slouken@382
   245
	// print an SDL error based on the error status
slouken@382
   246
	switch (status) {
slouken@382
   247
		case FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC:
slouken@382
   248
			SDL_SetError ("Error processing the FLAC file [LOST_SYNC].");
slouken@382
   249
		break;
slouken@382
   250
		case FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER:
slouken@382
   251
			SDL_SetError ("Error processing the FLAC file [BAD_HEADER].");
slouken@382
   252
		break;
slouken@382
   253
		case FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH:
slouken@382
   254
			SDL_SetError ("Error processing the FLAC file [CRC_MISMATCH].");
slouken@382
   255
		break;
slouken@382
   256
		case FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM:
slouken@382
   257
			SDL_SetError ("Error processing the FLAC file [UNPARSEABLE].");
slouken@382
   258
		break;
slouken@382
   259
		default:
slouken@382
   260
			SDL_SetError ("Error processing the FLAC file [UNKNOWN].");
slouken@382
   261
		break;
slouken@382
   262
	}
slouken@382
   263
}
slouken@382
   264
slouken@382
   265
/* don't call this directly; use Mix_LoadWAV_RW() for now. */
slouken@382
   266
SDL_AudioSpec *Mix_LoadFLAC_RW (SDL_RWops *src, int freesrc,
slouken@470
   267
        SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len)
slouken@470
   268
{
slouken@382
   269
	FLAC__StreamDecoder *decoder = 0;
slouken@382
   270
	FLAC__StreamDecoderInitStatus init_status;
slouken@382
   271
	int was_error = 1;
slouken@382
   272
	int was_init = 0;
slouken@382
   273
	Uint32 samplesize;
slouken@382
   274
slouken@382
   275
	// create the client data passing information
slouken@382
   276
	FLAC_SDL_Data* client_data;
slouken@561
   277
	client_data = (FLAC_SDL_Data *)SDL_malloc (sizeof (FLAC_SDL_Data));
slouken@382
   278
slouken@470
   279
	if ((!src) || (!audio_buf) || (!audio_len))   /* sanity checks. */
slouken@470
   280
		goto done;
slouken@382
   281
slouken@470
   282
	if (!Mix_Init(MIX_INIT_FLAC))
slouken@470
   283
		goto done;
slouken@382
   284
slouken@382
   285
	if ((decoder = flac.FLAC__stream_decoder_new ()) == NULL) {
slouken@382
   286
		SDL_SetError ("Unable to allocate FLAC decoder.");
slouken@382
   287
		goto done;
slouken@382
   288
	}
slouken@382
   289
slouken@382
   290
	init_status = flac.FLAC__stream_decoder_init_stream (decoder,
slouken@382
   291
								flac_read_load_cb, flac_seek_load_cb,
slouken@382
   292
								flac_tell_load_cb, flac_length_load_cb,
slouken@382
   293
								flac_eof_load_cb, flac_write_load_cb,
slouken@382
   294
								flac_metadata_load_cb, flac_error_load_cb,
slouken@382
   295
								client_data);
slouken@382
   296
slouken@382
   297
	if (init_status != FLAC__STREAM_DECODER_INIT_STATUS_OK) {
slouken@382
   298
		SDL_SetError ("Unable to initialize FLAC stream decoder.");
slouken@382
   299
		goto done;
slouken@382
   300
	}
slouken@382
   301
slouken@382
   302
	was_init = 1;
slouken@382
   303
slouken@382
   304
	client_data->sdl_src = src;
slouken@382
   305
	client_data->sdl_spec = spec;
slouken@382
   306
	client_data->sdl_audio_buf = audio_buf;
slouken@382
   307
	client_data->sdl_audio_len = audio_len;
slouken@382
   308
slouken@382
   309
	if (!flac.FLAC__stream_decoder_process_until_end_of_stream (decoder)) {
slouken@382
   310
		SDL_SetError ("Unable to process FLAC file.");
slouken@382
   311
		goto done;
slouken@382
   312
	}
slouken@382
   313
slouken@382
   314
	was_error = 0;
slouken@382
   315
slouken@561
   316
	/* Don't return a buffer that isn't a multiple of samplesize */
slouken@561
   317
	samplesize = ((spec->format & 0xFF) / 8) * spec->channels;
slouken@561
   318
	*audio_len &= ~(samplesize - 1);
slouken@382
   319
slouken@382
   320
done:
slouken@470
   321
	if (was_init && decoder) {
slouken@382
   322
		flac.FLAC__stream_decoder_finish (decoder);
slouken@382
   323
	}
slouken@382
   324
slouken@470
   325
	if (decoder) {
slouken@382
   326
		flac.FLAC__stream_decoder_delete (decoder);
slouken@382
   327
	}
slouken@382
   328
slouken@470
   329
	if (src) {
slouken@470
   330
		if (freesrc)
slouken@470
   331
			SDL_RWclose (src);
slouken@470
   332
		else
slouken@473
   333
			SDL_RWseek (src, 0, RW_SEEK_SET);
slouken@470
   334
	}
slouken@382
   335
slouken@470
   336
	if (was_error)
slouken@470
   337
		spec = NULL;
slouken@382
   338
slouken@382
   339
    return spec;
slouken@382
   340
}
slouken@382
   341
slouken@382
   342
#endif // FLAC_MUSIC