mirror of
https://github.com/nzp-team/quakespasm.git
synced 2024-11-22 11:51:04 +00:00
391 lines
11 KiB
C
391 lines
11 KiB
C
|
/*
|
||
|
* fLaC streaming music support, loosely based QuakeForge implementation
|
||
|
* with modifications. requires libFLAC >= 1.0.4 at compile and runtime.
|
||
|
*
|
||
|
* Copyright (C) 2005 Bill Currie <bill@taniwha.org>
|
||
|
* Copyright (C) 2013 O.Sezer <sezero@users.sourceforge.net>
|
||
|
*
|
||
|
* This program is free software; you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU General Public License as published by
|
||
|
* the Free Software Foundation; either version 2 of the License, or (at
|
||
|
* your option) any later version.
|
||
|
*
|
||
|
* This program 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 General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU General Public License along
|
||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#include "quakedef.h"
|
||
|
|
||
|
#if defined(USE_CODEC_FLAC)
|
||
|
#include "snd_codec.h"
|
||
|
#include "snd_codeci.h"
|
||
|
#include "snd_flac.h"
|
||
|
|
||
|
#undef LEGACY_FLAC
|
||
|
#include <FLAC/stream_decoder.h>
|
||
|
/* FLAC 1.1.3 has FLAC_API_VERSION_CURRENT == 8 */
|
||
|
#if !defined(FLAC_API_VERSION_CURRENT) || ((FLAC_API_VERSION_CURRENT+0) < 8)
|
||
|
#define LEGACY_FLAC
|
||
|
#include <FLAC/seekable_stream_decoder.h>
|
||
|
#endif
|
||
|
|
||
|
#ifdef LEGACY_FLAC
|
||
|
#define FLAC__StreamDecoder FLAC__SeekableStreamDecoder
|
||
|
#define FLAC__StreamDecoderReadStatus FLAC__SeekableStreamDecoderReadStatus
|
||
|
#define FLAC__StreamDecoderSeekStatus FLAC__SeekableStreamDecoderSeekStatus
|
||
|
#define FLAC__StreamDecoderTellStatus FLAC__SeekableStreamDecoderTellStatus
|
||
|
#define FLAC__StreamDecoderLengthStatus FLAC__SeekableStreamDecoderLengthStatus
|
||
|
|
||
|
#define FLAC__stream_decoder_new FLAC__seekable_stream_decoder_new
|
||
|
#define FLAC__stream_decoder_finish FLAC__seekable_stream_decoder_finish
|
||
|
#define FLAC__stream_decoder_delete FLAC__seekable_stream_decoder_delete
|
||
|
#define FLAC__stream_decoder_process_single FLAC__seekable_stream_decoder_process_single
|
||
|
#define FLAC__stream_decoder_seek_absolute FLAC__seekable_stream_decoder_seek_absolute
|
||
|
#define FLAC__stream_decoder_process_until_end_of_metadata FLAC__seekable_stream_decoder_process_until_end_of_metadata
|
||
|
#define FLAC__stream_decoder_get_state FLAC__seekable_stream_decoder_get_state
|
||
|
|
||
|
#define FLAC__STREAM_DECODER_INIT_STATUS_OK FLAC__SEEKABLE_STREAM_DECODER_OK
|
||
|
#define FLAC__STREAM_DECODER_READ_STATUS_CONTINUE FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_OK
|
||
|
#define FLAC__STREAM_DECODER_READ_STATUS_ABORT FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_ERROR
|
||
|
#define FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_OK /* !!! */
|
||
|
#define FLAC__STREAM_DECODER_WRITE_STATUS_ABORT FLAC__STREAM_DECODER_WRITE_STATUS_ABORT
|
||
|
#define FLAC__STREAM_DECODER_SEEK_STATUS_OK FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_OK
|
||
|
#define FLAC__STREAM_DECODER_SEEK_STATUS_ERROR FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_ERROR
|
||
|
#define FLAC__STREAM_DECODER_TELL_STATUS_OK FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_OK
|
||
|
#define FLAC__STREAM_DECODER_TELL_STATUS_ERROR FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_ERROR
|
||
|
#define FLAC__STREAM_DECODER_LENGTH_STATUS_OK FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_OK
|
||
|
typedef unsigned FLAC_SIZE_T;
|
||
|
#else
|
||
|
typedef size_t FLAC_SIZE_T;
|
||
|
#endif
|
||
|
|
||
|
typedef struct {
|
||
|
FLAC__StreamDecoder *decoder;
|
||
|
fshandle_t *file;
|
||
|
snd_info_t *info;
|
||
|
byte *buffer;
|
||
|
int size, pos, error;
|
||
|
} flacfile_t;
|
||
|
|
||
|
/* CALLBACK FUNCTIONS: */
|
||
|
static void
|
||
|
flac_error_func (const FLAC__StreamDecoder *decoder,
|
||
|
FLAC__StreamDecoderErrorStatus status, void *client_data)
|
||
|
{
|
||
|
flacfile_t *ff = (flacfile_t *) client_data;
|
||
|
ff->error = -1;
|
||
|
Con_Printf ("FLAC: decoder error %i\n", status);
|
||
|
}
|
||
|
|
||
|
static FLAC__StreamDecoderReadStatus
|
||
|
flac_read_func (const FLAC__StreamDecoder *decoder, FLAC__byte buffer[],
|
||
|
FLAC_SIZE_T *bytes, void *client_data)
|
||
|
{
|
||
|
flacfile_t *ff = (flacfile_t *) client_data;
|
||
|
if (*bytes > 0)
|
||
|
{
|
||
|
*bytes = FS_fread(buffer, 1, *bytes, ff->file);
|
||
|
if (FS_ferror(ff->file))
|
||
|
return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
|
||
|
if (*bytes == 0)
|
||
|
return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
|
||
|
return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
|
||
|
}
|
||
|
return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
|
||
|
}
|
||
|
|
||
|
static FLAC__StreamDecoderSeekStatus
|
||
|
flac_seek_func (const FLAC__StreamDecoder *decoder,
|
||
|
FLAC__uint64 absolute_byte_offset, void *client_data)
|
||
|
{
|
||
|
flacfile_t *ff = (flacfile_t *) client_data;
|
||
|
if (FS_fseek(ff->file, (long)absolute_byte_offset, SEEK_SET) < 0)
|
||
|
return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR;
|
||
|
return FLAC__STREAM_DECODER_SEEK_STATUS_OK;
|
||
|
}
|
||
|
|
||
|
static FLAC__StreamDecoderTellStatus
|
||
|
flac_tell_func (const FLAC__StreamDecoder *decoder,
|
||
|
FLAC__uint64 *absolute_byte_offset, void *client_data)
|
||
|
{
|
||
|
flacfile_t *ff = (flacfile_t *) client_data;
|
||
|
long pos = FS_ftell (ff->file);
|
||
|
if (pos < 0) return FLAC__STREAM_DECODER_TELL_STATUS_ERROR;
|
||
|
*absolute_byte_offset = (FLAC__uint64) pos;
|
||
|
return FLAC__STREAM_DECODER_TELL_STATUS_OK;
|
||
|
}
|
||
|
|
||
|
static FLAC__StreamDecoderLengthStatus
|
||
|
flac_length_func (const FLAC__StreamDecoder *decoder,
|
||
|
FLAC__uint64 *stream_length, void *client_data)
|
||
|
{
|
||
|
flacfile_t *ff = (flacfile_t *) client_data;
|
||
|
*stream_length = (FLAC__uint64) FS_filelength (ff->file);
|
||
|
return FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
|
||
|
}
|
||
|
|
||
|
static FLAC__bool
|
||
|
flac_eof_func (const FLAC__StreamDecoder *decoder, void *client_data)
|
||
|
{
|
||
|
flacfile_t *ff = (flacfile_t *) client_data;
|
||
|
if (FS_feof (ff->file)) return true;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
static FLAC__StreamDecoderWriteStatus
|
||
|
flac_write_func (const FLAC__StreamDecoder *decoder,
|
||
|
const FLAC__Frame *frame, const FLAC__int32 * const buffer[],
|
||
|
void *client_data)
|
||
|
{
|
||
|
flacfile_t *ff = (flacfile_t *) client_data;
|
||
|
|
||
|
if (!ff->buffer) {
|
||
|
ff->buffer = (byte *) malloc (ff->info->blocksize * ff->info->channels * ff->info->width);
|
||
|
if (!ff->buffer) {
|
||
|
ff->error = -1; /* needn't set this here, but... */
|
||
|
Con_Printf("Insufficient memory for fLaC audio\n");
|
||
|
return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (ff->info->channels == 1)
|
||
|
{
|
||
|
unsigned i;
|
||
|
const FLAC__int32 *in = buffer[0];
|
||
|
|
||
|
if (ff->info->bits == 8)
|
||
|
{
|
||
|
byte *out = ff->buffer;
|
||
|
for (i = 0; i < frame->header.blocksize; i++)
|
||
|
*out++ = *in++ + 128;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
short *out = (short *) ff->buffer;
|
||
|
for (i = 0; i < frame->header.blocksize; i++)
|
||
|
*out++ = *in++;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
unsigned i;
|
||
|
const FLAC__int32 *li = buffer[0];
|
||
|
const FLAC__int32 *ri = buffer[1];
|
||
|
|
||
|
if (ff->info->bits == 8)
|
||
|
{
|
||
|
char *lo = (char *) ff->buffer + 0;
|
||
|
char *ro = (char *) ff->buffer + 1;
|
||
|
for (i = 0; i < frame->header.blocksize; i++, lo++, ro++)
|
||
|
{
|
||
|
*lo++ = *li++ + 128;
|
||
|
*ro++ = *ri++ + 128;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
short *lo = (short *) ff->buffer + 0;
|
||
|
short *ro = (short *) ff->buffer + 1;
|
||
|
for (i = 0; i < frame->header.blocksize; i++, lo++, ro++)
|
||
|
{
|
||
|
*lo++ = *li++;
|
||
|
*ro++ = *ri++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ff->size = frame->header.blocksize * ff->info->width * ff->info->channels;
|
||
|
ff->pos = 0;
|
||
|
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
flac_meta_func (const FLAC__StreamDecoder *decoder,
|
||
|
const FLAC__StreamMetadata *metadata, void *client_data)
|
||
|
{
|
||
|
flacfile_t *ff = (flacfile_t *) client_data;
|
||
|
if (metadata->type == FLAC__METADATA_TYPE_STREAMINFO)
|
||
|
{
|
||
|
ff->info->rate = metadata->data.stream_info.sample_rate;
|
||
|
ff->info->bits = metadata->data.stream_info.bits_per_sample;
|
||
|
ff->info->width = ff->info->bits / 8;
|
||
|
ff->info->channels = metadata->data.stream_info.channels;
|
||
|
ff->info->blocksize = metadata->data.stream_info.max_blocksize;
|
||
|
ff->info->dataofs = 0; /* got the STREAMINFO metadata */
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
static qboolean S_FLAC_CodecInitialize (void)
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
static void S_FLAC_CodecShutdown (void)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
static qboolean S_FLAC_CodecOpenStream (snd_stream_t *stream)
|
||
|
{
|
||
|
flacfile_t *ff;
|
||
|
int rc;
|
||
|
|
||
|
ff = (flacfile_t *) Z_Malloc(sizeof(flacfile_t));
|
||
|
|
||
|
ff->decoder = FLAC__stream_decoder_new ();
|
||
|
if (ff->decoder == NULL)
|
||
|
{
|
||
|
Con_Printf("Unable to create fLaC decoder\n");
|
||
|
goto _fail;
|
||
|
}
|
||
|
|
||
|
stream->priv = ff;
|
||
|
ff->info = & stream->info;
|
||
|
ff->file = & stream->fh;
|
||
|
ff->info->dataofs = -1; /* check for STREAMINFO metadata existence */
|
||
|
|
||
|
#ifdef LEGACY_FLAC
|
||
|
FLAC__seekable_stream_decoder_set_error_callback (ff->decoder, flac_error_func);
|
||
|
FLAC__seekable_stream_decoder_set_read_callback (ff->decoder, flac_read_func);
|
||
|
FLAC__seekable_stream_decoder_set_seek_callback (ff->decoder, flac_seek_func);
|
||
|
FLAC__seekable_stream_decoder_set_tell_callback (ff->decoder, flac_tell_func);
|
||
|
FLAC__seekable_stream_decoder_set_length_callback (ff->decoder, flac_length_func);
|
||
|
FLAC__seekable_stream_decoder_set_eof_callback (ff->decoder, flac_eof_func);
|
||
|
FLAC__seekable_stream_decoder_set_write_callback (ff->decoder, flac_write_func);
|
||
|
FLAC__seekable_stream_decoder_set_metadata_callback (ff->decoder, flac_meta_func);
|
||
|
FLAC__seekable_stream_decoder_set_client_data (ff->decoder, ff);
|
||
|
rc = FLAC__seekable_stream_decoder_init (ff->decoder);
|
||
|
#else
|
||
|
rc = FLAC__stream_decoder_init_stream(ff->decoder,
|
||
|
flac_read_func,
|
||
|
flac_seek_func,
|
||
|
flac_tell_func,
|
||
|
flac_length_func,
|
||
|
flac_eof_func,
|
||
|
flac_write_func,
|
||
|
flac_meta_func,
|
||
|
flac_error_func,
|
||
|
ff);
|
||
|
#endif
|
||
|
if (rc != FLAC__STREAM_DECODER_INIT_STATUS_OK) /* unlikely */
|
||
|
{
|
||
|
Con_Printf ("FLAC: decoder init error %i\n", rc);
|
||
|
goto _fail;
|
||
|
}
|
||
|
|
||
|
rc = FLAC__stream_decoder_process_until_end_of_metadata (ff->decoder);
|
||
|
if (rc == false || ff->error)
|
||
|
{
|
||
|
rc = FLAC__stream_decoder_get_state(ff->decoder);
|
||
|
Con_Printf("%s not a valid flac file? (decoder state %i)\n",
|
||
|
stream->name, rc);
|
||
|
goto _fail;
|
||
|
}
|
||
|
|
||
|
if (ff->info->dataofs < 0)
|
||
|
{
|
||
|
Con_Printf("%s has no STREAMINFO\n", stream->name);
|
||
|
goto _fail;
|
||
|
}
|
||
|
if (ff->info->bits != 8 && ff->info->bits != 16)
|
||
|
{
|
||
|
Con_Printf("%s is not 8 or 16 bit\n", stream->name);
|
||
|
goto _fail;
|
||
|
}
|
||
|
if (ff->info->channels != 1 && ff->info->channels != 2)
|
||
|
{
|
||
|
Con_Printf("Unsupported number of channels %d in %s\n",
|
||
|
ff->info->channels, stream->name);
|
||
|
goto _fail;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
|
||
|
_fail:
|
||
|
if (ff->decoder)
|
||
|
{
|
||
|
FLAC__stream_decoder_finish (ff->decoder);
|
||
|
FLAC__stream_decoder_delete (ff->decoder);
|
||
|
}
|
||
|
Z_Free(ff);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
static int S_FLAC_CodecReadStream (snd_stream_t *stream, int len, void *buffer)
|
||
|
{
|
||
|
flacfile_t *ff = (flacfile_t *) stream->priv;
|
||
|
byte *buf = (byte *) buffer;
|
||
|
int count = 0;
|
||
|
|
||
|
while (len) {
|
||
|
int res = 0;
|
||
|
if (ff->size == ff->pos)
|
||
|
FLAC__stream_decoder_process_single (ff->decoder);
|
||
|
if (ff->error) return -1;
|
||
|
res = ff->size - ff->pos;
|
||
|
if (res > len)
|
||
|
res = len;
|
||
|
if (res > 0) {
|
||
|
memcpy (buf, ff->buffer + ff->pos, res);
|
||
|
count += res;
|
||
|
len -= res;
|
||
|
buf += res;
|
||
|
ff->pos += res;
|
||
|
} else if (res < 0) { /* error */
|
||
|
return -1;
|
||
|
} else {
|
||
|
Con_DPrintf ("FLAC: EOF\n");
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
static void S_FLAC_CodecCloseStream (snd_stream_t *stream)
|
||
|
{
|
||
|
flacfile_t *ff = (flacfile_t *) stream->priv;
|
||
|
|
||
|
FLAC__stream_decoder_finish (ff->decoder);
|
||
|
FLAC__stream_decoder_delete (ff->decoder);
|
||
|
|
||
|
if (ff->buffer)
|
||
|
free(ff->buffer);
|
||
|
Z_Free(ff);
|
||
|
|
||
|
S_CodecUtilClose(&stream);
|
||
|
}
|
||
|
|
||
|
static int S_FLAC_CodecRewindStream (snd_stream_t *stream)
|
||
|
{
|
||
|
flacfile_t *ff = (flacfile_t *) stream->priv;
|
||
|
|
||
|
ff->pos = ff->size = 0;
|
||
|
if (FLAC__stream_decoder_seek_absolute(ff->decoder, 0)) return 0;
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
snd_codec_t flac_codec =
|
||
|
{
|
||
|
CODECTYPE_FLAC,
|
||
|
true, /* always available. */
|
||
|
"flac",
|
||
|
S_FLAC_CodecInitialize,
|
||
|
S_FLAC_CodecShutdown,
|
||
|
S_FLAC_CodecOpenStream,
|
||
|
S_FLAC_CodecReadStream,
|
||
|
S_FLAC_CodecRewindStream,
|
||
|
S_FLAC_CodecCloseStream,
|
||
|
NULL
|
||
|
};
|
||
|
|
||
|
#endif /* USE_CODEC_FLAC */
|
||
|
|