diff -ur chocolate-doom-3.0.1/src/i_sdlsound.c chocolate-doom-3.0.1-resamp/src/i_sdlsound.c --- chocolate-doom-3.0.1/src/i_sdlsound.c 2020-04-04 15:49:05.000000000 -0400 +++ chocolate-doom-3.0.1-resamp/src/i_sdlsound.c 2022-06-05 16:58:38.829477391 -0400 @@ -23,6 +23,7 @@ #include #include #include +#include #include "SDL.h" #include "SDL_mixer.h" @@ -64,10 +65,6 @@ static Uint16 mixer_format; static int mixer_channels; static boolean use_sfx_prefix; -static boolean (*ExpandSoundData)(sfxinfo_t *sfxinfo, - byte *data, - int samplerate, - int length) = NULL; // Doubly-linked list of allocated sounds. // When a sound is played, it is moved to the head, so that the oldest @@ -79,14 +76,6 @@ int use_libsamplerate = 0; -// Scale factor used when converting libsamplerate floating point numbers -// to integers. Too high means the sounds can clip; too low means they -// will be too quiet. This is an amount that should avoid clipping most -// of the time: with all the Doom IWAD sound effects, at least. If a PWAD -// is used, clipping might occur. - -float libsamplerate_scale = 0.65f; - // Hook a sound into the linked list at the head. static void AllocatedSoundLink(allocated_sound_t *snd) @@ -142,83 +131,20 @@ free(snd); } -// Search from the tail backwards along the allocated sounds list, find -// and free a sound that is not in use, to free up memory. Return true -// for success. - -static boolean FindAndFreeSound(void) -{ - allocated_sound_t *snd; - - snd = allocated_sounds_tail; - - while (snd != NULL) - { - if (snd->use_count == 0) - { - FreeAllocatedSound(snd); - return true; - } - - snd = snd->prev; - } - - // No available sounds to free... - - return false; -} - -// Enforce SFX cache size limit. We are just about to allocate "len" -// bytes on the heap for a new sound effect, so free up some space -// so that we keep allocated_sounds_size < snd_cachesize - -static void ReserveCacheSpace(size_t len) -{ - if (snd_cachesize <= 0) - { - return; - } - - // Keep freeing sound effects that aren't currently being played, - // until there is enough space for the new sound. - - while (allocated_sounds_size + len > snd_cachesize) - { - // Free a sound. If there is nothing more to free, stop. - - if (!FindAndFreeSound()) - { - break; - } - } -} - // Allocate a block for a new sound effect. static allocated_sound_t *AllocateSound(sfxinfo_t *sfxinfo, size_t len) { allocated_sound_t *snd; - // Keep allocated sounds within the cache size. - - ReserveCacheSpace(len); - // Allocate the sound structure and data. The data will immediately // follow the structure, which acts as a header. - do + snd = malloc(sizeof(allocated_sound_t) + len); + if (snd == NULL) { - snd = malloc(sizeof(allocated_sound_t) + len); - - // Out of memory? Try to free an old sound, then loop round - // and try again. - - if (snd == NULL && !FindAndFreeSound()) - { - return NULL; - } - - } while (snd == NULL); + return NULL; + } // Skip past the chunk structure for the audio buffer @@ -335,7 +261,7 @@ // When a sound stops, check if it is still playing. If it is not, // we can mark the sound data as CACHE to be freed back for other -// means. +// means. [No longer relevant] static void ReleaseSoundOnChannel(int channel) { @@ -389,127 +315,6 @@ } } -// libsamplerate-based generic sound expansion function for any sample rate -// unsigned 8 bits --> signed 16 bits -// mono --> stereo -// samplerate --> mixer_freq -// Returns number of clipped samples. -// DWF 2008-02-10 with cleanups by Simon Howard. - -static boolean ExpandSoundData_SRC(sfxinfo_t *sfxinfo, - byte *data, - int samplerate, - int length) -{ - SRC_DATA src_data; - float *data_in; - uint32_t i, abuf_index=0, clipped=0; -// uint32_t alen; - int retn; - int16_t *expanded; - allocated_sound_t *snd; - Mix_Chunk *chunk; - - src_data.input_frames = length; - data_in = malloc(length * sizeof(float)); - src_data.data_in = data_in; - src_data.src_ratio = (double)mixer_freq / samplerate; - - // We include some extra space here in case of rounding-up. - src_data.output_frames = src_data.src_ratio * length + (mixer_freq / 4); - src_data.data_out = malloc(src_data.output_frames * sizeof(float)); - - assert(src_data.data_in != NULL && src_data.data_out != NULL); - - // Convert input data to floats - - for (i=0; ichunk; - expanded = (int16_t *) chunk->abuf; - - // Convert the result back into 16-bit integers. - - for (i=0; i INT16_MAX) - { - cvtval_i = INT16_MAX; - ++clipped; - } - - // Left and right channels - - expanded[abuf_index++] = cvtval_i; - expanded[abuf_index++] = cvtval_i; - } - - free(data_in); - free(src_data.data_out); - - if (clipped > 0) - { - fprintf(stderr, "Sound '%s': clipped %u samples (%0.2f %%)\n", - sfxinfo->name, clipped, - 400.0 * clipped / chunk->alen); - } - - return true; -} - #endif static boolean ConvertibleRatio(int freq1, int freq2) @@ -592,12 +397,11 @@ #endif // Generic sound expansion function for any sample rate. -// Returns number of clipped samples (always 0). -static boolean ExpandSoundData_SDL(sfxinfo_t *sfxinfo, - byte *data, - int samplerate, - int length) +static boolean ExpandSoundData(sfxinfo_t *sfxinfo, + byte *data, + int samplerate, + int length) { SDL_AudioCVT convertor; allocated_sound_t *snd; @@ -708,40 +512,47 @@ return true; } -// Load and convert a sound effect -// Returns true if successful - -static boolean CacheSFX(sfxinfo_t *sfxinfo) +// Load and validate a sound effect lump. +// Postconditions if sound is valid: +// returns true +// starred parameters are set, with data_ref pointing to start of sound +// caller is responsible for releasing the identified lump +// Postconditions if sound is invalid: +// returns false +// starred parameters are garbage +// lump already released + +static boolean LoadSoundLump(int lumpnum, + int *samplerate, + uint32_t *length, + byte **data_ref) { - int lumpnum; - unsigned int lumplen; - int samplerate; - unsigned int length; + int lumplen; byte *data; - // need to load the sound + // Load the sound - lumpnum = sfxinfo->lumpnum; - data = W_CacheLumpNum(lumpnum, PU_STATIC); + *data_ref = W_CacheLumpNum(lumpnum, PU_STATIC); lumplen = W_LumpLength(lumpnum); + data = *data_ref; - // Check the header, and ensure this is a valid sound + // Ensure this is a valid sound - if (lumplen < 8 - || data[0] != 0x03 || data[1] != 0x00) + if (lumplen < 8 || data[0] != 0x03 || data[1] != 0x00) { - // Invalid sound - - return false; + // Invalid sound + W_ReleaseLumpNum(lumpnum); + return false; } // 16 bit sample rate field, 32 bit length field - samplerate = (data[3] << 8) | data[2]; - length = (data[7] << 24) | (data[6] << 16) | (data[5] << 8) | data[4]; + *samplerate = (data[3] << 8) | data[2]; + *length = (data[7] << 24) | (data[6] << 16) | (data[5] << 8) | data[4]; - // If the header specifies that the length of the sound is greater than - // the length of the lump itself, this is an invalid sound lump + // If the header specifies that the length of the sound is + // greater than the length of the lump itself, this is an invalid + // sound lump. // We also discard sound lumps that are less than 49 samples long, // as this is how DMX behaves - although the actual cut-off length @@ -749,20 +560,42 @@ // further investigation to better understand the correct // behavior. - if (length > lumplen - 8 || length <= 48) + if (*length > lumplen - 8 || *length <= 48) { - return false; + W_ReleaseLumpNum(lumpnum); + return false; } + // Prune header + *data_ref += 8; + // The DMX sound library seems to skip the first 16 and last 16 // bytes of the lump - reason unknown. - data += 16; - length -= 32; + *data_ref += 16; + *length -= 32; - // Sample rate conversion + return true; +} + +// Load and convert a sound effect +// Returns true if successful + +static boolean CacheSFX(sfxinfo_t *sfxinfo) +{ + int samplerate; + unsigned int length; + byte *data; + +#ifdef HAVE_LIBSAMPLERATE + assert(!use_libsamplerate); // Pre-cache or die +#endif - if (!ExpandSoundData(sfxinfo, data + 8, samplerate, length)) + if (!LoadSoundLump(sfxinfo->lumpnum, &samplerate, &length, &data)) + return false; + + // Sample rate conversion + if (!ExpandSoundData(sfxinfo, data, samplerate, length)) { return false; } @@ -780,9 +613,7 @@ #endif // don't need the original lump any more - - W_ReleaseLumpNum(lumpnum); - + W_ReleaseLumpNum(sfxinfo->lumpnum); return true; } @@ -815,7 +646,12 @@ static void I_SDL_PrecacheSounds(sfxinfo_t *sounds, int num_sounds) { char namebuf[9]; - int i; + uint32_t sound_i, sample_i; + boolean good_sound[num_sounds]; + float *resampled_sound[num_sounds]; + uint32_t resampled_sound_length[num_sounds]; + float norm_factor; + float max_amp = 0; // Don't need to precache the sounds unless we are using libsamplerate. @@ -824,27 +660,121 @@ return; } - printf("I_SDL_PrecacheSounds: Precaching all sound effects.."); - - for (i=0; i 0); + resampled_sound[sound_i] = src_data.data_out; + resampled_sound_length[sound_i] = src_data.output_frames_gen; + free(data_in_nonconst); + good_sound[sound_i] = true; + + // Track maximum amplitude for later normalization + rsound = resampled_sound[sound_i]; + rlen = resampled_sound_length[sound_i]; + for (sample_i=0; sample_i max_amp) + max_amp = fabs_amp; + } + } + } + + // Pass 2: normalize and convert to signed 16-bit stereo. + + if (max_amp <= 0) + max_amp = 1; + norm_factor = INT16_MAX / max_amp; + + for (sound_i=0; sound_ichunk; + expanded = (int16_t *) chunk->abuf; + abuf_index=0; + rsound = resampled_sound[sound_i]; + for (sample_i=0; sample_i