X-Git-Url: http://de.git.xonotic.org/?p=xonotic%2Fdarkplaces.git;a=blobdiff_plain;f=snd_mem.c;h=f6f9c145d85c073cf9aa2421f5cdab6d3dc17068;hp=07c32eb1865d4c1f780dd89e2e73cea311e64455;hb=2075ae43356d724bae305ce8fd36ea570718b14a;hpb=0e936f973c3dcb7f74691912db3ad85e8805ccf1 diff --git a/snd_mem.c b/snd_mem.c index 07c32eb1..f6f9c145 100644 --- a/snd_mem.c +++ b/snd_mem.c @@ -27,123 +27,273 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. /* -================ -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). +==================== +*/ +snd_ringbuffer_t *Snd_CreateRingBuffer (const snd_format_t* format, unsigned int sampleframes, void* buffer) +{ + snd_ringbuffer_t *ringbuffer; + + // If the caller provides a buffer, it must give us its size + if (sampleframes == 0 && buffer != NULL) + return NULL; + + ringbuffer = (snd_ringbuffer_t*)Mem_Alloc(snd_mempool, sizeof (*ringbuffer)); + memset(ringbuffer, 0, sizeof(*ringbuffer)); + memcpy(&ringbuffer->format, format, sizeof(ringbuffer->format)); + + // If we haven't been given a buffer + if (buffer == NULL) + { + unsigned int maxframes; + size_t memsize; + + if (sampleframes == 0) + maxframes = (format->speed + 1) / 2; // Make the sound buffer large enough for containing 0.5 sec of sound + else + maxframes = sampleframes; + + memsize = maxframes * format->width * format->channels; + ringbuffer->ring = (unsigned char *) Mem_Alloc(snd_mempool, memsize); + ringbuffer->maxframes = maxframes; + } + else + { + ringbuffer->ring = (unsigned char *) buffer; + ringbuffer->maxframes = sampleframes; + } + + return ringbuffer; +} + + +/* +==================== +Snd_CreateSndBuffer +==================== */ -size_t ResampleSfx (const qbyte *in_data, size_t in_length, const snd_format_t* in_format, qbyte *out_data, const char* sfxname) +snd_buffer_t *Snd_CreateSndBuffer (const unsigned char *samples, unsigned int sampleframes, const snd_format_t* in_format, unsigned int sb_speed) { - size_t srclength, outcount, i; + size_t newsampleframes, memsize; + snd_buffer_t* sb; + + newsampleframes = (size_t) ceil((double)sampleframes * (double)sb_speed / (double)in_format->speed); - srclength = in_length * in_format->channels; - outcount = (double)in_length * shm->format.speed / in_format->speed; + memsize = newsampleframes * in_format->channels * in_format->width; + memsize += sizeof (*sb) - sizeof (sb->samples); - //Con_DPrintf("ResampleSfx(%s): %d samples @ %dHz -> %d samples @ %dHz\n", - // sfxname, in_length, in_format->speed, outcount, shm->format.speed); + 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 (!Snd_AppendToSndBuffer (sb, samples, sampleframes, in_format)) + { + Mem_Free (sb); + return NULL; + } + + return sb; +} + + +/* +==================== +Snd_AppendToSndBuffer +==================== +*/ +qboolean Snd_AppendToSndBuffer (snd_buffer_t* sb, const unsigned char *samples, unsigned int sampleframes, const snd_format_t* format) +{ + size_t srclength, outcount; + unsigned char *out_data; + + //Con_DPrintf("ResampleSfx: %d samples @ %dHz -> %d samples @ %dHz\n", + // sampleframes, format->speed, outcount, sb->format.speed); + + // If the formats are incompatible + if (sb->format.channels != format->channels || sb->format.width != format->width) + { + Con_Print("AppendToSndBuffer: incompatible sound formats!\n"); + return false; + } + + outcount = (size_t) ((double)sampleframes * (double)sb->format.speed / (double)format->speed); + + // If the sound buffer is too short + if (outcount > sb->maxframes - sb->nbframes) + { + Con_Print("AppendToSndBuffer: sound buffer too short!\n"); + return false; + } + + out_data = &sb->samples[sb->nbframes * sb->format.width * sb->format.channels]; + srclength = sampleframes * format->channels; // Trivial case (direct transfer) - if (in_format->speed == shm->format.speed) + if (format->speed == sb->format.speed) { - if (in_format->width == 1) + if (format->width == 1) { + size_t i; + for (i = 0; i < srclength; i++) - ((signed char*)out_data)[i] = in_data[i] - 128; + ((signed char*)out_data)[i] = samples[i] - 128; } - else // if (in_format->width == 2) - memcpy (out_data, in_data, srclength * in_format->width); + 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) +# define FRACTIONAL_BITS 14 +# define FRACTIONAL_MASK ((1 << FRACTIONAL_BITS) - 1) +# define INTEGER_BITS (sizeof(samplefrac)*8 - FRACTIONAL_BITS) else { - const unsigned int fracstep = (double)in_format->speed / shm->format.speed * (1 << FRACTIONAL_BITS); + 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 qbyte *in_ptr = in_data; - qbyte *out_ptr = out_data; + const unsigned char *in_ptr = samples; + unsigned char *out_ptr = out_data; // Check that we can handle one second of that sound - if (in_format->speed * in_format->channels > (1 << INTEGER_BITS)) - Sys_Error ("ResampleSfx: sound quality too high for resampling (%uHz, %u channel(s))", - in_format->speed, in_format->channels); + if (format->speed * format->channels > (1 << INTEGER_BITS)) + { + Con_Printf ("ResampleSfx: sound quality too high for resampling (%uHz, %u channel(s))\n", + format->speed, format->channels); + return 0; + } // We work 1 sec at a time to make sure we don't accumulate any // significant error when adding "fracstep" over several seconds, and // also to be able to handle very long sounds. while (total_out < outcount) { - size_t tmpcount; + 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 > shm->format.speed) - tmpcount = shm->format.speed; + 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; + } - // Convert up to 1 sec of sound - for (i = 0; i < tmpcount; i++) + // 16 bit samples + if (format->width == 2) { - unsigned int j = 0; - unsigned int srcsample = (samplefrac >> FRACTIONAL_BITS) * in_format->channels; - int a, b; + const short* in_ptr_short; - // 16 bit samples - if (in_format->width == 2) + // Interpolated part + for (i = 0; i < interpolation_limit; i++) { - for (j = 0; j < in_format->channels; j++, srcsample++) + srcsample = (samplefrac >> FRACTIONAL_BITS) * format->channels; + in_ptr_short = &((const short*)in_ptr)[srcsample]; + + for (j = 0; j < format->channels; j++) { - // No value to interpolate with? - if (srcsample + in_format->channels < remain_in) - { - a = ((const short*)in_ptr)[srcsample]; - b = ((const short*)in_ptr)[srcsample + in_format->channels]; - *((short*)out_ptr) = (((b - a) * (samplefrac & FRACTIONAL_MASK)) >> FRACTIONAL_BITS) + a; - } - else - *((short*)out_ptr) = ((const short*)in_ptr)[srcsample]; + 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; } - // 8 bit samples - else // if (in_format->width == 1) + + // Non-interpolated part + for (/* nothing */; i < tmpcount; i++) { - for (j = 0; j < in_format->channels; j++, srcsample++) + srcsample = (samplefrac >> FRACTIONAL_BITS) * format->channels; + in_ptr_short = &((const short*)in_ptr)[srcsample]; + + for (j = 0; j < format->channels; j++) { - // No more value to interpolate with? - if (srcsample + in_format->channels < remain_in) - { - a = ((const qbyte*)in_ptr)[srcsample] - 128; - b = ((const qbyte*)in_ptr)[srcsample + in_format->channels] - 128; - *((signed char*)out_ptr) = (((b - a) * (samplefrac & FRACTIONAL_MASK)) >> FRACTIONAL_BITS) + a; - } - else - *((signed char*)out_ptr) = ((const qbyte*)in_ptr)[srcsample] - 128; + *((short*)out_ptr) = *in_ptr_short; + + in_ptr_short++; + out_ptr += sizeof (short); + } + + samplefrac += fracstep; + } + } + // 8 bit samples + else // if (format->width == 1) + { + const unsigned char* in_ptr_byte; + + // Convert up to 1 sec of sound + for (i = 0; i < interpolation_limit; i++) + { + srcsample = (samplefrac >> FRACTIONAL_BITS) * format->channels; + in_ptr_byte = &((const unsigned char*)in_ptr)[srcsample]; + + for (j = 0; j < format->channels; j++) + { + int a, b; + + a = *in_ptr_byte - 128; + b = *(in_ptr_byte + format->channels) - 128; + *((signed char*)out_ptr) = (((b - a) * (samplefrac & FRACTIONAL_MASK)) >> FRACTIONAL_BITS) + a; + in_ptr_byte++; out_ptr += sizeof (signed char); } + + samplefrac += fracstep; } - 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 -= in_format->speed * in_format->channels; - in_ptr += in_format->speed * in_format->channels * in_format->width; + 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; } + //============================================================================= /* @@ -151,79 +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 + 16]; size_t len; - if (!shm || !shm->format.speed) - return false; + // See if already loaded + if (sfx->fetcher != NULL) + return true; // If we weren't able to load it previously, no need to retry - if (s->flags & SFXFLAG_FILEMISSING) + // Note: S_PrecacheSound clears this flag to cause a retry + if (sfx->flags & SFXFLAG_FILEMISSING) return false; - // See if in memory - 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; - } + // No sound? + if (snd_renderbuffer == NULL) + return false; + + // Initialize volume peak to 0; if ReplayGain is supported, the loader will change this away + sfx->volume_peak = 0.0; + + if (developer_loading.integer) + Con_Printf("loading sound %s\n", sfx->name); + + SCR_PushLoadingScreen(true, sfx->name, 1); // LordHavoc: if the sound filename does not begin with sound/, try adding it - if (strncasecmp(s->name, "sound/", 6)) + if (strncasecmp(sfx->name, "sound/", 6)) { - len = dpsnprintf (namebuffer, sizeof(namebuffer), "sound/%s", s->name); - if (len < 0) + dpsnprintf (namebuffer, sizeof(namebuffer), "sound/%s", sfx->name); + len = strlen(namebuffer); + if (len >= 4 && !strcasecmp (namebuffer + len - 4, ".wav")) { - // name too long - Con_Printf("S_LoadSound: name \"%s\" is too long\n", s->name); - return false; + 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 (S_LoadWavFile (namebuffer, s)) - return true; - if (len >= 4 && !strcasecmp (namebuffer + len - 4, ".wav")) - strcpy (namebuffer + len - 3, "ogg"); - if (OGG_LoadVorbisFile (namebuffer, s)) - return true; } // LordHavoc: then try without the added sound/ as wav and ogg - len = dpsnprintf (namebuffer, sizeof(namebuffer), "%s", s->name); - if (len < 0) + 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")) { - // name too long - Con_Printf("S_LoadSound: name \"%s\" is too long\n", s->name); - return false; + 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 (S_LoadWavFile (namebuffer, s)) - return true; - if (len >= 4 && !strcasecmp (namebuffer + len - 4, ".wav")) - strcpy (namebuffer + len - 3, "ogg"); - if (OGG_LoadVorbisFile (namebuffer, s)) - return true; // Can't load the sound! - s->flags |= SFXFLAG_FILEMISSING; + sfx->flags |= SFXFLAG_FILEMISSING; if (complain) - Con_Printf("S_LoadSound: Couldn't load \"%s\"\n", s->name); - return false; -} - -void S_UnloadSound (sfx_t *s) -{ - if (s->fetcher != NULL) - { - unsigned int i; + Con_DPrintf("failed to load sound \"%s\"\n", sfx->name); - // Stop all channels that use this sound - for (i = 0; i < total_channels ; i++) - if (channels[i].sfx == s) - S_StopChannel (i); + SCR_PopLoadingScreen(false); + return false; - s->fetcher = NULL; - s->fetcher_data = NULL; - Mem_FreePool(&s->mempool); - } +loaded: + SCR_PopLoadingScreen(false); + return true; }