X-Git-Url: http://de.git.xonotic.org/?p=xonotic%2Fdarkplaces.git;a=blobdiff_plain;f=snd_mem.c;h=f6f9c145d85c073cf9aa2421f5cdab6d3dc17068;hp=2cb01bee3adae3d371761c105cad2975153f9c53;hb=2075ae43356d724bae305ce8fd36ea570718b14a;hpb=f3e79d752a76a9d6329759a83ec9800a5e4cc92b diff --git a/snd_mem.c b/snd_mem.c index 2cb01bee..f6f9c145 100644 --- a/snd_mem.c +++ b/snd_mem.c @@ -17,194 +17,283 @@ 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" +#include "snd_main.h" #include "snd_ogg.h" #include "snd_wav.h" /* -================ -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). +==================== */ -size_t ResampleSfx (const qbyte *in_data, size_t in_length, const snd_format_t* in_format, qbyte *out_data, const char* sfxname) +snd_ringbuffer_t *Snd_CreateRingBuffer (const snd_format_t* format, unsigned int sampleframes, void* buffer) { - int samplefrac, fracstep; - size_t i, srcsample, srclength, outcount; + snd_ringbuffer_t *ringbuffer; + + // If the caller provides a buffer, it must give us its size + if (sampleframes == 0 && buffer != NULL) + return NULL; + + ringbuffer = (snd_ringbuffer_t*)Mem_Alloc(snd_mempool, sizeof (*ringbuffer)); + memset(ringbuffer, 0, sizeof(*ringbuffer)); + memcpy(&ringbuffer->format, format, sizeof(ringbuffer->format)); + + // If we haven't been given a buffer + if (buffer == NULL) + { + unsigned int maxframes; + size_t memsize; + + if (sampleframes == 0) + maxframes = (format->speed + 1) / 2; // Make the sound buffer large enough for containing 0.5 sec of sound + else + maxframes = sampleframes; + + memsize = maxframes * format->width * format->channels; + ringbuffer->ring = (unsigned char *) Mem_Alloc(snd_mempool, memsize); + ringbuffer->maxframes = maxframes; + } + else + { + ringbuffer->ring = (unsigned char *) buffer; + ringbuffer->maxframes = sampleframes; + } + + return ringbuffer; +} - // this is usually 0.5 (128), 1 (256), or 2 (512) - fracstep = ((double) in_format->speed / (double) shm->format.speed) * 256.0; - srclength = in_length * in_format->channels; +/* +==================== +Snd_CreateSndBuffer +==================== +*/ +snd_buffer_t *Snd_CreateSndBuffer (const unsigned char *samples, unsigned int sampleframes, const snd_format_t* in_format, unsigned int sb_speed) +{ + size_t newsampleframes, memsize; + snd_buffer_t* sb; + + newsampleframes = (size_t) ceil((double)sampleframes * (double)sb_speed / (double)in_format->speed); - 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); + memsize = newsampleframes * in_format->channels * in_format->width; + memsize += sizeof (*sb) - sizeof (sb->samples); -// resample / decimate to the current source rate + 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 = (unsigned int)newsampleframes; + sb->nbframes = 0; - if (fracstep == 256) + if (!Snd_AppendToSndBuffer (sb, samples, sampleframes, in_format)) { - // fast case for direct transfer - if (in_format->width == 1) // 8bit - for (i = 0;i < srclength;i++) - ((signed char *)out_data)[i] = ((unsigned char *)in_data)[i] - 128; - else //if (sb->width == 2) // 16bit - for (i = 0;i < srclength;i++) - ((short *)out_data)[i] = ((short *)in_data)[i]; + Mem_Free (sb); + return NULL; } + + return sb; +} + + +/* +==================== +Snd_AppendToSndBuffer +==================== +*/ +qboolean Snd_AppendToSndBuffer (snd_buffer_t* sb, const unsigned char *samples, unsigned int sampleframes, const snd_format_t* format) +{ + size_t srclength, outcount; + unsigned char *out_data; + + //Con_DPrintf("ResampleSfx: %d samples @ %dHz -> %d samples @ %dHz\n", + // sampleframes, format->speed, outcount, sb->format.speed); + + // If the formats are incompatible + if (sb->format.channels != format->channels || sb->format.width != format->width) + { + Con_Print("AppendToSndBuffer: incompatible sound formats!\n"); + return false; + } + + outcount = (size_t) ((double)sampleframes * (double)sb->format.speed / (double)format->speed); + + // If the sound buffer is too short + if (outcount > sb->maxframes - sb->nbframes) + { + Con_Print("AppendToSndBuffer: sound buffer too short!\n"); + return false; + } + + out_data = &sb->samples[sb->nbframes * sb->format.width * sb->format.channels]; + srclength = sampleframes * format->channels; + + // Trivial case (direct transfer) + if (format->speed == sb->format.speed) + { + if (format->width == 1) + { + size_t i; + + for (i = 0; i < srclength; i++) + ((signed char*)out_data)[i] = samples[i] - 128; + } + else // if (format->width == 2) + memcpy (out_data, samples, srclength * format->width); + } + + // General case (linear interpolation with a fixed-point fractional + // step, 18-bit integer part and 14-bit fractional part) + // Can handle up to 2^18 (262144) samples per second (> 96KHz stereo) +# define FRACTIONAL_BITS 14 +# define FRACTIONAL_MASK ((1 << FRACTIONAL_BITS) - 1) +# define INTEGER_BITS (sizeof(samplefrac)*8 - FRACTIONAL_BITS) else { - // general case - samplefrac = 0; - if ((fracstep & 255) == 0) // skipping points on perfect multiple + 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)) { - srcsample = 0; - fracstep >>= 8; - if (in_format->width == 2) + Con_Printf ("ResampleSfx: sound quality too high for resampling (%uHz, %u channel(s))\n", + format->speed, format->channels); + return 0; + } + + // We work 1 sec at a time to make sure we don't accumulate any + // significant error when adding "fracstep" over several seconds, and + // also to be able to handle very long sounds. + while (total_out < outcount) + { + size_t tmpcount, interpolation_limit, i, j; + unsigned int srcsample; + + samplefrac = 0; + + // If more than 1 sec of sound remains to be converted + if (outcount - total_out > sb->format.speed) { - 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 ; iformat.speed; + interpolation_limit = tmpcount; // all samples can be interpolated } else { - signed char *out = out_data; - const unsigned char *in = in_data; - if (in_format->channels == 2) - { - fracstep <<= 1; - for (i=0 ; ichannels) - 1) << FRACTIONAL_BITS) / fracstep); + if (interpolation_limit > tmpcount) + interpolation_limit = tmpcount; } - } - else - { - int sample; - int a, b; - if (in_format->width == 2) + + // 16 bit samples + if (format->width == 2) { - short *out = (short*)out_data; - const short *in = (const short*)in_data; - if (in_format->channels == 2) + const short* in_ptr_short; + + // Interpolated part + for (i = 0; i < interpolation_limit; i++) { - for (i=0 ; i> FRACTIONAL_BITS) * format->channels; + in_ptr_short = &((const short*)in_ptr)[srcsample]; + + for (j = 0; j < format->channels; j++) { - srcsample = (samplefrac >> 8) << 1; - a = in[srcsample ]; - if (srcsample+2 >= srclength) - b = 0; - else - b = in[srcsample+2]; - sample = (((b - a) * (samplefrac & 255)) >> 8) + a; - *out++ = (short) sample; - a = in[srcsample+1]; - if (srcsample+2 >= srclength) - b = 0; - else - b = in[srcsample+3]; - sample = (((b - a) * (samplefrac & 255)) >> 8) + a; - *out++ = (short) sample; - samplefrac += fracstep; + int a, b; + + a = *in_ptr_short; + b = *(in_ptr_short + format->channels); + *((short*)out_ptr) = (((b - a) * (samplefrac & FRACTIONAL_MASK)) >> FRACTIONAL_BITS) + a; + + in_ptr_short++; + out_ptr += sizeof (short); } + + samplefrac += fracstep; } - else + + // Non-interpolated part + for (/* nothing */; i < tmpcount; i++) { - for (i=0 ; i> FRACTIONAL_BITS) * format->channels; + in_ptr_short = &((const short*)in_ptr)[srcsample]; + + for (j = 0; j < format->channels; j++) { - srcsample = samplefrac >> 8; - a = in[srcsample ]; - if (srcsample+1 >= srclength) - b = 0; - else - b = in[srcsample+1]; - sample = (((b - a) * (samplefrac & 255)) >> 8) + a; - *out++ = (short) sample; - samplefrac += fracstep; + *((short*)out_ptr) = *in_ptr_short; + + in_ptr_short++; + out_ptr += sizeof (short); } + + samplefrac += fracstep; } } - else + // 8 bit samples + else // if (format->width == 1) { - signed char *out = out_data; - const unsigned char *in = in_data; - if (in_format->channels == 2) + const unsigned char* in_ptr_byte; + + // Convert up to 1 sec of sound + for (i = 0; i < interpolation_limit; i++) { - for (i=0 ; i> FRACTIONAL_BITS) * format->channels; + in_ptr_byte = &((const unsigned char*)in_ptr)[srcsample]; + + for (j = 0; j < format->channels; j++) { - srcsample = (samplefrac >> 8) << 1; - a = (int) in[srcsample ] - 128; - if (srcsample+2 >= srclength) - b = 0; - else - b = (int) in[srcsample+2] - 128; - sample = (((b - a) * (samplefrac & 255)) >> 8) + a; - *out++ = (signed char) sample; - a = (int) in[srcsample+1] - 128; - if (srcsample+2 >= srclength) - b = 0; - else - b = (int) in[srcsample+3] - 128; - sample = (((b - a) * (samplefrac & 255)) >> 8) + a; - *out++ = (signed char) sample; - samplefrac += fracstep; + 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; } - else + + // Non-interpolated part + for (/* nothing */; i < tmpcount; i++) { - for (i=0 ; i> FRACTIONAL_BITS) * format->channels; + in_ptr_byte = &((const unsigned char*)in_ptr)[srcsample]; + + for (j = 0; j < format->channels; j++) { - srcsample = samplefrac >> 8; - a = (int) in[srcsample ] - 128; - if (srcsample+1 >= srclength) - b = 0; - else - b = (int) in[srcsample+1] - 128; - sample = (((b - a) * (samplefrac & 255)) >> 8) + a; - *out++ = (signed char) sample; - samplefrac += fracstep; + *((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; } } - return outcount; + sb->nbframes += (unsigned int)outcount; + return true; } + //============================================================================= /* @@ -212,67 +301,77 @@ size_t ResampleSfx (const qbyte *in_data, size_t in_length, const snd_format_t* S_LoadSound ============== */ -qboolean S_LoadSound (sfx_t *s, qboolean complain) +qboolean S_LoadSound (sfx_t *sfx, qboolean complain) { - char namebuffer[MAX_QPATH]; + char namebuffer[MAX_QPATH + 16]; size_t len; - qboolean modified_name = false; - // see if still in memory - 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); + // See if already loaded + if (sfx->fetcher != NULL) return true; - } - len = strlcpy (namebuffer, s->name, sizeof (namebuffer)); - if (len >= sizeof (namebuffer)) + // 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; - // Try to load it as a WAV file - if (S_LoadWavFile (namebuffer, s)) - return true; + // No sound? + if (snd_renderbuffer == NULL) + return false; - // Else, try to load it as an Ogg Vorbis file - if (!strcasecmp (namebuffer + len - 4, ".wav")) + // Initialize volume peak to 0; if ReplayGain is supported, the loader will change this away + sfx->volume_peak = 0.0; + + if (developer_loading.integer) + Con_Printf("loading sound %s\n", sfx->name); + + SCR_PushLoadingScreen(true, sfx->name, 1); + + // LordHavoc: if the sound filename does not begin with sound/, try adding it + if (strncasecmp(sfx->name, "sound/", 6)) { - strcpy (namebuffer + len - 3, "ogg"); - modified_name = true; + dpsnprintf (namebuffer, sizeof(namebuffer), "sound/%s", sfx->name); + len = strlen(namebuffer); + if (len >= 4 && !strcasecmp (namebuffer + len - 4, ".wav")) + { + if (S_LoadWavFile (namebuffer, sfx)) + goto loaded; + memcpy (namebuffer + len - 3, "ogg", 4); + } + if (len >= 4 && !strcasecmp (namebuffer + len - 4, ".ogg")) + { + if (OGG_LoadVorbisFile (namebuffer, sfx)) + goto loaded; + } } - if (OGG_LoadVorbisFile (namebuffer, s)) - return true; - // Can't load the sound! - if (!complain) - s->flags |= SFXFLAG_SILENTLYMISSING; - else - s->flags &= ~SFXFLAG_SILENTLYMISSING; - if (complain) + // LordHavoc: then try without the added sound/ as wav and ogg + dpsnprintf (namebuffer, sizeof(namebuffer), "%s", sfx->name); + len = strlen(namebuffer); + // request foo.wav: tries foo.wav, then foo.ogg + // request foo.ogg: tries foo.ogg only + // request foo.mod: tries foo.mod only + if (len >= 4 && !strcasecmp (namebuffer + len - 4, ".wav")) { - if (modified_name) - strcpy (namebuffer + len - 3, "wav"); - Con_Printf("Couldn't load %s\n", namebuffer); + if (S_LoadWavFile (namebuffer, sfx)) + goto loaded; + memcpy (namebuffer + len - 3, "ogg", 4); } - return false; -} - -void S_UnloadSound(sfx_t *s) -{ - if (s->fetcher != NULL) + if (len >= 4 && !strcasecmp (namebuffer + len - 4, ".ogg")) { - unsigned int i; + if (OGG_LoadVorbisFile (namebuffer, sfx)) + goto loaded; + } - s->fetcher = NULL; - s->fetcher_data = NULL; - Mem_FreePool(&s->mempool); + // Can't load the sound! + sfx->flags |= SFXFLAG_FILEMISSING; + if (complain) + Con_DPrintf("failed to load sound \"%s\"\n", sfx->name); - // 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; - } + SCR_PopLoadingScreen(false); + return false; + +loaded: + SCR_PopLoadingScreen(false); + return true; }