This repository has been archived on 2022-06-27. You can view files and clone it, but cannot push or open issues or pull requests.
Xash3DArchive/snd_dx/s_load.c

609 lines
13 KiB
C

//=======================================================================
// Copyright XashXT Group 2007 ©
// s_load.c - sound managment
//=======================================================================
#include "sound.h"
#include "byteorder.h"
// during registration it is possible to have more sounds
// than could actually be referenced during gameplay,
// because we don't want to free anything until we are
// sure we won't need it.
#define MAX_SFX 4096
static sfx_t s_knownSfx[MAX_SFX];
static int s_numSfx = 0;
bool s_registering = false;
int s_registration_sequence = 0;
typedef struct loadformat_s
{
char *formatstring;
char *ext;
bool (*loadfunc)( const char *name, byte **wav, wavinfo_t *info );
} loadformat_t;
/*
=================
S_SoundList_f
=================
*/
void S_SoundList_f( void )
{
int i;
sfx_t *sfx;
sfxcache_t *sc;
int size, totalSfx = 0;
int totalSize = 0;
for( i = 0, sfx = s_knownSfx; i < s_numSfx; i++, sfx++ )
{
if( !sfx->registration_sequence )
continue;
sc = sfx->cache;
if( sc )
{
size = sc->length * sc->width * (sc->stereo + 1);
totalSize += size;
if( sc->loopstart >= 0 ) Msg( "L" );
else Msg( " " );
if( sfx->name[0] == '#' )
Msg( " (%2db) %s : %s\n", sc->width * 8, memprint( size ), &sfx->name[1] );
else Msg( " (%2db) %s : sound/%s\n", sc->width * 8, memprint( size ), sfx->name );
totalSfx++;
}
}
Msg("-------------------------------------------\n");
Msg("%i total sounds\n", totalSfx );
Msg("%s total memory\n", memprint( totalSize ));
Msg("\n");
}
/*
================
S_ResampleSfx
================
*/
void S_ResampleSfx( sfx_t *sfx, int inrate, int inwidth, byte *data )
{
float stepscale;
int outcount, srcsample;
int i, sample, samplefrac, fracstep;
sfxcache_t *sc;
if( !sfx ) return;
sc = sfx->cache;
if( !sc ) return;
stepscale = (float)inrate / dma.speed; // this is usually 0.5, 1, or 2
outcount = sc->length / stepscale;
sc->length = outcount;
if( sc->loopstart != -1 )
sc->loopstart = sc->loopstart / stepscale;
sc->speed = dma.speed;
if( s_loadas8bit->integer )
sc->width = 1;
else sc->width = inwidth;
sc->stereo = 0;
// resample / decimate to the current source rate
if( stepscale == 1 && inwidth == 1 && sc->width == 1 )
{
// fast special case
for( i = 0; i < outcount; i++ )
((signed char *)sc->data)[i] = (int)((unsigned char)(data[i]) - 128);
}
else
{
// general case
samplefrac = 0;
fracstep = stepscale * 256;
for( i = 0; i < outcount; i++ )
{
srcsample = samplefrac >> 8;
samplefrac += fracstep;
if( inwidth == 2 ) sample = LittleShort(((short *)data)[srcsample] );
else sample = (int)( (unsigned char)(data[srcsample]) - 128) << 8;
if( sc->width == 2 ) ((short *)sc->data)[i] = sample;
else ((signed char *)sc->data)[i] = sample >> 8;
}
}
}
/*
===============================================================================
WAV loading
===============================================================================
*/
static byte *iff_data;
static byte *iff_dataPtr;
static byte *iff_end;
static byte *iff_lastChunk;
static int iff_chunkLen;
/*
=================
S_GetLittleShort
=================
*/
static short S_GetLittleShort( void )
{
short val = 0;
val += (*(iff_dataPtr+0) << 0);
val += (*(iff_dataPtr+1) << 8);
iff_dataPtr += 2;
return val;
}
/*
=================
S_GetLittleLong
=================
*/
static int S_GetLittleLong( void )
{
int val = 0;
val += (*(iff_dataPtr+0) << 0);
val += (*(iff_dataPtr+1) << 8);
val += (*(iff_dataPtr+2) << 16);
val += (*(iff_dataPtr+3) << 24);
iff_dataPtr += 4;
return val;
}
/*
=================
S_FindNextChunk
=================
*/
static void S_FindNextChunk( const char *name )
{
while( 1 )
{
iff_dataPtr = iff_lastChunk;
if( iff_dataPtr >= iff_end )
{
// didn't find the chunk
iff_dataPtr = NULL;
return;
}
iff_dataPtr += 4;
iff_chunkLen = S_GetLittleLong();
if (iff_chunkLen < 0)
{
iff_dataPtr = NULL;
return;
}
iff_dataPtr -= 8;
iff_lastChunk = iff_dataPtr + 8 + ((iff_chunkLen + 1) & ~1);
if(!com.strncmp( iff_dataPtr, name, 4 ))
return;
}
}
/*
=================
S_FindChunk
=================
*/
static void S_FindChunk( const char *name )
{
iff_lastChunk = iff_data;
S_FindNextChunk( name );
}
/*
=================
S_LoadWAV
=================
*/
static bool S_LoadWAV( const char *name, byte **wav, wavinfo_t *info )
{
byte *buffer, *out;
int length, samples;
buffer = FS_LoadFile( name, &length );
if( !buffer ) return false;
iff_data = buffer;
iff_end = buffer + length;
// dind "RIFF" chunk
S_FindChunk( "RIFF" );
if( !( iff_dataPtr && !com.strncmp( iff_dataPtr + 8, "WAVE", 4 )))
{
MsgDev( D_WARN, "S_LoadWAV: missing 'RIFF/WAVE' chunks (%s)\n", name );
Mem_Free( buffer );
return false;
}
// get "fmt " chunk
iff_data = iff_dataPtr + 12;
S_FindChunk( "fmt " );
if( !iff_dataPtr )
{
MsgDev( D_WARN, "S_LoadWAV: missing 'fmt ' chunk (%s)\n", name );
Mem_Free( buffer );
return false;
}
iff_dataPtr += 8;
if( S_GetLittleShort() != 1 )
{
MsgDev( D_WARN, "S_LoadWAV: microsoft PCM format only (%s)\n", name );
Mem_Free( buffer );
return false;
}
info->channels = S_GetLittleShort();
if( info->channels != 1 )
{
MsgDev( D_WARN, "S_LoadWAV: only mono WAV files supported (%s)\n", name );
Mem_Free( buffer );
return false;
}
info->rate = S_GetLittleLong();
iff_dataPtr += 4+2;
info->width = S_GetLittleShort() / 8;
if( info->width != 1 && info->width != 2 )
{
MsgDev( D_WARN, "S_LoadWAV: only 8 and 16 bit WAV files supported (%s)\n", name );
Mem_Free( buffer );
return false;
}
// get cue chunk
S_FindChunk( "cue " );
if( iff_dataPtr )
{
iff_dataPtr += 32;
info->loopstart = S_GetLittleLong();
S_FindNextChunk( "LIST" ); // if the next chunk is a LIST chunk, look for a cue length marker
if( iff_dataPtr )
{
if( !com.strncmp((const char *)iff_dataPtr + 28, "mark", 4 ))
{
// this is not a proper parse, but it works with CoolEdit...
iff_dataPtr += 24;
info->samples = info->loopstart + S_GetLittleLong(); // samples in loop
}
}
}
else
{
info->loopstart = -1;
info->samples = 0;
}
// find data chunk
S_FindChunk( "data" );
if( !iff_dataPtr )
{
MsgDev( D_WARN, "S_LoadWAV: missing 'data' chunk (%s)\n", name );
Mem_Free( buffer );
return false;
}
iff_dataPtr += 4;
samples = S_GetLittleLong() / info->width;
if( info->samples )
{
if( samples < info->samples )
{
MsgDev( D_ERROR, "S_LoadWAV: %s has a bad loop length\n", name );
Mem_Free( buffer );
return false;
}
}
else info->samples = samples;
if( info->samples <= 0 )
{
MsgDev( D_WARN, "S_LoadWAV: file with %i samples (%s)\n", info->samples, name );
Mem_Free( buffer );
return false;
}
// Load the data
*wav = out = Z_Malloc( info->samples * info->width );
Mem_Copy( out, buffer + (iff_dataPtr - buffer), info->samples * info->width );
Mem_Free( buffer );
return true;
}
/*
=================
S_UploadSound
=================
*/
static void S_UploadSound( byte *data, wavinfo_t *info, sfx_t *sfx )
{
sfxcache_t *sc;
size_t size, samples;
float stepscale;
// calculate buffer size
stepscale = (float)info->rate / dma.speed;
samples = info->samples / stepscale;
size = samples * info->width * info->channels;
sc = sfx->cache = Z_Malloc( size + sizeof( sfxcache_t ));
sc->length = info->samples;
sc->loopstart = info->loopstart;
sc->speed = info->rate;
sc->width = info->width;
sc->stereo = info->channels;
S_ResampleSfx( sfx, sc->speed, sc->width, data + info->dataofs );
}
/*
=================
S_CreateDefaultSound
=================
*/
static void S_CreateDefaultSound( byte **wav, wavinfo_t *info )
{
byte *out;
int i;
info->rate = 22050;
info->width = 2;
info->channels = 1;
info->samples = 11025;
*wav = out = Z_Malloc( info->samples * info->width );
if( s_check_errors->integer )
{
// create 1 kHz tone as default sound
for( i = 0; i < info->samples; i++ )
((short *)out)[i] = com.sin( i * 0.1f ) * 20000;
}
else
{
// create silent sound
for( i = 0; i < info->samples; i++ )
((short *)out)[i] = i;
}
}
/*
=================
S_LoadSound
=================
*/
loadformat_t load_formats[] =
{
{ "sound/%s.%s", "wav", S_LoadWAV },
{ "%s.%s", "wav", S_LoadWAV },
{ NULL, NULL }
};
sfxcache_t *S_LoadSound( sfx_t *sfx )
{
byte *data;
wavinfo_t info;
const char *ext;
string loadname, path;
loadformat_t *format;
bool anyformat;
if( !sfx ) return NULL;
if( sfx->name[0] == '*' ) return NULL;
if( sfx->cache ) return sfx->cache; // see if still in memory
// load it from disk
ext = FS_FileExtension( sfx->name );
anyformat = !com.stricmp( ext, "" ) ? true : false;
com.strncpy( loadname, sfx->name, sizeof( loadname ));
FS_StripExtension( loadname ); // remove extension if needed
Mem_Set( &info, 0, sizeof( info ));
// developer warning
if( !anyformat ) MsgDev( D_NOTE, "Note: %s will be loading only with ext .%s\n", loadname, ext );
// now try all the formats in the selected list
for( format = load_formats; format->formatstring; format++ )
{
if( anyformat || !com.stricmp( ext, format->ext ))
{
com.sprintf( path, format->formatstring, loadname, format->ext );
if( format->loadfunc( path, &data, &info ))
goto snd_loaded;
}
}
sfx->default_sound = true;
MsgDev( D_WARN, "FS_LoadSound: couldn't load %s\n", sfx->name );
S_CreateDefaultSound( &data, &info );
info.loopstart = -1;
snd_loaded:
// load it in
S_UploadSound( data, &info, sfx );
Mem_Free( data );
return sfx->cache;
}
// =======================================================================
// Load a sound
// =======================================================================
/*
==================
S_FindSound
==================
*/
sfx_t *S_FindSound( const char *name )
{
int i;
sfx_t *sfx;
if( !name || !name[0] ) return NULL;
if( com.strlen( name ) >= MAX_STRING )
{
MsgDev( D_ERROR, "S_FindSound: sound name too long: %s", name );
return NULL;
}
// see if already loaded
for( i = 0; i < s_numSfx; i++ )
{
sfx = &s_knownSfx[i];
if( !com.strcmp( sfx->name, name ))
{
// prolonge registration
sfx->registration_sequence = s_registration_sequence;
return sfx;
}
}
// find a free sfx slot spot
for( i = 0, sfx = s_knownSfx; i < s_numSfx; i++, sfx++)
{
if( !sfx->name[0] ) break; // free spot
}
if( i == s_numSfx )
{
if( s_numSfx == MAX_SFX )
{
MsgDev( D_ERROR, "S_FindName: MAX_SFX limit exceeded\n" );
return NULL;
}
s_numSfx++;
}
sfx = &s_knownSfx[i];
Mem_Set( sfx, 0, sizeof( *sfx ));
com.strncpy( sfx->name, name, MAX_STRING );
sfx->registration_sequence = s_registration_sequence;
return sfx;
}
/*
=====================
S_BeginRegistration
=====================
*/
void S_BeginRegistration( void )
{
s_registration_sequence++;
s_registering = true;
}
/*
=====================
S_EndRegistration
=====================
*/
void S_EndRegistration( void )
{
sfx_t *sfx;
int i;
// free any sounds not from this registration sequence
for( i = 0, sfx = s_knownSfx; i < s_numSfx; i++, sfx++ )
{
if( !sfx->name[0] ) continue;
if( sfx->registration_sequence != s_registration_sequence )
{
// don't need this sound
if( sfx->cache ) Mem_Free( sfx->cache );
Mem_Set( sfx, 0, sizeof( *sfx ));
}
}
// load everything in
for( i = 0, sfx = s_knownSfx; i < s_numSfx; i++, sfx++ )
{
if( !sfx->name[0] ) continue;
S_LoadSound( sfx );
}
s_registering = false;
}
/*
==================
S_RegisterSound
==================
*/
sound_t S_RegisterSound( const char *name )
{
sfx_t *sfx;
if( !sound_started )
return -1;
sfx = S_FindSound( name );
if( !sfx ) return -1;
sfx->registration_sequence = s_registration_sequence;
if( !s_registering ) S_LoadSound( sfx );
return sfx - s_knownSfx;
}
sfx_t *S_GetSfxByHandle( sound_t handle )
{
if( handle < 0 || handle >= s_numSfx )
{
MsgDev( D_ERROR, "S_GetSfxByHandle: handle %i out of range (%i)\n", handle, s_numSfx );
return NULL;
}
return &s_knownSfx[handle];
}
/*
=================
S_FreeSounds
=================
*/
void S_FreeSounds( void )
{
sfx_t *sfx;
int i;
// stop all sounds
S_StopAllSounds();
// free all sounds
for( i = 0, sfx = s_knownSfx; i < s_numSfx; i++, sfx++ )
{
if( !sfx->name[0] ) continue;
if( sfx->cache ) Mem_Free( sfx->cache );
Mem_Set( sfx, 0, sizeof( *sfx ));
}
Mem_Set( s_knownSfx, 0, sizeof(s_knownSfx));
s_numSfx = 0;
}