diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b8eb9b4e3..c852d5b0a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -42,7 +42,7 @@ if(ENABLE_AUDIO) find_package(Vorbis) if (VORBIS_FOUND) set(USE_AUDIO 1) - set(audio_SRCS sound.cpp sound_openal.cpp) + set(audio_SRCS sound_openal.cpp) set(AUDIO_INCLUDE_DIRS ${OPENAL_INCLUDE_DIR} ${VORBIS_INCLUDE_DIR} @@ -125,6 +125,7 @@ configure_file( ) set(common_SRCS + sound.cpp quicktune.cpp subgame.cpp inventorymanager.cpp @@ -234,7 +235,7 @@ include_directories( ${CMAKE_BUILD_TYPE} ${PNG_INCLUDE_DIR} ${GETTEXT_INCLUDE_DIR} - ${AUDIO_INLCUDE_DIR} + ${AUDIO_INLCUDE_DIRS} ${JTHREAD_INCLUDE_DIR} ${SQLITE3_INCLUDE_DIR} ${LUA_INCLUDE_DIR} diff --git a/src/camera.cpp b/src/camera.cpp index 2a15386d9..8dad8dc63 100644 --- a/src/camera.cpp +++ b/src/camera.cpp @@ -30,8 +30,11 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "settings.h" #include "itemdef.h" // For wield visualization #include "noise.h" // easeCurve +#include "gamedef.h" +#include "sound.h" -Camera::Camera(scene::ISceneManager* smgr, MapDrawControl& draw_control): +Camera::Camera(scene::ISceneManager* smgr, MapDrawControl& draw_control, + IGameDef *gamedef): m_smgr(smgr), m_playernode(NULL), m_headnode(NULL), @@ -42,6 +45,7 @@ Camera::Camera(scene::ISceneManager* smgr, MapDrawControl& draw_control): m_wieldlight(0), m_draw_control(draw_control), + m_gamedef(gamedef), m_camera_position(0,0,0), m_camera_direction(0,0,0), @@ -168,7 +172,13 @@ void Camera::step(f32 dtime) } else { + float was = m_view_bobbing_anim; m_view_bobbing_anim = my_modf(m_view_bobbing_anim + offset); + bool step = (was == 0 || + (was < 0.5f && m_view_bobbing_anim >= 0.5f) || + (was > 0.5f && m_view_bobbing_anim <= 0.5f)); + if(step) + m_gamedef->sound()->playSound("default_grass_walk", false, 1.0); } } @@ -180,6 +190,7 @@ void Camera::step(f32 dtime) { m_digging_anim = 0; m_digging_button = -1; + m_gamedef->sound()->playSound("dig", false, 1.0); } } } @@ -481,9 +492,9 @@ void Camera::setDigging(s32 button) m_digging_button = button; } -void Camera::wield(const ItemStack &item, IGameDef *gamedef) +void Camera::wield(const ItemStack &item) { - IItemDefManager *idef = gamedef->idef(); + IItemDefManager *idef = m_gamedef->idef(); scene::IMesh *wield_mesh = item.getDefinition(idef).wield_mesh; if(wield_mesh) { diff --git a/src/camera.h b/src/camera.h index 168863c3e..0180d99e2 100644 --- a/src/camera.h +++ b/src/camera.h @@ -39,7 +39,8 @@ class IGameDef; class Camera { public: - Camera(scene::ISceneManager* smgr, MapDrawControl& draw_control); + Camera(scene::ISceneManager* smgr, MapDrawControl& draw_control, + IGameDef *gamedef); ~Camera(); // Get player scene node. @@ -116,7 +117,7 @@ public: void setDigging(s32 button); // Replace the wielded item mesh - void wield(const ItemStack &item, IGameDef *gamedef); + void wield(const ItemStack &item); // Draw the wielded tool. // This has to happen *after* the main scene is drawn. @@ -136,6 +137,8 @@ private: // draw control MapDrawControl& m_draw_control; + + IGameDef *m_gamedef; // Absolute camera position v3f m_camera_position; diff --git a/src/client.cpp b/src/client.cpp index 220fd04dd..cb6d2075e 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -225,11 +225,13 @@ Client::Client( MapDrawControl &control, IWritableTextureSource *tsrc, IWritableItemDefManager *itemdef, - IWritableNodeDefManager *nodedef + IWritableNodeDefManager *nodedef, + ISoundManager *sound ): m_tsrc(tsrc), m_itemdef(itemdef), m_nodedef(nodedef), + m_sound(sound), m_mesh_update_thread(this), m_env( new ClientMap(this, this, control, @@ -2326,6 +2328,6 @@ u16 Client::allocateUnknownNodeId(const std::string &name) } ISoundManager* Client::getSoundManager() { - return &dummySoundManager; + return m_sound; } diff --git a/src/client.h b/src/client.h index 6063631da..2a1681e92 100644 --- a/src/client.h +++ b/src/client.h @@ -174,7 +174,8 @@ public: MapDrawControl &control, IWritableTextureSource *tsrc, IWritableItemDefManager *itemdef, - IWritableNodeDefManager *nodedef + IWritableNodeDefManager *nodedef, + ISoundManager *sound ); ~Client(); @@ -333,6 +334,7 @@ private: IWritableTextureSource *m_tsrc; IWritableItemDefManager *m_itemdef; IWritableNodeDefManager *m_nodedef; + ISoundManager *m_sound; MeshUpdateThread m_mesh_update_thread; ClientEnvironment m_env; con::Connection m_con; diff --git a/src/game.cpp b/src/game.cpp index 35a0e2533..1b24df59b 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -56,6 +56,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "clientmap.h" #include "sky.h" #include "sound.h" +#if USE_AUDIO + #include "sound_openal.h" +#endif #include /* @@ -823,6 +826,27 @@ void the_game( IWritableItemDefManager *itemdef = createItemDefManager(); // Create node definition manager IWritableNodeDefManager *nodedef = createNodeDefManager(); + + // Sound manager + ISoundManager *sound = NULL; + bool sound_is_dummy = false; +#if USE_AUDIO + infostream<<"Attempting to use OpenAL audio"<loadSound("default_grass_walk", porting::path_share + DIR_DELIM + + "sounds" + DIR_DELIM + "default_grass_walk3_mono.ogg"); + //sound->playSound("default_grass_walk", false, 1.0); + //sound->playSoundAt("default_grass_walk", true, 1.0, v3f(0,10,0)*BS); // Add chat log output for errors to be shown in chat LogOutputBuffer chat_log_error_buf(LMT_ERROR); @@ -855,7 +879,7 @@ void the_game( MapDrawControl draw_control; Client client(device, playername.c_str(), password, draw_control, - tsrc, itemdef, nodedef); + tsrc, itemdef, nodedef, sound); // Client acts as our GameDef IGameDef *gamedef = &client; @@ -1020,7 +1044,7 @@ void the_game( /* Create the camera node */ - Camera camera(smgr, draw_control); + Camera camera(smgr, draw_control, gamedef); if (!camera.successfullyCreated(error_message)) return; @@ -1923,9 +1947,12 @@ void the_game( client.getEnv().getClientMap().updateCamera(camera_position, camera_direction, camera_fov); } - - //timer2.stop(); - //TimeTaker //timer3("//timer3"); + + // Update sound listener + sound->updateListener(camera.getCameraNode()->getPosition(), + v3f(0,0,0), // velocity + camera.getCameraNode()->getTarget(), + camera.getCameraNode()->getUpVector()); /* Calculate what block is the crosshair pointing to @@ -2536,7 +2563,7 @@ void the_game( ItemStack item; if(mlist != NULL) item = mlist->getItem(client.getPlayerItem()); - camera.wield(item, gamedef); + camera.wield(item); } /* @@ -2729,10 +2756,12 @@ void the_game( // Client scope (client is destructed before destructing *def and tsrc) }while(0); - - delete tsrc; + + if(!sound_is_dummy) + delete sound; delete nodedef; delete itemdef; + delete tsrc; } diff --git a/src/sound.h b/src/sound.h index 6b20bbd32..72764345e 100644 --- a/src/sound.h +++ b/src/sound.h @@ -28,8 +28,9 @@ with this program; if not, write to the Free Software Foundation, Inc., class OnDemandSoundFetcher { public: - virtual void getSoundFilenames(const std::string &name, - std::set &dst); + virtual void fetchSounds(const std::string &name, + std::set &dst_paths, + std::set > &dst_datas) = 0; }; class ISoundManager @@ -48,10 +49,10 @@ public: 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, + virtual int playSound(const std::string &name, bool loop, float volume) = 0; - virtual int playSoundAt(const std::string &name, int loopcount, - v3f pos, float volume) = 0; + virtual int playSoundAt(const std::string &name, bool loop, + float volume, v3f pos) = 0; virtual void stopSound(int sound) = 0; }; @@ -63,10 +64,10 @@ public: 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, + int playSound(const std::string &name, bool loop, float volume) {return 0;} - int playSoundAt(const std::string &name, int loopcount, - v3f pos, float volume) {return 0;} + int playSoundAt(const std::string &name, bool loop, + float volume, v3f pos) {return 0;} void stopSound(int sound) {} }; diff --git a/src/sound_openal.cpp b/src/sound_openal.cpp index 9566f95c2..4f056888b 100644 --- a/src/sound_openal.cpp +++ b/src/sound_openal.cpp @@ -84,6 +84,14 @@ static const char *alErrorString(ALenum err) } } +static ALenum warn_if_error(ALenum err, const char *desc) +{ + if(err == AL_NO_ERROR) + return err; + errorstream<<"WARNING: "< buffer; }; @@ -146,8 +154,8 @@ SoundBuffer* loadOggFile(const std::string &filepath) snd->buffer.insert(snd->buffer.end(), array, array + bytes); } while (bytes > 0); - alGenBuffers(1, &snd->bufferID); - alBufferData(snd->bufferID, snd->format, + alGenBuffers(1, &snd->buffer_id); + alBufferData(snd->buffer_id, snd->format, &(snd->buffer[0]), snd->buffer.size(), snd->freq); @@ -168,19 +176,24 @@ SoundBuffer* loadOggFile(const std::string &filepath) struct PlayingSound { + ALuint source_id; + bool loop; }; class OpenALSoundManager: public ISoundManager { private: + OnDemandSoundFetcher *m_fetcher; ALCdevice *m_device; ALCcontext *m_context; bool m_can_vorbis; int m_next_id; std::map > m_buffers; std::map m_sounds_playing; + v3f m_listener_pos; public: - OpenALSoundManager(): + OpenALSoundManager(OnDemandSoundFetcher *fetcher): + m_fetcher(fetcher), m_device(NULL), m_context(NULL), m_can_vorbis(false), @@ -258,6 +271,7 @@ public: } std::vector bufs; bufs.push_back(buf); + m_buffers[name] = bufs; return; } @@ -272,18 +286,6 @@ public: 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) { @@ -300,23 +302,186 @@ public: return false; } - int playSound(const std::string &name, int loopcount, + PlayingSound* createPlayingSound(SoundBuffer *buf, bool loop, float volume) { - return -1; + infostream<<"OpenALSoundManager: Creating playing sound"<source_id); + alSourcei(sound->source_id, AL_BUFFER, buf->buffer_id); + alSourcei(sound->source_id, AL_SOURCE_RELATIVE, true); + alSource3f(sound->source_id, AL_POSITION, 0, 0, 0); + alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0); + alSourcei(sound->source_id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE); + volume = MYMAX(0.0, volume); + alSourcef(sound->source_id, AL_GAIN, volume); + alSourcePlay(sound->source_id); + warn_if_error(alGetError(), "createPlayingSound"); + return sound; } - int playSoundAt(const std::string &name, int loopcount, - v3f pos, float volume) + + PlayingSound* createPlayingSoundAt(SoundBuffer *buf, bool loop, + float volume, v3f pos) { - return -1; + infostream<<"OpenALSoundManager: Creating positional playing sound" + <source_id); + alSourcei(sound->source_id, AL_BUFFER, buf->buffer_id); + alSourcei(sound->source_id, AL_SOURCE_RELATIVE, false); + alSource3f(sound->source_id, AL_POSITION, pos.X, pos.Y, pos.Z); + alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0); + //alSourcef(sound->source_id, AL_ROLLOFF_FACTOR, 0.7); + alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 30.0); + alSourcei(sound->source_id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE); + volume = MYMAX(0.0, volume); + alSourcef(sound->source_id, AL_GAIN, volume); + alSourcePlay(sound->source_id); + warn_if_error(alGetError(), "createPlayingSoundAt"); + return sound; + } + + int playSoundRaw(SoundBuffer *buf, bool loop, float volume) + { + assert(buf); + PlayingSound *sound = createPlayingSound(buf, loop, volume); + if(!sound) + return -1; + int id = m_next_id++; + m_sounds_playing[id] = sound; + return id; + } + + int playSoundRawAt(SoundBuffer *buf, bool loop, float volume, v3f pos) + { + assert(buf); + PlayingSound *sound = createPlayingSoundAt(buf, loop, volume, pos); + if(!sound) + return -1; + int id = m_next_id++; + m_sounds_playing[id] = sound; + return id; + } + + void deleteSound(int id) + { + std::map::iterator i = + m_sounds_playing.find(id); + if(i == m_sounds_playing.end()) + return; + PlayingSound *sound = i->second; + + alDeleteSources(1, &sound->source_id); + + delete sound; + m_sounds_playing.erase(id); + } + + /* If buffer does not exist, consult the fetcher */ + SoundBuffer* getFetchBuffer(const std::string name) + { + SoundBuffer *buf = getBuffer(name); + if(buf) + return buf; + if(!m_fetcher) + return NULL; + std::set paths; + std::set > datas; + m_fetcher->fetchSounds(name, paths, datas); + for(std::set::iterator i = paths.begin(); + i != paths.end(); i++){ + loadSound(name, *i); + } + for(std::set >::iterator i = datas.begin(); + i != datas.end(); i++){ + loadSound(name, *i); + } + return getBuffer(name); + } + + // Remove stopped sounds + void maintain() + { + verbosestream<<"OpenALSoundManager::maintain(): " + < del_list; + for(std::map::iterator + i = m_sounds_playing.begin(); + i != m_sounds_playing.end(); i++) + { + int id = i->first; + PlayingSound *sound = i->second; + // If not playing, remove it + { + ALint state; + alGetSourcei(sound->source_id, AL_SOURCE_STATE, &state); + if(state != AL_PLAYING){ + del_list.insert(id); + } + } + } + if(del_list.size() != 0) + verbosestream<<"OpenALSoundManager::maintain(): deleting " + <::iterator i = del_list.begin(); + i != del_list.end(); i++) + { + deleteSound(*i); + } + } + + /* Interface */ + + void updateListener(v3f pos, v3f vel, v3f at, v3f up) + { + m_listener_pos = pos; + alListener3f(AL_POSITION, pos.X, pos.Y, pos.Z); + alListener3f(AL_VELOCITY, vel.X, vel.Y, vel.Z); + ALfloat f[6]; + f3_set(f, at); + f3_set(f+3, up); + alListenerfv(AL_ORIENTATION, f); + warn_if_error(alGetError(), "updateListener"); + } + + int playSound(const std::string &name, bool loop, float volume) + { + maintain(); + SoundBuffer *buf = getFetchBuffer(name); + if(!buf){ + infostream<<"OpenALSoundManager: \""<