diff options
-rw-r--r-- | src/i_sdlsound.c | 226 |
1 files changed, 179 insertions, 47 deletions
diff --git a/src/i_sdlsound.c b/src/i_sdlsound.c index 1cfafa6f..bd987c9c 100644 --- a/src/i_sdlsound.c +++ b/src/i_sdlsound.c @@ -53,6 +53,16 @@ #define MAX_SOUND_SLICE_TIME 70 /* ms */ #define NUM_CHANNELS 16 +typedef struct allocated_sound_s allocated_sound_t; + +struct allocated_sound_s +{ + sfxinfo_t *sfxinfo; + Mix_Chunk chunk; + int use_count; + allocated_sound_t *prev, *next; +}; + static boolean setpanning_workaround = false; static boolean sound_initialized = false; @@ -68,59 +78,190 @@ static void (*ExpandSoundData)(sfxinfo_t *sfxinfo, 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 +// sounds not used recently are at the tail. + +static allocated_sound_t *allocated_sounds_head = NULL; +static allocated_sound_t *allocated_sounds_tail = NULL; +static int allocated_sounds_size = 0; + int use_libsamplerate = 0; -// 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. +// Hook a sound into the linked list at the head. -static void ReleaseSoundOnChannel(int channel) +static void AllocatedSoundLink(allocated_sound_t *snd) { - int i; - sfxinfo_t *sfxinfo = channels_playing[channel]; + snd->prev = NULL; - if (sfxinfo == NULL) + snd->next = allocated_sounds_head; + allocated_sounds_head = snd; + + if (allocated_sounds_tail == NULL) { - return; + allocated_sounds_tail = snd; } +} - channels_playing[channel] = NULL; - - for (i=0; i<NUM_CHANNELS; ++i) +// Unlink a sound from the linked list. + +static void AllocatedSoundUnlink(allocated_sound_t *snd) +{ + if (snd->prev == NULL) { - // Playing on this channel? if so, don't release. + allocated_sounds_head = snd->next; + } + else + { + snd->prev->next = snd->next; + } - if (channels_playing[i] == sfxinfo) - return; + if (snd->next == NULL) + { + allocated_sounds_tail = snd->prev; } + else + { + snd->next->prev = snd->prev; + } +} + +static void FreeAllocatedSound(allocated_sound_t *snd) +{ + // Unlink from linked list. + + AllocatedSoundUnlink(snd); + + // Unlink from higher-level code. - // Not used on any channel, and can be safely released + snd->sfxinfo->driver_data = NULL; - Z_ChangeTag(sfxinfo->driver_data, PU_CACHE); + // Keep track of the amount of allocated sound data: + + allocated_sounds_size -= snd->chunk.alen; + + free(snd); } -// Allocate a new Mix_Chunk along with its data, storing -// the result in the variable pointed to by variable +// 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 Mix_Chunk *AllocateChunk(sfxinfo_t *sfxinfo, uint32_t len) +static boolean FindAndFreeSound(void) { - Mix_Chunk *chunk; + 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; +} + +// Allocate a block for a new sound effect. + +static Mix_Chunk *AllocateSound(sfxinfo_t *sfxinfo, size_t len) +{ + allocated_sound_t *snd; + + // 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); + + // Out of memory? Try to free an old sound, then loop round + // and try again. - // Allocate the chunk and the audio buffer together + if (snd == NULL && !FindAndFreeSound()) + { + return NULL; + } - chunk = Z_Malloc(len + sizeof(Mix_Chunk), - PU_STATIC, - &sfxinfo->driver_data); - sfxinfo->driver_data = chunk; + } while (snd == NULL); // Skip past the chunk structure for the audio buffer - chunk->abuf = (byte *) (chunk + 1); - chunk->alen = len; - chunk->allocated = 1; - chunk->volume = MIX_MAX_VOLUME; + snd->chunk.abuf = (byte *) (snd + 1); + snd->chunk.alen = len; + snd->chunk.allocated = 1; + snd->chunk.volume = MIX_MAX_VOLUME; + + snd->sfxinfo = sfxinfo; + snd->use_count = 0; + + // driver_data pointer points to the allocated_sound structure. + + sfxinfo->driver_data = snd; + + // Keep track of how much memory all these cached sounds are using... + + allocated_sounds_size += len; + + AllocatedSoundLink(snd); + + return &snd->chunk; +} + +// Lock a sound, to indicate that it may not be freed. + +static void LockAllocatedSound(allocated_sound_t *snd) +{ + // Increase use count, to stop the sound being freed. + + ++snd->use_count; + + //printf("++ %s: Use count=%i\n", snd->sfxinfo->name, snd->use_count); + + // When we use a sound, re-link it into the list at the head, so + // that the oldest sounds fall to the end of the list for freeing. + + AllocatedSoundUnlink(snd); + AllocatedSoundLink(snd); +} + +// Unlock a sound to indicate that it may now be freed. + +static void UnlockAllocatedSound(allocated_sound_t *snd) +{ + if (snd->use_count <= 0) + { + I_Error("Sound effect released more times than it was locked..."); + } + + --snd->use_count; - return chunk; + //printf("-- %s: Use count=%i\n", snd->sfxinfo->name, snd->use_count); +} + +// 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. + +static void ReleaseSoundOnChannel(int channel) +{ + sfxinfo_t *sfxinfo = channels_playing[channel]; + + if (sfxinfo == NULL) + { + return; + } + + channels_playing[channel] = NULL; + + UnlockAllocatedSound(sfxinfo->driver_data); } #ifdef HAVE_LIBSAMPLERATE @@ -200,7 +341,7 @@ static void ExpandSoundData_SRC(sfxinfo_t *sfxinfo, alen = src_data.output_frames_gen * 4; - chunk = AllocateChunk(sfxinfo, src_data.output_frames_gen * 4); + chunk = AllocateSound(sfxinfo, src_data.output_frames_gen * 4); expanded = (int16_t *) chunk->abuf; // Convert the result back into 16-bit integers. @@ -363,7 +504,7 @@ static void ExpandSoundData_SDL(sfxinfo_t *sfxinfo, // Allocate a chunk in which to expand the sound - chunk = AllocateChunk(sfxinfo, expanded_length); + chunk = AllocateSound(sfxinfo, expanded_length); // If we can, use the standard / optimized SDL conversion routines. @@ -559,12 +700,7 @@ static void I_SDL_PrecacheSounds(sfxinfo_t *sounds, int num_sounds) if (sounds[i].lumpnum != -1) { - // Try to cache the sound and then release it as cache - - if (CacheSFX(&sounds[i])) - { - Z_ChangeTag(sounds[i].driver_data, PU_CACHE); - } + CacheSFX(&sounds[i]); } } @@ -593,12 +729,8 @@ static boolean LockSound(sfxinfo_t *sfxinfo) return false; } } - else - { - // Lock the sound effect into memory - - Z_ChangeTag(sfxinfo->driver_data, PU_STATIC); - } + + LockAllocatedSound(sfxinfo->driver_data); return true; } @@ -662,7 +794,7 @@ static void I_SDL_UpdateSoundParams(int handle, int vol, int sep) static int I_SDL_StartSound(sfxinfo_t *sfxinfo, int channel, int vol, int sep) { - Mix_Chunk *chunk; + allocated_sound_t *snd; if (!sound_initialized) { @@ -681,11 +813,11 @@ static int I_SDL_StartSound(sfxinfo_t *sfxinfo, int channel, int vol, int sep) return -1; } - chunk = (Mix_Chunk *) sfxinfo->driver_data; + snd = sfxinfo->driver_data; // play sound - Mix_PlayChannelTimed(channel, chunk, 0, -1); + Mix_PlayChannelTimed(channel, &snd->chunk, 0, -1); channels_playing[channel] = sfxinfo; |