load_flac.c
author Sam Lantinga <slouken@libsdl.org>
Wed, 27 Feb 2008 07:31:03 +0000
changeset 382 50501e45c57b
child 386 695494546b3c
permissions -rw-r--r--
Austen Dicken - Tue Feb 26 23:28:27 PST 2008

Ok, here is the patch I made for FLAC support.

I have tested it relatively thoroughly and currently the patch allows:
1. Pre-loading FLAC files and playing them via LoadWAV
2. The patch allows for FLAC support in the LoadMUS setting as well as:
* Pause / Resume
* Volume control
* Seeking

I also did a little benchmarking by comparing memory/cpu usage of playmus to
that of mplayer, and the results were very good. playmus typically took about
half the RAM as mplayer, though that may be attributed to mplayer being a more
"bulky" program. As such I would say that the two are probably about equal in
efficiency.

Also, it is important to note that, similar to the OGG support currently
built-in, my FLAC patch only supports 16 bit stereo-encoded sound. Also, it
is only for Native FLAC (standard) and not the derivative, Ogg-FLAC.

I have tried to find a simple way to detect Ogg-FLAC files, as the only
difference between Ogg-FLAC and Native FLAC support is changing the init_
function call, but after digging a little deeper it seems that Ogg-FLAC is
basically FLAC wrapped in an Ogg transport layer, so it would be better to have
a way to read the Ogg transport layer which then reads the inner audio files
according to the proper codec.

But anyway, that's another job for another day! For now this should provide
Native FLAC support!
     1 /*
     2     SDL_mixer:  An audio mixer library based on the SDL library
     3     Copyright (C) 1997-2004 Sam Lantinga
     4 
     5     This library is free software; you can redistribute it and/or
     6     modify it under the terms of the GNU Library General Public
     7     License as published by the Free Software Foundation; either
     8     version 2 of the License, or (at your option) any later version.
     9 
    10     This library is distributed in the hope that it will be useful,
    11     but WITHOUT ANY WARRANTY; without even the implied warranty of
    12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    13     Library General Public License for more details.
    14 
    15     You should have received a copy of the GNU Library General Public
    16     License along with this library; if not, write to the Free
    17     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    18 
    19     This is the source needed to decode a FLAC into a waveform.
    20     										~ Austen Dicken (admin@cvpcs.org).
    21 */
    22 
    23 #ifdef FLAC_MUSIC
    24 
    25 #include <stdio.h>
    26 #include <stdlib.h>
    27 #include <string.h>
    28 
    29 #include "SDL_mutex.h"
    30 #include "SDL_endian.h"
    31 #include "SDL_timer.h"
    32 
    33 #include "SDL_mixer.h"
    34 #include "dynamic_flac.h"
    35 #include "load_flac.h"
    36 
    37 #include <FLAC/stream_decoder.h>
    38 
    39 typedef struct {
    40 	SDL_RWops* sdl_src;
    41 	SDL_AudioSpec* sdl_spec;
    42 	Uint8** sdl_audio_buf;
    43 	Uint32* sdl_audio_len;
    44 	int sdl_audio_read;
    45 	FLAC__uint64 flac_total_samples;
    46 	unsigned flac_bps;
    47 } FLAC_SDL_Data;
    48 
    49 static FLAC__StreamDecoderReadStatus flac_read_load_cb(
    50 									const FLAC__StreamDecoder *decoder,
    51 									FLAC__byte buffer[],
    52 									size_t *bytes,
    53 									void *client_data) {
    54 	// make sure there is something to be reading
    55 	if (*bytes > 0) {
    56 		FLAC_SDL_Data *data = (FLAC_SDL_Data *)client_data;
    57 
    58 		*bytes = SDL_RWread (data->sdl_src, buffer, sizeof (FLAC__byte),
    59 								*bytes);
    60 
    61 		if(*bytes < 0) { // error in read
    62 			return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
    63 		}
    64 		else if(*bytes == 0) { // no data was read (EOF)
    65 			return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
    66 		}
    67 		else { // data was read, continue
    68 			return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
    69 		}
    70 	}
    71 	else {
    72 		return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
    73 	}
    74 }
    75 
    76 static FLAC__StreamDecoderSeekStatus flac_seek_load_cb(
    77 									const FLAC__StreamDecoder *decoder,
    78 									FLAC__uint64 absolute_byte_offset,
    79 									void *client_data) {
    80 	FLAC_SDL_Data *data = (FLAC_SDL_Data *)client_data;
    81 
    82 	if (SDL_RWseek (data->sdl_src, absolute_byte_offset, SEEK_SET) < 0) {
    83 		return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR;
    84 	}
    85 	else {
    86 		return FLAC__STREAM_DECODER_SEEK_STATUS_OK;
    87 	}
    88 }
    89 
    90 static FLAC__StreamDecoderTellStatus flac_tell_load_cb(
    91 									const FLAC__StreamDecoder *decoder,
    92 									FLAC__uint64 *absolute_byte_offset,
    93 									void *client_data) {
    94 	FLAC_SDL_Data *data = (FLAC_SDL_Data *)client_data;
    95 
    96 	int pos = SDL_RWtell (data->sdl_src);
    97 
    98 	if (pos < 0) {
    99 		return FLAC__STREAM_DECODER_TELL_STATUS_ERROR;
   100 	}
   101 	else {
   102 		*absolute_byte_offset = (FLAC__uint64)pos;
   103 		return FLAC__STREAM_DECODER_TELL_STATUS_OK;
   104 	}
   105 }
   106 
   107 static FLAC__StreamDecoderLengthStatus flac_length_load_cb(
   108 									const FLAC__StreamDecoder *decoder,
   109 									FLAC__uint64 *stream_length,
   110 									void *client_data) {
   111 	FLAC_SDL_Data *data = (FLAC_SDL_Data *)client_data;
   112 
   113 	int pos = SDL_RWtell (data->sdl_src);
   114 	int length = SDL_RWseek (data->sdl_src, 0, SEEK_END);
   115 
   116 	if (SDL_RWseek (data->sdl_src, pos, SEEK_SET) != pos || length < 0) {
   117 		/* there was an error attempting to return the stream to the original
   118 		 * position, or the length was invalid. */
   119 		return FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR;
   120 	}
   121 	else {
   122 		*stream_length = (FLAC__uint64)length;
   123 		return FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
   124 	}
   125 }
   126 
   127 static FLAC__bool flac_eof_load_cb(const FLAC__StreamDecoder *decoder,
   128 									void *client_data) {
   129 	FLAC_SDL_Data *data = (FLAC_SDL_Data *)client_data;
   130 
   131 	int pos = SDL_RWtell (data->sdl_src);
   132 	int end = SDL_RWseek (data->sdl_src, 0, SEEK_END);
   133 
   134 	// was the original position equal to the end (a.k.a. the seek didn't move)?
   135 	if (pos == end) {
   136 		// must be EOF
   137 		return true;
   138 	}
   139 	else {
   140 		// not EOF, return to the original position
   141 		SDL_RWseek (data->sdl_src, pos, SEEK_SET);
   142 
   143 		return false;
   144 	}
   145 }
   146 
   147 static FLAC__StreamDecoderWriteStatus flac_write_load_cb(
   148 									const FLAC__StreamDecoder *decoder,
   149 									const FLAC__Frame *frame,
   150 									const FLAC__int32 *const buffer[],
   151 									void *client_data) {
   152 	FLAC_SDL_Data *data = (FLAC_SDL_Data *)client_data;
   153 	size_t i;
   154 
   155 	if (data->flac_total_samples == 0) {
   156 		SDL_SetError ("Given FLAC file does not specify its sample count.");
   157 		return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
   158 	}
   159 
   160 	if (data->sdl_spec->channels != 2 || data->flac_bps != 16) {
   161 		SDL_SetError ("Current FLAC support is only for 16 bit Stereo files.");
   162 		return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
   163 	}
   164 
   165 	// check if it is the first audio frame so we can initialize the output
   166 	// buffer
   167 	if (frame->header.number.sample_number == 0) {
   168 		*(data->sdl_audio_len) = data->sdl_spec->size;
   169 		data->sdl_audio_read = 0;
   170     	*(data->sdl_audio_buf) = malloc (*(data->sdl_audio_len));
   171 
   172     	if (*(data->sdl_audio_buf) == NULL) {
   173     		SDL_SetError
   174 					("Unable to allocate memory to store the FLAC stream.");
   175     		return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
   176     	}
   177 	}
   178 
   179 	Uint8 *buf = *(data->sdl_audio_buf);
   180 
   181 	for (i = 0; i < frame->header.blocksize; i++) {
   182 		FLAC__int16 i16;
   183 		FLAC__uint16 ui16;
   184 
   185 		i16 = (FLAC__int16)buffer[0][i];
   186 		ui16 = (FLAC__uint16)i16;
   187 
   188 		*(buf + (data->sdl_audio_read++)) = (char)(ui16);
   189 		*(buf + (data->sdl_audio_read++)) = (char)(ui16 >> 8);
   190 
   191 		i16 = (FLAC__int16)buffer[1][i];
   192 		ui16 = (FLAC__uint16)i16;
   193 
   194 		*(buf + (data->sdl_audio_read++)) = (char)(ui16);
   195 		*(buf + (data->sdl_audio_read++)) = (char)(ui16 >> 8);
   196 	}
   197 
   198 	return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
   199 }
   200 
   201 static void flac_metadata_load_cb(
   202 					const FLAC__StreamDecoder *decoder,
   203 					const FLAC__StreamMetadata *metadata,
   204 					void *client_data) {
   205 	FLAC_SDL_Data *data = (FLAC_SDL_Data *)client_data;
   206 	FLAC__uint64 total_samples;
   207 	unsigned bps;
   208 
   209 	if (metadata->type == FLAC__METADATA_TYPE_STREAMINFO) {
   210 		// save the metadata right now for use later on
   211 		*(data->sdl_audio_buf) = NULL;
   212 		*(data->sdl_audio_len) = 0;
   213 		memset (data->sdl_spec, '\0', sizeof (SDL_AudioSpec));
   214 
   215 		data->sdl_spec->format = AUDIO_S16;
   216 		data->sdl_spec->freq = (int)(metadata->data.stream_info.sample_rate);
   217 		data->sdl_spec->channels = (Uint8)(metadata->data.stream_info.channels);
   218 		data->sdl_spec->samples = 8192; /* buffer size */
   219 
   220 		total_samples = metadata->data.stream_info.total_samples;
   221 		bps = metadata->data.stream_info.bits_per_sample;
   222 
   223 		data->sdl_spec->size = total_samples * data->sdl_spec->channels *
   224 								(bps / 8);
   225 		data->flac_total_samples = total_samples;
   226 		data->flac_bps = bps;
   227 	}
   228 }
   229 
   230 static void flac_error_load_cb(
   231 				const FLAC__StreamDecoder *decoder,
   232 				FLAC__StreamDecoderErrorStatus status,
   233 				void *client_data) {
   234 	// print an SDL error based on the error status
   235 	switch (status) {
   236 		case FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC:
   237 			SDL_SetError ("Error processing the FLAC file [LOST_SYNC].");
   238 		break;
   239 		case FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER:
   240 			SDL_SetError ("Error processing the FLAC file [BAD_HEADER].");
   241 		break;
   242 		case FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH:
   243 			SDL_SetError ("Error processing the FLAC file [CRC_MISMATCH].");
   244 		break;
   245 		case FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM:
   246 			SDL_SetError ("Error processing the FLAC file [UNPARSEABLE].");
   247 		break;
   248 		default:
   249 			SDL_SetError ("Error processing the FLAC file [UNKNOWN].");
   250 		break;
   251 	}
   252 }
   253 
   254 /* don't call this directly; use Mix_LoadWAV_RW() for now. */
   255 SDL_AudioSpec *Mix_LoadFLAC_RW (SDL_RWops *src, int freesrc,
   256         SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len) {
   257 	FLAC__StreamDecoder *decoder = 0;
   258 	FLAC__StreamDecoderInitStatus init_status;
   259 	int was_error = 1;
   260 	int was_init = 0;
   261 	Uint32 samplesize;
   262 
   263 	// create the client data passing information
   264 	FLAC_SDL_Data* client_data;
   265 	client_data = (FLAC_SDL_Data *)malloc (sizeof (FLAC_SDL_Data));
   266 
   267     if ((!src) || (!audio_buf) || (!audio_len))   /* sanity checks. */
   268         goto done;
   269 
   270     if (Mix_InitFLAC() < 0)
   271         goto done;
   272 
   273 	if ((decoder = flac.FLAC__stream_decoder_new ()) == NULL) {
   274 		SDL_SetError ("Unable to allocate FLAC decoder.");
   275 		goto done;
   276 	}
   277 
   278 	init_status = flac.FLAC__stream_decoder_init_stream (decoder,
   279 								flac_read_load_cb, flac_seek_load_cb,
   280 								flac_tell_load_cb, flac_length_load_cb,
   281 								flac_eof_load_cb, flac_write_load_cb,
   282 								flac_metadata_load_cb, flac_error_load_cb,
   283 								client_data);
   284 
   285 	if (init_status != FLAC__STREAM_DECODER_INIT_STATUS_OK) {
   286 		SDL_SetError ("Unable to initialize FLAC stream decoder.");
   287 		goto done;
   288 	}
   289 
   290 	was_init = 1;
   291 
   292 	client_data->sdl_src = src;
   293 	client_data->sdl_spec = spec;
   294 	client_data->sdl_audio_buf = audio_buf;
   295 	client_data->sdl_audio_len = audio_len;
   296 
   297 	if (!flac.FLAC__stream_decoder_process_until_end_of_stream (decoder)) {
   298 		SDL_SetError ("Unable to process FLAC file.");
   299 		goto done;
   300 	}
   301 
   302 	was_error = 0;
   303 
   304     /* Don't return a buffer that isn't a multiple of samplesize */
   305     samplesize = ((spec->format & 0xFF) / 8) * spec->channels;
   306     *audio_len &= ~(samplesize - 1);
   307 
   308 done:
   309 	if(was_init && decoder) {
   310 		flac.FLAC__stream_decoder_finish (decoder);
   311 	}
   312 
   313 	if(decoder) {
   314 		flac.FLAC__stream_decoder_delete (decoder);
   315 	}
   316 
   317     if (src) {
   318         if (freesrc)
   319             SDL_RWclose (src);
   320         else
   321             SDL_RWseek (src, 0, SEEK_SET);
   322     }
   323 
   324     if (was_error)
   325         spec = NULL;
   326 
   327     Mix_QuitFLAC ();
   328 
   329     return spec;
   330 }
   331 
   332 #endif // FLAC_MUSIC