Initial commit.
This commit is contained in:
419
code/client/cl_mp3.org
Normal file
419
code/client/cl_mp3.org
Normal file
@@ -0,0 +1,419 @@
|
||||
// Filename:- cl_mp3.cpp
|
||||
//
|
||||
// (The interface module between all the MP3 stuff and Trek)
|
||||
//
|
||||
#include "client.h"
|
||||
#include "cl_mp3.h" //(only included directly from snd_mem.cpp, so not in client.h)
|
||||
#include "../mp3code/mp3struct.h" // keep this rather awful file secret from the rest of the program
|
||||
|
||||
// call the real worker code in the messy C stuff...
|
||||
//
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
char* C_MP3_IsValid (void *pvData, int iDataLen);
|
||||
char* C_MP3_GetUnpackedSize (void *pvData, int iDataLen, int *piUnpackedSize);
|
||||
char* C_MP3_UnpackRawPCM (void *pvData, int iDataLen, int *piUnpackedSize, void *pbUnpackBuffer);
|
||||
char* C_MP3_GetHeaderData (void *pvData, int iDataLen, int *piRate, int *piWidth, int *piChannels);
|
||||
char* C_MP3Stream_DecodeInit (LP_MP3STREAM pSFX_MP3Stream, void *pvSourceData, int iSourceBytesRemaining,
|
||||
int iGameAudioSampleRate, int iGameAudioSampleBits );
|
||||
unsigned int C_MP3Stream_Decode (LP_MP3STREAM pSFX_MP3Stream);
|
||||
char* C_MP3Stream_Rewind (LP_MP3STREAM pSFX_MP3Stream);
|
||||
|
||||
|
||||
// these two are temp and will eventually be deleted... honest...
|
||||
//
|
||||
char* C_TEST_MP3_GetUnpackedSize( const char *_FILENAME1, const char *_FILENAME2, const char *_FILENAME3,
|
||||
void *data1,void *data2,void *data3,
|
||||
int size1,int size2,int size3,
|
||||
int *iUnpackedSize1,int *iUnpackedSize2,int *iUnpackedSize3
|
||||
);
|
||||
char * C_TEST_MP3_UnpackRawPCM(const char *_FILENAME1, const char *_FILENAME2, const char *_FILENAME3,
|
||||
void *data1,void *data2,void *data3,
|
||||
int iSourceBytesRemaining1,int iSourceBytesRemaining2,int iSourceBytesRemaining3,
|
||||
int *piUnpackedSize1,int *piUnpackedSize2,int *piUnpackedSize3,
|
||||
void *pbUnpackBuffer1,void *pbUnpackBuffer2,void *pbUnpackBuffer3
|
||||
);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
// expects data already loaded, filename arg is for error printing only
|
||||
//
|
||||
// returns success/fail
|
||||
//
|
||||
qboolean MP3_IsValid( const char *psLocalFilename, void *pvData, int iDataLen )
|
||||
{
|
||||
char *psError = C_MP3_IsValid(pvData, iDataLen);
|
||||
|
||||
if (psError)
|
||||
{
|
||||
Com_Printf(va(S_COLOR_RED"%s\n(File: %s)\n",psError, psLocalFilename));
|
||||
}
|
||||
|
||||
return !psError;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// expects data already loaded, filename arg is for error printing only
|
||||
//
|
||||
// returns unpacked length, or 0 for errors (which will be printed internally)
|
||||
//
|
||||
int MP3_GetUnpackedSize( const char *psLocalFilename, void *pvData, int iDataLen, qboolean qbIgnoreID3Tag /* = qfalse */)
|
||||
{
|
||||
int iUnpackedSize = 0;
|
||||
|
||||
if (qbIgnoreID3Tag || !MP3_ReadSpecialTagInfo((byte *)pvData, iDataLen, NULL, &iUnpackedSize))
|
||||
{
|
||||
char *psError = C_MP3_GetUnpackedSize( pvData, iDataLen, &iUnpackedSize);
|
||||
|
||||
if (psError)
|
||||
{
|
||||
Com_Printf(va(S_COLOR_RED"%s\n(File: %s)\n",psError, psLocalFilename));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return iUnpackedSize;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// expects data already loaded, filename arg is for error printing only
|
||||
//
|
||||
// returns byte count of unpacked data (effectively a success/fail bool)
|
||||
//
|
||||
int MP3_UnpackRawPCM( const char *psLocalFilename, void *pvData, int iDataLen, byte *pbUnpackBuffer )
|
||||
{
|
||||
int iUnpackedSize;
|
||||
char *psError = C_MP3_UnpackRawPCM( pvData, iDataLen, &iUnpackedSize, pbUnpackBuffer);
|
||||
|
||||
if (psError)
|
||||
{
|
||||
Com_Printf(va(S_COLOR_RED"%s\n(File: %s)\n",psError, psLocalFilename));
|
||||
return 0;
|
||||
}
|
||||
|
||||
return iUnpackedSize;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// expects data already loaded, filename arg is for error printing only
|
||||
//
|
||||
qboolean MP3_FakeUpWAVInfo( const char *psLocalFilename, void *pvData, int iDataLen, int iUnpackedDataLength, int &format, int &rate, int &width, int &channels, int &samples, int &dataofs)
|
||||
{
|
||||
// some things can be done instantly...
|
||||
//
|
||||
format = 1; // 1 for MS format
|
||||
dataofs= 0; // will be 0 for me (since there's no header in the unpacked data)
|
||||
|
||||
// some things need to be read...
|
||||
//
|
||||
char *psError = C_MP3_GetHeaderData(pvData, iDataLen, &rate, &width, &channels);
|
||||
if (psError)
|
||||
{
|
||||
Com_Printf(va(S_COLOR_RED"%s\n(File: %s)\n",psError, psLocalFilename));
|
||||
}
|
||||
|
||||
// and some stuff needs calculating...
|
||||
//
|
||||
samples = iUnpackedDataLength / width;
|
||||
|
||||
|
||||
return !psError;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
const char sKEY_MAXVOL[]="#MAXVOL"; // formerly #defines
|
||||
const char sKEY_UNCOMP[]="#UNCOMP"; // " "
|
||||
|
||||
// returns qtrue for success...
|
||||
//
|
||||
qboolean MP3_ReadSpecialTagInfo(byte *pbLoadedFile, int iLoadedFileLen, // (in)
|
||||
id3v1_1** ppTAG, // (out), can be NULL
|
||||
int *piUncompressedSize, float *pfMaxVol // (out), can be NULL
|
||||
)
|
||||
{
|
||||
qboolean qbError = qfalse;
|
||||
|
||||
id3v1_1* pTAG = (id3v1_1*) ((pbLoadedFile+iLoadedFileLen)-sizeof(id3v1_1)); // sizeof = 128
|
||||
|
||||
if (!strncmp(pTAG->id, "TAG", 3))
|
||||
{
|
||||
// TAG found...
|
||||
//
|
||||
|
||||
// read MAXVOL key...
|
||||
//
|
||||
if (strncmp(pTAG->comment, sKEY_MAXVOL, strlen(sKEY_MAXVOL)))
|
||||
{
|
||||
qbError = qtrue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( pfMaxVol)
|
||||
{
|
||||
*pfMaxVol = atof(pTAG->comment + strlen(sKEY_MAXVOL));
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// read UNCOMP key...
|
||||
//
|
||||
if (strncmp(pTAG->album, sKEY_UNCOMP, strlen(sKEY_UNCOMP)))
|
||||
{
|
||||
qbError = qtrue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( piUncompressedSize)
|
||||
{
|
||||
*piUncompressedSize = atoi(pTAG->album + strlen(sKEY_UNCOMP));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pTAG = NULL;
|
||||
}
|
||||
|
||||
if (ppTAG)
|
||||
{
|
||||
*ppTAG = pTAG;
|
||||
}
|
||||
|
||||
return (pTAG && !qbError);
|
||||
}
|
||||
|
||||
|
||||
|
||||
qboolean TEST_MP3_GetUnpackedSize(const char *_FILENAME1, const char *_FILENAME2, const char *_FILENAME3,
|
||||
void *data1,void *data2,void *data3,
|
||||
int size1,int size2,int size3,
|
||||
int *iUnpackedSize1,int *iUnpackedSize2,int *iUnpackedSize3
|
||||
)
|
||||
{
|
||||
char *psError = C_TEST_MP3_GetUnpackedSize(_FILENAME1, _FILENAME2, _FILENAME3,
|
||||
data1,data2,data3,
|
||||
size1,size2,size3,
|
||||
iUnpackedSize1,iUnpackedSize2,iUnpackedSize3
|
||||
);
|
||||
|
||||
if (psError)
|
||||
{
|
||||
Com_Printf(va(S_COLOR_RED"%s\n",psError));
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
|
||||
// expects data already loaded, filename arg is for error printing only
|
||||
//
|
||||
// returns byte count of unpacked data (effectively a success/fail bool)
|
||||
//
|
||||
qboolean TEST_MP3_UnpackRawPCM( const char *_FILENAME1, const char *_FILENAME2, const char *_FILENAME3,
|
||||
void *data1,void *data2,void *data3,
|
||||
int iSourceBytesRemaining1,int iSourceBytesRemaining2,int iSourceBytesRemaining3,
|
||||
int *piUnpackedSize1,int *piUnpackedSize2,int *piUnpackedSize3,
|
||||
void *pbUnpackBuffer1,void *pbUnpackBuffer2,void *pbUnpackBuffer3
|
||||
)
|
||||
{
|
||||
char *psError = C_TEST_MP3_UnpackRawPCM(_FILENAME1, _FILENAME2, _FILENAME3,
|
||||
data1,data2,data3,
|
||||
iSourceBytesRemaining1,iSourceBytesRemaining2,iSourceBytesRemaining3,
|
||||
piUnpackedSize1,piUnpackedSize2,piUnpackedSize3,
|
||||
pbUnpackBuffer1,pbUnpackBuffer2,pbUnpackBuffer3
|
||||
);
|
||||
if (psError)
|
||||
{
|
||||
Com_Printf(va(S_COLOR_RED"%s\n",psError));
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// a file has been loaded in memory, see if we want to keep it as MP3, else as normal WAV...
|
||||
//
|
||||
// return = qtrue if keeping as MP3
|
||||
//
|
||||
// (note: the reason I pass in the unpacked size rather than working it out here is simply because I already have it)
|
||||
//
|
||||
qboolean MP3Stream_InitFromFile( sfx_t* sfx, byte *pbSrcData, int iSrcDatalen, const char *psSrcDataFilename, int iMP3UnPackedSize )
|
||||
{
|
||||
// first, make a decision based on size here as to whether or not it's worth it because of MP3 buffer space
|
||||
// making small files much bigger (and therefore best left as WAV)...
|
||||
//
|
||||
#define FUZZY_AMOUNT (5*1024) // so it has to be significantly over, not just break even, because of
|
||||
// the xtra CPU time versus memory saving
|
||||
|
||||
if (iSrcDatalen + sizeof(MP3STREAM) + FUZZY_AMOUNT < iMP3UnPackedSize)
|
||||
{
|
||||
// ok, let's keep it as MP3 then...
|
||||
//
|
||||
|
||||
float fMaxVol = 128; // seems to be a reasonable typical default for maxvol (for lip synch). Naturally there's no #define I can use instead...
|
||||
|
||||
MP3_ReadSpecialTagInfo(pbSrcData, iSrcDatalen, NULL, NULL, &fMaxVol ); // try and read a read maxvol from MP3 header
|
||||
|
||||
// fill in some sfx_t fields...
|
||||
//
|
||||
sfx->eCompressionType = ct_MP3;
|
||||
sfx->data = (byte*) Hunk_Alloc( iSrcDatalen ); // will err_drop if fails
|
||||
memcpy ( sfx->data, pbSrcData, iSrcDatalen ); // ... so the -> data field is MP3, not PCM
|
||||
sfx->width = 2;//(s_compression->value == 1)?1:2;
|
||||
sfx->length = (iMP3UnPackedSize / sfx->width) / (44100 / dma.speed);
|
||||
sfx->vol_range = fMaxVol;
|
||||
|
||||
// now init the low-level MP3 stuff...
|
||||
//
|
||||
MP3STREAM SFX_MP3Stream = {0};
|
||||
char *psError = C_MP3Stream_DecodeInit( &SFX_MP3Stream, sfx->data, iSrcDatalen,
|
||||
dma.speed,//(s_khz->value == 44)?44100:(s_khz->value == 22)?22050:11025,
|
||||
sfx->width * 8
|
||||
);
|
||||
if (psError)
|
||||
{
|
||||
// This should never happen, since any errors or problems with the MP3 file would have stopped us getting
|
||||
// to this whole function, but just in case...
|
||||
//
|
||||
Com_Printf(va(S_COLOR_YELLOW"File \"%s\": %s\n",psSrcDataFilename,psError));
|
||||
|
||||
// This will leave iSrcDatalen bytes on the hunk stack (since you can't dealloc that), but MP3 files are
|
||||
// usually small, and like I say, it should never happen.
|
||||
//
|
||||
// Strictly speaking, I should do a Z_Malloc above, then I could do a Z_Free if failed, else do a Hunk_Alloc
|
||||
// to copy the Z_Malloc data into, then Z_Free, but for something that shouldn't happen it seemed bad to
|
||||
// penalise the rest of the game with extra malloc demands.
|
||||
//
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
// success ( ...on a plate).
|
||||
//
|
||||
// make a copy of the filled-in stream struct and attach to the sfx_t struct...
|
||||
//
|
||||
sfx->pMP3StreamHeader = (MP3STREAM *) Hunk_Alloc( sizeof(MP3STREAM) );
|
||||
memcpy( sfx->pMP3StreamHeader, &SFX_MP3Stream, sizeof(MP3STREAM) );
|
||||
//
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
|
||||
// return is decoded byte count, else 0 for finished
|
||||
//
|
||||
int MP3Stream_Decode( LP_MP3STREAM lpMP3Stream )
|
||||
{
|
||||
lpMP3Stream->iCopyOffset = 0;
|
||||
return C_MP3Stream_Decode( lpMP3Stream );
|
||||
}
|
||||
|
||||
// returns qtrue for all ok
|
||||
//
|
||||
// (this can be optimised by copying the whole header from the sfx struct sometime)
|
||||
//
|
||||
qboolean MP3Stream_Rewind( channel_t *ch )
|
||||
{
|
||||
/* char *psError = C_MP3Stream_Rewind( lpMP3Stream );
|
||||
|
||||
if (psError)
|
||||
{
|
||||
Com_Printf(S_COLOR_YELLOW"%s\n",psError);
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
return qtrue;
|
||||
*/
|
||||
memcpy(&ch->MP3StreamHeader, ch->sfx->pMP3StreamHeader, sizeof(ch->MP3StreamHeader));
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
void MP3Stream_GetSamples( channel_t *ch, int startingSampleNum, int count, short *buf )
|
||||
{
|
||||
static const int iQuarterOfSlidingBuffer = sizeof(ch->MP3SlidingDecodeBuffer)/4;
|
||||
static const int iThreeQuartersOfSlidingBuffer = (sizeof(ch->MP3SlidingDecodeBuffer)*3)/4;
|
||||
|
||||
// Com_Printf("startingSampleNum %d\n",startingSampleNum);
|
||||
|
||||
count *= ch->sfx->width; // count arg was for words, so double it for bytes;
|
||||
|
||||
startingSampleNum *= ch->sfx->width;
|
||||
|
||||
if ( startingSampleNum < ch->iMP3SlidingDecodeWindowPos)
|
||||
{
|
||||
// what?!?!?! Fucking time travel needed or something?, forget it
|
||||
memset(buf,0,count);
|
||||
return;
|
||||
}
|
||||
|
||||
// OutputDebugString(va("\nRequest: startingSampleNum %d, count %d\n",startingSampleNum,count));
|
||||
// OutputDebugString(va("WindowPos %d, WindowWritePos %d\n",ch->iMP3SlidingDecodeWindowPos,ch->iMP3SlidingDecodeWritePos));
|
||||
|
||||
while (!
|
||||
(
|
||||
(startingSampleNum >= ch->iMP3SlidingDecodeWindowPos)
|
||||
&&
|
||||
(startingSampleNum + count < ch->iMP3SlidingDecodeWindowPos + ch->iMP3SlidingDecodeWritePos)
|
||||
)
|
||||
)
|
||||
{
|
||||
// OutputDebugString("Scrolling...");
|
||||
|
||||
int _iBytesDecoded = MP3Stream_Decode( (LP_MP3STREAM) &ch->MP3StreamHeader );
|
||||
// OutputDebugString(va("%d bytes decoded\n",_iBytesDecoded));
|
||||
if (_iBytesDecoded == 0)
|
||||
{
|
||||
// no more source data left so clear the remainder of the buffer...
|
||||
//
|
||||
memset(ch->MP3SlidingDecodeBuffer + ch->iMP3SlidingDecodeWritePos, 0, sizeof(ch->MP3SlidingDecodeBuffer)-ch->iMP3SlidingDecodeWritePos);
|
||||
//MP3Stream_Rewind(ch); // should I do this???
|
||||
// OutputDebugString("Finished\n");
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(ch->MP3SlidingDecodeBuffer + ch->iMP3SlidingDecodeWritePos,ch->MP3StreamHeader.bDecodeBuffer,_iBytesDecoded);
|
||||
|
||||
ch->iMP3SlidingDecodeWritePos += _iBytesDecoded;
|
||||
|
||||
// if reached 3/4 of buffer pos, backscroll the decode window by one quarter...
|
||||
//
|
||||
if (ch->iMP3SlidingDecodeWritePos > (sizeof(ch->MP3SlidingDecodeBuffer)*3)/4)
|
||||
{
|
||||
memmove(ch->MP3SlidingDecodeBuffer, ((byte *)ch->MP3SlidingDecodeBuffer + (sizeof(ch->MP3SlidingDecodeBuffer)/4)), (sizeof(ch->MP3SlidingDecodeBuffer)*3)/4);
|
||||
ch->iMP3SlidingDecodeWritePos -= sizeof(ch->MP3SlidingDecodeBuffer)/4;
|
||||
ch->iMP3SlidingDecodeWindowPos+= sizeof(ch->MP3SlidingDecodeBuffer)/4;
|
||||
}
|
||||
}
|
||||
// OutputDebugString(va("WindowPos %d, WindowWritePos %d\n",ch->iMP3SlidingDecodeWindowPos,ch->iMP3SlidingDecodeWritePos));
|
||||
}
|
||||
|
||||
assert(startingSampleNum >= ch->iMP3SlidingDecodeWindowPos);
|
||||
memcpy( buf, ch->MP3SlidingDecodeBuffer + (startingSampleNum-ch->iMP3SlidingDecodeWindowPos), count);
|
||||
|
||||
|
||||
// OutputDebugString("OK\n");
|
||||
}
|
||||
|
||||
|
||||
///////////// eof /////////////
|
||||
|
||||
Reference in New Issue
Block a user