#include "quakedef.h"
-static const int tryrates[] = {44100, 22051, 11025, 8000};
+static const int tryrates[] = {44100, 22050, 11025, 8000};
static int audio_fd = -1;
static qboolean snd_inited = false;
// Look for an appropriate sound format
// TODO: we should also test mono/stereo and bits
// TODO: support "-sndspeed", "-sndbits", "-sndmono" and "-sndstereo"
- shm->channels = 2;
- shm->samplebits = 16;
+ shm->format.channels = 2;
+ shm->format.width = 2;
for (i = 0; i < sizeof (tryrates) / sizeof (tryrates[0]); i++)
{
- shm->speed = tryrates[i];
+ shm->format.speed = tryrates[i];
AUDIO_INITINFO (&info);
- info.play.sample_rate = shm->speed;
- info.play.channels = shm->channels;
- info.play.precision = shm->samplebits;
+ info.play.sample_rate = shm->format.speed;
+ info.play.channels = shm->format.channels;
+ info.play.precision = shm->format.width * 8;
// We only handle sound cards of the same endianess than the CPU
#if BYTE_ORDER == BIG_ENDIAN
info.play.encoding = AUDIO_ENCODING_SLINEAR_BE;
(info.play.channels == 2) ? "stereo" : "mono",
info.play.sample_rate);
- shm->samples = sizeof (dma_buffer) / (shm->samplebits / 8);
+ shm->samples = sizeof (dma_buffer) / shm->format.width;
shm->samplepos = 0;
shm->buffer = dma_buffer;
return 0;
}
- return ((info.play.samples * shm->channels) % shm->samples);
+ return ((info.play.samples * shm->format.channels) % shm->samples);
}
void SNDDMA_Shutdown (void)
if (paintedtime < wbufp)
wbufp = 0; // reset
- bsize = shm->channels * (shm->samplebits / 8);
+ bsize = shm->format.channels * shm->format.width;
bytes = (paintedtime - wbufp) * bsize;
if (!bytes)
// =======================================================================
channel_t channels[MAX_CHANNELS];
-int total_channels;
+unsigned int total_channels;
int snd_blocked = 0;
static qboolean snd_ambient = 1;
cvar_t snd_initialized = { CVAR_READONLY, "snd_initialized", "0"};
+cvar_t snd_streaming = { CVAR_SAVE, "snd_streaming", "1"};
// pointer should go away
volatile dma_t *shm = 0;
return;
}
- Con_Printf("%5d stereo\n", shm->channels - 1);
+ Con_Printf("%5d stereo\n", shm->format.channels - 1);
Con_Printf("%5d samples\n", shm->samples);
Con_Printf("%5d samplepos\n", shm->samplepos);
- Con_Printf("%5d samplebits\n", shm->samplebits);
- Con_Printf("%5d speed\n", shm->speed);
+ Con_Printf("%5d samplebits\n", shm->format.width * 8);
+ Con_Printf("%5d speed\n", shm->format.speed);
Con_Printf("0x%x dma buffer\n", shm->buffer);
- Con_Printf("%5d total_channels\n", total_channels);
+ Con_Printf("%5u total_channels\n", total_channels);
}
void S_UnloadSounds(void)
if (fakedma)
{
- shm->samplebits = 16;
- shm->speed = 22050;
- shm->channels = 2;
+ shm->format.width = 2;
+ shm->format.speed = 22050;
+ shm->format.channels = 2;
shm->samples = 32768;
shm->samplepos = 0;
- shm->buffer = Mem_Alloc(snd_mempool, shm->channels * shm->samples * (shm->samplebits / 8));
+ shm->buffer = Mem_Alloc(snd_mempool, shm->format.channels * shm->samples * shm->format.width);
}
else
{
sound_started = 1;
- Con_DPrintf("Sound sampling rate: %i\n", shm->speed);
+ Con_DPrintf("Sound sampling rate: %i\n", shm->format.speed);
//S_LoadSounds();
Cvar_RegisterVariable(&nosound);
Cvar_RegisterVariable(&snd_precache);
Cvar_RegisterVariable(&snd_initialized);
+ Cvar_RegisterVariable(&snd_streaming);
Cvar_RegisterVariable(&bgmbuffer);
Cvar_RegisterVariable(&ambient_level);
Cvar_RegisterVariable(&ambient_fade);
void S_StartSound(int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float fvol, float attenuation)
{
channel_t *target_chan, *check;
- sfxcache_t *sc;
int vol;
int ch_idx;
- int skip;
+ size_t skip;
- if (!sound_started || !sfx || !sfx->sfxcache || nosound.integer)
+ if (!sound_started || !sfx || !sfx->fetcher || nosound.integer)
return;
vol = fvol*255;
//if (!target_chan->leftvol && !target_chan->rightvol)
// return; // not audible at all
-// new channel
- sc = S_LoadSound (sfx, true);
- if (!sc)
+ // new channel
+ if (!S_LoadSound (sfx, true))
{
target_chan->sfx = NULL;
return; // couldn't load the sound's data
target_chan->sfx = sfx;
target_chan->pos = 0.0;
- target_chan->end = paintedtime + sc->length;
+ target_chan->end = paintedtime + sfx->total_length;
// if an identical sound has also been started this frame, offset the pos
// a bit to keep it from just making the first one louder
if (check->sfx == sfx && !check->pos)
{
// LordHavoc: fixed skip calculations
- skip = 0.1 * sc->speed;
- if (skip > sc->length)
- skip = sc->length;
+ skip = 0.1 * sfx->format.speed;
+ if (skip > sfx->total_length)
+ skip = sfx->total_length;
if (skip > 0)
skip = rand() % skip;
target_chan->pos += skip;
if (!sound_started || !shm)
return;
- if (shm->samplebits == 8)
+ if (shm->format.width == 1)
clear = 0x80;
else
clear = 0;
}
}
- memset(pData, clear, shm->samples * shm->samplebits/8);
+ memset(pData, clear, shm->samples * shm->format.width);
pDSBuf->lpVtbl->Unlock(pDSBuf, pData, dwSize, NULL, 0);
#endif
if (shm->buffer)
{
- int setsize = shm->samples * shm->samplebits / 8;
+ int setsize = shm->samples * shm->format.width;
char *buf = shm->buffer;
while (setsize--)
void S_StaticSound (sfx_t *sfx, vec3_t origin, float vol, float attenuation)
{
channel_t *ss;
- sfxcache_t *sc;
if (!sfx)
return;
return;
}
- sc = S_LoadSound (sfx, true);
- if (!sc)
+ if (!S_LoadSound (sfx, true))
return;
- if (sc->loopstart == -1)
+ if (sfx->loopstart == -1)
Con_DPrintf("Quake compatibility warning: Static sound \"%s\" is not looped\n", sfx->name);
ss = &channels[total_channels++];
VectorCopy (origin, ss->origin);
ss->master_vol = vol;
ss->dist_mult = (attenuation/64) / sound_nominal_clip_dist;
- ss->end = paintedtime + sc->length;
+ ss->end = paintedtime + sfx->total_length;
SND_Spatialize (ss, true);
}
*/
void S_Update(vec3_t origin, vec3_t forward, vec3_t left, vec3_t up)
{
- int i, j;
- int total;
- channel_t *ch;
- channel_t *combine;
+ unsigned int i, j, total;
+ channel_t *ch, *combine;
if (!snd_initialized.integer || (snd_blocked > 0))
return;
if (ch->sfx && (ch->leftvol || ch->rightvol) )
total++;
- Con_Printf("----(%i)----\n", total);
+ Con_Printf("----(%u)----\n", total);
}
// mix some sound
static int oldsamplepos;
int fullsamples;
- fullsamples = shm->samples / shm->channels;
+ fullsamples = shm->samples / shm->format.channels;
// it is possible to miscount buffers if it has wrapped twice between
// calls to S_Update. Oh well.
}
oldsamplepos = samplepos;
- soundtime = buffers*fullsamples + samplepos/shm->channels;
+ soundtime = buffers * fullsamples + samplepos / shm->format.channels;
}
void IN_Accumulate (void);
paintedtime = soundtime;
// mix ahead of current position
- endtime = soundtime + _snd_mixahead.value * shm->speed;
- samps = shm->samples >> (shm->channels-1);
+ endtime = soundtime + _snd_mixahead.value * shm->format.speed;
+ samps = shm->samples >> (shm->format.channels - 1);
if (endtime > (unsigned int)(soundtime + samps))
endtime = soundtime + samps;
{
int i;
sfx_t *sfx;
- sfxcache_t *sc;
int size, total;
total = 0;
for (sfx=known_sfx, i=0 ; i<num_sfx ; i++, sfx++)
{
- sc = sfx->sfxcache;
- if (sc)
+ if (sfx->fetcher != NULL)
{
- size = sc->length*sc->width*(sc->stereo+1);
+ size = sfx->mempool->totalsize;
total += size;
- Con_Printf("%c(%2db) %6i : %s\n", sc->loopstart >= 0 ? 'L' : ' ',sc->width*8, size, sfx->name);
+ Con_Printf("%c(%2db) %7i : %s\n", sfx->loopstart >= 0 ? 'L' : ' ', sfx->format.width * 8, size, sfx->name);
}
}
Con_Printf("Total resident: %i\n", total);
#define RAWSAMPLESBUFFER 32768
short s_rawsamplesbuffer[RAWSAMPLESBUFFER * 2];
int s_rawsamplesbuffer_start;
-int s_rawsamplesbuffer_count;
+size_t s_rawsamplesbuffer_count;
void S_RawSamples_Enqueue(short *samples, unsigned int length)
{
void S_RawSamples_Dequeue(int *samples, unsigned int length)
{
- int b1, b2, l;
+ int b1, b2;
+ size_t l;
int i;
short *in;
int *out;
int S_RawSamples_QueueWantsMore(void)
{
- if (shm != NULL && s_rawsamplesbuffer_count < min(shm->speed >> 1, RAWSAMPLESBUFFER >> 1))
+ if (shm != NULL && s_rawsamplesbuffer_count < min(shm->format.speed >> 1, RAWSAMPLESBUFFER >> 1))
return RAWSAMPLESBUFFER - s_rawsamplesbuffer_count;
else
return 0;
int S_RawSamples_SampleRate(void)
{
- return shm != NULL ? shm->speed : 0;
+ return shm != NULL ? shm->format.speed : 0;
}
ResampleSfx
================
*/
-void ResampleSfx (sfxcache_t *sc, qbyte *data, char *name)
+size_t ResampleSfx (const qbyte *in_data, size_t in_length, const snd_format_t* in_format, qbyte *out_data, const char* sfxname)
{
- int i, outcount, srcsample, srclength, samplefrac, fracstep;
+ int samplefrac, fracstep;
+ size_t i, srcsample, srclength, outcount;
// this is usually 0.5 (128), 1 (256), or 2 (512)
- fracstep = ((double) sc->speed / (double) shm->speed) * 256.0;
+ fracstep = ((double) in_format->speed / (double) shm->format.speed) * 256.0;
- srclength = sc->length << sc->stereo;
+ srclength = in_length * in_format->channels;
- outcount = (double) sc->length * (double) shm->speed / (double) sc->speed;
- Con_DPrintf("ResampleSfx: resampling sound %s from %dhz to %dhz (%d samples to %d samples)\n", name, sc->speed, shm->speed, sc->length, outcount);
- sc->length = outcount;
- if (sc->loopstart != -1)
- sc->loopstart = (double) sc->loopstart * (double) shm->speed / (double) sc->speed;
-
- sc->speed = shm->speed;
+ outcount = (double) in_length * (double) shm->format.speed / (double) in_format->speed;
+ Con_DPrintf("ResampleSfx: resampling sound \"%s\" from %dHz to %dHz (%d samples to %d samples)\n",
+ sfxname, in_format->speed, shm->format.speed, in_length, outcount);
// resample / decimate to the current source rate
if (fracstep == 256)
{
// fast case for direct transfer
- if (sc->width == 1) // 8bit
+ if (in_format->width == 1) // 8bit
for (i = 0;i < srclength;i++)
- ((signed char *)sc->data)[i] = ((unsigned char *)data)[i] - 128;
- else //if (sc->width == 2) // 16bit
+ ((signed char *)out_data)[i] = ((unsigned char *)in_data)[i] - 128;
+ else //if (sb->width == 2) // 16bit
for (i = 0;i < srclength;i++)
- ((short *)sc->data)[i] = ((short *)data)[i];
+ ((short *)out_data)[i] = ((short *)in_data)[i];
}
else
{
{
srcsample = 0;
fracstep >>= 8;
- if (sc->width == 2)
+ if (in_format->width == 2)
{
- short *out = (void *)sc->data, *in = (void *)data;
- if (sc->stereo) // LordHavoc: stereo sound support
+ short *out = (short*)out_data;
+ const short *in = (const short*)in_data;
+ if (in_format->channels == 2) // LordHavoc: stereo sound support
{
fracstep <<= 1;
for (i=0 ; i<outcount ; i++)
}
else
{
- signed char *out = (void *)sc->data;
- unsigned char *in = (void *)data;
- if (sc->stereo) // LordHavoc: stereo sound support
+ signed char *out = out_data;
+ const unsigned char *in = in_data;
+ if (in_format->channels == 2)
{
fracstep <<= 1;
for (i=0 ; i<outcount ; i++)
{
int sample;
int a, b;
- if (sc->width == 2)
+ if (in_format->width == 2)
{
- short *out = (void *)sc->data, *in = (void *)data;
- if (sc->stereo) // LordHavoc: stereo sound support
+ short *out = (short*)out_data;
+ const short *in = (const short*)in_data;
+ if (in_format->channels == 2)
{
for (i=0 ; i<outcount ; i++)
{
}
else
{
- signed char *out = (void *)sc->data;
- unsigned char *in = (void *)data;
- if (sc->stereo) // LordHavoc: stereo sound support
+ signed char *out = out_data;
+ const unsigned char *in = in_data;
+ if (in_format->channels == 2)
{
for (i=0 ; i<outcount ; i++)
{
}
}
- // LordHavoc: use this for testing if it ever becomes useful again
- //COM_WriteFile (va("sound/%s.pcm", name), sc->data, (sc->length << sc->stereo) * sc->width);
+ return outcount;
}
//=============================================================================
+wavinfo_t GetWavinfo (char *name, qbyte *wav, int wavlength);
+
+/*
+====================
+WAV_FetchSound
+====================
+*/
+static const sfxbuffer_t* WAV_FetchSound (channel_t* ch, unsigned int start, unsigned int nbsamples)
+{
+ return ch->sfx->fetcher_data;
+}
+
+
+snd_fetcher_t wav_fetcher = { WAV_FetchSound, NULL };
+
+
/*
==============
S_LoadWavFile
==============
*/
-sfxcache_t *S_LoadWavFile (const char *filename, sfx_t *s)
+qboolean S_LoadWavFile (const char *filename, sfx_t *s)
{
qbyte *data;
wavinfo_t info;
int len;
- sfxcache_t *sc;
+ sfxbuffer_t* sb;
+
+ Mem_FreePool (&s->mempool);
+ s->mempool = Mem_AllocPool(s->name);
// Load the file
- data = FS_LoadFile(filename, tempmempool, false);
+ data = FS_LoadFile(filename, s->mempool, false);
if (!data)
- return NULL;
+ {
+ Mem_FreePool (&s->mempool);
+ return false;
+ }
// Don't try to load it if it's not a WAV file
if (memcmp (data, "RIFF", 4) || memcmp (data + 8, "WAVE", 4))
- return NULL;
+ {
+ Mem_FreePool (&s->mempool);
+ return false;
+ }
+
+ Con_DPrintf ("Loading WAV file \"%s\"\n", filename);
info = GetWavinfo (s->name, data, fs_filesize);
// Stereo sounds are allowed (intended for music)
if (info.channels < 1 || info.channels > 2)
{
Con_Printf("%s has an unsupported number of channels (%i)\n",s->name, info.channels);
- Mem_Free(data);
- return NULL;
+ Mem_FreePool (&s->mempool);
+ return false;
}
// calculate resampled length
- len = (int) ((double) info.samples * (double) shm->speed / (double) info.rate);
+ len = (int) ((double) info.samples * (double) shm->format.speed / (double) info.rate);
len = len * info.width * info.channels;
- Mem_FreePool(&s->mempool);
- s->mempool = Mem_AllocPool(s->name);
- sc = s->sfxcache = Mem_Alloc(s->mempool, len + sizeof(sfxcache_t));
- if (!sc)
+ sb = Mem_Alloc (s->mempool, len + sizeof (*sb) - sizeof (sb->data));
+ if (sb == NULL)
{
Con_Printf("failed to allocate memory for sound \"%s\"\n", s->name);
Mem_FreePool(&s->mempool);
- Mem_Free(data);
- return NULL;
+ return false;
}
- sc->length = info.samples;
- sc->loopstart = info.loopstart;
- sc->speed = info.rate;
- sc->width = info.width;
- sc->stereo = info.channels == 2;
+ s->fetcher = &wav_fetcher;
+ s->fetcher_data = sb;
+ s->format.speed = info.rate;
+ s->format.width = info.width;
+ s->format.channels = info.channels;
+ if (info.loopstart < 0)
+ s->loopstart = -1;
+ else
+ s->loopstart = (double) s->loopstart * (double) shm->format.speed / (double) s->format.speed;
#if BYTE_ORDER != LITTLE_ENDIAN
// We must convert the WAV data from little endian
}
#endif
- ResampleSfx(sc, data + info.dataofs, s->name);
+ sb->length = ResampleSfx (data + info.dataofs, info.samples, &s->format, sb->data, s->name);
+ s->format.speed = shm->format.speed;
+ s->total_length = sb->length;
+ sb->offset = 0;
- Mem_Free(data);
- return sc;
+ Mem_Free (data);
+ return true;
}
S_LoadSound
==============
*/
-sfxcache_t *S_LoadSound (sfx_t *s, int complain)
+qboolean S_LoadSound (sfx_t *s, int complain)
{
char namebuffer[MAX_QPATH];
size_t len;
- sfxcache_t *sc;
qboolean modified_name = false;
// see if still in memory
- if (!shm || !shm->speed)
- return NULL;
- if (s->sfxcache && (s->sfxcache->speed == shm->speed))
- return s->sfxcache;
+ if (!shm || !shm->format.speed)
+ return false;
+ if (s->fetcher != NULL)
+ {
+ if (s->format.speed != shm->format.speed)
+ Sys_Error ("S_LoadSound: sound %s hasn't been resampled (%uHz instead of %uHz)", s->name);
+ return true;
+ }
len = snprintf (namebuffer, sizeof (namebuffer), "sound/%s", s->name);
if (len >= sizeof (namebuffer))
- return NULL;
+ return false;
// Try to load it as a WAV file
- sc = S_LoadWavFile (namebuffer, s);
- if (sc != NULL)
- return sc;
+ if (S_LoadWavFile (namebuffer, s))
+ return true;
// Else, try to load it as an Ogg Vorbis file
if (!strcasecmp (namebuffer + len - 4, ".wav"))
strcpy (namebuffer + len - 3, "ogg");
modified_name = true;
}
- sc = OGG_LoadVorbisFile (namebuffer, s);
- if (sc != NULL)
- return sc;
+ if (OGG_LoadVorbisFile (namebuffer, s))
+ return true;
// Can't load the sound!
if (!complain)
strcpy (namebuffer + len - 3, "wav");
Con_Printf("Couldn't load %s\n", namebuffer);
}
- return NULL;
+ return false;
}
void S_UnloadSound(sfx_t *s)
{
- if (s->sfxcache)
+ if (s->fetcher != NULL)
{
- s->sfxcache = NULL;
+ unsigned int i;
+
+ s->fetcher = NULL;
+ s->fetcher_data = NULL;
Mem_FreePool(&s->mempool);
+
+ // At this point, some per-channel data pointers may point to freed zones.
+ // Practically, it shouldn't be a problem; but it's wrong, so we fix that
+ for (i = 0; i < total_channels ; i++)
+ if (channels[i].sfx == s)
+ channels[i].fetcher_data = NULL;
}
}
snd_p = (int *) paintbuffer;
snd_vol = volume.value*256;
lpaintedtime = paintedtime;
- if (shm->samplebits == 16)
+ if (shm->format.width == 2)
{
// 16bit
short *snd_out;
- if (shm->channels == 2)
+ if (shm->format.channels == 2)
{
// 16bit 2 channels (stereo)
while (lpaintedtime < endtime)
{
// 8bit
unsigned char *snd_out;
- if (shm->channels == 2)
+ if (shm->format.channels == 2)
{
// 8bit 2 channels (stereo)
while (lpaintedtime < endtime)
===============================================================================
*/
-void SND_PaintChannelFrom8 (channel_t *ch, sfxcache_t *sc, int endtime);
-void SND_PaintChannelFrom16 (channel_t *ch, sfxcache_t *sc, int endtime);
+qboolean SND_PaintChannelFrom8 (channel_t *ch, int endtime);
+qboolean SND_PaintChannelFrom16 (channel_t *ch, int endtime);
void S_PaintChannels(int endtime)
{
- int i;
+ unsigned int i;
int end;
channel_t *ch;
- sfxcache_t *sc;
+ sfx_t *sfx;
int ltime, count;
while (paintedtime < endtime)
ch = channels;
for (i=0; i<total_channels ; i++, ch++)
{
- if (!ch->sfx)
+ sfx = ch->sfx;
+ if (!sfx)
continue;
if (!ch->leftvol && !ch->rightvol)
continue;
- sc = S_LoadSound (ch->sfx, true);
- if (!sc)
+ if (!S_LoadSound (sfx, true))
continue;
ltime = paintedtime;
while (ltime < end)
{
+ qboolean stop_paint;
+
// paint up to end
if (ch->end < end)
count = ch->end - ltime;
if (count > 0)
{
- if (sc->width == 1)
- SND_PaintChannelFrom8(ch, sc, count);
+ if (sfx->format.width == 1)
+ stop_paint = !SND_PaintChannelFrom8(ch, count);
else
- SND_PaintChannelFrom16(ch, sc, count);
+ stop_paint = !SND_PaintChannelFrom16(ch, count);
ltime += count;
}
+ else
+ stop_paint = false;
- // if at end of loop, restart
if (ltime >= ch->end)
{
- if (sc->loopstart >= 0 || ch->forceloop)
+ // if at end of loop, restart
+ if ((sfx->loopstart >= 0 || ch->forceloop) && !stop_paint)
{
- ch->pos = bound(0, sc->loopstart, sc->length - 1);
- ch->end = ltime + sc->length - ch->pos;
+ ch->pos = bound(0, sfx->loopstart, (int)sfx->total_length - 1);
+ ch->end = ltime + sfx->total_length - ch->pos;
}
+ // channel just stopped
else
- {
- // channel just stopped
- ch->sfx = NULL;
- break;
- }
+ stop_paint = true;
+ }
+
+ if (stop_paint)
+ {
+ if (ch->sfx->fetcher->end != NULL)
+ ch->sfx->fetcher->end (ch);
+ ch->sfx = NULL;
+ break;
}
}
}
}
-void SND_PaintChannelFrom8 (channel_t *ch, sfxcache_t *sc, int count)
+qboolean SND_PaintChannelFrom8 (channel_t *ch, int count)
{
int *lscale, *rscale;
unsigned char *sfx;
+ const sfxbuffer_t *sb;
int i, n;
if (ch->leftvol > 255)
lscale = snd_scaletable[ch->leftvol >> 3];
rscale = snd_scaletable[ch->rightvol >> 3];
- if (sc->stereo)
+
+ sb = ch->sfx->fetcher->getsb (ch, ch->pos, count);
+ if (sb == NULL)
+ return false;
+
+ if (ch->sfx->format.channels == 2)
{
// LordHavoc: stereo sound support, and optimizations
- sfx = (unsigned char *)sc->data + ch->pos * 2;
+ sfx = (unsigned char *)sb->data + (ch->pos - sb->offset) * 2;
for (i = 0;i < count;i++)
{
paintbuffer[i].left += lscale[*sfx++];
}
else
{
- sfx = (unsigned char *)sc->data + ch->pos;
+ sfx = (unsigned char *)sb->data + ch->pos - sb->offset;
for (i = 0;i < count;i++)
{
n = *sfx++;
}
ch->pos += count;
+ return true;
}
-void SND_PaintChannelFrom16 (channel_t *ch, sfxcache_t *sc, int count)
+qboolean SND_PaintChannelFrom16 (channel_t *ch, int count)
{
int leftvol, rightvol;
signed short *sfx;
+ const sfxbuffer_t *sb;
int i;
leftvol = ch->leftvol;
rightvol = ch->rightvol;
- if (sc->stereo)
+
+ sb = ch->sfx->fetcher->getsb (ch, ch->pos, count);
+ if (sb == NULL)
+ return false;
+
+ if (ch->sfx->format.channels == 2)
{
// LordHavoc: stereo sound support, and optimizations
- sfx = (signed short *)sc->data + ch->pos * 2;
+ sfx = (signed short *)sb->data + (ch->pos - sb->offset) * 2;
for (i=0 ; i<count ; i++)
{
}
else
{
- sfx = (signed short *)sc->data + ch->pos;
+ sfx = (signed short *)sb->data + ch->pos - sb->offset;
for (i=0 ; i<count ; i++)
{
}
ch->pos += count;
+ return true;
}
/*
- Copyright (C) 2003 Mathieu Olivier
+ Copyright (C) 2003-2004 Mathieu Olivier
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
#include "snd_ogg.h"
-extern void ResampleSfx (sfxcache_t *sc, qbyte *data, char *name);
-
-
/*
=================================================================
static int (*qov_open_callbacks) (void *datasource, OggVorbis_File *vf,
char *initial, long ibytes,
ov_callbacks callbacks);
+static int (*qov_pcm_seek) (OggVorbis_File *vf,ogg_int64_t pos);
static ogg_int64_t (*qov_pcm_total) (OggVorbis_File *vf,int i);
static long (*qov_read) (OggVorbis_File *vf,char *buffer,int length,
int bigendianp,int word,int sgned,int *bitstream);
{"ov_clear", (void **) &qov_clear},
{"ov_info", (void **) &qov_info},
{"ov_open_callbacks", (void **) &qov_open_callbacks},
+ {"ov_pcm_seek", (void **) &qov_pcm_seek},
{"ov_pcm_total", (void **) &qov_pcm_total},
{"ov_read", (void **) &qov_read},
{NULL, NULL}
=================================================================
*/
+#define STREAM_BUFFER_SIZE (128 * 1024)
+
+// Note: it must be able to contain enough samples at 48 KHz (max speed)
+// to fill STREAM_BUFFER_SIZE bytes of samples at 8 KHz (min speed)
+// TODO: dynamically allocate this buffer depending on the shm and min sound speeds
+static qbyte resampling_buffer [STREAM_BUFFER_SIZE * (48000 / 8000)];
+
+
+// Per-sfx data structure
+typedef struct
+{
+ qbyte *file;
+ size_t filesize;
+} ogg_stream_persfx_t;
+
+// Per-channel data structure
+typedef struct
+{
+ OggVorbis_File vf;
+ ov_decode_t ov_decode;
+ int bs;
+ snd_format_t format;
+ sfxbuffer_t sb; // must be at the end due to its dynamically allocated size
+} ogg_stream_perchannel_t;
+
+
+static const ov_callbacks callbacks = {ovcb_read, ovcb_seek, ovcb_close, ovcb_tell};
+
+/*
+====================
+OGG_FetchSound
+====================
+*/
+static const sfxbuffer_t* OGG_FetchSound (channel_t* ch, unsigned int start, unsigned int nbsamples)
+{
+ ogg_stream_perchannel_t* per_ch;
+ sfxbuffer_t* sb;
+ int newlength, done, ret, bigendian;
+ unsigned int factor;
+
+ per_ch = ch->fetcher_data;
+
+ // If there's no fetcher structure attached to the channel yet
+ if (per_ch == NULL)
+ {
+ sfx_t* sfx;
+ vorbis_info *vi;
+ ogg_stream_persfx_t* per_sfx;
+
+ sfx = ch->sfx;
+ per_ch = Mem_Alloc (sfx->mempool, sizeof (*per_ch) - sizeof (per_ch->sb.data) + STREAM_BUFFER_SIZE);
+ per_sfx = sfx->fetcher_data;
+
+ // Open it with the VorbisFile API
+ per_ch->ov_decode.buffer = per_sfx->file;
+ per_ch->ov_decode.ind = 0;
+ per_ch->ov_decode.buffsize = per_sfx->filesize;
+ if (qov_open_callbacks (&per_ch->ov_decode, &per_ch->vf, NULL, 0, callbacks) < 0)
+ {
+ Con_Printf("error while reading Ogg Vorbis stream \"%s\"\n", sfx->name);
+ Mem_Free (per_ch);
+ return NULL;
+ }
+
+ // Get the stream information
+ vi = qov_info (&per_ch->vf, -1);
+ per_ch->format.speed = vi->rate;
+ per_ch->format.width = sfx->format.width;
+ per_ch->format.channels = sfx->format.channels;
+
+ per_ch->sb.offset = 0;
+ per_ch->sb.length = 0;
+ per_ch->bs = 0;
+
+ ch->fetcher_data = per_ch;
+ }
+
+ sb = &per_ch->sb;
+
+ // If the data we need has already been decompressed in the sfxbuffer, just return it
+ if (sb->offset <= start && sb->offset + sb->length >= start + nbsamples)
+ return sb;
+
+ newlength = sb->offset + sb->length - start;
+ factor = per_ch->format.width * per_ch->format.channels;
+
+ // If we need to skip some data before decompressing the rest, or if the stream has looped
+ if (newlength < 0 || sb->offset > start)
+ {
+ if (qov_pcm_seek (&per_ch->vf, (ogg_int64_t)start) != 0)
+ return NULL;
+
+ sb->offset = start;
+ sb->length = 0;
+ newlength = 0;
+ }
+ // Else, move forward the samples we need to keep in the sfxbuffer
+ else
+ {
+ memmove (sb->data, sb->data + (start - sb->offset) * factor, newlength * factor);
+ sb->offset = start;
+ sb->length = newlength;
+ }
+
+ // How many free bytes do we have in the sfxbuffer now?
+ newlength = STREAM_BUFFER_SIZE - (newlength * factor);
+
+ // Decompress in the resampling_buffer to get STREAM_BUFFER_SIZE samples after resampling
+#if BYTE_ORDER == LITTLE_ENDIAN
+ bigendian = 0;
+#else
+ bigendian = 1;
+#endif
+ done = 0;
+ while ((ret = qov_read (&per_ch->vf, &resampling_buffer[done], (int)(newlength - done), bigendian, 2, 1, &per_ch->bs)) > 0)
+ done += ret;
+
+ // Resample in the sfxbuffer
+ newlength = ResampleSfx (resampling_buffer, (size_t)done / factor, &per_ch->format, sb->data + sb->length * factor, ch->sfx->name);
+ sb->length += newlength;
+
+ return sb;
+}
+
+
+/*
+====================
+OGG_FetchEnd
+====================
+*/
+static void OGG_FetchEnd (channel_t* ch)
+{
+ ogg_stream_perchannel_t* per_ch;
+
+ per_ch = ch->fetcher_data;
+ if (per_ch != NULL)
+ {
+ // Free the ogg vorbis decoder
+ qov_clear (&per_ch->vf);
+
+ Mem_Free (per_ch);
+ ch->fetcher_data = NULL;
+ }
+}
+
+static const snd_fetcher_t ogg_fetcher = { OGG_FetchSound, OGG_FetchEnd };
+extern snd_fetcher_t wav_fetcher;
+
+
/*
====================
OGG_LoadVorbisFile
-Load an Ogg Vorbis file into a sfxcache_t
+Load an Ogg Vorbis file into memory
====================
*/
-sfxcache_t *OGG_LoadVorbisFile (const char *filename, sfx_t *s)
+qboolean OGG_LoadVorbisFile (const char *filename, sfx_t *s)
{
qbyte *data;
ov_decode_t ov_decode;
OggVorbis_File vf;
- ov_callbacks callbacks = {ovcb_read, ovcb_seek, ovcb_close, ovcb_tell};
vorbis_info *vi;
ogg_int64_t len;
- char *buff;
- ogg_int64_t done;
- int bs, bigendian;
- long ret;
- sfxcache_t *sc;
if (!vf_dll)
- return NULL;
+ return false;
+
+ Mem_FreePool (&s->mempool);
+ s->mempool = Mem_AllocPool (s->name);
// Load the file
- data = FS_LoadFile (filename, tempmempool, false);
+ data = FS_LoadFile (filename, s->mempool, false);
if (data == NULL)
- return NULL;
+ {
+ Mem_FreePool (&s->mempool);
+ return false;
+ }
+
+ Con_DPrintf ("Loading Ogg Vorbis file \"%s\"\n", filename);
// Open it with the VorbisFile API
ov_decode.buffer = data;
ov_decode.buffsize = fs_filesize;
if (qov_open_callbacks (&ov_decode, &vf, NULL, 0, callbacks) < 0)
{
- Con_Printf("error while opening Ogg Vorbis file \"%s\"\n", filename);
- Mem_Free (data);
- return NULL;
+ Con_Printf ("error while opening Ogg Vorbis file \"%s\"\n", filename);
+ Mem_FreePool (&s->mempool);
+ return false;
}
// Get the stream information
Con_Printf("%s has an unsupported number of channels (%i)\n",
s->name, vi->channels);
qov_clear (&vf);
- Mem_Free (data);
- return NULL;
+ Mem_FreePool (&s->mempool);
+ return false;
}
- // Decode it
len = qov_pcm_total (&vf, -1) * vi->channels * 2; // 16 bits => "* 2"
- buff = Mem_Alloc (tempmempool, (int)len);
- done = 0;
- bs = 0;
-#if BYTE_ORDER == LITTLE_ENDIAN
- bigendian = 0;
-#else
- bigendian = 1;
-#endif
- while ((ret = qov_read (&vf, &buff[done], (int)(len - done), bigendian, 2, 1, &bs)) > 0)
- done += ret;
-
- // Calculate resampled length
- len = (double)done * (double)shm->speed / (double)vi->rate;
- // Resample it
- Mem_FreePool (&s->mempool);
- s->mempool = Mem_AllocPool (s->name);
- sc = s->sfxcache = Mem_Alloc (s->mempool, (int)len + sizeof (sfxcache_t));
- if (sc != NULL)
+ // Decide if we go for a stream or a simple PCM cache
+ if (snd_streaming.integer && len > fs_filesize + 3 * STREAM_BUFFER_SIZE)
{
- sc->length = (int)done / (vi->channels * 2);
- sc->loopstart = -1;
- sc->speed = vi->rate;
- sc->width = 2; // We always work with 16 bits samples
- sc->stereo = (vi->channels == 2);
-
- ResampleSfx (sc, buff, s->name);
+ ogg_stream_persfx_t* per_sfx;
+
+ Con_DPrintf ("\"%s\" will be streamed\n", filename);
+ per_sfx = Mem_Alloc (s->mempool, sizeof (*per_sfx));
+ per_sfx->file = data;
+ per_sfx->filesize = fs_filesize;
+ s->fetcher_data = per_sfx;
+ s->fetcher = &ogg_fetcher;
+ s->format.speed = shm->format.speed;
+ s->format.width = 2; // We always work with 16 bits samples
+ s->format.channels = vi->channels;
+ s->loopstart = -1;
+ s->total_length = (size_t)len / (vi->channels * 2) * (float)(shm->format.speed / vi->rate);
}
else
{
- Con_Printf("failed to allocate memory for sound \"%s\"\n", s->name);
- Mem_FreePool (&s->mempool);
- }
+ char *buff;
+ ogg_int64_t done;
+ int bs, bigendian;
+ long ret;
+ sfxbuffer_t *sb;
+
+ Con_DPrintf ("\"%s\" will be streamed\n", filename);
+
+ // Decode it
+ buff = Mem_Alloc (s->mempool, (int)len);
+ done = 0;
+ bs = 0;
+#if BYTE_ORDER == LITTLE_ENDIAN
+ bigendian = 0;
+#else
+ bigendian = 1;
+#endif
+ while ((ret = qov_read (&vf, &buff[done], (int)(len - done), bigendian, 2, 1, &bs)) > 0)
+ done += ret;
+
+ // Calculate resampled length
+ len = (double)done * (double)shm->format.speed / (double)vi->rate;
+
+ // Resample it
+ sb = Mem_Alloc (s->mempool, (size_t)len + sizeof (*sb) - sizeof (sb->data));
+ s->fetcher_data = sb;
+ s->fetcher = &wav_fetcher;
+ s->format.speed = vi->rate;
+ s->format.width = 2; // We always work with 16 bits samples
+ s->format.channels = vi->channels;
+ s->loopstart = -1;
+
+ sb->length = ResampleSfx (buff, (size_t)done / (vi->channels * 2), &s->format, sb->data, s->name);
+ s->format.speed = shm->format.speed;
+ s->total_length = sb->length;
+ sb->offset = 0;
- qov_clear (&vf);
- Mem_Free (buff);
- Mem_Free (data);
+ qov_clear (&vf);
+ Mem_Free (data);
+ Mem_Free (buff);
+ }
- return sc;
+ return true;
}
qboolean OGG_OpenLibrary (void);
void OGG_CloseLibrary (void);
-sfxcache_t *OGG_LoadVorbisFile (const char *filename, sfx_t *s);
+qboolean OGG_LoadVorbisFile (const char *filename, sfx_t *s);
#endif
int audio_fd;
int snd_inited;
-static int tryrates[] = {44100, 22051, 11025, 8000};
+static int tryrates[] = {44100, 22050, 11025, 8000};
qboolean SNDDMA_Init(void)
{
// set sample bits & speed
s = getenv("QUAKE_SOUND_SAMPLEBITS");
if (s)
- shm->samplebits = atoi(s);
+ shm->format.width = atoi(s) / 8;
else if ((i = COM_CheckParm("-sndbits")) != 0)
- shm->samplebits = atoi(com_argv[i+1]);
+ shm->format.width = atoi(com_argv[i+1]) / 8;
- if (shm->samplebits != 16 && shm->samplebits != 8)
+ if (shm->format.width != 2 && shm->format.width != 1)
{
ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &fmt);
if (fmt & format16bit)
- shm->samplebits = 16;
+ shm->format.width = 2;
else if (fmt & AFMT_U8)
- shm->samplebits = 8;
+ shm->format.width = 1;
}
s = getenv("QUAKE_SOUND_SPEED");
if (s)
- shm->speed = atoi(s);
+ shm->format.speed = atoi(s);
else if ((i = COM_CheckParm("-sndspeed")) != 0)
- shm->speed = atoi(com_argv[i+1]);
+ shm->format.speed = atoi(com_argv[i+1]);
else
{
for (i = 0;i < (int) sizeof(tryrates) / 4;i++)
if (!ioctl(audio_fd, SNDCTL_DSP_SPEED, &tryrates[i]))
break;
- shm->speed = tryrates[i];
+ shm->format.speed = tryrates[i];
}
s = getenv("QUAKE_SOUND_CHANNELS");
if (s)
- shm->channels = atoi(s);
+ shm->format.channels = atoi(s);
else if ((i = COM_CheckParm("-sndmono")) != 0)
- shm->channels = 1;
+ shm->format.channels = 1;
else if ((i = COM_CheckParm("-sndstereo")) != 0)
- shm->channels = 2;
+ shm->format.channels = 2;
else
- shm->channels = 2;
+ shm->format.channels = 2;
- shm->samples = info.fragstotal * info.fragsize / (shm->samplebits/8);
+ shm->samples = info.fragstotal * info.fragsize / shm->format.width;
// memory map the dma buffer
shm->bufferlength = info.fragstotal * info.fragsize;
}
tmp = 0;
- if (shm->channels == 2)
+ if (shm->format.channels == 2)
tmp = 1;
rc = ioctl(audio_fd, SNDCTL_DSP_STEREO, &tmp);
if (rc < 0)
{
perror("/dev/dsp");
- Con_Printf("Could not set /dev/dsp to stereo=%d\n", shm->channels);
+ Con_Printf("Could not set /dev/dsp to stereo=%d\n", shm->format.channels);
close(audio_fd);
return 0;
}
if (tmp)
- shm->channels = 2;
+ shm->format.channels = 2;
else
- shm->channels = 1;
+ shm->format.channels = 1;
- rc = ioctl(audio_fd, SNDCTL_DSP_SPEED, &shm->speed);
+ rc = ioctl(audio_fd, SNDCTL_DSP_SPEED, &shm->format.speed);
if (rc < 0)
{
perror("/dev/dsp");
- Con_Printf("Could not set /dev/dsp speed to %d\n", shm->speed);
+ Con_Printf("Could not set /dev/dsp speed to %d\n", shm->format.speed);
close(audio_fd);
return 0;
}
- if (shm->samplebits == 16)
+ if (shm->format.width == 2)
{
rc = format16bit;
rc = ioctl(audio_fd, SNDCTL_DSP_SETFMT, &rc);
return 0;
}
}
- else if (shm->samplebits == 8)
+ else if (shm->format.width == 1)
{
rc = AFMT_U8;
rc = ioctl(audio_fd, SNDCTL_DSP_SETFMT, &rc);
else
{
perror("/dev/dsp");
- Con_Printf("%d-bit sound not supported.\n", shm->samplebits);
+ Con_Printf("%d-bit sound not supported.\n", shm->format.width * 8);
close(audio_fd);
return 0;
}
snd_inited = 0;
return 0;
}
- shm->samplepos = count.ptr / (shm->samplebits / 8);
+ shm->samplepos = count.ptr / shm->format.width;
return shm->samplepos;
}
int i;
memset((void *)shm, 0, sizeof(*shm));
- shm->channels = 2;
- shm->samplebits = 16;
+ shm->format.channels = 2;
+ shm->format.width = 2;
i = COM_CheckParm ("-sndspeed"); // LordHavoc: -sndspeed option
if (i && i != (com_argc - 1))
- shm->speed = atoi(com_argv[i+1]);
+ shm->format.speed = atoi(com_argv[i+1]);
else
- shm->speed = 44100;
+ shm->format.speed = 44100;
memset (&format, 0, sizeof(format));
format.wFormatTag = WAVE_FORMAT_PCM;
- format.nChannels = shm->channels;
- format.wBitsPerSample = shm->samplebits;
- format.nSamplesPerSec = shm->speed;
+ format.nChannels = shm->format.channels;
+ format.wBitsPerSample = shm->format.width * 8;
+ format.nSamplesPerSec = shm->format.speed;
format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8;
format.cbSize = 0;
format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;
return SIS_FAILURE;
}
- shm->channels = format.nChannels;
- shm->samplebits = format.wBitsPerSample;
- shm->speed = format.nSamplesPerSec;
+ shm->format.channels = format.nChannels;
+ shm->format.width = format.wBitsPerSample / 8;
+ shm->format.speed = format.nSamplesPerSec;
if (DS_OK != pDSBuf->lpVtbl->GetCaps (pDSBuf, &dsbcaps))
{
Con_SafePrintf(" %d channel(s)\n"
" %d bits/sample\n"
" %d samples/sec\n",
- shm->channels, shm->samplebits, shm->speed);
+ shm->format.channels, shm->format.width * 8, shm->format.speed);
gSndBufSize = dsbcaps.dwBufferBytes;
pDSBuf->lpVtbl->GetCurrentPosition(pDSBuf, &mmstarttime.u.sample, &dwWrite);
pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
- shm->samples = gSndBufSize/(shm->samplebits/8);
+ shm->samples = gSndBufSize / shm->format.width;
shm->samplepos = 0;
shm->buffer = (unsigned char *) lpData;
- sample16 = (shm->samplebits/8) - 1;
+ sample16 = shm->format.width - 1;
dsound_init = true;
snd_completed = 0;
memset((void *)shm, 0, sizeof(*shm));
- shm->channels = 2;
- shm->samplebits = 16;
+ shm->format.channels = 2;
+ shm->format.width = 2;
i = COM_CheckParm ("-sndspeed"); // LordHavoc: -sndspeed option
if (i && i != (com_argc - 1))
- shm->speed = atoi(com_argv[i+1]);
+ shm->format.speed = atoi(com_argv[i+1]);
else
- shm->speed = 44100;
+ shm->format.speed = 44100;
memset (&format, 0, sizeof(format));
format.wFormatTag = WAVE_FORMAT_PCM;
- format.nChannels = shm->channels;
- format.wBitsPerSample = shm->samplebits;
- format.nSamplesPerSec = shm->speed;
+ format.nChannels = shm->format.channels;
+ format.wBitsPerSample = shm->format.width * 8;
+ format.nSamplesPerSec = shm->format.speed;
format.nBlockAlign = format.nChannels
*format.wBitsPerSample / 8;
format.cbSize = 0;
}
}
- shm->samples = gSndBufSize/(shm->samplebits/8);
+ shm->samples = gSndBufSize / shm->format.width;
shm->samplepos = 0;
shm->buffer = (unsigned char *) lpData;
- sample16 = (shm->samplebits/8) - 1;
+ sample16 = shm->format.width - 1;
wav_init = true;
*/
// sound.h -- client sound i/o functions
-#ifndef __SOUND__
-#define __SOUND__
+#ifndef SOUND_H
+#define SOUND_H
#define DEFAULT_SOUND_PACKET_VOLUME 255
#define DEFAULT_SOUND_PACKET_ATTENUATION 1.0
typedef struct
{
- int length;
- int loopstart;
- int speed;
- int width;
- int stereo;
- qbyte data[1]; // variable sized
-} sfxcache_t;
+ size_t length;
+ size_t offset;
+ qbyte data[4]; // variable sized
+} sfxbuffer_t;
+
+typedef struct
+{
+ unsigned int speed;
+ unsigned int width;
+ unsigned int channels;
+} snd_format_t;
// sfx_t flags
#define SFXFLAG_SILENTLYMISSING (1 << 0) // if the sfx is missing and loaded with complain = false
#define SFXFLAG_USED (1 << 1)
+typedef struct snd_fetcher_s snd_fetcher_t;
typedef struct sfx_s
{
- char name[MAX_QPATH];
- mempool_t *mempool;
- sfxcache_t *sfxcache;
- unsigned int flags; // cf SFXFLAG_* defines
+ char name[MAX_QPATH];
+ mempool_t *mempool;
+ unsigned int flags; // cf SFXFLAG_* defines
+ snd_format_t format;
+ int loopstart;
+ size_t total_length;
+ const snd_fetcher_t *fetcher;
+ void *fetcher_data; // Per-sfx data for the sound fetching functions
} sfx_t;
typedef struct
{
- int channels;
- int samples; // mono samples in buffer
- int samplepos; // in mono samples
- int samplebits;
- int speed;
+ snd_format_t format;
+ int samples; // mono samples in buffer
+ int samplepos; // in mono samples
unsigned char *buffer;
- int bufferlength; // used only by certain drivers
+ int bufferlength; // used only by certain drivers
} dma_t;
typedef struct
int pos; // sample position in sfx
int looping; // where to loop, -1 = no looping
int entnum; // to allow overriding a specific sound
- int entchannel; //
+ int entchannel;
vec3_t origin; // origin of sound effect
vec_t dist_mult; // distance multiplier (attenuation/clipK)
int master_vol; // 0-255 master volume
+ void *fetcher_data; // Per-channel data for the sound fetching function
} channel_t;
+typedef const sfxbuffer_t* (*snd_fetcher_getsb_t) (channel_t* ch, unsigned int start, unsigned int nbsamples);
+typedef void (*snd_fetcher_end_t) (channel_t* ch);
+struct snd_fetcher_s
+{
+ snd_fetcher_getsb_t getsb;
+ snd_fetcher_end_t end;
+};
+
typedef struct
{
int rate;
// shutdown the DMA xfer.
void SNDDMA_Shutdown(void);
+extern size_t ResampleSfx (const qbyte *in_data, size_t in_length, const snd_format_t* in_format, qbyte *out_data, const char* sfxname);
+
// ====================================================================
// User-setable variables
// ====================================================================
// MAX_DYNAMIC_CHANNELS to MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS -1 = water, etc
// MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS to total_channels = static sounds
-extern int total_channels;
+extern unsigned int total_channels;
//
// Fake dma is a synchronous faking of the DMA progress used for
extern cvar_t snd_swapstereo;
extern cvar_t cdaudioinitialized;
-extern cvar_t snd_initialized;
+extern cvar_t snd_initialized;
+extern cvar_t snd_streaming;
extern int snd_blocked;
void S_LocalSound (char *s);
-sfxcache_t *S_LoadSound (sfx_t *s, int complain);
+qboolean S_LoadSound (sfx_t *s, int complain);
void S_UnloadSound(sfx_t *s);
-wavinfo_t GetWavinfo (char *name, qbyte *wav, int wavlength);
-
void SND_InitScaletable (void);
void SNDDMA_Submit(void);