diff options
author | Perttu Ahola <celeron55@gmail.com> | 2012-03-23 12:05:17 +0200 |
---|---|---|
committer | Perttu Ahola <celeron55@gmail.com> | 2012-03-24 04:24:23 +0200 |
commit | c301e3c82af4ad384f4baaa8244b3a5f26da5213 (patch) | |
tree | a23bfb49606347f678e81eb7f5fe1eef985d9d02 | |
parent | 9dd78a8a451a7200bfb68b06e034ffb88e32bcd3 (diff) | |
download | minetest-c301e3c82af4ad384f4baaa8244b3a5f26da5213.tar.gz minetest-c301e3c82af4ad384f4baaa8244b3a5f26da5213.tar.bz2 minetest-c301e3c82af4ad384f4baaa8244b3a5f26da5213.zip |
celeron55's sound system initial framework
-rw-r--r-- | cmake/Modules/FindVorbis.cmake | 45 | ||||
-rw-r--r-- | src/CMakeLists.txt | 34 | ||||
-rw-r--r-- | src/client.cpp | 5 | ||||
-rw-r--r-- | src/client.h | 1 | ||||
-rw-r--r-- | src/game.cpp | 1 | ||||
-rw-r--r-- | src/gamedef.h | 4 | ||||
-rw-r--r-- | src/server.cpp | 5 | ||||
-rw-r--r-- | src/server.h | 1 | ||||
-rw-r--r-- | src/sound.cpp | 25 | ||||
-rw-r--r-- | src/sound.h | 77 | ||||
-rw-r--r-- | src/sound_openal.cpp | 322 | ||||
-rw-r--r-- | src/sound_openal.h | 28 |
12 files changed, 548 insertions, 0 deletions
diff --git a/cmake/Modules/FindVorbis.cmake b/cmake/Modules/FindVorbis.cmake new file mode 100644 index 000000000..e0cb2c1ac --- /dev/null +++ b/cmake/Modules/FindVorbis.cmake @@ -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) + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b8f0341f0..b8eb9b4e3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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} diff --git a/src/client.cpp b/src/client.cpp index f446200a3..220fd04dd 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -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; +} diff --git a/src/client.h b/src/client.h index 1903f3aa7..6063631da 100644 --- a/src/client.h +++ b/src/client.h @@ -306,6 +306,7 @@ public: virtual ICraftDefManager* getCraftDefManager(); virtual ITextureSource* getTextureSource(); virtual u16 allocateUnknownNodeId(const std::string &name); + virtual ISoundManager* getSoundManager(); private: diff --git a/src/game.cpp b/src/game.cpp index 3e9f448d3..35a0e2533 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -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> /* diff --git a/src/gamedef.h b/src/gamedef.h index 10ab0b0bc..91bcc0e65 100644 --- a/src/gamedef.h +++ b/src/gamedef.h @@ -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 diff --git a/src/server.cpp b/src/server.cpp index 7afb22846..d762f2688 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -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() { diff --git a/src/server.h b/src/server.h index 328c7fb9c..cdedd06d8 100644 --- a/src/server.h +++ b/src/server.h @@ -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 index 000000000..fe8da73b3 --- /dev/null +++ b/src/sound.cpp @@ -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 index 000000000..6b20bbd32 --- /dev/null +++ b/src/sound.h @@ -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 index 000000000..9566f95c2 --- /dev/null +++ b/src/sound_openal.cpp @@ -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 index 000000000..de1ca3056 --- /dev/null +++ b/src/sound_openal.h @@ -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 + |