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 /* 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 + +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 + +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 +#include +#include + +class OnDemandSoundFetcher +{ +public: + virtual void getSoundFilenames(const std::string &name, + std::set &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 &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 &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 +OpenAL support based on work by: +Copyright (C) 2011 Sebastian 'Bahamada' Rühl +Copyright (C) 2011 Cyriaque 'Cisoun' Skrapits +Copyright (C) 2011 Giuseppe Bilotta + +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 + #include + #include +#elif defined(__APPLE__) + #include + #include + #include +#else + #include + #include + #include +#endif +#include +#include "log.h" +#include +#include +#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 ""; + } +} + +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 ""; + } +} + +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 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 "<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 "<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: "< > m_buffers; + std::map 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..."< >::iterator i = + m_buffers.find(name); + if(i != m_buffers.end()){ + i->second.push_back(buf); + return; + } + std::vector bufs; + bufs.push_back(buf); + return; + } + + SoundBuffer* getBuffer(const std::string &name) + { + std::map >::iterator i = + m_buffers.find(name); + if(i == m_buffers.end()) + return NULL; + std::vector &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 &filedata) + { + errorstream<<"OpenALSoundManager: Loading from filedata not" + " implemented"< + +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 +