fteqw/engine/client/snd_mp3.c

498 lines
12 KiB
C
Raw Normal View History

//source file by david walton, derived from minimad.c
//modified slightly to use new library functions instead for single threaded asyncronous mp3 sound.
//in Quake!
//Be aware that MP3s are patented and thus not GPLable. This file is an interface to madlib which contains the decoder.
//This file is excluded from the main source tree because of this, and madlib is not distributed - in binary or source form.
//The GPL disallows distribution of patented software. You may reactive, but do not distribute binaries.
//Really madlib should come with the exception of the mp3 patent.
#include "quakedef.h"
#ifdef AVAIL_MP3
#include "winquake.h"
#undef channels
#ifdef _WIN32
#define inline _inline
#endif
//the problem with this is that we store the entire decoded file in memory.
//this takes a lot of mem.
//uses madlib
/*
* mad - MPEG audio decoder
* Copyright (C) 2000-2001 Robert Leslie
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
# include "../../mp3/libmad/mad.h"
/*
* This is perhaps the simplest example use of the MAD high-level API.
* Standard input is mapped into memory via mmap(), then the high-level API
* is invoked with three callbacks: input, output, and error. The output
* callback converts MAD's high-resolution PCM samples to 16 bits, then
* writes them to standard output in little-endian, stereo-interleaved
* format.
*/
/*
* This is a private message structure. A generic pointer to this structure
* is passed to each of the callback functions. Put here any data you need
* to access from within the callbacks.
*/
typedef struct {
unsigned char *start;
unsigned long length;
qboolean failed;
sfxcache_t mp3sc;
char *mp3aswavdata;
int mp3aswavpos;
int mp3aswavbuflen;
struct mad_decoder decoder;
sfx_t *s;
} decoderbuffer_t;
int mymad_run(struct mad_decoder *decoder);
int mymad_reset(struct mad_decoder *decoder);
void mymad_finish(struct mad_decoder *decoder);
int startdecode(unsigned char *start, unsigned long length, decoderbuffer_t *buffer);
#define BUFFERSIZEINC (2*1024*1024)
int DecodeSomeMP3(sfx_t *s, int minlength);
byte *COM_LoadFile (char *path, int usehunk);
int MP3_decode(unsigned char *start, unsigned long length, decoderbuffer_t *buffer);
void CancelDecoder(sfx_t *s);
sfxcache_t *S_LoadMP3Sound (sfx_t *s)
{
char namebuffer[MAX_OSPATH];
char *name;
decoderbuffer_t *buffer;
char *data;
qboolean telluser;
int len;
name = s->name;
if (name[0] == '#')
strcpy(namebuffer, &name[1]);
else
sprintf (namebuffer, "sound/%s", name);
len = strlen(namebuffer);
telluser = strcmp(namebuffer+len-4, ".wav");
if (!telluser)
strcpy(namebuffer+len-4, ".mp3");
//try opening from a quake path
data = COM_LoadFile(namebuffer, 5);
if (!data)
{//if that didn't work, try opening direct from exe - this is media after all.
FILE *f;
if (!telluser)
return NULL; //never go out of the quake path for a wav replacement.
#ifndef _WIN32
char unixname[128];
if (name[1] == ':' && name[2] == '\\') //convert from windows to a suitable alternative.
{
sprintf(unixname, "/mnt/%c/%s", name[0]-'A'+'a', name+3);
name = unixname;
while (*name)
{
if (*name == '\\')
*name = '/';
name++;
}
name = unixname;
}
#endif
if ((f = fopen(name, "rb")))
{
com_filesize = COM_filelength(f);
data = BZ_Malloc(com_filesize);
fread(data, 1, com_filesize, f);
fclose(f);
}
else
{
Con_SafePrintf ("Couldn't load %s\n", namebuffer);
return NULL;
}
}
if (!s->decoder)
s->decoder = Z_Malloc(sizeof(decoderbuffer_t) + sizeof(sfxdecode_t));
buffer = (decoderbuffer_t*)(s->decoder+1);
buffer->mp3aswavpos=0;
buffer->mp3sc.length=0;
buffer->s = s;
s->decoder->buf = buffer;
s->decoder->decodemore = DecodeSomeMP3;
s->decoder->abort = CancelDecoder;
if (!startdecode(data, com_filesize, buffer))
{
if (buffer->mp3aswavdata)
{
BZ_Free(buffer->mp3aswavdata);
buffer->mp3aswavdata=NULL;
}
Con_SafePrintf ("Couldn't load %s - corrupt.\n", namebuffer);
return NULL;
}
s->decoder->decodemore(s, 100);
s->cache.fake=true;
return buffer->s->cache.data;
}
/*
* This is the input callback. The purpose of this callback is to (re)fill
* the stream buffer which is to be decoded. In this example, an entire file
* has been mapped into memory, so we just call mad_stream_buffer() with the
* address and length of the mapping. When this callback is called a second
* time, we are finished decoding.
*/
static
enum mad_flow input(void *data,
struct mad_stream *stream)
{
decoderbuffer_t *buffer = data;
if (!buffer->length)
return MAD_FLOW_STOP;
mad_stream_buffer(stream, buffer->start, buffer->length);
buffer->length = 0;
return MAD_FLOW_CONTINUE;
}
/*
* The following utility routine performs simple rounding, clipping, and
* scaling of MAD's high-resolution samples down to 16 bits. It does not
* perform any dithering or noise shaping, which would be recommended to
* obtain any exceptional audio quality. It is therefore not recommended to
* use this routine if high-quality output is desired.
*/
static inline
signed int scale(mad_fixed_t sample)
{
/* round */
sample += (1L << (MAD_F_FRACBITS - 16));
/* clip */
if (sample >= MAD_F_ONE)
sample = MAD_F_ONE - 1;
else if (sample < -MAD_F_ONE)
sample = -MAD_F_ONE;
/* quantize */
return sample >> (MAD_F_FRACBITS + 1 - 16);
}
/*
* This is the output callback function. It is called after each frame of
* MPEG audio data has been completely decoded. The purpose of this callback
* is to output (or play) the decoded PCM audio.
*/
static
enum mad_flow output(void *data,
struct mad_header const *header,
struct mad_pcm *pcm)
{
decoderbuffer_t *buffer = data;
sfxcache_t *cache;
unsigned int nchannels, nsamples;
mad_fixed_t const *left_ch, *right_ch;
char *outpos;
signed int sample;
float speedfactor;
speedfactor = (float)snd_speed/buffer->mp3sc.speed;
/* pcm->samplerate contains the sampling frequency */
nchannels = pcm->channels;
nsamples = pcm->length*speedfactor;
left_ch = pcm->samples[0];
right_ch = pcm->samples[1];
if (buffer->mp3aswavpos + nsamples*nchannels*sizeof(short) > buffer->mp3aswavbuflen || !buffer->mp3aswavdata)
{
int newsize = buffer->mp3aswavpos + nsamples*nchannels*sizeof(short) + BUFFERSIZEINC+sizeof(sfxcache_t);
char *newbuf = BZ_Malloc(newsize);
if (!newbuf)
return MAD_FLOW_STOP; //damn and blast!
if (buffer->mp3aswavdata) //if we had an old buffer, fill the new one and free the old
{
// memset(newbuf, 0, newsize);
memcpy(newbuf, buffer->mp3aswavdata, buffer->mp3aswavpos+sizeof(sfxcache_t));
// newbuf[newsize] = '\0';
BZ_Free(buffer->mp3aswavdata);
}
buffer->mp3aswavdata = newbuf;
buffer->mp3aswavbuflen = newsize;
buffer->s->cache.data = buffer->mp3aswavdata;
}
cache = (sfxcache_t *)buffer->mp3aswavdata;
outpos = buffer->mp3aswavdata+sizeof(sfxcache_t) + buffer->mp3aswavpos;
if (nchannels == 1)
{
if (speedfactor==1) //fast
{
while (nsamples--)
{
sample = scale(*left_ch++);
*outpos++ = ((sample >> 0) & 0xff);
*outpos++ = ((sample >> 8) & 0xff);
}
}
else
{
int src=0;
int pos=0;
while (pos < nsamples)
{
src = pos/speedfactor;
sample = scale(left_ch[src]);
*outpos++ = (((sample >> 0) & 0xff));
*outpos++ = (((sample >> 8) & 0xff));
pos++;
}
}
cache->stereo = 0;
}
else
{
if (speedfactor==1) //fast
{
while (nsamples--)
{ //merge channels?
sample = scale(*left_ch++);
*outpos++ = (((sample >> 0) & 0xff));
*outpos++ = (((sample >> 8) & 0xff));
sample = scale(*right_ch++)/2;
*outpos++ = (((sample >> 0) & 0xff));
*outpos++ = (((sample >> 8) & 0xff));
}
}
else
{
int src=0;
int pos=0;
while (pos < nsamples)
{
src = pos/speedfactor;
sample = scale(left_ch[src]);
*outpos++ = (((sample >> 0) & 0xff));
*outpos++ = (((sample >> 8) & 0xff));
sample = scale(right_ch[src]);
*outpos++ = (((sample >> 0) & 0xff));
*outpos++ = (((sample >> 8) & 0xff));
pos++;
}
}
cache->stereo = 1;
}
buffer->mp3aswavpos += pcm->length*speedfactor*sizeof(short)*nchannels;
buffer->mp3sc.length+= pcm->length*speedfactor;
cache->speed = snd_speed;
cache->length = buffer->s->decoder->decodedlen = buffer->mp3sc.length;
cache->loopstart = -1;//buffer->mp3sc.loopstart;
cache->width = buffer->mp3sc.width;
return MAD_FLOW_CONTINUE;
}
/*
* This is the error callback function. It is called whenever a decoding
* error occurs. The error is indicated by stream->error; the list of
* possible MAD_ERROR_* errors can be found in the mad.h (or
* libmad/stream.h) header file.
*/
static
enum mad_flow error(void *data,
struct mad_stream *stream,
struct mad_frame *frame)
{
decoderbuffer_t *buffer = data;
if (stream->error == 257)
return MAD_FLOW_IGNORE;
if (developer.value)
Con_SafePrintf("MP3Error: decoding error 0x%04x (%s) at byte offset %u\n",
stream->error, mad_stream_errorstr(stream),
stream->this_frame - buffer->start);
// buffer->failed = true;
return MAD_FLOW_IGNORE;
}
static enum mad_flow header(void *data,
struct mad_header const *header)
{
decoderbuffer_t *buffer = data;
buffer->mp3sc.speed = header->samplerate;
buffer->mp3sc.width = 2;
return MAD_FLOW_CONTINUE;
}
void CancelDecoder(sfx_t *s)
{
decoderbuffer_t *dec = s->decoder->buf;
mymad_finish(&dec->decoder);
mad_decoder_finish(&dec->decoder);
s->cache.fake = false; //give it a true cache now, and hope that we don't need to free it while it's still playing.
s->cache.data=NULL;
BZ_Free(dec->mp3aswavdata);
if (dec->start)
BZ_Free(dec->start);
Z_Free(s->decoder);
s->decoder = NULL;
}
/*
* This is the function called by main() above to perform all the
* decoding. It instantiates a decoder object and configures it with the
* input, output, and error callback functions above. A single call to
* mad_decoder_run() continues until a callback function returns
* MAD_FLOW_STOP (to stop decoding) or MAD_FLOW_BREAK (to stop decoding and
* signal an error).
*/
int DecodeSomeMP3(sfx_t *s, int minlength)
{
decoderbuffer_t *dec = s->decoder->buf;
// int oldlen;
if (!dec->start)
return 1;
while(dec->mp3sc.length < minlength)
{
if (!mymad_run(&dec->decoder) || dec->failed)
{
if (dec->mp3aswavbuflen>441000)//10 secs at highest quality
{ //big fat heffers of mp3s are kept like this and are flushed when they finish playing fully.
BZ_Free(dec->start);
dec->start = NULL;
}
else
{ //small mp3 files are now treated like wavs
void *newmem;
mymad_finish(&dec->decoder);
mad_decoder_finish(&dec->decoder);
s->cache.fake = false; //give it a true cache now, and hope that we don't need to free it while it's still playing.
s->cache.data=NULL;
newmem = Cache_Alloc(&s->cache, dec->mp3aswavbuflen+sizeof(sfxcache_t), s->name);
if (dec->mp3aswavdata)
{
memcpy(newmem, dec->mp3aswavdata, dec->mp3aswavbuflen+sizeof(sfxcache_t));
BZ_Free(dec->mp3aswavdata);
}
BZ_Free(dec->start);
Z_Free(s->decoder);
s->decoder = NULL;
}
return 1;
}
}
return 0;
}
int mymad_decoder_run(struct mad_decoder *decoder, enum mad_decoder_mode mode);
static
int startdecode(unsigned char *start, unsigned long length, decoderbuffer_t *buffer)
{
/* initialize our private message structure */
buffer->start = start;
buffer->length = length;
buffer->failed = false;
/* configure input, output, and error functions */
mad_decoder_init(&buffer->decoder, buffer,
input, header, 0 /* filter */, output,
error, 0 /* message */);
/* start decoding */
mymad_reset(&buffer->decoder);
if (buffer->failed)
return false;
return true;
}
#endif