jkxr/Projects/Android/jni/OpenJK/code/client/snd_mem.cpp

1052 lines
27 KiB
C++
Raw Normal View History

/*
===========================================================================
Copyright (C) 1999 - 2005, Id Software, Inc.
Copyright (C) 2000 - 2013, Raven Software, Inc.
Copyright (C) 2001 - 2013, Activision, Inc.
Copyright (C) 2013 - 2015, OpenJK contributors
This file is part of the OpenJK source code.
OpenJK is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation.
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, see <http://www.gnu.org/licenses/>.
===========================================================================
*/
// snd_mem.c: sound caching
#include "../server/exe_headers.h"
#include "snd_local.h"
#include "cl_mp3.h"
#include <string>
#ifdef USE_OPENAL
// Open AL
void S_PreProcessLipSync(sfx_t *sfx);
extern int s_UseOpenAL;
#endif
/*
===============================================================================
WAV loading
===============================================================================
*/
byte *data_p;
byte *iff_end;
byte *last_chunk;
byte *iff_data;
int iff_chunk_len;
extern sfx_t s_knownSfx[];
extern int s_numSfx;
extern cvar_t *s_lip_threshold_1;
extern cvar_t *s_lip_threshold_2;
extern cvar_t *s_lip_threshold_3;
extern cvar_t *s_lip_threshold_4;
short GetLittleShort(void)
{
short val = 0;
val = *data_p;
val = (short)(val + (*(data_p+1)<<8));
data_p += 2;
return val;
}
int GetLittleLong(void)
{
int val = 0;
val = *data_p;
val = val + (*(data_p+1)<<8);
val = val + (*(data_p+2)<<16);
val = val + (*(data_p+3)<<24);
data_p += 4;
return val;
}
void FindNextChunk(const char *name)
{
while (1)
{
data_p=last_chunk;
if (data_p >= iff_end)
{ // didn't find the chunk
data_p = NULL;
return;
}
data_p += 4;
iff_chunk_len = GetLittleLong();
if (iff_chunk_len < 0)
{
data_p = NULL;
return;
}
data_p -= 8;
last_chunk = data_p + 8 + ( (iff_chunk_len + 1) & ~1 );
if (!strncmp((char *)data_p, name, 4))
return;
}
}
void FindChunk(const char *name)
{
last_chunk = iff_data;
FindNextChunk (name);
}
void DumpChunks(void)
{
char str[5];
str[4] = 0;
data_p=iff_data;
do
{
memcpy (str, data_p, 4);
data_p += 4;
iff_chunk_len = GetLittleLong();
Com_Printf ("0x%x : %s (%d)\n", (intptr_t)(data_p - 4), str, iff_chunk_len);
data_p += (iff_chunk_len + 1) & ~1;
} while (data_p < iff_end);
}
/*
============
GetWavinfo
============
*/
wavinfo_t GetWavinfo (const char *name, byte *wav, int wavlength)
{
wavinfo_t info;
int samples;
memset (&info, 0, sizeof(info));
if (!wav)
return info;
iff_data = wav;
iff_end = wav + wavlength;
// find "RIFF" chunk
FindChunk("RIFF");
if (!(data_p && !strncmp((char *)data_p+8, "WAVE", 4)))
{
Com_Printf("Missing RIFF/WAVE chunks\n");
return info;
}
// get "fmt " chunk
iff_data = data_p + 12;
// DumpChunks ();
FindChunk("fmt ");
if (!data_p)
{
Com_Printf("Missing fmt chunk\n");
return info;
}
data_p += 8;
info.format = GetLittleShort();
info.channels = GetLittleShort();
info.rate = GetLittleLong();
data_p += 4+2;
info.width = GetLittleShort() / 8;
if (info.format != 1)
{
Com_Printf("Microsoft PCM format only\n");
return info;
}
// find data chunk
FindChunk("data");
if (!data_p)
{
Com_Printf("Missing data chunk\n");
return info;
}
data_p += 4;
samples = GetLittleLong () / info.width;
if (info.samples)
{
if (samples < info.samples)
Com_Error (ERR_DROP, "Sound %s has a bad loop length", name);
}
else
info.samples = samples;
info.dataofs = data_p - wav;
return info;
}
/*
================
ResampleSfx
resample / decimate to the current source rate
================
*/
void ResampleSfx (sfx_t *sfx, int iInRate, int iInWidth, byte *pData)
{
int iOutCount;
int iSrcSample;
float fStepScale;
int i;
int iSample;
unsigned int uiSampleFrac, uiFracStep; // uiSampleFrac MUST be unsigned, or large samples (eg music tracks) crash
fStepScale = (float)iInRate / dma.speed; // this is usually 0.5, 1, or 2
// When stepscale is > 1 (we're downsampling), we really ought to run a low pass filter on the samples
iOutCount = (int)(sfx->iSoundLengthInSamples / fStepScale);
sfx->iSoundLengthInSamples = iOutCount;
sfx->pSoundData = (short *) SND_malloc( sfx->iSoundLengthInSamples*2 ,sfx );
sfx->fVolRange = 0;
uiSampleFrac = 0;
uiFracStep = (int)(fStepScale*256);
for (i=0 ; i<sfx->iSoundLengthInSamples ; i++)
{
iSrcSample = uiSampleFrac >> 8;
uiSampleFrac += uiFracStep;
if (iInWidth == 2) {
iSample = LittleShort ( ((short *)pData)[iSrcSample] );
} else {
iSample = (int)( (unsigned char)(pData[iSrcSample]) - 128) << 8;
}
sfx->pSoundData[i] = (short)iSample;
// work out max vol for this sample...
//
if (iSample < 0)
iSample = -iSample;
if (sfx->fVolRange < (iSample >> 8) )
{
sfx->fVolRange = iSample >> 8;
}
}
}
//=============================================================================
void S_LoadSound_Finalize(wavinfo_t *info, sfx_t *sfx, byte *data)
{
float stepscale = (float)info->rate / dma.speed;
int len = (int)(info->samples / stepscale);
len *= info->width;
sfx->eSoundCompressionMethod = ct_16;
sfx->iSoundLengthInSamples = info->samples;
ResampleSfx( sfx, info->rate, info->width, data + info->dataofs );
}
// maybe I'm re-inventing the wheel, here, but I can't see any functions that already do this, so...
//
char *Filename_WithoutPath(const char *psFilename)
{
static char sString[MAX_QPATH]; // !!
const char *p = strrchr(psFilename,'\\');
if (!p++)
p=psFilename;
strcpy(sString,p);
return sString;
}
// returns (eg) "\dir\name" for "\dir\name.bmp"
//
char *Filename_WithoutExt(const char *psFilename)
{
static char sString[MAX_QPATH]; // !
strcpy(sString,psFilename);
char *p = strrchr(sString,'.');
char *p2= strrchr(sString,'\\');
// special check, make sure the first suffix we found from the end wasn't just a directory suffix (eg on a path'd filename with no extension anyway)
//
if (p && (p2==0 || (p2 && p>p2)))
*p=0;
return sString;
}
int iFilesFound;
int iFilesUpdated;
int iErrors;
qboolean qbForceRescan;
qboolean qbForceStereo;
std::string strErrors;
void R_CheckMP3s( const char *psDir )
{
// Com_Printf(va("Scanning Dir: %s\n",psDir));
Com_Printf("."); // stops useful info scrolling off screen
char **sysFiles, **dirFiles;
int numSysFiles, i, numdirs;
dirFiles = FS_ListFiles( psDir, "/", &numdirs);
if (numdirs > 2)
{
for (i=2;i<numdirs;i++)
{
char sDirName[MAX_QPATH];
sprintf(sDirName, "%s\\%s", psDir, dirFiles[i]);
R_CheckMP3s(sDirName);
}
}
sysFiles = FS_ListFiles( psDir, ".mp3", &numSysFiles );
for(i=0; i<numSysFiles; i++)
{
char sFilename[MAX_QPATH];
sprintf(sFilename,"%s\\%s", psDir, sysFiles[i]);
Com_Printf("%sFound file: %s",!i?"\n":"",sFilename);
iFilesFound++;
// read it in...
//
byte *pbData = NULL;
int iSize = FS_ReadFile( sFilename, (void **)&pbData);
if (pbData)
{
id3v1_1* pTAG;
// do NOT check 'qbForceRescan' here as an opt, because we need to actually fill in 'pTAG' if there is one...
//
qboolean qbTagNeedsUpdating = (/* qbForceRescan || */ !MP3_ReadSpecialTagInfo(pbData, iSize, &pTAG))?qtrue:qfalse;
if (pTAG == NULL || qbTagNeedsUpdating || qbForceRescan)
{
Com_Printf(" ( Updating )\n");
// I need to scan this file to get the volume...
//
// For EF1 I used a temp sfx_t struct, but I can't do that now with this new alloc scheme,
// I have to ask for it legally, so I'll keep re-using one, and restoring it's name after use.
// (slightly dodgy, but works ok if no-one else changes stuff)
//
//sfx_t SFX = {0};
extern sfx_t *S_FindName( const char *name );
//
static sfx_t *pSFX = NULL;
const char sReservedSFXEntrynameForMP3[] = "reserved_for_mp3"; // ( strlen() < MAX_QPATH )
if (pSFX == NULL) // once only
{
pSFX = S_FindName(sReservedSFXEntrynameForMP3); // always returns, else ERR_FATAL
}
if (MP3_IsValid(sFilename,pbData, iSize, qbForceStereo))
{
wavinfo_t info;
int iRawPCMDataSize = MP3_GetUnpackedSize(sFilename, pbData, iSize, qtrue, qbForceStereo);
if (iRawPCMDataSize) // should always be true, unless file is fucked, in which case, stop this conversion process
{
float fMaxVol = 128; // any old default
int iActualUnpackedSize = iRawPCMDataSize; // default, override later if not doing music
if (!qbForceStereo) // no point for stereo files, which are for music and therefore no lip-sync
{
byte *pbUnpackBuffer = (byte *) Z_Malloc( iRawPCMDataSize+10, TAG_TEMP_WORKSPACE, qfalse ); // won't return if fails
iActualUnpackedSize = MP3_UnpackRawPCM( sFilename, pbData, iSize, pbUnpackBuffer );
if (iActualUnpackedSize != iRawPCMDataSize)
{
Com_Error(ERR_DROP, "******* Whoah! MP3 %s unpacked to %d bytes, but size calc said %d!\n",sFilename,iActualUnpackedSize,iRawPCMDataSize);
}
// fake up a WAV structure so I can use the other post-load sound code such as volume calc for lip-synching
//
MP3_FakeUpWAVInfo( sFilename, pbData, iSize, iActualUnpackedSize,
// these params are all references...
info.format, info.rate, info.width, info.channels, info.samples, info.dataofs
);
S_LoadSound_Finalize(&info, pSFX, pbUnpackBuffer); // all this just for lipsynch. Oh well.
fMaxVol = pSFX->fVolRange;
// free sfx->data...
//
{
#ifndef INT_MIN
#define INT_MIN (-2147483647 - 1) /* minimum (signed) int value */
#endif
//
pSFX->iLastTimeUsed = INT_MIN; // force this to be oldest sound file, therefore disposable...
pSFX->bInMemory = true;
SND_FreeOldestSound(); // ... and do the disposal
// now set our temp SFX struct back to default name so nothing else accidentally uses it...
//
strcpy(pSFX->sSoundName, sReservedSFXEntrynameForMP3);
pSFX->bDefaultSound = false;
}
// OutputDebugString(va("File: \"%s\" MaxVol %f\n",sFilename,pSFX->fVolRange));
// other stuff...
//
Z_Free(pbUnpackBuffer);
}
// well, time to update the file now...
//
fileHandle_t f = FS_FOpenFileWrite( sFilename );
if (f)
{
// write the file back out, but omitting the tag if there was one...
//
int iWritten = FS_Write(pbData, iSize-(pTAG?sizeof(*pTAG):0), f);
if (iWritten)
{
// make up a new tag if we didn't find one in the original file...
//
id3v1_1 TAG;
if (!pTAG)
{
pTAG = &TAG;
memset(&TAG,0,sizeof(TAG));
strncpy(pTAG->id,"TAG",3);
}
strncpy(pTAG->title, Filename_WithoutPath(Filename_WithoutExt(sFilename)), sizeof(pTAG->title));
strncpy(pTAG->artist, "Raven Software", sizeof(pTAG->artist) );
strncpy(pTAG->year, "2002", sizeof(pTAG->year) );
strncpy(pTAG->comment, va("%s %g",sKEY_MAXVOL,fMaxVol), sizeof(pTAG->comment) );
strncpy(pTAG->album, va("%s %d",sKEY_UNCOMP,iActualUnpackedSize),sizeof(pTAG->album) );
if (FS_Write( pTAG, sizeof(*pTAG), f )) // NZ = success
{
iFilesUpdated++;
}
else
{
Com_Printf("*********** Failed write to file \"%s\"!\n",sFilename);
iErrors++;
strErrors += va("Failed to write: \"%s\"\n",sFilename);
}
}
else
{
Com_Printf("*********** Failed write to file \"%s\"!\n",sFilename);
iErrors++;
strErrors += va("Failed to write: \"%s\"\n",sFilename);
}
FS_FCloseFile( f );
}
else
{
Com_Printf("*********** Failed to re-open for write \"%s\"!\n",sFilename);
iErrors++;
strErrors += va("Failed to re-open for write: \"%s\"\n",sFilename);
}
}
else
{
Com_Error(ERR_DROP, "******* This MP3 should be deleted: \"%s\"\n",sFilename);
}
}
else
{
Com_Printf("*********** File was not a valid MP3!: \"%s\"\n",sFilename);
iErrors++;
strErrors += va("Not game-legal MP3 format: \"%s\"\n",sFilename);
}
}
else
{
Com_Printf(" ( OK )\n");
}
FS_FreeFile( pbData );
}
}
FS_FreeFileList( sysFiles );
FS_FreeFileList( dirFiles );
}
// this console-function is for development purposes, and makes sure that sound/*.mp3 /s have tags in them
// specifying stuff like their max volume (and uncompressed size) etc...
//
void S_MP3_CalcVols_f( void )
{
char sStartDir[MAX_QPATH] = {"sound"};
const char sUsage[] = "Usage: mp3_calcvols [-rescan] <startdir>\ne.g. mp3_calcvols sound/chars";
if (Cmd_Argc() == 1 || Cmd_Argc()>4) // 3 optional arguments
{
Com_Printf(sUsage);
return;
}
S_StopAllSounds();
qbForceRescan = qfalse;
qbForceStereo = qfalse;
iFilesFound = 0;
iFilesUpdated = 0;
iErrors = 0;
strErrors = "";
for (int i=1; i<Cmd_Argc(); i++)
{
if (Cmd_Argv(i)[0] == '-')
{
if (!Q_stricmp(Cmd_Argv(i),"-rescan"))
{
qbForceRescan = qtrue;
}
else
if (!Q_stricmp(Cmd_Argv(i),"-stereo"))
{
qbForceStereo = qtrue;
}
else
{
// unknown switch...
//
Com_Printf(sUsage);
return;
}
continue;
}
strcpy(sStartDir,Cmd_Argv(i));
}
Com_Printf(va("Starting Scan for Updates in Dir: %s\n",sStartDir));
R_CheckMP3s( sStartDir );
Com_Printf("\n%d files found/scanned, %d files updated ( %d errors total)\n",iFilesFound,iFilesUpdated,iErrors);
if (iErrors)
{
Com_Printf("\nBad Files:\n%s\n",strErrors.c_str());
}
}
// adjust filename for foreign languages and WAV/MP3 issues.
//
// returns qfalse if failed to load, else fills in *pData
//
extern cvar_t *com_buildScript;
static qboolean S_LoadSound_FileLoadAndNameAdjuster(char *psFilename, byte **pData, int *piSize, int iNameStrlen)
{
char *psVoice = strstr(psFilename,"chars");
if (psVoice)
{
// cache foreign voices...
//
if (com_buildScript->integer)
{
fileHandle_t hFile;
//German
strncpy(psVoice,"chr_d",5); // same number of letters as "chars"
FS_FOpenFileRead(psFilename, &hFile, qfalse); //cache the wav
if (!hFile)
{
strcpy(&psFilename[iNameStrlen-3],"mp3"); //not there try mp3
FS_FOpenFileRead(psFilename, &hFile, qfalse); //cache the mp3
}
if (hFile)
{
FS_FCloseFile(hFile);
}
strcpy(&psFilename[iNameStrlen-3],"wav"); //put it back to wav
//French
strncpy(psVoice,"chr_f",5); // same number of letters as "chars"
FS_FOpenFileRead(psFilename, &hFile, qfalse); //cache the wav
if (!hFile)
{
strcpy(&psFilename[iNameStrlen-3],"mp3"); //not there try mp3
FS_FOpenFileRead(psFilename, &hFile, qfalse); //cache the mp3
}
if (hFile)
{
FS_FCloseFile(hFile);
}
strcpy(&psFilename[iNameStrlen-3],"wav"); //put it back to wav
//Spanish
strncpy(psVoice,"chr_e",5); // same number of letters as "chars"
FS_FOpenFileRead(psFilename, &hFile, qfalse); //cache the wav
if (!hFile)
{
strcpy(&psFilename[iNameStrlen-3],"mp3"); //not there try mp3
FS_FOpenFileRead(psFilename, &hFile, qfalse); //cache the mp3
}
if (hFile)
{
FS_FCloseFile(hFile);
}
strcpy(&psFilename[iNameStrlen-3],"wav"); //put it back to wav
strncpy(psVoice,"chars",5); //put it back to chars
}
// account for foreign voices...
//
extern cvar_t* s_language;
if (s_language && Q_stricmp("DEUTSCH",s_language->string)==0)
{
strncpy(psVoice,"chr_d",5); // same number of letters as "chars"
}
else if (s_language && Q_stricmp("FRANCAIS",s_language->string)==0)
{
strncpy(psVoice,"chr_f",5); // same number of letters as "chars"
}
else if (s_language && Q_stricmp("ESPANOL",s_language->string)==0)
{
strncpy(psVoice,"chr_e",5); // same number of letters as "chars"
}
else
{
psVoice = NULL; // use this ptr as a flag as to whether or not we substituted with a foreign version
}
}
*piSize = FS_ReadFile( psFilename, (void **)pData ); // try WAV
if ( !*pData ) {
psFilename[iNameStrlen-3] = 'm';
psFilename[iNameStrlen-2] = 'p';
psFilename[iNameStrlen-1] = '3';
*piSize = FS_ReadFile( psFilename, (void **)pData ); // try MP3
if ( !*pData )
{
//hmmm, not found, ok, maybe we were trying a foreign noise ("arghhhhh.mp3" that doesn't matter?) but it
// was missing? Can't tell really, since both types are now in sound/chars. Oh well, fall back to English for now...
if (psVoice) // were we trying to load foreign?
{
// yep, so fallback to re-try the english...
//
#ifndef FINAL_BUILD
Com_Printf(S_COLOR_YELLOW "Foreign file missing: \"%s\"! (using English...)\n",psFilename);
#endif
strncpy(psVoice,"chars",5);
psFilename[iNameStrlen-3] = 'w';
psFilename[iNameStrlen-2] = 'a';
psFilename[iNameStrlen-1] = 'v';
*piSize = FS_ReadFile( psFilename, (void **)pData ); // try English WAV
if ( !*pData )
{
psFilename[iNameStrlen-3] = 'm';
psFilename[iNameStrlen-2] = 'p';
psFilename[iNameStrlen-1] = '3';
*piSize = FS_ReadFile( psFilename, (void **)pData ); // try English MP3
}
}
if (!*pData)
{
return qfalse; // sod it, give up...
}
}
}
return qtrue;
}
// returns qtrue if this dir is allowed to keep loaded MP3s, else qfalse if they should be WAV'd instead...
//
// note that this is passed the original, un-language'd name
//
// (I was going to remove this, but on kejim_post I hit an assert because someone had got an ambient sound when the
// perimter fence goes online that was an MP3, then it tried to get added as looping. Presumably it sounded ok or
// they'd have noticed, but we therefore need to stop other levels using those. "sound/ambience" I can check for,
// but doors etc could be anything. Sigh...)
//
#define SOUND_CHARS_DIR "sound/chars/"
#define SOUND_CHARS_DIR_LENGTH 12 // strlen( SOUND_CHARS_DIR )
static qboolean S_LoadSound_DirIsAllowedToKeepMP3s( const char *psFilename )
{
if ( Q_stricmpn( psFilename, SOUND_CHARS_DIR, SOUND_CHARS_DIR_LENGTH ) == 0 )
return qtrue; // found a dir that's allowed to keep MP3s
return qfalse;
}
/*
==============
S_LoadSound
The filename may be different than sfx->name in the case
of a forced fallback of a player specific sound (or of a wav/mp3 substitution now -Ste)
==============
*/
qboolean gbInsideLoadSound = qfalse;
static qboolean S_LoadSound_Actual( sfx_t *sfx )
{
byte *data;
short *samples;
wavinfo_t info;
int size;
char *psExt;
char sLoadName[MAX_QPATH];
int len = strlen(sfx->sSoundName);
if (len<5)
{
return qfalse;
}
// player specific sounds are never directly loaded...
//
if ( sfx->sSoundName[0] == '*') {
return qfalse;
}
// make up a local filename to try wav/mp3 substitutes...
//
Q_strncpyz(sLoadName, sfx->sSoundName, sizeof(sLoadName));
Q_strlwr( sLoadName );
//
// Ensure name has an extension (which it must have, but you never know), and get ptr to it...
//
psExt = &sLoadName[strlen(sLoadName)-4];
if (*psExt != '.')
{
//Com_Printf( "WARNING: soundname '%s' does not have 3-letter extension\n",sLoadName);
COM_DefaultExtension(sLoadName,sizeof(sLoadName),".wav"); // so psExt below is always valid
psExt = &sLoadName[strlen(sLoadName)-4];
len = strlen(sLoadName);
}
if (!S_LoadSound_FileLoadAndNameAdjuster(sLoadName, &data, &size, len))
{
return qfalse;
}
SND_TouchSFX(sfx);
//=========
if (Q_stricmpn(psExt,".mp3",4)==0)
{
// load MP3 file instead...
//
if (MP3_IsValid(sLoadName,data, size, qfalse))
{
int iRawPCMDataSize = MP3_GetUnpackedSize(sLoadName,data,size,qfalse,qfalse);
if (S_LoadSound_DirIsAllowedToKeepMP3s(sfx->sSoundName) // NOT sLoadName, this uses original un-languaged name
&&
MP3Stream_InitFromFile(sfx, data, size, sLoadName, iRawPCMDataSize + 2304 /* + 1 MP3 frame size, jic */,qfalse)
)
{
// Com_DPrintf("(Keeping file \"%s\" as MP3)\n",sLoadName);
#ifdef USE_OPENAL
if (s_UseOpenAL)
{
// Create space for lipsync data (4 lip sync values per streaming AL buffer)
if (strstr(sfx->sSoundName, "chars") )
sfx->lipSyncData = (char *)Z_Malloc(16, TAG_SND_RAWDATA, qfalse);
else
sfx->lipSyncData = NULL;
}
#endif
}
else
{
// small file, not worth keeping as MP3 since it would increase in size (with MP3 header etc)...
//
Com_DPrintf("S_LoadSound: Unpacking MP3 file \"%s\" to wav.\n",sLoadName);
//
// unpack and convert into WAV...
//
{
byte *pbUnpackBuffer = (byte *) Z_Malloc( iRawPCMDataSize+10 +2304 /* <g> */, TAG_TEMP_WORKSPACE, qfalse ); // won't return if fails
{
int iResultBytes = MP3_UnpackRawPCM( sLoadName, data, size, pbUnpackBuffer, qfalse );
if (iResultBytes!= iRawPCMDataSize){
Com_Printf(S_COLOR_YELLOW"**** MP3 %s final unpack size %d different to previous value %d\n",sLoadName,iResultBytes,iRawPCMDataSize);
//assert (iResultBytes == iRawPCMDataSize);
}
// fake up a WAV structure so I can use the other post-load sound code such as volume calc for lip-synching
//
// (this is a bit crap really, but it lets me drop through into existing code)...
//
MP3_FakeUpWAVInfo( sLoadName, data, size, iResultBytes,
// these params are all references...
info.format, info.rate, info.width, info.channels, info.samples, info.dataofs,
qfalse
);
S_LoadSound_Finalize(&info,sfx,pbUnpackBuffer);
#ifdef Q3_BIG_ENDIAN
// the MP3 decoder returns the samples in the correct endianness, but ResampleSfx byteswaps them,
// so we have to swap them again...
sfx->fVolRange = 0;
for (int i = 0; i < sfx->iSoundLengthInSamples; i++)
{
sfx->pSoundData[i] = LittleShort(sfx->pSoundData[i]);
// C++11 defines double abs(short) which is not what we want here,
// because double >> int is not defined. Force interpretation as int
if (sfx->fVolRange < (abs(static_cast<int>(sfx->pSoundData[i])) >> 8))
{
sfx->fVolRange = abs(static_cast<int>(sfx->pSoundData[i])) >> 8;
}
}
#endif
// Open AL
#ifdef USE_OPENAL
if (s_UseOpenAL)
{
if (strstr(sfx->sSoundName, "chars"))
{
sfx->lipSyncData = (char *)Z_Malloc((sfx->iSoundLengthInSamples / 1000) + 1, TAG_SND_RAWDATA, qfalse);
S_PreProcessLipSync(sfx);
}
else
sfx->lipSyncData = NULL;
// Clear Open AL Error state
alGetError();
// Generate AL Buffer
ALuint Buffer;
alGenBuffers(1, &Buffer);
if (alGetError() == AL_NO_ERROR)
{
// Copy audio data to AL Buffer
alBufferData(Buffer, AL_FORMAT_MONO16, sfx->pSoundData, sfx->iSoundLengthInSamples*2, 22050);
if (alGetError() == AL_NO_ERROR)
{
sfx->Buffer = Buffer;
Z_Free(sfx->pSoundData);
sfx->pSoundData = NULL;
}
}
}
#endif
Z_Free(pbUnpackBuffer);
}
}
}
}
else
{
// MP3_IsValid() will already have printed any errors via Com_Printf at this point...
//
FS_FreeFile (data);
return qfalse;
}
}
else
{
// loading a WAV, presumably...
//=========
info = GetWavinfo( sLoadName, data, size );
if ( info.channels != 1 ) {
Com_Printf ("%s is a stereo wav file\n", sLoadName);
FS_FreeFile (data);
return qfalse;
}
/* if ( info.width == 1 ) {
Com_Printf(S_COLOR_YELLOW "WARNING: %s is a 8 bit wav file\n", sLoadName);
}
if ( info.rate != 22050 ) {
Com_Printf(S_COLOR_YELLOW "WARNING: %s is not a 22kHz wav file\n", sLoadName);
}
*/
samples = (short *)Z_Malloc(info.samples * sizeof(short) * 2, TAG_TEMP_WORKSPACE, qfalse);
sfx->eSoundCompressionMethod = ct_16;
sfx->iSoundLengthInSamples = info.samples;
sfx->pSoundData = NULL;
ResampleSfx( sfx, info.rate, info.width, data + info.dataofs );
// Open AL
#ifdef USE_OPENAL
if (s_UseOpenAL)
{
if ((strstr(sfx->sSoundName, "chars")) || (strstr(sfx->sSoundName, "CHARS")))
{
sfx->lipSyncData = (char *)Z_Malloc((sfx->iSoundLengthInSamples / 1000) + 1, TAG_SND_RAWDATA, qfalse);
S_PreProcessLipSync(sfx);
}
else
sfx->lipSyncData = NULL;
// Clear Open AL Error State
alGetError();
// Generate AL Buffer
ALuint Buffer;
alGenBuffers(1, &Buffer);
if (alGetError() == AL_NO_ERROR)
{
// Copy audio data to AL Buffer
alBufferData(Buffer, AL_FORMAT_MONO16, sfx->pSoundData, sfx->iSoundLengthInSamples*2, 22050);
if (alGetError() == AL_NO_ERROR)
{
// Store AL Buffer in sfx struct, and release sample data
sfx->Buffer = Buffer;
Z_Free(sfx->pSoundData);
sfx->pSoundData = NULL;
}
}
}
#endif
Z_Free(samples);
}
FS_FreeFile( data );
return qtrue;
}
// wrapper function for above so I can guarantee that we don't attempt any audio-dumping during this call because
// of a z_malloc() fail recovery...
//
qboolean S_LoadSound( sfx_t *sfx )
{
gbInsideLoadSound = qtrue; // !!!!!!!!!!!!!
qboolean bReturn = S_LoadSound_Actual( sfx );
gbInsideLoadSound = qfalse; // !!!!!!!!!!!!!
return bReturn;
}
#ifdef USE_OPENAL
/*
Precalculate the lipsync values for the whole sample
*/
void S_PreProcessLipSync(sfx_t *sfx)
{
int i, j;
int sample;
int sampleTotal = 0;
j = 0;
for (i = 0; i < sfx->iSoundLengthInSamples; i += 100)
{
sample = LittleShort(sfx->pSoundData[i]);
sample = sample >> 8;
sampleTotal += sample * sample;
if (((i + 100) % 1000) == 0)
{
sampleTotal /= 10;
if (sampleTotal < sfx->fVolRange * s_lip_threshold_1->value)
{
// tell the scripts that are relying on this that we are still going, but actually silent right now.
sample = -1;
}
else if (sampleTotal < sfx->fVolRange * s_lip_threshold_2->value)
sample = 1;
else if (sampleTotal < sfx->fVolRange * s_lip_threshold_3->value)
sample = 2;
else if (sampleTotal < sfx->fVolRange * s_lip_threshold_4->value)
sample = 3;
else
sample = 4;
sfx->lipSyncData[j] = sample;
j++;
sampleTotal = 0;
}
}
if ((i % 1000) == 0)
return;
i -= 100;
i = i % 1000;
i = i / 100;
// Process last < 1000 samples
if (i != 0)
sampleTotal /= i;
else
sampleTotal = 0;
if (sampleTotal < sfx->fVolRange * s_lip_threshold_1->value)
{
// tell the scripts that are relying on this that we are still going, but actually silent right now.
sample = -1;
}
else if (sampleTotal < sfx->fVolRange * s_lip_threshold_2->value)
sample = 1;
else if (sampleTotal < sfx->fVolRange * s_lip_threshold_3->value)
sample = 2;
else if (sampleTotal < sfx->fVolRange * s_lip_threshold_4->value)
sample = 3;
else
sample = 4;
sfx->lipSyncData[j] = sample;
}
#endif