mirror of
https://github.com/DrBeef/QuakeQuest.git
synced 2024-12-01 00:22:14 +00:00
388 lines
10 KiB
C
388 lines
10 KiB
C
/*
|
|
Copyright (C) 1996-1997 Id Software, Inc.
|
|
|
|
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 "quakedef.h"
|
|
|
|
#include "snd_main.h"
|
|
#include "snd_ogg.h"
|
|
#include "snd_wav.h"
|
|
#include "snd_modplug.h"
|
|
|
|
|
|
/*
|
|
====================
|
|
Snd_CreateRingBuffer
|
|
|
|
If "buffer" is NULL, the function allocates one buffer of "sampleframes" sample frames itself
|
|
(if "sampleframes" is 0, the function chooses the size).
|
|
====================
|
|
*/
|
|
snd_ringbuffer_t *Snd_CreateRingBuffer (const snd_format_t* format, unsigned int sampleframes, void* buffer)
|
|
{
|
|
snd_ringbuffer_t *ringbuffer;
|
|
|
|
// If the caller provides a buffer, it must give us its size
|
|
if (sampleframes == 0 && buffer != NULL)
|
|
return NULL;
|
|
|
|
ringbuffer = (snd_ringbuffer_t*)Mem_Alloc(snd_mempool, sizeof (*ringbuffer));
|
|
memset(ringbuffer, 0, sizeof(*ringbuffer));
|
|
memcpy(&ringbuffer->format, format, sizeof(ringbuffer->format));
|
|
|
|
// If we haven't been given a buffer
|
|
if (buffer == NULL)
|
|
{
|
|
unsigned int maxframes;
|
|
size_t memsize;
|
|
|
|
if (sampleframes == 0)
|
|
maxframes = (format->speed + 1) / 2; // Make the sound buffer large enough for containing 0.5 sec of sound
|
|
else
|
|
maxframes = sampleframes;
|
|
|
|
memsize = maxframes * format->width * format->channels;
|
|
ringbuffer->ring = (unsigned char *) Mem_Alloc(snd_mempool, memsize);
|
|
ringbuffer->maxframes = maxframes;
|
|
}
|
|
else
|
|
{
|
|
ringbuffer->ring = (unsigned char *) buffer;
|
|
ringbuffer->maxframes = sampleframes;
|
|
}
|
|
|
|
return ringbuffer;
|
|
}
|
|
|
|
|
|
/*
|
|
====================
|
|
Snd_CreateSndBuffer
|
|
====================
|
|
*/
|
|
snd_buffer_t *Snd_CreateSndBuffer (const unsigned char *samples, unsigned int sampleframes, const snd_format_t* in_format, unsigned int sb_speed)
|
|
{
|
|
size_t newsampleframes, memsize;
|
|
snd_buffer_t* sb;
|
|
|
|
newsampleframes = (size_t) ceil((double)sampleframes * (double)sb_speed / (double)in_format->speed);
|
|
|
|
memsize = newsampleframes * in_format->channels * in_format->width;
|
|
memsize += sizeof (*sb) - sizeof (sb->samples);
|
|
|
|
sb = (snd_buffer_t*)Mem_Alloc (snd_mempool, memsize);
|
|
sb->format.channels = in_format->channels;
|
|
sb->format.width = in_format->width;
|
|
sb->format.speed = sb_speed;
|
|
sb->maxframes = newsampleframes;
|
|
sb->nbframes = 0;
|
|
|
|
if (!Snd_AppendToSndBuffer (sb, samples, sampleframes, in_format))
|
|
{
|
|
Mem_Free (sb);
|
|
return NULL;
|
|
}
|
|
|
|
return sb;
|
|
}
|
|
|
|
|
|
/*
|
|
====================
|
|
Snd_AppendToSndBuffer
|
|
====================
|
|
*/
|
|
qboolean Snd_AppendToSndBuffer (snd_buffer_t* sb, const unsigned char *samples, unsigned int sampleframes, const snd_format_t* format)
|
|
{
|
|
size_t srclength, outcount;
|
|
unsigned char *out_data;
|
|
|
|
//Con_DPrintf("ResampleSfx: %d samples @ %dHz -> %d samples @ %dHz\n",
|
|
// sampleframes, format->speed, outcount, sb->format.speed);
|
|
|
|
// If the formats are incompatible
|
|
if (sb->format.channels != format->channels || sb->format.width != format->width)
|
|
{
|
|
Con_Print("AppendToSndBuffer: incompatible sound formats!\n");
|
|
return false;
|
|
}
|
|
|
|
outcount = (size_t) ((double)sampleframes * (double)sb->format.speed / (double)format->speed);
|
|
|
|
// If the sound buffer is too short
|
|
if (outcount > sb->maxframes - sb->nbframes)
|
|
{
|
|
Con_Print("AppendToSndBuffer: sound buffer too short!\n");
|
|
return false;
|
|
}
|
|
|
|
out_data = &sb->samples[sb->nbframes * sb->format.width * sb->format.channels];
|
|
srclength = sampleframes * format->channels;
|
|
|
|
// Trivial case (direct transfer)
|
|
if (format->speed == sb->format.speed)
|
|
{
|
|
if (format->width == 1)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < srclength; i++)
|
|
((signed char*)out_data)[i] = samples[i] - 128;
|
|
}
|
|
else // if (format->width == 2)
|
|
memcpy (out_data, samples, srclength * format->width);
|
|
}
|
|
|
|
// General case (linear interpolation with a fixed-point fractional
|
|
// step, 18-bit integer part and 14-bit fractional part)
|
|
// Can handle up to 2^18 (262144) samples per second (> 96KHz stereo)
|
|
# define FRACTIONAL_BITS 14
|
|
# define FRACTIONAL_MASK ((1 << FRACTIONAL_BITS) - 1)
|
|
# define INTEGER_BITS (sizeof(samplefrac)*8 - FRACTIONAL_BITS)
|
|
else
|
|
{
|
|
const unsigned int fracstep = (unsigned int)((double)format->speed / sb->format.speed * (1 << FRACTIONAL_BITS));
|
|
size_t remain_in = srclength, total_out = 0;
|
|
unsigned int samplefrac;
|
|
const unsigned char *in_ptr = samples;
|
|
unsigned char *out_ptr = out_data;
|
|
|
|
// Check that we can handle one second of that sound
|
|
if (format->speed * format->channels > (1 << INTEGER_BITS))
|
|
{
|
|
Con_Printf ("ResampleSfx: sound quality too high for resampling (%uHz, %u channel(s))\n",
|
|
format->speed, format->channels);
|
|
return 0;
|
|
}
|
|
|
|
// We work 1 sec at a time to make sure we don't accumulate any
|
|
// significant error when adding "fracstep" over several seconds, and
|
|
// also to be able to handle very long sounds.
|
|
while (total_out < outcount)
|
|
{
|
|
size_t tmpcount, interpolation_limit, i, j;
|
|
unsigned int srcsample;
|
|
|
|
samplefrac = 0;
|
|
|
|
// If more than 1 sec of sound remains to be converted
|
|
if (outcount - total_out > sb->format.speed)
|
|
{
|
|
tmpcount = sb->format.speed;
|
|
interpolation_limit = tmpcount; // all samples can be interpolated
|
|
}
|
|
else
|
|
{
|
|
tmpcount = outcount - total_out;
|
|
interpolation_limit = (int)ceil((double)(((remain_in / format->channels) - 1) << FRACTIONAL_BITS) / fracstep);
|
|
if (interpolation_limit > tmpcount)
|
|
interpolation_limit = tmpcount;
|
|
}
|
|
|
|
// 16 bit samples
|
|
if (format->width == 2)
|
|
{
|
|
const short* in_ptr_short;
|
|
|
|
// Interpolated part
|
|
for (i = 0; i < interpolation_limit; i++)
|
|
{
|
|
srcsample = (samplefrac >> FRACTIONAL_BITS) * format->channels;
|
|
in_ptr_short = &((const short*)in_ptr)[srcsample];
|
|
|
|
for (j = 0; j < format->channels; j++)
|
|
{
|
|
int a, b;
|
|
|
|
a = *in_ptr_short;
|
|
b = *(in_ptr_short + format->channels);
|
|
*((short*)out_ptr) = (((b - a) * (samplefrac & FRACTIONAL_MASK)) >> FRACTIONAL_BITS) + a;
|
|
|
|
in_ptr_short++;
|
|
out_ptr += sizeof (short);
|
|
}
|
|
|
|
samplefrac += fracstep;
|
|
}
|
|
|
|
// Non-interpolated part
|
|
for (/* nothing */; i < tmpcount; i++)
|
|
{
|
|
srcsample = (samplefrac >> FRACTIONAL_BITS) * format->channels;
|
|
in_ptr_short = &((const short*)in_ptr)[srcsample];
|
|
|
|
for (j = 0; j < format->channels; j++)
|
|
{
|
|
*((short*)out_ptr) = *in_ptr_short;
|
|
|
|
in_ptr_short++;
|
|
out_ptr += sizeof (short);
|
|
}
|
|
|
|
samplefrac += fracstep;
|
|
}
|
|
}
|
|
// 8 bit samples
|
|
else // if (format->width == 1)
|
|
{
|
|
const unsigned char* in_ptr_byte;
|
|
|
|
// Convert up to 1 sec of sound
|
|
for (i = 0; i < interpolation_limit; i++)
|
|
{
|
|
srcsample = (samplefrac >> FRACTIONAL_BITS) * format->channels;
|
|
in_ptr_byte = &((const unsigned char*)in_ptr)[srcsample];
|
|
|
|
for (j = 0; j < format->channels; j++)
|
|
{
|
|
int a, b;
|
|
|
|
a = *in_ptr_byte - 128;
|
|
b = *(in_ptr_byte + format->channels) - 128;
|
|
*((signed char*)out_ptr) = (((b - a) * (samplefrac & FRACTIONAL_MASK)) >> FRACTIONAL_BITS) + a;
|
|
|
|
in_ptr_byte++;
|
|
out_ptr += sizeof (signed char);
|
|
}
|
|
|
|
samplefrac += fracstep;
|
|
}
|
|
|
|
// Non-interpolated part
|
|
for (/* nothing */; i < tmpcount; i++)
|
|
{
|
|
srcsample = (samplefrac >> FRACTIONAL_BITS) * format->channels;
|
|
in_ptr_byte = &((const unsigned char*)in_ptr)[srcsample];
|
|
|
|
for (j = 0; j < format->channels; j++)
|
|
{
|
|
*((signed char*)out_ptr) = *in_ptr_byte - 128;
|
|
|
|
in_ptr_byte++;
|
|
out_ptr += sizeof (signed char);
|
|
}
|
|
|
|
samplefrac += fracstep;
|
|
}
|
|
}
|
|
|
|
// Update the counters and the buffer position
|
|
remain_in -= format->speed * format->channels;
|
|
in_ptr += format->speed * format->channels * format->width;
|
|
total_out += tmpcount;
|
|
}
|
|
}
|
|
|
|
sb->nbframes += outcount;
|
|
return true;
|
|
}
|
|
|
|
|
|
//=============================================================================
|
|
|
|
/*
|
|
==============
|
|
S_LoadSound
|
|
==============
|
|
*/
|
|
qboolean S_LoadSound (sfx_t *sfx, qboolean complain)
|
|
{
|
|
char namebuffer[MAX_QPATH + 16];
|
|
size_t len;
|
|
|
|
// See if already loaded
|
|
if (sfx->fetcher != NULL)
|
|
return true;
|
|
|
|
// If we weren't able to load it previously, no need to retry
|
|
// Note: S_PrecacheSound clears this flag to cause a retry
|
|
if (sfx->flags & SFXFLAG_FILEMISSING)
|
|
return false;
|
|
|
|
// No sound?
|
|
if (snd_renderbuffer == NULL)
|
|
return false;
|
|
|
|
// Initialize volume peak to 0; if ReplayGain is supported, the loader will change this away
|
|
sfx->volume_peak = 0.0;
|
|
|
|
if (developer_loading.integer)
|
|
Con_Printf("loading sound %s\n", sfx->name);
|
|
|
|
SCR_PushLoadingScreen(true, sfx->name, 1);
|
|
|
|
// LordHavoc: if the sound filename does not begin with sound/, try adding it
|
|
if (strncasecmp(sfx->name, "sound/", 6))
|
|
{
|
|
dpsnprintf (namebuffer, sizeof(namebuffer), "sound/%s", sfx->name);
|
|
len = strlen(namebuffer);
|
|
if (len >= 4 && !strcasecmp (namebuffer + len - 4, ".wav"))
|
|
{
|
|
if (S_LoadWavFile (namebuffer, sfx))
|
|
goto loaded;
|
|
memcpy (namebuffer + len - 3, "ogg", 4);
|
|
}
|
|
if (len >= 4 && !strcasecmp (namebuffer + len - 4, ".ogg"))
|
|
{
|
|
if (OGG_LoadVorbisFile (namebuffer, sfx))
|
|
goto loaded;
|
|
}
|
|
else
|
|
{
|
|
if (ModPlug_LoadModPlugFile (namebuffer, sfx))
|
|
goto loaded;
|
|
}
|
|
}
|
|
|
|
// LordHavoc: then try without the added sound/ as wav and ogg
|
|
dpsnprintf (namebuffer, sizeof(namebuffer), "%s", sfx->name);
|
|
len = strlen(namebuffer);
|
|
// request foo.wav: tries foo.wav, then foo.ogg
|
|
// request foo.ogg: tries foo.ogg only
|
|
// request foo.mod: tries foo.mod only
|
|
if (len >= 4 && !strcasecmp (namebuffer + len - 4, ".wav"))
|
|
{
|
|
if (S_LoadWavFile (namebuffer, sfx))
|
|
goto loaded;
|
|
memcpy (namebuffer + len - 3, "ogg", 4);
|
|
}
|
|
if (len >= 4 && !strcasecmp (namebuffer + len - 4, ".ogg"))
|
|
{
|
|
if (OGG_LoadVorbisFile (namebuffer, sfx))
|
|
goto loaded;
|
|
}
|
|
else
|
|
{
|
|
if (ModPlug_LoadModPlugFile (namebuffer, sfx))
|
|
goto loaded;
|
|
}
|
|
|
|
// Can't load the sound!
|
|
sfx->flags |= SFXFLAG_FILEMISSING;
|
|
if (complain)
|
|
Con_DPrintf("failed to load sound \"%s\"\n", sfx->name);
|
|
|
|
SCR_PopLoadingScreen(false);
|
|
return false;
|
|
|
|
loaded:
|
|
SCR_PopLoadingScreen(false);
|
|
return true;
|
|
}
|