celeron55's sound system initial framework
authorPerttu Ahola <celeron55@gmail.com>
Fri, 23 Mar 2012 10:05:17 +0000 (12:05 +0200)
committerPerttu Ahola <celeron55@gmail.com>
Sat, 24 Mar 2012 02:24:23 +0000 (04:24 +0200)
12 files changed:
cmake/Modules/FindVorbis.cmake [new file with mode: 0644]
src/CMakeLists.txt
src/client.cpp
src/client.h
src/game.cpp
src/gamedef.h
src/server.cpp
src/server.h
src/sound.cpp [new file with mode: 0644]
src/sound.h [new file with mode: 0644]
src/sound_openal.cpp [new file with mode: 0644]
src/sound_openal.h [new file with mode: 0644]

diff --git a/cmake/Modules/FindVorbis.cmake b/cmake/Modules/FindVorbis.cmake
new file mode 100644 (file)
index 0000000..e0cb2c1
--- /dev/null
@@ -0,0 +1,45 @@
+# - Find vorbis
+# Find the native vorbis includes and libraries
+#
+#  VORBIS_INCLUDE_DIR - where to find vorbis.h, etc.
+#  VORBIS_LIBRARIES   - List of libraries when using vorbis(file).
+#  VORBIS_FOUND       - True if vorbis found.
+
+if(NOT GP2XWIZ)
+    if(VORBIS_INCLUDE_DIR)
+        # Already in cache, be silent
+        set(VORBIS_FIND_QUIETLY TRUE)
+    endif(VORBIS_INCLUDE_DIR)
+    find_path(OGG_INCLUDE_DIR ogg/ogg.h)
+    find_path(VORBIS_INCLUDE_DIR vorbis/vorbisfile.h)
+    # MSVC built ogg/vorbis may be named ogg_static and vorbis_static
+    find_library(OGG_LIBRARY NAMES ogg ogg_static)
+    find_library(VORBIS_LIBRARY NAMES vorbis vorbis_static)
+    find_library(VORBISFILE_LIBRARY NAMES vorbisfile vorbisfile_static)
+    # Handle the QUIETLY and REQUIRED arguments and set VORBIS_FOUND
+    # to TRUE if all listed variables are TRUE.
+    include(FindPackageHandleStandardArgs)
+    find_package_handle_standard_args(VORBIS DEFAULT_MSG
+        OGG_INCLUDE_DIR VORBIS_INCLUDE_DIR
+        OGG_LIBRARY VORBIS_LIBRARY VORBISFILE_LIBRARY)
+else(NOT GP2XWIZ)
+    find_path(VORBIS_INCLUDE_DIR tremor/ivorbisfile.h)
+    find_library(VORBIS_LIBRARY NAMES vorbis_dec)
+    find_package_handle_standard_args(VORBIS DEFAULT_MSG
+        VORBIS_INCLUDE_DIR VORBIS_LIBRARY)
+endif(NOT GP2XWIZ)
+    
+if(VORBIS_FOUND)
+  if(NOT GP2XWIZ)
+     set(VORBIS_LIBRARIES ${VORBISFILE_LIBRARY} ${VORBIS_LIBRARY}
+           ${OGG_LIBRARY})
+  else(NOT GP2XWIZ)
+     set(VORBIS_LIBRARIES ${VORBIS_LIBRARY})
+  endif(NOT GP2XWIZ)
+else(VORBIS_FOUND)
+  set(VORBIS_LIBRARIES)
+endif(VORBIS_FOUND)
+
+mark_as_advanced(OGG_INCLUDE_DIR VORBIS_INCLUDE_DIR)
+mark_as_advanced(OGG_LIBRARY VORBIS_LIBRARY VORBISFILE_LIBRARY)
+
index b8f0341f098166f1b18b7b49956fdf3e55feaf87..b8eb9b4e34e90b348d44cda7876c4df35287da86 100644 (file)
@@ -29,6 +29,37 @@ else(GETTEXT_FOUND AND ENABLE_GETTEXT)
        message(STATUS "GetText disabled")
 endif(GETTEXT_FOUND AND ENABLE_GETTEXT)
 
+# user visible option to enable/disable audio
+OPTION(ENABLE_AUDIO "Enable audio" ON)
+
+# this is only set to 1 if audio is enabled _and_ available
+set(USE_AUDIO 0)
+
+if(ENABLE_AUDIO)
+       # Sound libraries
+       find_package(OpenAL)
+       if (OPENAL_FOUND)
+               find_package(Vorbis)
+               if (VORBIS_FOUND)
+                       set(USE_AUDIO 1)
+                       set(audio_SRCS sound.cpp sound_openal.cpp)
+                       set(AUDIO_INCLUDE_DIRS
+                               ${OPENAL_INCLUDE_DIR}
+                               ${VORBIS_INCLUDE_DIR}
+                               )
+                       set(AUDIO_LIBRARIES
+                               ${OPENAL_LIBRARY}
+                               ${VORBIS_LIBRARIES}
+                               )
+                       message(STATUS "Sound enabled")
+               else(VORBIS_FOUND)
+                       message(FATAL_ERROR "Sound enabled, but Vorbis libraries not found!")
+               endif(VORBIS_FOUND)
+       else(OPENAL_FOUND)
+               message(FATAL_ERROR "Sound enabled, but OpenAL not found!")
+       endif(OPENAL_FOUND)
+endif(ENABLE_AUDIO)
+
 if(NOT MSVC)
        set(USE_GPROF 0 CACHE BOOL "Use -pg flag for g++")
 endif()
@@ -159,6 +190,7 @@ endif()
 # Client sources
 set(minetest_SRCS
        ${common_SRCS}
+       ${audio_SRCS}
        sky.cpp
        clientmap.cpp
        content_cso.cpp
@@ -202,6 +234,7 @@ include_directories(
        ${CMAKE_BUILD_TYPE}
        ${PNG_INCLUDE_DIR}
        ${GETTEXT_INCLUDE_DIR}
+       ${AUDIO_INLCUDE_DIR}
        ${JTHREAD_INCLUDE_DIR}
        ${SQLITE3_INCLUDE_DIR}
        ${LUA_INCLUDE_DIR}
@@ -221,6 +254,7 @@ if(BUILD_CLIENT)
                ${PNG_LIBRARIES}
                ${X11_LIBRARIES}
                ${GETTEXT_LIBRARY}
+               ${AUDIO_LIBRARIES}
                ${JTHREAD_LIBRARY}
                ${SQLITE3_LIBRARY}
                ${LUA_LIBRARY}
index f446200a322060ef1a5d6fae8aaee64fb941a342..220fd04dda92186acc75186e3db6067f1408e1d9 100644 (file)
@@ -38,6 +38,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "sha1.h"
 #include "base64.h"
 #include "clientmap.h"
+#include "sound.h"
 
 static std::string getTextureCacheDir()
 {
@@ -2323,4 +2324,8 @@ u16 Client::allocateUnknownNodeId(const std::string &name)
        assert(0);
        return CONTENT_IGNORE;
 }
+ISoundManager* Client::getSoundManager()
+{
+       return &dummySoundManager;
+}
 
index 1903f3aa7b56c1bc6bf0dd9ec06ce20bf6383e6d..6063631dac7c9054fc6c82e717105a0a1bf6b8b4 100644 (file)
@@ -306,6 +306,7 @@ public:
        virtual ICraftDefManager* getCraftDefManager();
        virtual ITextureSource* getTextureSource();
        virtual u16 allocateUnknownNodeId(const std::string &name);
+       virtual ISoundManager* getSoundManager();
 
 private:
        
index 3e9f448d3dcef77d116947ec63538d040c0a8f51..35a0e2533498abc3c1479ca53637b58fc01cf004 100644 (file)
@@ -55,6 +55,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "quicktune_shortcutter.h"
 #include "clientmap.h"
 #include "sky.h"
+#include "sound.h"
 #include <list>
 
 /*
index 10ab0b0bcfb57e4ecd9cc71516366ca81302d950..91bcc0e6594bbe1cc46d1b397fe00a81b0c1804d 100644 (file)
@@ -27,6 +27,7 @@ class IItemDefManager;
 class INodeDefManager;
 class ICraftDefManager;
 class ITextureSource;
+class ISoundManager;
 
 /*
        An interface for fetching game-global definitions like tool and
@@ -46,6 +47,8 @@ public:
        // pointers in other threads than main thread will make things explode.
        virtual ITextureSource* getTextureSource()=0;
        
+       virtual ISoundManager* getSoundManager()=0;
+       
        // Used for keeping track of names/ids of unknown nodes
        virtual u16 allocateUnknownNodeId(const std::string &name)=0;
 
@@ -54,6 +57,7 @@ public:
        INodeDefManager* ndef(){return getNodeDefManager();}
        ICraftDefManager* cdef(){return getCraftDefManager();}
        ITextureSource* tsrc(){return getTextureSource();}
+       ISoundManager* sound(){return getSoundManager();}
 };
 
 #endif
index 7afb22846e867622624e78d15a2fcb136fd4ba9a..d762f2688d0d80dd458512ded1938065ca494cda 100644 (file)
@@ -49,6 +49,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "base64.h"
 #include "tool.h"
 #include "utility_string.h"
+#include "sound.h" // dummySoundManager
 
 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
 
@@ -4270,6 +4271,10 @@ u16 Server::allocateUnknownNodeId(const std::string &name)
 {
        return m_nodedef->allocateDummy(name);
 }
+ISoundManager* Server::getSoundManager()
+{
+       return &dummySoundManager;
+}
 
 IWritableItemDefManager* Server::getWritableItemDefManager()
 {
index 328c7fb9cc12bc1d31ce33af3aaaf9fba89be650..cdedd06d89aec7c5714b01cfb2b9b3ce48f290f7 100644 (file)
@@ -513,6 +513,7 @@ public:
        virtual ICraftDefManager* getCraftDefManager();
        virtual ITextureSource* getTextureSource();
        virtual u16 allocateUnknownNodeId(const std::string &name);
+       virtual ISoundManager* getSoundManager();
        
        IWritableItemDefManager* getWritableItemDefManager();
        IWritableNodeDefManager* getWritableNodeDefManager();
diff --git a/src/sound.cpp b/src/sound.cpp
new file mode 100644 (file)
index 0000000..fe8da73
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+Minetest-c55
+Copyright (C) 2012 celeron55, Perttu Ahola <celeron55@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "sound.h"
+
+// Global DummySoundManager singleton
+DummySoundManager dummySoundManager;
+
+
diff --git a/src/sound.h b/src/sound.h
new file mode 100644 (file)
index 0000000..6b20bbd
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+Minetest-c55
+Copyright (C) 2012 celeron55, Perttu Ahola <celeron55@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef SOUND_HEADER
+#define SOUND_HEADER
+
+#include "irrlichttypes.h"
+#include <string>
+#include <vector>
+#include <set>
+
+class OnDemandSoundFetcher
+{
+public:
+       virtual void getSoundFilenames(const std::string &name,
+                       std::set<std::string> &dst);
+};
+
+class ISoundManager
+{
+public:
+       virtual ~ISoundManager(){}
+       
+       // Multiple sounds can be loaded per name; when played, the sound
+       // should be chosen randomly from alternatives
+       // Return value determines success/failure
+       virtual bool loadSound(const std::string &name,
+                       const std::string &filepath) = 0;
+       virtual bool loadSound(const std::string &name,
+                       const std::vector<char> &filedata) = 0;
+
+       virtual void updateListener(v3f pos, v3f vel, v3f at, v3f up) = 0;
+       // playSound functions return -1 on failure, otherwise a handle to the
+       // sound
+       virtual int playSound(const std::string &name, int loopcount,
+                       float volume) = 0;
+       virtual int playSoundAt(const std::string &name, int loopcount,
+                       v3f pos, float volume) = 0;
+       virtual void stopSound(int sound) = 0;
+};
+
+class DummySoundManager: public ISoundManager
+{
+public:
+       virtual bool loadSound(const std::string &name,
+                       const std::string &filepath) {return true;}
+       virtual bool loadSound(const std::string &name,
+                       const std::vector<char> &filedata) {return true;}
+       void updateListener(v3f pos, v3f vel, v3f at, v3f up) {}
+       int playSound(const std::string &name, int loopcount,
+                       float volume) {return 0;}
+       int playSoundAt(const std::string &name, int loopcount,
+                       v3f pos, float volume) {return 0;}
+       void stopSound(int sound) {}
+};
+
+// Global DummySoundManager singleton
+extern DummySoundManager dummySoundManager;
+
+#endif
+
diff --git a/src/sound_openal.cpp b/src/sound_openal.cpp
new file mode 100644 (file)
index 0000000..9566f95
--- /dev/null
@@ -0,0 +1,322 @@
+/*
+Minetest-c55
+Copyright (C) 2012 celeron55, Perttu Ahola <celeron55@gmail.com>
+OpenAL support based on work by:
+Copyright (C) 2011 Sebastian 'Bahamada' Rühl
+Copyright (C) 2011 Cyriaque 'Cisoun' Skrapits <cysoun@gmail.com>
+Copyright (C) 2011 Giuseppe Bilotta <giuseppe.bilotta@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; ifnot, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "sound_openal.h"
+
+#if defined(_MSC_VER)
+       #include <al.h>
+       #include <alc.h>
+       #include <alext.h>
+#elif defined(__APPLE__)
+       #include <OpenAL/al.h>
+       #include <OpenAL/alc.h>
+       #include <OpenAL/alext.h>
+#else
+       #include <AL/al.h>
+       #include <AL/alc.h>
+       #include <AL/alext.h>
+#endif
+#include <vorbis/vorbisfile.h>
+#include "log.h"
+#include <map>
+#include <vector>
+#include "utility.h" // myrand()
+
+#define BUFFER_SIZE 30000
+
+static const char *alcErrorString(ALCenum err)
+{
+       switch (err) {
+       case ALC_NO_ERROR:
+               return "no error";
+       case ALC_INVALID_DEVICE:
+               return "invalid device";
+       case ALC_INVALID_CONTEXT:
+               return "invalid context";
+       case ALC_INVALID_ENUM:
+               return "invalid enum";
+       case ALC_INVALID_VALUE:
+               return "invalid value";
+       case ALC_OUT_OF_MEMORY:
+               return "out of memory";
+       default:
+               return "<unknown OpenAL error>";
+       }
+}
+
+static const char *alErrorString(ALenum err)
+{
+       switch (err) {
+       case AL_NO_ERROR:
+               return "no error";
+       case AL_INVALID_NAME:
+               return "invalid name";
+       case AL_INVALID_ENUM:
+               return "invalid enum";
+       case AL_INVALID_VALUE:
+               return "invalid value";
+       case AL_INVALID_OPERATION:
+               return "invalid operation";
+       case AL_OUT_OF_MEMORY:
+               return "out of memory";
+       default:
+               return "<unknown OpenAL error>";
+       }
+}
+
+void f3_set(ALfloat *f3, v3f v)
+{
+       f3[0] = v.X;
+       f3[1] = v.Y;
+       f3[2] = v.Z;
+}
+
+struct SoundBuffer
+{
+       ALenum  format;
+       ALsizei freq;
+       ALuint  bufferID;
+       std::vector<char> buffer;
+};
+
+SoundBuffer* loadOggFile(const std::string &filepath)
+{
+       int endian = 0; // 0 for Little-Endian, 1 for Big-Endian
+       int bitStream;
+       long bytes;
+       char array[BUFFER_SIZE]; // Local fixed size array
+       vorbis_info *pInfo;
+       OggVorbis_File oggFile;
+
+       // Try opening the given file
+       if(ov_fopen(filepath.c_str(), &oggFile) != 0)
+       {
+               infostream<<"Audio: Error opening "<<filepath<<" for decoding"<<std::endl;
+               return NULL;
+       }
+
+       SoundBuffer *snd = new SoundBuffer;
+
+       // Get some information about the OGG file
+       pInfo = ov_info(&oggFile, -1);
+
+       // Check the number of channels... always use 16-bit samples
+       if(pInfo->channels == 1)
+               snd->format = AL_FORMAT_MONO16;
+       else
+               snd->format = AL_FORMAT_STEREO16;
+
+       // The frequency of the sampling rate
+       snd->freq = pInfo->rate;
+
+       // Keep reading until all is read
+       do
+       {
+               // Read up to a buffer's worth of decoded sound data
+               bytes = ov_read(&oggFile, array, BUFFER_SIZE, endian, 2, 1, &bitStream);
+
+               if(bytes < 0)
+               {
+                       ov_clear(&oggFile);
+                       infostream<<"Audio: Error decoding "<<filepath<<std::endl;
+                       return NULL;
+               }
+
+               // Append to end of buffer
+               snd->buffer.insert(snd->buffer.end(), array, array + bytes);
+       } while (bytes > 0);
+
+       alGenBuffers(1, &snd->bufferID);
+       alBufferData(snd->bufferID, snd->format,
+                       &(snd->buffer[0]), snd->buffer.size(),
+                       snd->freq);
+
+       ALenum error = alGetError();
+
+       if(error != AL_NO_ERROR){
+               infostream<<"Audio: OpenAL error: "<<alErrorString(error)
+                               <<"preparing sound buffer"<<std::endl;
+       }
+
+       infostream<<"Audio file "<<filepath<<" loaded"<<std::endl;
+
+       // Clean up!
+       ov_clear(&oggFile);
+
+       return snd;
+}
+
+struct PlayingSound
+{
+};
+
+class OpenALSoundManager: public ISoundManager
+{
+private:
+       ALCdevice *m_device;
+       ALCcontext *m_context;
+       bool m_can_vorbis;
+       int m_next_id;
+       std::map<std::string, std::vector<SoundBuffer*> > m_buffers;
+       std::map<int, PlayingSound*> m_sounds_playing;
+public:
+       OpenALSoundManager():
+               m_device(NULL),
+               m_context(NULL),
+               m_can_vorbis(false),
+               m_next_id(1)
+       {
+               ALCenum error = ALC_NO_ERROR;
+               
+               infostream<<"Audio: Initializing..."<<std::endl;
+
+               m_device = alcOpenDevice(NULL);
+               if(!m_device){
+                       infostream<<"Audio: No audio device available, audio system "
+                               <<"not initialized"<<std::endl;
+                       return;
+               }
+
+               if(alcIsExtensionPresent(m_device, "EXT_vorbis")){
+                       infostream<<"Audio: Vorbis extension present"<<std::endl;
+                       m_can_vorbis = true;
+               } else{
+                       infostream<<"Audio: Vorbis extension NOT present"<<std::endl;
+                       m_can_vorbis = false;
+               }
+
+               m_context = alcCreateContext(m_device, NULL);
+               if(!m_context){
+                       error = alcGetError(m_device);
+                       infostream<<"Audio: Unable to initialize audio context, "
+                                       <<"aborting audio initialization ("<<alcErrorString(error)
+                                       <<")"<<std::endl;
+                       alcCloseDevice(m_device);
+                       m_device = NULL;
+                       return;
+               }
+
+               if(!alcMakeContextCurrent(m_context) ||
+                               (error = alcGetError(m_device) != ALC_NO_ERROR))
+               {
+                       infostream<<"Audio: Error setting audio context, aborting audio "
+                                       <<"initialization ("<<alcErrorString(error)<<")"<<std::endl;
+                       alcDestroyContext(m_context);
+                       m_context = NULL;
+                       alcCloseDevice(m_device);
+                       m_device = NULL;
+                       return;
+               }
+
+               alDistanceModel(AL_EXPONENT_DISTANCE);
+
+               infostream<<"Audio: Initialized: OpenAL "<<alGetString(AL_VERSION)
+                               <<", using "<<alcGetString(m_device, ALC_DEVICE_SPECIFIER)
+                               <<std::endl;
+       }
+
+       ~OpenALSoundManager()
+       {
+               infostream<<"Audio: Deinitializing..."<<std::endl;
+               // KABOOM!
+               // TODO: Clear SoundBuffers
+               alcMakeContextCurrent(NULL);
+               alcDestroyContext(m_context);
+               m_context = NULL;
+               alcCloseDevice(m_device);
+               m_device = NULL;
+               infostream<<"Audio: Deinitialized."<<std::endl;
+       }
+       
+       void addBuffer(const std::string &name, SoundBuffer *buf)
+       {
+               std::map<std::string, std::vector<SoundBuffer*> >::iterator i =
+                               m_buffers.find(name);
+               if(i != m_buffers.end()){
+                       i->second.push_back(buf);
+                       return;
+               }
+               std::vector<SoundBuffer*> bufs;
+               bufs.push_back(buf);
+               return;
+       }
+
+       SoundBuffer* getBuffer(const std::string &name)
+       {
+               std::map<std::string, std::vector<SoundBuffer*> >::iterator i =
+                               m_buffers.find(name);
+               if(i == m_buffers.end())
+                       return NULL;
+               std::vector<SoundBuffer*> &bufs = i->second;
+               int j = myrand() % bufs.size();
+               return bufs[j];
+       }
+
+       void updateListener(v3f pos, v3f vel, v3f at, v3f up)
+       {
+               ALfloat f[6];
+               f3_set(f, pos);
+               alListenerfv(AL_POSITION, f);
+               f3_set(f, vel);
+               alListenerfv(AL_VELOCITY, f);
+               f3_set(f, at);
+               f3_set(f+3, up);
+               alListenerfv(AL_ORIENTATION, f);
+       }
+       
+       bool loadSound(const std::string &name,
+                       const std::string &filepath)
+       {
+               SoundBuffer *buf = loadOggFile(filepath);
+               if(buf)
+                       addBuffer(name, buf);
+               return false;
+       }
+       bool loadSound(const std::string &name,
+                       const std::vector<char> &filedata)
+       {
+               errorstream<<"OpenALSoundManager: Loading from filedata not"
+                               " implemented"<<std::endl;
+               return false;
+       }
+
+       int playSound(const std::string &name, int loopcount,
+                       float volume)
+       {
+               return -1;
+       }
+       int playSoundAt(const std::string &name, int loopcount,
+                       v3f pos, float volume)
+       {
+               return -1;
+       }
+       void stopSound(int sound)
+       {
+       }
+};
+
+ISoundManager *createSoundManager()
+{
+       return new OpenALSoundManager();
+};
+
diff --git a/src/sound_openal.h b/src/sound_openal.h
new file mode 100644 (file)
index 0000000..de1ca30
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+Minetest-c55
+Copyright (C) 2012 celeron55, Perttu Ahola <celeron55@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef SOUND_OPENAL_HEADER
+#define SOUND_OPENAL_HEADER
+
+#include "sound.h"
+
+ISoundManager *createOpenALSoundManager();
+
+#endif
+