From ad40941adf0fe050fcc609b588cc05266bf26b84 Mon Sep 17 00:00:00 2001 From: molivier Date: Tue, 23 Mar 2004 08:28:28 +0000 Subject: [PATCH] Ogg vorbis streaming support; the code decides whether it will cache or stream the sound depending on the file and uncompressed sound sizes. It should be stable enough for a regular use now; you lose a few FPS once every second or so of course, it's not free, but you can expect the Vorbis stream to use between 5 and 12 times less memory than before, depending on the sound quality and the number of SFX sources using it. This patch also contains a couple of fixes (a pretty annoying typo in the OSS and BSD driver - 22051Hz instead of 22050Hz, and a potential memory leak in WAV loader) git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@4052 d7cf8633-e32d-0410-b094-e92efae38249 --- snd_bsd.c | 20 ++-- snd_dma.c | 87 ++++++++--------- snd_mem.c | 173 ++++++++++++++++++++------------- snd_mix.c | 83 ++++++++++------ snd_ogg.c | 279 +++++++++++++++++++++++++++++++++++++++++++----------- snd_ogg.h | 2 +- snd_oss.c | 48 +++++----- snd_win.c | 44 ++++----- sound.h | 67 ++++++++----- 9 files changed, 525 insertions(+), 278 deletions(-) diff --git a/snd_bsd.c b/snd_bsd.c index 1c39404c..5735013a 100644 --- a/snd_bsd.c +++ b/snd_bsd.c @@ -30,7 +30,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #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; @@ -61,16 +61,16 @@ qboolean SNDDMA_Init (void) // 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; @@ -93,7 +93,7 @@ qboolean SNDDMA_Init (void) (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; @@ -115,7 +115,7 @@ int SNDDMA_GetDMAPos (void) return 0; } - return ((info.play.samples * shm->channels) % shm->samples); + return ((info.play.samples * shm->format.channels) % shm->samples); } void SNDDMA_Shutdown (void) @@ -150,7 +150,7 @@ void SNDDMA_Submit (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) diff --git a/snd_dma.c b/snd_dma.c index b55c553b..48bbaf98 100644 --- a/snd_dma.c +++ b/snd_dma.c @@ -45,11 +45,12 @@ void S_StopAllSoundsC(void); // ======================================================================= 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; @@ -126,13 +127,13 @@ void S_SoundInfo_f(void) 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) @@ -161,12 +162,12 @@ void S_Startup(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 { @@ -181,7 +182,7 @@ void S_Startup(void) sound_started = 1; - Con_DPrintf("Sound sampling rate: %i\n", shm->speed); + Con_DPrintf("Sound sampling rate: %i\n", shm->format.speed); //S_LoadSounds(); @@ -244,6 +245,7 @@ void S_Init(void) 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); @@ -517,12 +519,11 @@ void SND_Spatialize(channel_t *ch, int isstatic) 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; @@ -545,9 +546,8 @@ void S_StartSound(int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float f //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 @@ -555,7 +555,7 @@ void S_StartSound(int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float f 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 @@ -567,9 +567,9 @@ void S_StartSound(int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float f 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; @@ -616,7 +616,7 @@ void S_ClearBuffer(void) if (!sound_started || !shm) return; - if (shm->samplebits == 8) + if (shm->format.width == 1) clear = 0x80; else clear = 0; @@ -648,7 +648,7 @@ void S_ClearBuffer(void) } } - memset(pData, clear, shm->samples * shm->samplebits/8); + memset(pData, clear, shm->samples * shm->format.width); pDSBuf->lpVtbl->Unlock(pDSBuf, pData, dwSize, NULL, 0); @@ -657,7 +657,7 @@ void S_ClearBuffer(void) #endif if (shm->buffer) { - int setsize = shm->samples * shm->samplebits / 8; + int setsize = shm->samples * shm->format.width; char *buf = shm->buffer; while (setsize--) @@ -679,7 +679,6 @@ S_StaticSound void S_StaticSound (sfx_t *sfx, vec3_t origin, float vol, float attenuation) { channel_t *ss; - sfxcache_t *sc; if (!sfx) return; @@ -690,11 +689,10 @@ void S_StaticSound (sfx_t *sfx, vec3_t origin, float vol, float attenuation) 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++]; @@ -704,7 +702,7 @@ void S_StaticSound (sfx_t *sfx, vec3_t origin, float vol, float attenuation) 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); } @@ -775,10 +773,8 @@ Called once each time through the main loop */ 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; @@ -848,7 +844,7 @@ void S_Update(vec3_t origin, vec3_t forward, vec3_t left, vec3_t up) if (ch->sfx && (ch->leftvol || ch->rightvol) ) total++; - Con_Printf("----(%i)----\n", total); + Con_Printf("----(%u)----\n", total); } // mix some sound @@ -862,7 +858,7 @@ void GetSoundtime(void) 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. @@ -881,7 +877,7 @@ void GetSoundtime(void) } oldsamplepos = samplepos; - soundtime = buffers*fullsamples + samplepos/shm->channels; + soundtime = buffers * fullsamples + samplepos / shm->format.channels; } void IN_Accumulate (void); @@ -914,8 +910,8 @@ void S_Update_(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; @@ -998,18 +994,16 @@ void S_SoundList(void) { int i; sfx_t *sfx; - sfxcache_t *sc; int size, total; total = 0; for (sfx=known_sfx, i=0 ; isfxcache; - 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); @@ -1036,7 +1030,7 @@ void S_LocalSound (char *sound) #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) { @@ -1058,7 +1052,8 @@ 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; @@ -1102,7 +1097,7 @@ void S_RawSamples_ClearQueue(void) 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; @@ -1140,6 +1135,6 @@ void S_ResampleBuffer16Stereo(short *input, int inputlength, short *output, int int S_RawSamples_SampleRate(void) { - return shm != NULL ? shm->speed : 0; + return shm != NULL ? shm->format.speed : 0; } diff --git a/snd_mem.c b/snd_mem.c index d0671eb8..d924d66e 100644 --- a/snd_mem.c +++ b/snd_mem.c @@ -29,34 +29,31 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 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 { @@ -66,10 +63,11 @@ void ResampleSfx (sfxcache_t *sc, qbyte *data, char *name) { 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 ; idata; - 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 ; iwidth == 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 ; idata; - 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 ; idata, (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 @@ -274,10 +300,13 @@ sfxcache_t *S_LoadWavFile (const char *filename, sfx_t *s) } #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; } @@ -286,27 +315,29 @@ sfxcache_t *S_LoadWavFile (const char *filename, sfx_t *s) 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")) @@ -314,9 +345,8 @@ sfxcache_t *S_LoadSound (sfx_t *s, int complain) 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) @@ -329,15 +359,24 @@ sfxcache_t *S_LoadSound (sfx_t *s, int 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; } } diff --git a/snd_mix.c b/snd_mix.c index e27b97dd..6bb06589 100644 --- a/snd_mix.c +++ b/snd_mix.c @@ -121,11 +121,11 @@ void S_TransferPaintBuffer(int endtime) 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) @@ -186,7 +186,7 @@ void S_TransferPaintBuffer(int endtime) { // 8bit unsigned char *snd_out; - if (shm->channels == 2) + if (shm->format.channels == 2) { // 8bit 2 channels (stereo) while (lpaintedtime < endtime) @@ -257,15 +257,15 @@ CHANNEL MIXING =============================================================================== */ -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) @@ -282,18 +282,20 @@ void S_PaintChannels(int endtime) ch = channels; for (i=0; isfx) + 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; @@ -302,28 +304,35 @@ void S_PaintChannels(int endtime) 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; } } } @@ -345,10 +354,11 @@ void SND_InitScaletable (void) } -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) @@ -358,10 +368,15 @@ void SND_PaintChannelFrom8 (channel_t *ch, sfxcache_t *sc, int count) 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++]; @@ -370,7 +385,7 @@ void SND_PaintChannelFrom8 (channel_t *ch, sfxcache_t *sc, int count) } 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++; @@ -380,20 +395,27 @@ void SND_PaintChannelFrom8 (channel_t *ch, sfxcache_t *sc, int count) } 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 ; idata + ch->pos; + sfx = (signed short *)sb->data + ch->pos - sb->offset; for (i=0 ; ipos += count; + return true; } diff --git a/snd_ogg.c b/snd_ogg.c index bf7bd2a3..d1a44f26 100644 --- a/snd_ogg.c +++ b/snd_ogg.c @@ -1,5 +1,5 @@ /* - 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 @@ -26,9 +26,6 @@ #include "snd_ogg.h" -extern void ResampleSfx (sfxcache_t *sc, qbyte *data, char *name); - - /* ================================================================= @@ -200,6 +197,7 @@ static vorbis_info* (*qov_info) (OggVorbis_File *vf,int link); 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); @@ -209,6 +207,7 @@ static dllfunction_t oggvorbisfuncs[] = {"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} @@ -355,34 +354,185 @@ void OGG_CloseLibrary (void) ================================================================= */ +#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; @@ -390,9 +540,9 @@ sfxcache_t *OGG_LoadVorbisFile (const char *filename, sfx_t *s) 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 @@ -402,49 +552,72 @@ sfxcache_t *OGG_LoadVorbisFile (const char *filename, sfx_t *s) 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; } diff --git a/snd_ogg.h b/snd_ogg.h index c25e32ac..4d200b79 100644 --- a/snd_ogg.h +++ b/snd_ogg.h @@ -27,7 +27,7 @@ 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 diff --git a/snd_oss.c b/snd_oss.c index e5fddc2f..d06e4780 100644 --- a/snd_oss.c +++ b/snd_oss.c @@ -32,7 +32,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. int audio_fd; int snd_inited; -static int tryrates[] = {44100, 22051, 11025, 8000}; +static int tryrates[] = {44100, 22050, 11025, 8000}; qboolean SNDDMA_Init(void) { @@ -95,44 +95,44 @@ 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; @@ -146,32 +146,32 @@ qboolean SNDDMA_Init(void) } 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); @@ -183,7 +183,7 @@ qboolean SNDDMA_Init(void) return 0; } } - else if (shm->samplebits == 8) + else if (shm->format.width == 1) { rc = AFMT_U8; rc = ioctl(audio_fd, SNDCTL_DSP_SETFMT, &rc); @@ -198,7 +198,7 @@ qboolean SNDDMA_Init(void) 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; } @@ -244,7 +244,7 @@ int SNDDMA_GetDMAPos(void) snd_inited = 0; return 0; } - shm->samplepos = count.ptr / (shm->samplebits / 8); + shm->samplepos = count.ptr / shm->format.width; return shm->samplepos; } diff --git a/snd_win.c b/snd_win.c index 9806adaa..bea76b56 100644 --- a/snd_win.c +++ b/snd_win.c @@ -194,19 +194,19 @@ sndinitstat SNDDMA_InitDirect (void) 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; @@ -322,9 +322,9 @@ sndinitstat SNDDMA_InitDirect (void) 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)) { @@ -362,7 +362,7 @@ sndinitstat SNDDMA_InitDirect (void) 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; @@ -398,10 +398,10 @@ sndinitstat SNDDMA_InitDirect (void) 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; @@ -426,19 +426,19 @@ qboolean SNDDMA_InitWav (void) 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; @@ -531,10 +531,10 @@ qboolean SNDDMA_InitWav (void) } } - 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; diff --git a/sound.h b/sound.h index 075719ab..ffefe940 100644 --- a/sound.h +++ b/sound.h @@ -19,8 +19,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // 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 @@ -33,35 +33,42 @@ typedef struct 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 @@ -74,12 +81,21 @@ 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; @@ -124,6 +140,8 @@ int SNDDMA_GetDMAPos(void); // 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 // ==================================================================== @@ -139,7 +157,7 @@ extern channel_t channels[MAX_CHANNELS]; // 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 @@ -165,16 +183,15 @@ extern cvar_t volume; 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); -- 2.39.2