X-Git-Url: https://de.git.xonotic.org/?p=xonotic%2Fdarkplaces.git;a=blobdiff_plain;f=snd_mem.c;h=3da76b39b108d3c4f98990e5f0570409a6ad6e92;hp=f573c370365e49061e35837322f9ab92175b2591;hb=77f8bde166d6e0061eb25faf3c33cc24fce2caca;hpb=4ee1d4780706dc1ed70ec4f7aa8b7712518a5c4f diff --git a/snd_mem.c b/snd_mem.c index f573c370..3da76b39 100644 --- a/snd_mem.c +++ b/snd_mem.c @@ -8,7 +8,7 @@ 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. +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. @@ -17,461 +17,344 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -// snd_mem.c: sound caching + #include "quakedef.h" -int cache_full_cycle; +#include "snd_main.h" +#include "snd_ogg.h" +#include "snd_wav.h" -byte *S_Alloc (int size); /* -================ -ResampleSfx -================ +==================== +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). +==================== */ -void ResampleSfx (sfx_t *sfx, int inrate, byte *data) +snd_ringbuffer_t *Snd_CreateRingBuffer (const snd_format_t* format, unsigned int sampleframes, void* buffer) { - int outcount; - int srcsample; - float stepscale; - int i; - int samplefrac, fracstep; - sfxcache_t *sc; - - sc = Cache_Check (&sfx->cache); - if (!sc) - return; - - stepscale = (float)inrate / shm->speed; // this is usually 0.5, 1, or 2 - - outcount = sc->length / stepscale; - sc->length = outcount; - if (sc->loopstart != -1) - sc->loopstart = sc->loopstart / stepscale; - - sc->speed = shm->speed; -// if (loadas8bit.value) -// sc->width = 1; -// else -// sc->width = inwidth; -// sc->stereo = 0; - -// resample / decimate to the current source rate - - if (stepscale == 1/* && inwidth == 1*/ && sc->width == 1) - { -// fast special case - // LordHavoc: I do not serve the readability gods... - int *indata, *outdata; - int count4, count1; - count1 = outcount << sc->stereo; - count4 = count1 >> 2; - indata = (void *)data; - outdata = (void *)sc->data; - while (count4--) - *outdata++ = *indata++ ^ 0x80808080; - if (count1 & 2) - ((short*)outdata)[0] = ((short*)indata)[0] ^ 0x8080; - if (count1 & 1) - ((char*)outdata)[2] = ((char*)indata)[2] ^ 0x80; - /* - if (sc->stereo) // LordHavoc: stereo sound support - { - for (i=0 ; i<(outcount<<1) ; i++) - ((signed char *)sc->data)[i] = (int)( (unsigned char)(data[i]) - 128); - } - else - { - for (i=0 ; idata)[i] = (int)( (unsigned char)(data[i]) - 128); - } - */ - } - else if (stepscale == 1/* && inwidth == 2*/ && sc->width == 2) // LordHavoc: quick case for 16bit + 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) { - if (sc->stereo) // LordHavoc: stereo sound support - for (i=0 ; idata)[i] = LittleShort (((short *)data)[i]); + 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 - for (i=0 ; idata)[i] = LittleShort (((short *)data)[i]); + maxframes = sampleframes; + + memsize = maxframes * format->width * format->channels; + ringbuffer->ring = Mem_Alloc(snd_mempool, memsize); + ringbuffer->maxframes = maxframes; } else { -// general case - Con_DPrintf("ResampleSfx: resampling sound %s\n", sfx->name); - samplefrac = 0; - fracstep = stepscale*256; - if ((fracstep & 255) == 0) // skipping points on perfect multiple - { - srcsample = 0; - fracstep >>= 8; - if (sc->width == 2) - { - short *out = (void *)sc->data, *in = (void *)data; - if (sc->stereo) // LordHavoc: stereo sound support - { - fracstep <<= 1; - for (i=0 ; idata, *in = (void *)data; - if (sc->stereo) // LordHavoc: stereo sound support - { - fracstep <<= 1; - for (i=0 ; iwidth == 2) - { - short *out = (void *)sc->data, *in = (void *)data; - if (sc->stereo) // LordHavoc: stereo sound support - { - for (i=0 ; i> 7) & ~1; - samplefrac += fracstep; - sample = (LittleShort (in[srcsample ]) * (256 - (samplefrac & 255)) + LittleShort (in[srcsample+2]) * (samplefrac & 255)) >> 8; - *out++ = (short) sample; - sample = (LittleShort (in[srcsample+1]) * (256 - (samplefrac & 255)) + LittleShort (in[srcsample+3]) * (samplefrac & 255)) >> 8; - *out++ = (short) sample; - } - } - else - { - for (i=0 ; i> 8; - samplefrac += fracstep; - sample = (LittleShort (in[srcsample ]) * (256 - (samplefrac & 255)) + LittleShort (in[srcsample+1]) * (samplefrac & 255)) >> 8; - *out++ = (short) sample; - } - } - } - else - { - signed char *out = (void *)sc->data, *in = (void *)data; - if (sc->stereo) // LordHavoc: stereo sound support - { - for (i=0 ; i> 7) & ~1; - samplefrac += fracstep; - sample = ((((unsigned char) in[srcsample ] - 128) * (256 - (samplefrac & 255))) + (((unsigned char) in[srcsample+2] - 128) * (samplefrac & 255))) >> 8; - *out++ = (signed char) sample; - sample = ((((unsigned char) in[srcsample+1] - 128) * (256 - (samplefrac & 255))) + (((unsigned char) in[srcsample+3] - 128) * (samplefrac & 255))) >> 8; - *out++ = (signed char) sample; - } - } - else - { - for (i=0 ; i> 8; - samplefrac += fracstep; - sample = ((((unsigned char) in[srcsample ] - 128) * (256 - (samplefrac & 255))) + (((unsigned char) in[srcsample+1] - 128) * (samplefrac & 255))) >> 8; - *out++ = (signed char) sample; - } - } - } - } + ringbuffer->ring = buffer; + ringbuffer->maxframes = sampleframes; } + + return ringbuffer; } -//============================================================================= /* -============== -S_LoadSound -============== +==================== +Snd_CreateSndBuffer +==================== */ -sfxcache_t *S_LoadSound (sfx_t *s) +snd_buffer_t *Snd_CreateSndBuffer (const unsigned char *samples, unsigned int sampleframes, const snd_format_t* in_format, unsigned int sb_speed) { - char namebuffer[256]; - byte *data; - wavinfo_t info; - int len; - float stepscale; - sfxcache_t *sc; - -// see if still in memory - sc = Cache_Check (&s->cache); - if (sc) - return sc; + size_t newsampleframes, memsize; + snd_buffer_t* sb; -//Con_Printf ("S_LoadSound: %x\n", (int)stackbuf); -// load it in - strcpy(namebuffer, "sound/"); - strcat(namebuffer, s->name); + newsampleframes = (double)sampleframes * (double)sb_speed / (double)in_format->speed; -// Con_Printf ("loading %s\n",namebuffer); + memsize = newsampleframes * in_format->channels * in_format->width; + memsize += sizeof (*sb) - sizeof (sb->samples); - data = COM_LoadMallocFile(namebuffer, false); + 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 (!data) + if (!Snd_AppendToSndBuffer (sb, samples, sampleframes, in_format)) { - Con_Printf ("Couldn't load %s\n", namebuffer); + Mem_Free (sb); return NULL; } - info = GetWavinfo (s->name, data, com_filesize); - // LordHavoc: stereo sounds are now allowed (intended for music) - if (info.channels < 1 || info.channels > 2) + 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_Printf ("%s has an unsupported number of channels (%i)\n",s->name, info.channels); - qfree(data); - return NULL; + Con_Print("AppendToSndBuffer: incompatible sound formats!\n"); + return false; } - /* - if (info.channels != 1) + + outcount = (double)sampleframes * (double)sb->format.speed / (double)format->speed; + + // If the sound buffer is too short + if (outcount > sb->maxframes - sb->nbframes) { - Con_Printf ("%s is a stereo sample\n",s->name); - return NULL; + Con_Print("AppendToSndBuffer: sound buffer too short!\n"); + return false; } - */ - - stepscale = (float)info.rate / shm->speed; - len = info.samples / stepscale; - len = len * info.width * info.channels; + out_data = &sb->samples[sb->nbframes * sb->format.width * sb->format.channels]; + srclength = sampleframes * format->channels; - sc = Cache_Alloc ( &s->cache, len + sizeof(sfxcache_t), s->name); - if (!sc) + // Trivial case (direct transfer) + if (format->speed == sb->format.speed) { - qfree(data); - return NULL; + 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); } - - sc->length = info.samples; - sc->loopstart = info.loopstart; - sc->speed = info.rate; - sc->width = info.width; - sc->stereo = info.channels == 2; - ResampleSfx (s, sc->speed, data + info.dataofs); + // 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; + } - qfree(data); - return sc; -} + // 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; -WAV loading + // 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; -byte *data_p; -byte *iff_end; -byte *last_chunk; -byte *iff_data; -int iff_chunk_len; + in_ptr_short++; + out_ptr += sizeof (short); + } + samplefrac += fracstep; + } -short GetLittleShort(void) -{ - short val = 0; - val = *data_p; - val = val + (*(data_p+1)<<8); - data_p += 2; - return val; -} + // Non-interpolated part + for (/* nothing */; i < tmpcount; i++) + { + srcsample = (samplefrac >> FRACTIONAL_BITS) * format->channels; + in_ptr_short = &((const short*)in_ptr)[srcsample]; -int GetLittleLong(void) -{ - int val = 0; - val = *data_p; - val = val + (*(data_p+1)<<8); - val = val + (*(data_p+2)<<16); - val = val + (*(data_p+3)<<24); - data_p += 4; - return val; -} + for (j = 0; j < format->channels; j++) + { + *((short*)out_ptr) = *in_ptr_short; -void FindNextChunk(char *name) -{ - while (1) - { - data_p=last_chunk; + in_ptr_short++; + out_ptr += sizeof (short); + } - if (data_p >= iff_end) - { // didn't find the chunk - data_p = NULL; - return; - } - - data_p += 4; - iff_chunk_len = GetLittleLong(); - if (iff_chunk_len < 0) - { - data_p = NULL; - return; + 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; } -// if (iff_chunk_len > 1024*1024) -// Sys_Error ("FindNextChunk: %i length is past the 1 meg sanity limit", iff_chunk_len); - data_p -= 8; - last_chunk = data_p + 8 + ( (iff_chunk_len + 1) & ~1 ); - if (!strncmp(data_p, name, 4)) - return; } -} -void FindChunk(char *name) -{ - last_chunk = iff_data; - FindNextChunk (name); + sb->nbframes += outcount; + return true; } -void DumpChunks(void) -{ - char str[5]; - - str[4] = 0; - data_p=iff_data; - do - { - memcpy (str, data_p, 4); - data_p += 4; - iff_chunk_len = GetLittleLong(); - Con_Printf ("0x%x : %s (%d)\n", (int)(data_p - 4), str, iff_chunk_len); - data_p += (iff_chunk_len + 1) & ~1; - } while (data_p < iff_end); -} +//============================================================================= /* -============ -GetWavinfo -============ +============== +S_LoadSound +============== */ -wavinfo_t GetWavinfo (char *name, byte *wav, int wavlength) +qboolean S_LoadSound (sfx_t *sfx, qboolean complain) { - wavinfo_t info; - int i; - int format; - int samples; - - memset (&info, 0, sizeof(info)); - - if (!wav) - return info; - - iff_data = wav; - iff_end = wav + wavlength; - -// find "RIFF" chunk - FindChunk("RIFF"); - if (!(data_p && !strncmp(data_p+8, "WAVE", 4))) - { - Con_Printf("Missing RIFF/WAVE chunks\n"); - return info; - } + char namebuffer[MAX_QPATH + 16]; + size_t len; -// get "fmt " chunk - iff_data = data_p + 12; -// DumpChunks (); + // See if already loaded + if (sfx->fetcher != NULL) + return true; - FindChunk("fmt "); - if (!data_p) - { - Con_Printf("Missing fmt chunk\n"); - return info; - } - data_p += 8; - format = GetLittleShort(); - if (format != 1) - { - Con_Printf("Microsoft PCM format only\n"); - return info; - } + // 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; - info.channels = GetLittleShort(); - info.rate = GetLittleLong(); - data_p += 4+2; - info.width = GetLittleShort() / 8; + // No sound? + if (snd_renderbuffer == NULL) + return false; -// get cue chunk - FindChunk("cue "); - if (data_p) + // LordHavoc: if the sound filename does not begin with sound/, try adding it + if (strncasecmp(sfx->name, "sound/", 6)) { - data_p += 32; - info.loopstart = GetLittleLong(); -// Con_Printf("loopstart=%d\n", sfx->loopstart); - - // if the next chunk is a LIST chunk, look for a cue length marker - FindNextChunk ("LIST"); - if (data_p) + len = dpsnprintf (namebuffer, sizeof(namebuffer), "sound/%s", sfx->name); + if (len < 0) { - if (!strncmp (data_p + 28, "mark", 4)) - { // this is not a proper parse, but it works with cooledit... - data_p += 24; - i = GetLittleLong (); // samples in loop - info.samples = info.loopstart + i; -// Con_Printf("looped length: %i\n", i); - } + // name too long + Con_DPrintf("S_LoadSound: name \"%s\" is too long\n", sfx->name); + return false; } + if (S_LoadWavFile (namebuffer, sfx)) + return true; + if (len >= 4 && !strcasecmp (namebuffer + len - 4, ".wav")) + memcpy (namebuffer + len - 3, "ogg", 4); + if (OGG_LoadVorbisFile (namebuffer, sfx)) + return true; } - else - info.loopstart = -1; - -// find data chunk - FindChunk("data"); - if (!data_p) - { - Con_Printf("Missing data chunk\n"); - return info; - } - - data_p += 4; - samples = GetLittleLong () / info.width; - if (info.samples) + // LordHavoc: then try without the added sound/ as wav and ogg + len = dpsnprintf (namebuffer, sizeof(namebuffer), "%s", sfx->name); + if (len < 0) { - if (samples < info.samples) - Host_Error ("Sound %s has a bad loop length", name); + // name too long + Con_DPrintf("S_LoadSound: name \"%s\" is too long\n", sfx->name); + return false; } - else - info.samples = samples; - - info.dataofs = data_p - wav; - - return info; + if (S_LoadWavFile (namebuffer, sfx)) + return true; + if (len >= 4 && !strcasecmp (namebuffer + len - 4, ".wav")) + memcpy (namebuffer + len - 3, "ogg", 4); + if (OGG_LoadVorbisFile (namebuffer, sfx)) + return true; + + // Can't load the sound! + sfx->flags |= SFXFLAG_FILEMISSING; + if (complain) + Con_DPrintf("S_LoadSound: Couldn't load \"%s\"\n", sfx->name); + return false; } -