mirror of
https://github.com/YGGverse/xash3d-fwgs.git
synced 2025-01-09 06:37:53 +00:00
5e0a0765ce
The `.editorconfig` file in this repo is configured to trim all trailing whitespace regardless of whether the line is modified. Trims all trailing whitespace in the repository to make the codebase easier to work with in editors that respect `.editorconfig`. `git blame` becomes less useful on these lines but it already isn't very useful. Commands: ``` find . -type f -name '*.h' -exec sed --in-place 's/[[:space:]]\+$//' {} \+ find . -type f -name '*.c' -exec sed --in-place 's/[[:space:]]\+$//' {} \+ ```
282 lines
6.5 KiB
C
282 lines
6.5 KiB
C
/*
|
|
snd_utils.c - sound common tools
|
|
Copyright (C) 2010 Uncle Mike
|
|
|
|
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 3 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.
|
|
*/
|
|
|
|
#include "soundlib.h"
|
|
|
|
/*
|
|
=============================================================================
|
|
|
|
XASH3D LOAD SOUND FORMATS
|
|
|
|
=============================================================================
|
|
*/
|
|
// stub
|
|
static const loadwavfmt_t load_null[] =
|
|
{
|
|
{ NULL, NULL, NULL }
|
|
};
|
|
|
|
static const loadwavfmt_t load_game[] =
|
|
{
|
|
{ DEFAULT_SOUNDPATH "%s%s.%s", "wav", Sound_LoadWAV },
|
|
{ "%s%s.%s", "wav", Sound_LoadWAV },
|
|
{ DEFAULT_SOUNDPATH "%s%s.%s", "mp3", Sound_LoadMPG },
|
|
{ "%s%s.%s", "mp3", Sound_LoadMPG },
|
|
{ NULL, NULL, NULL }
|
|
};
|
|
|
|
/*
|
|
=============================================================================
|
|
|
|
XASH3D PROCESS STREAM FORMATS
|
|
|
|
=============================================================================
|
|
*/
|
|
// stub
|
|
static const streamfmt_t stream_null[] =
|
|
{
|
|
{ NULL, NULL, NULL, NULL, NULL, NULL, NULL }
|
|
};
|
|
|
|
static const streamfmt_t stream_game[] =
|
|
{
|
|
{ "%s%s.%s", "mp3", Stream_OpenMPG, Stream_ReadMPG, Stream_SetPosMPG, Stream_GetPosMPG, Stream_FreeMPG },
|
|
{ "%s%s.%s", "wav", Stream_OpenWAV, Stream_ReadWAV, Stream_SetPosWAV, Stream_GetPosWAV, Stream_FreeWAV },
|
|
{ NULL, NULL, NULL, NULL, NULL, NULL, NULL }
|
|
};
|
|
|
|
void Sound_Init( void )
|
|
{
|
|
// init pools
|
|
host.soundpool = Mem_AllocPool( "SoundLib Pool" );
|
|
|
|
// install image formats (can be re-install later by Sound_Setup)
|
|
switch( host.type )
|
|
{
|
|
case HOST_NORMAL:
|
|
sound.loadformats = load_game;
|
|
sound.streamformat = stream_game;
|
|
break;
|
|
default: // all other instances not using soundlib or will be reinstalling later
|
|
sound.loadformats = load_null;
|
|
sound.streamformat = stream_null;
|
|
break;
|
|
}
|
|
sound.tempbuffer = NULL;
|
|
}
|
|
|
|
void Sound_Shutdown( void )
|
|
{
|
|
Mem_Check(); // check for leaks
|
|
Mem_FreePool( &host.soundpool );
|
|
}
|
|
|
|
byte *Sound_Copy( size_t size )
|
|
{
|
|
byte *out;
|
|
|
|
out = Mem_Malloc( host.soundpool, size );
|
|
memcpy( out, sound.tempbuffer, size );
|
|
|
|
return out;
|
|
}
|
|
|
|
uint GAME_EXPORT Sound_GetApproxWavePlayLen( const char *filepath )
|
|
{
|
|
file_t *f;
|
|
wavehdr_t wav;
|
|
size_t filesize;
|
|
float seconds;
|
|
uint samples;
|
|
|
|
f = FS_Open( filepath, "rb", false );
|
|
if( !f ) return 0;
|
|
|
|
if( FS_Read( f, &wav, sizeof( wav )) != sizeof( wav ))
|
|
{
|
|
FS_Close( f );
|
|
return 0;
|
|
}
|
|
|
|
filesize = FS_FileLength( f );
|
|
filesize -= ( sizeof( wavehdr_t ) + sizeof( chunkhdr_t ));
|
|
|
|
FS_Close( f );
|
|
|
|
// is real wav file ?
|
|
if( wav.riff_id != RIFFHEADER || wav.wave_id != WAVEHEADER || wav.fmt_id != FORMHEADER )
|
|
return 0;
|
|
|
|
if( wav.wFormatTag != 1 )
|
|
return 0;
|
|
|
|
if( wav.nChannels != 1 && wav.nChannels != 2 )
|
|
return 0;
|
|
|
|
if( wav.nBitsPerSample != 8 && wav.nBitsPerSample != 16 )
|
|
return 0;
|
|
|
|
// calc samplecount
|
|
seconds = (float)filesize / wav.nAvgBytesPerSec / wav.nChannels;
|
|
samples = (uint)(( wav.nSamplesPerSec * wav.nChannels ) * seconds );
|
|
|
|
// g-cont. this function returns samplecount or time in milliseconds ???
|
|
return (uint)(seconds * 1000);
|
|
}
|
|
|
|
/*
|
|
================
|
|
Sound_ConvertToSigned
|
|
|
|
Convert unsigned data to signed
|
|
================
|
|
*/
|
|
void Sound_ConvertToSigned( const byte *data, int channels, int samples )
|
|
{
|
|
int i;
|
|
|
|
if( channels == 2 )
|
|
{
|
|
for( i = 0; i < samples; i++ )
|
|
{
|
|
((signed char *)sound.tempbuffer)[i*2+0] = (int)((byte)(data[i*2+0]) - 128);
|
|
((signed char *)sound.tempbuffer)[i*2+1] = (int)((byte)(data[i*2+1]) - 128);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for( i = 0; i < samples; i++ )
|
|
((signed char *)sound.tempbuffer)[i] = (int)((unsigned char)(data[i]) - 128);
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
Sound_ResampleInternal
|
|
|
|
We need convert sound to signed even if nothing to resample
|
|
================
|
|
*/
|
|
qboolean Sound_ResampleInternal( wavdata_t *sc, int inrate, int inwidth, int outrate, int outwidth )
|
|
{
|
|
float stepscale;
|
|
int outcount, srcsample;
|
|
int i, sample, sample2, samplefrac, fracstep;
|
|
byte *data;
|
|
|
|
data = sc->buffer;
|
|
stepscale = (float)inrate / outrate; // this is usually 0.5, 1, or 2
|
|
outcount = sc->samples / stepscale;
|
|
sc->size = outcount * outwidth * sc->channels;
|
|
|
|
sound.tempbuffer = (byte *)Mem_Realloc( host.soundpool, sound.tempbuffer, sc->size );
|
|
|
|
sc->samples = outcount;
|
|
if( sc->loopStart != -1 )
|
|
sc->loopStart = sc->loopStart / stepscale;
|
|
|
|
// resample / decimate to the current source rate
|
|
if( stepscale == 1.0f && inwidth == 1 && outwidth == 1 )
|
|
{
|
|
Sound_ConvertToSigned( data, sc->channels, outcount );
|
|
}
|
|
else
|
|
{
|
|
// general case
|
|
samplefrac = 0;
|
|
fracstep = stepscale * 256;
|
|
|
|
if( sc->channels == 2 )
|
|
{
|
|
for( i = 0; i < outcount; i++ )
|
|
{
|
|
srcsample = samplefrac >> 8;
|
|
samplefrac += fracstep;
|
|
|
|
if( inwidth == 2 )
|
|
{
|
|
sample = ((short *)data)[srcsample*2+0];
|
|
sample2 = ((short *)data)[srcsample*2+1];
|
|
}
|
|
else
|
|
{
|
|
sample = (int)((char)(data[srcsample*2+0])) << 8;
|
|
sample2 = (int)((char)(data[srcsample*2+1])) << 8;
|
|
}
|
|
|
|
if( outwidth == 2 )
|
|
{
|
|
((short *)sound.tempbuffer)[i*2+0] = sample;
|
|
((short *)sound.tempbuffer)[i*2+1] = sample2;
|
|
}
|
|
else
|
|
{
|
|
((signed char *)sound.tempbuffer)[i*2+0] = sample >> 8;
|
|
((signed char *)sound.tempbuffer)[i*2+1] = sample2 >> 8;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for( i = 0; i < outcount; i++ )
|
|
{
|
|
srcsample = samplefrac >> 8;
|
|
samplefrac += fracstep;
|
|
|
|
if( inwidth == 2 ) sample = ((short *)data)[srcsample];
|
|
else sample = (int)( (char)(data[srcsample])) << 8;
|
|
|
|
if( outwidth == 2 ) ((short *)sound.tempbuffer)[i] = sample;
|
|
else ((signed char *)sound.tempbuffer)[i] = sample >> 8;
|
|
}
|
|
}
|
|
|
|
Con_Reportf( "Sound_Resample: from[%d bit %d kHz] to [%d bit %d kHz]\n", inwidth * 8, inrate, outwidth * 8, outrate );
|
|
}
|
|
|
|
sc->rate = outrate;
|
|
sc->width = outwidth;
|
|
|
|
return true;
|
|
}
|
|
|
|
qboolean Sound_Process( wavdata_t **wav, int rate, int width, uint flags )
|
|
{
|
|
wavdata_t *snd = *wav;
|
|
qboolean result = true;
|
|
|
|
// check for buffers
|
|
if( !snd || !snd->buffer )
|
|
return false;
|
|
|
|
if(( flags & SOUND_RESAMPLE ) && ( width > 0 || rate > 0 ))
|
|
{
|
|
if( Sound_ResampleInternal( snd, snd->rate, snd->width, rate, width ))
|
|
{
|
|
Mem_Free( snd->buffer ); // free original image buffer
|
|
snd->buffer = Sound_Copy( snd->size ); // unzone buffer (don't touch image.tempbuffer)
|
|
}
|
|
else
|
|
{
|
|
// not resampled
|
|
result = false;
|
|
}
|
|
}
|
|
|
|
*wav = snd;
|
|
|
|
return false;
|
|
}
|