summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSimon Howard2011-02-28 22:26:45 +0000
committerSimon Howard2011-02-28 22:26:45 +0000
commitaf19c6755955d7d397454cd2fd988270ee2f0561 (patch)
tree08af82af1481929ccfc35138a467cfa4ad4c3c08 /src
parentc16a0065efd2296533b5f165631be24ac55e2a44 (diff)
downloadchocolate-doom-af19c6755955d7d397454cd2fd988270ee2f0561.tar.gz
chocolate-doom-af19c6755955d7d397454cd2fd988270ee2f0561.tar.bz2
chocolate-doom-af19c6755955d7d397454cd2fd988270ee2f0561.zip
Rework sound chunk allocation to use native malloc() instead of the zone
memory system. This should fix the problems with running out of memory when playing the long Strife voice sounds. Subversion-branch: /branches/strife-branch Subversion-revision: 2285
Diffstat (limited to 'src')
-rw-r--r--src/i_sdlsound.c226
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;