Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
319 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,319 @@ | ||
/* | ||
* SDL_sound -- An abstract sound format decoding API. | ||
* Copyright (C) 2001 Ryan C. Gordon. | ||
* | ||
* This library is free software; you can redistribute it and/or | ||
* modify it under the terms of the GNU Lesser General Public | ||
* License as published by the Free Software Foundation; either | ||
* version 2.1 of the License, or (at your option) any later version. | ||
* | ||
* This library is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
* Lesser General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU Lesser General Public | ||
* License along with this library; if not, write to the Free Software | ||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
*/ | ||
|
||
/* | ||
* FLAC decoder for SDL_sound. | ||
* | ||
* This driver handles FLAC audio, that is to say the Free Lossless Audio | ||
* Codec. It depends on libFLAC for decoding, which can be grabbed from: | ||
* http://flac.sourceforge.net | ||
* | ||
* Please see the file LICENSE in the source's root directory. | ||
* | ||
* This file written by Torbjörn Andersson. (d91tan@Update.UU.SE) | ||
*/ | ||
|
||
#if HAVE_CONFIG_H | ||
# include <config.h> | ||
#endif | ||
|
||
#ifdef SOUND_SUPPORTS_FLAC | ||
|
||
#include <stdio.h> | ||
#include <stdlib.h> | ||
#include <string.h> | ||
#include <assert.h> | ||
|
||
#include "SDL_sound.h" | ||
|
||
#define __SDL_SOUND_INTERNAL__ | ||
#include "SDL_sound_internal.h" | ||
|
||
#include "FLAC/all.h" | ||
|
||
|
||
static int FLAC_init(void); | ||
static void FLAC_quit(void); | ||
static int FLAC_open(Sound_Sample *sample, const char *ext); | ||
static void FLAC_close(Sound_Sample *sample); | ||
static Uint32 FLAC_read(Sound_Sample *sample); | ||
|
||
static const char *extensions_flac[] = { "FLAC", "FLA", NULL }; | ||
|
||
const Sound_DecoderFunctions __Sound_DecoderFunctions_FLAC = | ||
{ | ||
{ | ||
extensions_flac, | ||
"Free Lossless Audio Codec", | ||
"Torbjörn Andersson <d91tan@Update.UU.SE>", | ||
"http://flac.sourceforge.net/" | ||
}, | ||
|
||
FLAC_init, /* init() method */ | ||
FLAC_quit, /* quit() method */ | ||
FLAC_open, /* open() method */ | ||
FLAC_close, /* close() method */ | ||
FLAC_read /* read() method */ | ||
}; | ||
|
||
/* This is what we store in our internal->decoder_private field. */ | ||
typedef struct | ||
{ | ||
FLAC__StreamDecoder *decoder; | ||
SDL_RWops *rw; | ||
Sound_Sample *sample; | ||
Uint32 frame_size; | ||
} flac_t; | ||
|
||
|
||
static FLAC__StreamDecoderReadStatus FLAC_read_callback( | ||
const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], | ||
unsigned int *bytes, void *client_data) | ||
{ | ||
flac_t *f = (flac_t *) client_data; | ||
Uint32 retval; | ||
|
||
#if 0 | ||
SNDDBG(("FLAC: Read callback\n")); | ||
#endif | ||
|
||
retval = SDL_RWread(f->rw, (Uint8 *) buffer, 1, *bytes); | ||
|
||
if (retval == 0) | ||
{ | ||
SNDDBG(("FLAC: End of file\n")); | ||
*bytes = 0; | ||
f->sample->flags |= SOUND_SAMPLEFLAG_EOF; | ||
return(FLAC__STREAM_DECODER_READ_END_OF_STREAM); | ||
} /* if */ | ||
|
||
if (retval == -1) | ||
{ | ||
*bytes = 0; | ||
f->sample->flags |= SOUND_SAMPLEFLAG_ERROR; | ||
return(FLAC__STREAM_DECODER_READ_ABORT); | ||
} /* if */ | ||
|
||
if (retval < *bytes) | ||
{ | ||
*bytes = retval; | ||
f->sample->flags |= SOUND_SAMPLEFLAG_EAGAIN; | ||
} /* if */ | ||
|
||
return(FLAC__STREAM_DECODER_READ_CONTINUE); | ||
} /* FLAC_read_callback */ | ||
|
||
|
||
static FLAC__StreamDecoderWriteStatus FLAC_write_callback( | ||
const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, | ||
const FLAC__int32 *buffer[], void *client_data) | ||
{ | ||
flac_t *f = (flac_t *) client_data; | ||
Uint32 i, j; | ||
Uint8 *dst; | ||
|
||
#if 0 | ||
SNDDBG(("FLAC: Write callback.\n")); | ||
#endif | ||
|
||
f->frame_size = frame->header.channels * frame->header.blocksize | ||
* frame->header.bits_per_sample / 8; | ||
|
||
if (f->frame_size > f->sample->buffer_size) | ||
Sound_SetBufferSize(f->sample, f->frame_size); | ||
|
||
dst = f->sample->buffer; | ||
|
||
if (frame->header.bits_per_sample == 8) | ||
{ | ||
for (i = 0; i < frame->header.blocksize; i++) | ||
for (j = 0; j < frame->header.channels; j++) | ||
*dst++ = buffer[j][i] & 0x000000ff; | ||
} /* if */ | ||
else | ||
{ | ||
for (i = 0; i < frame->header.blocksize; i++) | ||
for (j = 0; j < frame->header.channels; j++) | ||
{ | ||
*dst++ = (buffer[j][i] & 0x0000ff00) >> 8; | ||
*dst++ = buffer[j][i] & 0x000000ff; | ||
} /* for */ | ||
} /* else */ | ||
|
||
return(FLAC__STREAM_DECODER_WRITE_CONTINUE); | ||
} /* FLAC_write_callback */ | ||
|
||
|
||
void FLAC_metadata_callback( | ||
const FLAC__StreamDecoder *decoder, const FLAC__StreamMetaData *metadata, | ||
void *client_data) | ||
{ | ||
flac_t *f = (flac_t *) client_data; | ||
|
||
SNDDBG(("FLAC: Metadata callback.\n")); | ||
|
||
if (metadata->type == FLAC__METADATA_TYPE_STREAMINFO) | ||
{ | ||
SNDDBG(("FLAC: Metadata is streaminfo.\n")); | ||
f->sample->actual.channels = metadata->data.stream_info.channels; | ||
f->sample->actual.rate = metadata->data.stream_info.sample_rate; | ||
|
||
/* !!! FIXME: I believe bits_per_sample may be anywhere between | ||
* 4 and 24. We can only handle 8 and 16 at present. | ||
*/ | ||
switch (metadata->data.stream_info.bits_per_sample) | ||
{ | ||
case 8: | ||
f->sample->actual.format = AUDIO_S8; | ||
break; | ||
case 16: | ||
f->sample->actual.format = AUDIO_S16MSB; | ||
break; | ||
default: | ||
Sound_SetError("FLAC: Unsupported sample width.\n"); | ||
f->sample->actual.format = 0; | ||
break; | ||
} /* switch */ | ||
} /* if */ | ||
} /* FLAC_metadata_callback */ | ||
|
||
|
||
void FLAC_error_callback( | ||
const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, | ||
void *client_data) | ||
{ | ||
flac_t *f = (flac_t *) client_data; | ||
|
||
SNDDBG(("FLAC: Error callback.\n")); | ||
f->sample->flags |= SOUND_SAMPLEFLAG_ERROR; | ||
} /* FLAC_error_callback */ | ||
|
||
|
||
static int FLAC_init(void) | ||
{ | ||
return(1); /* always succeeds. */ | ||
} /* FLAC_init */ | ||
|
||
|
||
static void FLAC_quit(void) | ||
{ | ||
/* it's a no-op. */ | ||
} /* FLAC_quit */ | ||
|
||
|
||
static int FLAC_open(Sound_Sample *sample, const char *ext) | ||
{ | ||
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; | ||
SDL_RWops *rw = internal->rw; | ||
FLAC__StreamDecoder *decoder; | ||
Uint8 flac_magic[4]; | ||
flac_t *f; | ||
|
||
if (SDL_RWread(rw, flac_magic, sizeof (flac_magic), 1) != 1) | ||
{ | ||
Sound_SetError("FLAC: Could not read FLAC magic."); | ||
return(0); | ||
} /* if */ | ||
|
||
if (strncmp(flac_magic, "fLaC", sizeof (flac_magic)) != 0) | ||
{ | ||
Sound_SetError("FLAC: Not a FLAC stream."); | ||
return(0); | ||
} /* if */ | ||
|
||
SDL_RWseek(internal->rw, -sizeof (flac_magic), SEEK_CUR); | ||
|
||
f = (flac_t *) malloc(sizeof (flac_t)); | ||
BAIL_IF_MACRO(f == NULL, ERR_OUT_OF_MEMORY, 0); | ||
|
||
decoder = FLAC__stream_decoder_new(); | ||
if (decoder == NULL) | ||
{ | ||
Sound_SetError(ERR_OUT_OF_MEMORY); | ||
free(f); | ||
return(0); | ||
} /* if */ | ||
|
||
FLAC__stream_decoder_set_read_callback(decoder, FLAC_read_callback); | ||
FLAC__stream_decoder_set_write_callback(decoder, FLAC_write_callback); | ||
FLAC__stream_decoder_set_metadata_callback(decoder, FLAC_metadata_callback); | ||
FLAC__stream_decoder_set_error_callback(decoder, FLAC_error_callback); | ||
FLAC__stream_decoder_set_client_data(decoder, f); | ||
|
||
f->rw = internal->rw; | ||
f->sample = sample; | ||
f->decoder = decoder; | ||
|
||
FLAC__stream_decoder_init(decoder); | ||
internal->decoder_private = f; | ||
|
||
SNDDBG(("FLAC: Accepting data stream.\n")); | ||
|
||
FLAC__stream_decoder_process_metadata(decoder); | ||
|
||
if (f->sample->actual.format == 0) | ||
{ | ||
FLAC__stream_decoder_finish(decoder); | ||
FLAC__stream_decoder_delete(decoder); | ||
free(f); | ||
return(0); | ||
} /* if */ | ||
|
||
sample->flags = SOUND_SAMPLEFLAG_NONE; | ||
return(1); | ||
} /* FLAC_open */ | ||
|
||
|
||
static void FLAC_close(Sound_Sample *sample) | ||
{ | ||
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; | ||
flac_t *f = (flac_t *) internal->decoder_private; | ||
|
||
FLAC__stream_decoder_finish(f->decoder); | ||
FLAC__stream_decoder_delete(f->decoder); | ||
free(f); | ||
} /* FLAC_close */ | ||
|
||
|
||
static Uint32 FLAC_read(Sound_Sample *sample) | ||
{ | ||
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; | ||
flac_t *f = (flac_t *) internal->decoder_private; | ||
Uint32 len; | ||
|
||
if (FLAC__stream_decoder_get_state(f->decoder) == FLAC__STREAM_DECODER_END_OF_STREAM) | ||
{ | ||
sample->flags |= SOUND_SAMPLEFLAG_EOF; | ||
return(0); | ||
} /* if */ | ||
|
||
if (!FLAC__stream_decoder_process_one_frame(f->decoder)) | ||
{ | ||
SNDDBG(("FLAC: Couldn't decode frame.\n")); | ||
sample->flags |= SOUND_SAMPLEFLAG_ERROR; | ||
return(0); | ||
} /* if */ | ||
|
||
return(f->frame_size); | ||
} /* FLAC_read */ | ||
|
||
#endif /* SOUND_SUPPORTS_FLAC */ | ||
|
||
|
||
/* end of flac.c ... */ |