celeron55's sound system initial framework

This commit is contained in:
Perttu Ahola 2012-03-23 12:05:17 +02:00
parent 9dd78a8a45
commit c301e3c82a
12 changed files with 548 additions and 0 deletions

@ -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)

@ -29,6 +29,37 @@ else(GETTEXT_FOUND AND ENABLE_GETTEXT)
message(STATUS "GetText disabled") message(STATUS "GetText disabled")
endif(GETTEXT_FOUND AND ENABLE_GETTEXT) 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) if(NOT MSVC)
set(USE_GPROF 0 CACHE BOOL "Use -pg flag for g++") set(USE_GPROF 0 CACHE BOOL "Use -pg flag for g++")
endif() endif()
@ -159,6 +190,7 @@ endif()
# Client sources # Client sources
set(minetest_SRCS set(minetest_SRCS
${common_SRCS} ${common_SRCS}
${audio_SRCS}
sky.cpp sky.cpp
clientmap.cpp clientmap.cpp
content_cso.cpp content_cso.cpp
@ -202,6 +234,7 @@ include_directories(
${CMAKE_BUILD_TYPE} ${CMAKE_BUILD_TYPE}
${PNG_INCLUDE_DIR} ${PNG_INCLUDE_DIR}
${GETTEXT_INCLUDE_DIR} ${GETTEXT_INCLUDE_DIR}
${AUDIO_INLCUDE_DIR}
${JTHREAD_INCLUDE_DIR} ${JTHREAD_INCLUDE_DIR}
${SQLITE3_INCLUDE_DIR} ${SQLITE3_INCLUDE_DIR}
${LUA_INCLUDE_DIR} ${LUA_INCLUDE_DIR}
@ -221,6 +254,7 @@ if(BUILD_CLIENT)
${PNG_LIBRARIES} ${PNG_LIBRARIES}
${X11_LIBRARIES} ${X11_LIBRARIES}
${GETTEXT_LIBRARY} ${GETTEXT_LIBRARY}
${AUDIO_LIBRARIES}
${JTHREAD_LIBRARY} ${JTHREAD_LIBRARY}
${SQLITE3_LIBRARY} ${SQLITE3_LIBRARY}
${LUA_LIBRARY} ${LUA_LIBRARY}

@ -38,6 +38,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "sha1.h" #include "sha1.h"
#include "base64.h" #include "base64.h"
#include "clientmap.h" #include "clientmap.h"
#include "sound.h"
static std::string getTextureCacheDir() static std::string getTextureCacheDir()
{ {
@ -2323,4 +2324,8 @@ u16 Client::allocateUnknownNodeId(const std::string &name)
assert(0); assert(0);
return CONTENT_IGNORE; return CONTENT_IGNORE;
} }
ISoundManager* Client::getSoundManager()
{
return &dummySoundManager;
}

@ -306,6 +306,7 @@ public:
virtual ICraftDefManager* getCraftDefManager(); virtual ICraftDefManager* getCraftDefManager();
virtual ITextureSource* getTextureSource(); virtual ITextureSource* getTextureSource();
virtual u16 allocateUnknownNodeId(const std::string &name); virtual u16 allocateUnknownNodeId(const std::string &name);
virtual ISoundManager* getSoundManager();
private: private:

@ -55,6 +55,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "quicktune_shortcutter.h" #include "quicktune_shortcutter.h"
#include "clientmap.h" #include "clientmap.h"
#include "sky.h" #include "sky.h"
#include "sound.h"
#include <list> #include <list>
/* /*

@ -27,6 +27,7 @@ class IItemDefManager;
class INodeDefManager; class INodeDefManager;
class ICraftDefManager; class ICraftDefManager;
class ITextureSource; class ITextureSource;
class ISoundManager;
/* /*
An interface for fetching game-global definitions like tool and 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. // pointers in other threads than main thread will make things explode.
virtual ITextureSource* getTextureSource()=0; virtual ITextureSource* getTextureSource()=0;
virtual ISoundManager* getSoundManager()=0;
// Used for keeping track of names/ids of unknown nodes // Used for keeping track of names/ids of unknown nodes
virtual u16 allocateUnknownNodeId(const std::string &name)=0; virtual u16 allocateUnknownNodeId(const std::string &name)=0;
@ -54,6 +57,7 @@ public:
INodeDefManager* ndef(){return getNodeDefManager();} INodeDefManager* ndef(){return getNodeDefManager();}
ICraftDefManager* cdef(){return getCraftDefManager();} ICraftDefManager* cdef(){return getCraftDefManager();}
ITextureSource* tsrc(){return getTextureSource();} ITextureSource* tsrc(){return getTextureSource();}
ISoundManager* sound(){return getSoundManager();}
}; };
#endif #endif

@ -49,6 +49,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "base64.h" #include "base64.h"
#include "tool.h" #include "tool.h"
#include "utility_string.h" #include "utility_string.h"
#include "sound.h" // dummySoundManager
#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" #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); return m_nodedef->allocateDummy(name);
} }
ISoundManager* Server::getSoundManager()
{
return &dummySoundManager;
}
IWritableItemDefManager* Server::getWritableItemDefManager() IWritableItemDefManager* Server::getWritableItemDefManager()
{ {

@ -513,6 +513,7 @@ public:
virtual ICraftDefManager* getCraftDefManager(); virtual ICraftDefManager* getCraftDefManager();
virtual ITextureSource* getTextureSource(); virtual ITextureSource* getTextureSource();
virtual u16 allocateUnknownNodeId(const std::string &name); virtual u16 allocateUnknownNodeId(const std::string &name);
virtual ISoundManager* getSoundManager();
IWritableItemDefManager* getWritableItemDefManager(); IWritableItemDefManager* getWritableItemDefManager();
IWritableNodeDefManager* getWritableNodeDefManager(); IWritableNodeDefManager* getWritableNodeDefManager();

25
src/sound.cpp Normal file

@ -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;

77
src/sound.h Normal file

@ -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

322
src/sound_openal.cpp Normal file

@ -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();
};

28
src/sound_openal.h Normal file

@ -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