memory management for openalbridge
authorkoda
Mon, 21 Jun 2010 16:08:24 +0200
changeset 3529 0e968ba12a84
parent 3516 a8c673657b79 (diff)
parent 3528 0ad90165fde0 (current diff)
child 3530 390e5048d39c
memory management for openalbridge added openalbridge as dependency of hwengine usound formatted clearly
CMakeLists.txt
hedgewars/CMakeLists.txt
hedgewars/uSound.pas
misc/libopenalbridge/CMakeLists.txt
misc/libopenalbridge/commands.c
misc/libopenalbridge/openalbridge.c
misc/libopenalbridge/openalbridge.h
misc/libopenalbridge/openalbridge_t.h
misc/libopenalbridge/tester.c
misc/libopenalbridge/wrappers.c
misc/libopenalbridge/wrappers.h
--- a/CMakeLists.txt	Sun Jun 20 22:46:23 2010 -0400
+++ b/CMakeLists.txt	Mon Jun 21 16:08:24 2010 +0200
@@ -183,6 +183,7 @@
 endif(WITH_SERVER)
 
 add_subdirectory(hedgewars)
+add_subdirectory(misc/libopenalbridge)
 if(NOT BUILD_ENGINE_LIBRARY)
 	add_subdirectory(bin)
 	add_subdirectory(QTfrontend)
--- a/hedgewars/CMakeLists.txt	Sun Jun 20 22:46:23 2010 -0400
+++ b/hedgewars/CMakeLists.txt	Mon Jun 21 16:08:24 2010 +0200
@@ -76,6 +76,7 @@
 	SinTable.inc
 	options.inc
 	${CMAKE_CURRENT_BINARY_DIR}/config.inc
+	openalbridge
 	)
 
 if(BUILD_ENGINE_LIBRARY)
--- a/hedgewars/uSound.pas	Sun Jun 20 22:46:23 2010 -0400
+++ b/hedgewars/uSound.pas	Mon Jun 21 16:08:24 2010 +0200
@@ -40,8 +40,8 @@
 procedure PlaySound(snd: TSound; keepPlaying: boolean);
 procedure PlaySound(snd: TSound; voicepack: PVoicepack);
 procedure PlaySound(snd: TSound; voicepack: PVoicepack; keepPlaying: boolean);
-function LoopSound(snd: TSound): LongInt;
-function LoopSound(snd: TSound; voicepack: PVoicepack): LongInt;
+function  LoopSound(snd: TSound): LongInt;
+function  LoopSound(snd: TSound; voicepack: PVoicepack): LongInt;
 procedure PlayMusic;
 procedure PauseMusic;
 procedure ResumeMusic;
@@ -65,14 +65,14 @@
 var i: Longword;
 begin
 i:= 0;
-while (voicepacks[i].name <> name) and (voicepacks[i].name <> '') do
+    while (voicepacks[i].name <> name) and (voicepacks[i].name <> '') do
     begin
-    inc(i);
-    TryDo(i <= cMaxTeams, 'Engine bug: AskForVoicepack i > cMaxTeams', true)
+        inc(i);
+        TryDo(i <= cMaxTeams, 'Engine bug: AskForVoicepack i > cMaxTeams', true)
     end;
 
-voicepacks[i].name:= name;
-AskForVoicepack:= @voicepacks[i]
+    voicepacks[i].name:= name;
+    AskForVoicepack:= @voicepacks[i]
 end;
 
 procedure InitSound;
@@ -110,22 +110,22 @@
 var i: TSound;
     t: Longword;
 begin
-for t:= 0 to cMaxTeams do
-    if voicepacks[t].name <> '' then
-        for i:= Low(TSound) to High(TSound) do
-            if voicepacks[t].chunks[i] <> nil then
-                Mix_FreeChunk(voicepacks[t].chunks[i]);
+    for t:= 0 to cMaxTeams do
+        if voicepacks[t].name <> '' then
+            for i:= Low(TSound) to High(TSound) do
+                if voicepacks[t].chunks[i] <> nil then
+                    Mix_FreeChunk(voicepacks[t].chunks[i]);
 
-if Mus <> nil then
-    Mix_FreeMusic(Mus);
+    if Mus <> nil then
+        Mix_FreeMusic(Mus);
 
 {$IFDEF SDL_MIXER_NEWER}
-// make sure all instances of sdl_mixer are unloaded before continuing
-while Mix_Init(0) <> 0 do
-    Mix_Quit();
+    // make sure all instances of sdl_mixer are unloaded before continuing
+    while Mix_Init(0) <> 0 do
+        Mix_Quit();
 {$ENDIF}    
 
-Mix_CloseAudio();
+    Mix_CloseAudio();
 end;
 
 procedure SoundLoad;
@@ -137,28 +137,28 @@
 
     defVoicepack:= AskForVoicepack('Default');
 
-for i:= Low(TSound) to High(TSound) do
-    if (Soundz[i].Path <> ptVoices) and (Soundz[i].FileName <> '') then
+    for i:= Low(TSound) to High(TSound) do
+        if (Soundz[i].Path <> ptVoices) and (Soundz[i].FileName <> '') then
         begin
-        s:= Pathz[Soundz[i].Path] + '/' + Soundz[i].FileName;
-        WriteToConsole(msgLoading + s + ' ');
-        defVoicepack^.chunks[i]:= Mix_LoadWAV_RW(SDL_RWFromFile(Str2PChar(s), 'rb'), 1);
-        TryDo(defVoicepack^.chunks[i] <> nil, msgFailed, true);
-        WriteLnToConsole(msgOK);
+            s:= Pathz[Soundz[i].Path] + '/' + Soundz[i].FileName;
+            WriteToConsole(msgLoading + s + ' ');
+            defVoicepack^.chunks[i]:= Mix_LoadWAV_RW(SDL_RWFromFile(Str2PChar(s), 'rb'), 1);
+            TryDo(defVoicepack^.chunks[i] <> nil, msgFailed, true);
+            WriteLnToConsole(msgOK);
         end;
 
-for t:= 0 to cMaxTeams do
-    if voicepacks[t].name <> '' then
-        for i:= Low(TSound) to High(TSound) do
-            if (Soundz[i].Path = ptVoices) and (Soundz[i].FileName <> '') then
+    for t:= 0 to cMaxTeams do
+        if voicepacks[t].name <> '' then
+            for i:= Low(TSound) to High(TSound) do
+                if (Soundz[i].Path = ptVoices) and (Soundz[i].FileName <> '') then
                 begin
-                s:= Pathz[Soundz[i].Path] + '/' + voicepacks[t].name + '/' + Soundz[i].FileName;
-                WriteToConsole(msgLoading + s + ' ');
-                voicepacks[t].chunks[i]:= Mix_LoadWAV_RW(SDL_RWFromFile(Str2PChar(s), 'rb'), 1);
-                if voicepacks[t].chunks[i] = nil then
-                    WriteLnToConsole(msgFailed)
-                else
-                    WriteLnToConsole(msgOK)
+                    s:= Pathz[Soundz[i].Path] + '/' + voicepacks[t].name + '/' + Soundz[i].FileName;
+                    WriteToConsole(msgLoading + s + ' ');
+                    voicepacks[t].chunks[i]:= Mix_LoadWAV_RW(SDL_RWFromFile(Str2PChar(s), 'rb'), 1);
+                    if voicepacks[t].chunks[i] = nil then
+                        WriteLnToConsole(msgFailed)
+                    else
+                        WriteLnToConsole(msgOK)
                 end;
 end;
 
@@ -179,15 +179,16 @@
 
 procedure PlaySound(snd: TSound; voicepack: PVoicepack; keepPlaying: boolean);
 begin
-if (not isSoundEnabled) or fastUntilLag then exit;
-
-if keepPlaying and (lastChan[snd] <> -1) and (Mix_Playing(lastChan[snd]) <> 0) then
-    exit;
+    if (not isSoundEnabled) or fastUntilLag then
+        exit;
 
-if (voicepack <> nil) and (voicepack^.chunks[snd] <> nil) then
-    lastChan[snd]:= Mix_PlayChannelTimed(-1, voicepack^.chunks[snd], 0, -1)
-else
-    lastChan[snd]:= Mix_PlayChannelTimed(-1, defVoicepack^.chunks[snd], 0, -1)
+    if keepPlaying and (lastChan[snd] <> -1) and (Mix_Playing(lastChan[snd]) <> 0) then
+        exit;
+
+    if (voicepack <> nil) and (voicepack^.chunks[snd] <> nil) then
+        lastChan[snd]:= Mix_PlayChannelTimed(-1, voicepack^.chunks[snd], 0, -1)
+    else
+        lastChan[snd]:= Mix_PlayChannelTimed(-1, defVoicepack^.chunks[snd], 0, -1)
 end;
 
 function LoopSound(snd: TSound): LongInt;
@@ -197,76 +198,79 @@
 
 function LoopSound(snd: TSound; voicepack: PVoicepack): LongInt;
 begin
-if (not isSoundEnabled) or fastUntilLag then
+    if (not isSoundEnabled) or fastUntilLag then
     begin
-    LoopSound:= -1;
-    exit
+        LoopSound:= -1;
+        exit
     end;
 
-if (voicepack <> nil) and (voicepack^.chunks[snd] <> nil) then
-    LoopSound:= Mix_PlayChannelTimed(-1, voicepack^.chunks[snd], -1, -1)
-else
-    LoopSound:= Mix_PlayChannelTimed(-1, defVoicepack^.chunks[snd], -1, -1)
+    if (voicepack <> nil) and (voicepack^.chunks[snd] <> nil) then
+        LoopSound:= Mix_PlayChannelTimed(-1, voicepack^.chunks[snd], -1, -1)
+    else
+        LoopSound:= Mix_PlayChannelTimed(-1, defVoicepack^.chunks[snd], -1, -1)
 end;
 
 procedure StopSound(snd: TSound);
 begin
-if not isSoundEnabled then exit;
-if (lastChan[snd] <> -1) and (Mix_Playing(lastChan[snd]) <> 0) then
+    if not isSoundEnabled then exit;
+    if (lastChan[snd] <> -1) and (Mix_Playing(lastChan[snd]) <> 0) then
     begin
-    Mix_HaltChannel(lastChan[snd]);
-    lastChan[snd]:= -1;
+        Mix_HaltChannel(lastChan[snd]);
+        lastChan[snd]:= -1;
     end;
 end;
 
 procedure StopSound(chn: LongInt);
 begin
-if not isSoundEnabled then exit;
-if (chn <> -1) and (Mix_Playing(chn) <> 0) then Mix_HaltChannel(chn);
+    if not isSoundEnabled then exit;
+
+    if (chn <> -1) and (Mix_Playing(chn) <> 0) then
+        Mix_HaltChannel(chn);
 end;
 
 procedure PlayMusic;
 var s: shortstring;
 begin
-if (not isSoundEnabled)
-    or (MusicFN = '')
-    or (not isMusicEnabled) then exit;
+    if (not isSoundEnabled) or (MusicFN = '') or (not isMusicEnabled) then
+        exit;
+
+    s:= PathPrefix + '/Music/' + MusicFN;
+    WriteToConsole(msgLoading + s + ' ');
 
-s:= PathPrefix + '/Music/' + MusicFN;
-WriteToConsole(msgLoading + s + ' ');
+    Mus:= Mix_LoadMUS(Str2PChar(s));
+    TryDo(Mus <> nil, msgFailed, false);
+    WriteLnToConsole(msgOK);
 
-Mus:= Mix_LoadMUS(Str2PChar(s));
-TryDo(Mus <> nil, msgFailed, false);
-WriteLnToConsole(msgOK);
-
-SDLTry(Mix_FadeInMusic(Mus, -1, 3000) <> -1, false)
+    SDLTry(Mix_FadeInMusic(Mus, -1, 3000) <> -1, false)
 end;
 
 function ChangeVolume(voldelta: LongInt): LongInt;
 begin
-if not isSoundEnabled then
-    exit(0);
+    if not isSoundEnabled then
+        exit(0);
 
-inc(Volume, voldelta);
-if Volume < 0 then Volume:= 0;
-Mix_Volume(-1, Volume);
-Volume:= Mix_Volume(-1, -1);
-if isMusicEnabled then Mix_VolumeMusic(Volume * 4 div 8);
-ChangeVolume:= Volume * 100 div MIX_MAX_VOLUME
+    inc(Volume, voldelta);
+    if Volume < 0 then Volume:= 0;
+    Mix_Volume(-1, Volume);
+    Volume:= Mix_Volume(-1, -1);
+    if isMusicEnabled then Mix_VolumeMusic(Volume * 4 div 8);
+    ChangeVolume:= Volume * 100 div MIX_MAX_VOLUME
 end;
 
 procedure PauseMusic;
 begin
-if (MusicFN = '') or (not isMusicEnabled) then exit;
+    if (MusicFN = '') or (not isMusicEnabled) then
+        exit;
 
-Mix_PauseMusic(Mus);
+    Mix_PauseMusic(Mus);
 end;
 
 procedure ResumeMusic;
 begin
-if (MusicFN = '') or (not isMusicEnabled) then exit;
+    if (MusicFN = '') or (not isMusicEnabled) then
+        exit;
 
-Mix_ResumeMusic(Mus);
+    Mix_ResumeMusic(Mus);
 end;
 
 procedure initModule;
--- a/misc/libopenalbridge/CMakeLists.txt	Sun Jun 20 22:46:23 2010 -0400
+++ b/misc/libopenalbridge/CMakeLists.txt	Mon Jun 21 16:08:24 2010 +0200
@@ -7,9 +7,7 @@
 set(LIBRARY_OUTPUT_PATH ${EXECUTABLE_OUTPUT_PATH})
 
 #list of source files for libraries
-set(openal_src
-	openalbridge.c loaders.c wrappers.c errlib.c
-)
+set(openal_src openalbridge.c loaders.c wrappers.c commands.c)
 
 #build a static library for human systems
 set (build_type STATIC)
@@ -17,9 +15,7 @@
 #visualstudio and windows in general don't like static linking, so we're building the library in shared mode
 if(WIN32)
 #workaround for visualstudio (wants headers in the source list)
-	set(openal_src
-		openalbridge.h openalbridge_t.h loaders.h wrappers.h globals.h oggvorbis.h errlib.h ${openal_src}
-	)
+	set(openal_src *.h ${openal_src})
 #deps for the shared library
 	link_libraries(${VORBISFILE_LIBRARY})
 	link_libraries(${VORBIS_LIBRARY})
@@ -39,3 +35,8 @@
 #install it in the executable directory
 	install(TARGETS openalbridge DESTINATION bin)
 endif(WIN32)
+
+#type make openalbridge_test to get a small executable test
+add_executable(openalbridge_test "${hedgewars_SOURCE_DIR}/misc/libopenalbridge/tester.c")
+target_link_libraries(openalbridge_test openalbridge ${OPENAL_LIBRARY} ${OGGVORBIS_LIBRARIES})
+
--- a/misc/libopenalbridge/commands.c	Sun Jun 20 22:46:23 2010 -0400
+++ b/misc/libopenalbridge/commands.c	Mon Jun 21 16:08:24 2010 +0200
@@ -92,8 +92,6 @@
             fprintf(stderr,"(Bridge Warning) - failed to play sound %d\n", index);
             return;
         }
-        
-        the_sounds[index].stats++;
     }
 }
 
--- a/misc/libopenalbridge/openalbridge.c	Sun Jun 20 22:46:23 2010 -0400
+++ b/misc/libopenalbridge/openalbridge.c	Mon Jun 21 16:08:24 2010 +0200
@@ -34,22 +34,14 @@
 
 // Initialize an OpenAL contex and allocate memory space for data and buffers
 // It can be called twice to increase the cache size
-int openal_init (int memorysize) {
+int openal_init (void) {
     ALCcontext *context;
     ALCdevice *device;
     int i;
         
     // reuse old context and resize the existing 
     if (openal_ready() == AL_TRUE) {
-        cache_size += memorysize;
-        fprintf(stderr,"(Bridge Info) - already initialized, resizing cache to %d\n", cache_size);
-        the_sounds = (al_sound_t *)Realloc (the_sounds, sizeof(al_sound_t) * cache_size);
-        for (i = cache_size - memorysize; i < cache_size; i++) {
-            the_sounds[i].filename = NULL;
-            the_sounds[i].buffer = -1;
-            the_sounds[i].source_index = -1;
-            the_sounds[i].stats = 0;
-        }
+        fprintf(stderr,"(Bridge Info) - already initialized\n");
         instances_number++;
         return AL_TRUE;
     }
@@ -57,12 +49,9 @@
     cache_pointer = 0;
     instances_number++;
     
-    // set the memory dimentsion and the increment width when reallocating
-    if (memorysize <= 0)
-        cache_size = 50;
-    else
-        cache_size = memorysize;
-
+    // initial memory size
+    cache_size = 50;
+    
     // open hardware device if present
     device = alcOpenDevice(NULL);
     sources_number = 16;
@@ -110,12 +99,8 @@
     }
 
     the_sounds = (al_sound_t *)Malloc (sizeof(al_sound_t) * cache_size);
-    for (i = 0; i < cache_size; i++) {
-        the_sounds[i].filename = NULL;
-        the_sounds[i].buffer = -1;
-        the_sounds[i].source_index = -1;
-        the_sounds[i].stats = 0;
-    }
+    for (i = 0; i < cache_size; i++)
+        the_sounds[i] = new_sound_el();
 
     alGetError();
     return AL_TRUE;
@@ -135,15 +120,16 @@
 
     instances_number--;
     if (instances_number > 0) {
+        // release memory only when last session ends
         return;
     }
     
-    //TODO: free other stuff also
-    for (i = 0; i < cache_size; i++)
-        alDeleteBuffers (1, &the_sounds[i].buffer);
+    for (i = 0; i < cache_size; i++) {
+        openal_unloadfile(i);
+    }
     free(the_sounds);
 
-    alSourceStopv	(sources_number, Sources);
+    alSourceStopv (sources_number, Sources);
     alDeleteSources (sources_number, Sources);
 
     free(Sources);
@@ -174,8 +160,8 @@
     ALenum format, error;
     ALsizei bitsize, freq;
     uint32_t fileformat;
-    al_sound_t soundData;
-    int len, i;
+    al_sound_t sound_data;
+    int len, i, index = -1;
     char *data;
     FILE *fp;
     
@@ -193,16 +179,24 @@
 #endif
             return i;
         }
+        // if we don't have memory available search for a free element
+        if (cache_pointer >= cache_size)
+            if (the_sounds[i].is_used == AL_FALSE)
+                index = i; 
     }
 
-    if (cache_pointer >= cache_size) {
-        fprintf(stderr,"(Bridge ERROR) - Cache size limit reached; consider allocating more space\n", filename);
-        return -2;
-    }
+    if (index == -1 && cache_pointer >= cache_size) {
+        fprintf(stderr,"(Bridge Info) - No free spots found; doubling cache size\n", filename);
+        cache_size *= 2;
+        the_sounds = (al_sound_t *)Realloc (the_sounds, sizeof(al_sound_t) * cache_size);
+        for (i = cache_size - 50; i < cache_size; i++) 
+            the_sounds[i] = new_sound_el();
+    } else 
+        index = ++cache_pointer;
+    
     
     // detect the file format, as written in the first 4 bytes of the header
     fp = Fopen (filename, "rb");
-
     if (fp == NULL) {
         fprintf(stderr,"(Bridge ERROR) - File %s not loaded\n", filename);
         return -3;
@@ -210,7 +204,6 @@
 
     error = fread (&fileformat, sizeof(uint32_t), 1, fp);
     fclose (fp);
-
     if (error < 0) {
         fprintf(stderr,"(Bridge ERROR) - File %s is too short\n", filename);
         return -4;
@@ -231,26 +224,26 @@
 
     if (error != 0) {
         fprintf(stderr,"(Bridge ERROR) - error loading file %s\n", filename);
-        free(data);
+        if(data)
+            free(data);
         return -6;
     }
 
-    alGenBuffers(1, &soundData.buffer);
-    soundData.filename = filename;
-    soundData.source_index = -1;
-    soundData.stats = 0;
+    // alGenBuffers happens here
+    sound_data = init_sound_el(filename);
     
     if (AL_NO_ERROR != alGetError()) {
-        fprintf(stderr,"(Bridge ERROR) - Failed to allocate memory for buffers\n");
+        fprintf(stderr,"(Bridge ERROR) - Failed to allocate memory for buffer %d\n", index);
+        free(data);
         return -5;
     }
     
     // copy pcm data in one buffer and free it
-    alBufferData(soundData.buffer, format, data, bitsize, freq);
+    alBufferData(sound_data.buffer, format, data, bitsize, freq);
     free(data);
 
     if (AL_NO_ERROR != alGetError()) {
-        fprintf(stderr,"(Bridge ERROR) - Failed to write data to buffers\n");
+        fprintf(stderr,"(Bridge ERROR) - Failed to write data to buffer %d\n", index);
         return -8;
     }
     
@@ -260,6 +253,21 @@
     fprintf(stderr,"(Bridge Info) - successfully loaded %s\n", filename);
 
     // returns the index of the source you just loaded, increments it and exits
-    the_sounds[cache_pointer] = soundData;
-    return cache_pointer++;
+    the_sounds[index] = sound_data;
+    return index;
 }
+
+
+void openal_unloadfile (uint32_t index) {
+    ALint state;
+
+    if (openal_ready() == AL_TRUE && index < cache_size && the_sounds[index].is_used == AL_TRUE) {
+        alGetSourcei (Sources[the_sounds[index].source_index], AL_SOURCE_STATE, &state);
+        if (state == AL_PLAYING || state == AL_PAUSED)
+            openal_stopsound(index);
+        
+        // free memory and 
+        alDeleteBuffers (1, &the_sounds[index].buffer);
+        the_sounds[index] = new_sound_el();
+    }
+}
\ No newline at end of file
--- a/misc/libopenalbridge/openalbridge.h	Sun Jun 20 22:46:23 2010 -0400
+++ b/misc/libopenalbridge/openalbridge.h	Mon Jun 21 16:08:24 2010 +0200
@@ -27,7 +27,7 @@
 #endif
 
     // init audio context and allocate memory
-    int openal_init               (int memorysize);
+    int openal_init               (void);
 
     // close audio subsytem and free memory
     void openal_close             (void);
--- a/misc/libopenalbridge/openalbridge_t.h	Sun Jun 20 22:46:23 2010 -0400
+++ b/misc/libopenalbridge/openalbridge_t.h	Mon Jun 21 16:08:24 2010 +0200
@@ -32,7 +32,7 @@
     const char *filename;       // name of the sound file
     ALuint buffer;              // actual sound content
     uint32_t source_index;      // index of the associated source
-    uint32_t stats;             // number of times the sound has been played
+    ALboolean is_used;          // tells if the element can be overwritten
 } al_sound_t;
 #pragma pack()
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/libopenalbridge/tester.c	Mon Jun 21 16:08:24 2010 +0200
@@ -0,0 +1,11 @@
+#include <stdio.h>
+#include "openalbridge.h"
+
+int main (int argc, int **argv) {
+    
+    openal_init();
+    
+    openal_close();
+    
+    return 0;
+}
\ No newline at end of file
--- a/misc/libopenalbridge/wrappers.c	Sun Jun 20 22:46:23 2010 -0400
+++ b/misc/libopenalbridge/wrappers.c	Mon Jun 21 16:08:24 2010 +0200
@@ -44,7 +44,7 @@
 }
 
 
-FILE *Fopen (const char *fname, char *mode)	{
+FILE *Fopen (const char *fname, char *mode) {
     FILE *fp;
 
     fp = fopen(fname,mode);
@@ -55,3 +55,24 @@
 }
 
 
+al_sound_t new_sound_el (void) {
+    al_sound_t sound;
+    
+    sound.filename = NULL;
+    sound.buffer = -1;
+    sound.source_index = -1;
+    sound.is_used = AL_FALSE;
+
+    return sound;
+}
+
+al_sound_t init_sound_el (const char *str) {
+    al_sound_t sound;
+    
+    sound.filename = str;
+    sound.source_index = -1;
+    sound.is_used = AL_TRUE;
+    alGenBuffers(1, &sound.buffer);
+
+    return sound;
+}
--- a/misc/libopenalbridge/wrappers.h	Sun Jun 20 22:46:23 2010 -0400
+++ b/misc/libopenalbridge/wrappers.h	Mon Jun 21 16:08:24 2010 +0200
@@ -26,5 +26,7 @@
 void *Realloc (void *aptr, size_t nbytes);
 FILE *Fopen (const char *fname, char *mode);
 void helper_fade (void *tmp);
+al_sound_t new_sound_el (void);
+al_sound_t init_sound_el (const char *str);
 
 #endif /*_OALB_WRAPPERS_H*/