diff --git a/data/sounds/env_water.ogg b/data/sounds/env_water.ogg new file mode 100644 index 0000000..2078dfe Binary files /dev/null and b/data/sounds/env_water.ogg differ diff --git a/src/client.cpp b/src/client.cpp index fc2c0d3..d61c853 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -151,20 +151,9 @@ void * MeshUpdateThread::Thread() BEGIN_DEBUG_EXCEPTION_HANDLER - while(getRun()) - { - /*// Wait for output queue to flush. - // Allow 2 in queue, this makes less frametime jitter. - // Umm actually, there is no much difference - if(m_queue_out.size() >= 2) - { - sleep_ms(3); - continue; - }*/ - + while (getRun()) { QueuedMeshUpdate *q = m_queue_in.pop(); - if(q == NULL) - { + if (q == NULL) { sleep_ms(3); continue; } @@ -178,10 +167,6 @@ void * MeshUpdateThread::Thread() r.mesh = mesh_new; r.ack_block_to_server = q->ack_block_to_server; - /*infostream<<"MeshUpdateThread: Processed " - <<"("<p.X<<","<p.Y<<","<p.Z<<")" - <fill(getDayNightRatio(), b); } + data->m_sounds = &b->m_sounds; + // Debug wait //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10); diff --git a/src/content_mapblock.cpp b/src/content_mapblock.cpp index 4b4dea5..0cfcddf 100644 --- a/src/content_mapblock.cpp +++ b/src/content_mapblock.cpp @@ -31,6 +31,7 @@ #include "settings.h" #include "environment.h" #include "nodemetadata.h" +#include "sound.h" #ifndef SERVER // Create a cuboid. @@ -484,6 +485,32 @@ void mapblock_mesh_generate_special(MeshMakeData *data, data->m_temp_mods.get(p,&mod); selected = (mod == NODEMOD_SELECTION); + if (g_sound) { + std::string snd = content_features(n).sound_ambient; + std::map::iterator i = data->m_sounds->find(p); + if (snd != "") { + bool add_sound = true; + if (i != data->m_sounds->end()) { + if (i->second.name == snd && g_sound->soundExists(i->second.id)) { + add_sound = false; + }else{ + g_sound->stopSound(i->second.id); + } + } + if (add_sound) { + v3f pf = intToFloat(p+blockpos_nodes,BS); + MapBlockSound bsnd; + bsnd.id = g_sound->playSoundAt(snd,true,pf, true); + bsnd.name = snd; + if (bsnd.id > 0) + (*data->m_sounds)[p] = bsnd; + } + }else if (i != data->m_sounds->end()) { + g_sound->stopSound(i->second.id); + data->m_sounds->erase(i); + } + } + /* Add torches to mesh */ diff --git a/src/content_mapnode.cpp b/src/content_mapnode.cpp index 5eb9ae1..6543d70 100644 --- a/src/content_mapnode.cpp +++ b/src/content_mapnode.cpp @@ -2078,6 +2078,7 @@ void content_mapnode_init(bool repeat) f->setAllTextureTypes(MATERIAL_ALPHA_VERTEX); f->post_effect_color = video::SColor(64, 100, 100, 200); #endif + f->sound_ambient = "env-water"; f->suffocation_per_second = 2; i = CONTENT_WATERSOURCE; @@ -2197,6 +2198,7 @@ void content_mapnode_init(bool repeat) f->liquid_alternative_source = CONTENT_LAVASOURCE; f->liquid_viscosity = LAVA_VISC; f->damage_per_second = 4*2; + f->sound_ambient = "env-lava"; #ifndef SERVER f->post_effect_color = video::SColor(192, 255, 64, 0); #endif diff --git a/src/content_mapnode_special.cpp b/src/content_mapnode_special.cpp index b22cb24..faa70a1 100644 --- a/src/content_mapnode_special.cpp +++ b/src/content_mapnode_special.cpp @@ -955,6 +955,7 @@ void content_mapnode_special(bool repeat) f->diggable = false; f->buildable_to = true; f->damage_per_second = 8; + f->sound_ambient = "env-fire"; #ifndef SERVER f->post_effect_color = video::SColor(192, 255, 64, 0); #endif diff --git a/src/content_mob.cpp b/src/content_mob.cpp index b4f83b6..73caf40 100644 --- a/src/content_mob.cpp +++ b/src/content_mob.cpp @@ -219,6 +219,7 @@ void content_mob_init() f->punch_action = MPA_PICKUP; f->dropped_item = std::string("CraftItem2 ")+itos(CONTENT_CRAFTITEM_RAT)+" 1"; f->motion = MM_WANDER; + f->moves_silently = true; f->spawn_on = CONTENT_STONE; f->spawn_in = CONTENT_AIR; f->spawn_max_height = -10; diff --git a/src/mapblock.cpp b/src/mapblock.cpp index bd9291d..177a632 100644 --- a/src/mapblock.cpp +++ b/src/mapblock.cpp @@ -29,6 +29,9 @@ #include "main.h" #include "light.h" #include +#ifndef SERVER +#include "sound.h" +#endif /* MapBlock @@ -65,15 +68,19 @@ MapBlock::~MapBlock() { JMutexAutoLock lock(mesh_mutex); - if(mesh) - { + if (mesh) { delete mesh; mesh = NULL; } + if (g_sound) { + for (std::map::iterator i = m_sounds.begin(); i != m_sounds.end(); i++) { + g_sound->stopSound(i->second.id); + } + } } #endif - if(data) + if (data) delete[] data; } @@ -126,6 +133,7 @@ void MapBlock::updateMesh(u32 daynight_ratio, Environment *env, v3s16 camera_off MeshMakeData data; data.m_env = env; data.fill(daynight_ratio, this); + data.m_sounds = &m_sounds; MapBlockMesh *mesh_new = new MapBlockMesh(&data, camera_offset); diff --git a/src/mapblock.h b/src/mapblock.h index 4e044ca..79fa6a9 100644 --- a/src/mapblock.h +++ b/src/mapblock.h @@ -584,6 +584,8 @@ public: #ifndef SERVER // Only on client MapBlockMesh *mesh; JMutex mesh_mutex; + + std::map m_sounds; #endif NodeMetadataList m_node_metadata; diff --git a/src/mapblock_mesh.cpp b/src/mapblock_mesh.cpp index ad66f9e..1b66c1b 100644 --- a/src/mapblock_mesh.cpp +++ b/src/mapblock_mesh.cpp @@ -34,6 +34,7 @@ #include "profiler.h" #include "mesh.h" #include "base64.h" +#include "sound.h" void MeshMakeData::fill(u32 daynight_ratio, MapBlock *block) { diff --git a/src/mapblock_mesh.h b/src/mapblock_mesh.h index 2888287..6cb56eb 100644 --- a/src/mapblock_mesh.h +++ b/src/mapblock_mesh.h @@ -120,6 +120,12 @@ u8 getSmoothLight(v3s16 p, v3s16 corner, VoxelManipulator &vmanip, u32 daynight_ class MapBlock; class Environment; +struct MapBlockSound +{ + int id; + std::string name; +}; + struct MeshMakeData { u32 m_daynight_ratio; @@ -128,6 +134,8 @@ struct MeshMakeData v3s16 m_blockpos; Environment *m_env; + std::map *m_sounds; + /* Copy central data directly from block, and other data from parent of block. diff --git a/src/mapnode.h b/src/mapnode.h index b920c9b..8ea8d7e 100644 --- a/src/mapnode.h +++ b/src/mapnode.h @@ -402,6 +402,8 @@ struct ContentFeatures std::string sound_dig; // Sound played when the node is placed std::string sound_place; + // Sound played by the node always + std::string sound_ambient; // If the content is liquid, this is the flowing version of the liquid. // If content is liquid, this is the same content. @@ -500,6 +502,7 @@ struct ContentFeatures sound_step = ""; sound_dig = ""; sound_place = ""; + sound_ambient = ""; liquid_alternative_flowing = CONTENT_IGNORE; liquid_alternative_source = CONTENT_IGNORE; liquid_viscosity = 0; diff --git a/src/sound.cpp b/src/sound.cpp index b55a186..6ff6c9d 100644 --- a/src/sound.cpp +++ b/src/sound.cpp @@ -35,6 +35,8 @@ ISoundManager *g_sound = NULL; void init_sounds(ISoundManager *sound) { + // sounds must be mono sounds, stereo will not work right! + // exceptions: background music // walking // CMT_DIRT sound->loadSound("dirt-step-left","step_dirt.1.ogg"); @@ -95,12 +97,12 @@ void init_sounds(ISoundManager *sound) sound->loadSound("open-chest","open_chest.ogg"); // environment and node sounds - // these must be mono sounds, stereo will not work right! sound->loadSound("env-piston","env_piston.ogg"); sound->loadSound("env-dooropen","env_dooropen.ogg"); sound->loadSound("env-doorclose","env_doorclose.ogg"); sound->loadSound("env-fire","env_fire.ogg"); - sound->loadSound("env-lava","env_lava.ogg"); + sound->loadSound("env-lava","env_lava.ogg",0.2); + sound->loadSound("env-water","env_water.ogg",0.2); sound->loadSound("env-steam","env_steam.ogg"); sound->loadSound("env-tnt","env_tnt.ogg"); sound->loadSound("env-teleport","env_teleport.ogg"); diff --git a/src/sound.h b/src/sound.h index 9258268..5ee7e7c 100644 --- a/src/sound.h +++ b/src/sound.h @@ -48,7 +48,7 @@ public: // playSound functions return -1 on failure, otherwise a handle to the // sound. If name=="", call should be ignored without error. virtual int playSound(const std::string &name, bool loop) = 0; - virtual int playSoundAt(const std::string &name, bool loop, v3f pos, float gain=1.0) = 0; + virtual int playSoundAt(const std::string &name, bool loop, v3f pos, float gain=1.0, bool queue=false) = 0; virtual void stopSound(int sound) = 0; virtual bool soundExists(int sound) = 0; @@ -69,7 +69,7 @@ public: void setListenerGain(float gain) {} int playSound(const std::string &name, bool loop) {return 0;} - int playSoundAt(const std::string &name, bool loop, v3f pos, float gain) {return 0;} + int playSoundAt(const std::string &name, bool loop, v3f pos, float gain, bool queue) {return 0;} void stopSound(int sound) {} bool soundExists(int sound) {return false;} diff --git a/src/sound_openal.cpp b/src/sound_openal.cpp index aab5eee..cc70234 100644 --- a/src/sound_openal.cpp +++ b/src/sound_openal.cpp @@ -177,9 +177,14 @@ SoundBuffer* loadOggFile(const std::string &filepath) struct PlayingSound { + SoundBuffer *buf; ALuint source_id; bool loop; bool should_delete; + bool has_position; + v3f pos; + bool disabled; + float gain; }; class OpenALSoundManager: public ISoundManager @@ -195,6 +200,7 @@ private: std::map m_sounds_playing; std::map m_indexes; v3f m_listener_pos; + JMutex m_mutex; public: OpenALSoundManager(): m_device(NULL), @@ -208,6 +214,8 @@ public: infostream<<"Audio: Initializing..."<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); - float volume = MYMAX(0.0, buf->gain); - alSourcef(sound->source_id, AL_GAIN, volume); + sound->gain = 1.0; + alSourcef(sound->source_id, AL_GAIN, buf->gain); alSourcePlay(sound->source_id); sound->should_delete = false; + sound->loop = loop; + sound->has_position = false; + sound->disabled = false; + sound->buf = buf; return sound; } - PlayingSound* createPlayingSoundAt(SoundBuffer *buf, bool loop, v3f pos, float gain) + PlayingSound* createPlayingSoundAt(SoundBuffer *buf, bool loop, v3f pos, float gain, bool queue) { 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); - float volume = MYMAX(0.0, buf->gain*gain); - alSourcef(sound->source_id, AL_GAIN, volume); - alSourcePlay(sound->source_id); + float distance = m_listener_pos.getDistanceFrom(pos); + if (!queue && (!loop || distance < 240.0)) { + alGenSources(1, &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_REFERENCE_DISTANCE, 30.0); + alSourcei(sound->source_id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE); + float t_gain = MYMAX(0.0, buf->gain*gain); + alSourcef(sound->source_id, AL_GAIN, t_gain); + alSourcePlay(sound->source_id); + sound->disabled = false; + }else{ + sound->source_id = 0; + sound->disabled = true; + } + sound->gain = gain; sound->should_delete = false; + sound->loop = loop; + sound->has_position = true; + sound->pos = pos; + sound->buf = buf; return sound; } @@ -366,19 +390,27 @@ public: PlayingSound *sound = createPlayingSound(buf, loop); if (!sound) return -1; - int id = m_next_id++; - m_sounds_playing[id] = sound; + int id = -1; + { + JMutexAutoLock lock(m_mutex); + id = m_next_id++; + m_sounds_playing[id] = sound; + } return id; } - int playSoundRawAt(SoundBuffer *buf, bool loop, v3f pos, float gain) + int playSoundRawAt(SoundBuffer *buf, bool loop, v3f pos, float gain, bool queue) { assert(buf); - PlayingSound *sound = createPlayingSoundAt(buf, loop, pos, gain); + PlayingSound *sound = createPlayingSoundAt(buf, loop, pos, gain, queue); if (!sound) return -1; - int id = m_next_id++; - m_sounds_playing[id] = sound; + int id = -1; + { + JMutexAutoLock lock(m_mutex); + id = m_next_id++; + m_sounds_playing[id] = sound; + } return id; } @@ -391,24 +423,53 @@ public: m_music_last_id = 0; if (i == m_sounds_playing.end()) return; + PlayingSound *sound = i->second; + alSourceStop(sound->source_id); alDeleteSources(1, &sound->source_id); delete sound; + m_sounds_playing.erase(id); + } // Remove stopped sounds void maintain(float dtime) { + JMutexAutoLock lock(m_mutex); verbosestream<<"OpenALSoundManager::maintain(): " < del_list; + bool did_start = false; for (std::map::iterator i = m_sounds_playing.begin(); i != m_sounds_playing.end(); i++) { int id = i->first; PlayingSound *sound = i->second; + if (sound->has_position && sound->loop && !sound->should_delete) { + float distance = m_listener_pos.getDistanceFrom(sound->pos); + if (distance > 320.0 && !sound->disabled && !did_start) { + alSourceStop(sound->source_id); + alDeleteSources(1, &sound->source_id); + sound->disabled = true; + sound->source_id = 0; + }else if (distance < 240.0 && sound->disabled) { + did_start = true; + alGenSources(1, &sound->source_id); + alSourcei(sound->source_id, AL_BUFFER, sound->buf->buffer_id); + alSourcei(sound->source_id, AL_SOURCE_RELATIVE, false); + alSource3f(sound->source_id, AL_POSITION, sound->pos.X, sound->pos.Y, sound->pos.Z); + alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0); + alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 30.0); + alSourcei(sound->source_id, AL_LOOPING, AL_TRUE); + float t_gain = MYMAX(0.0, sound->buf->gain*sound->gain); + alSourcef(sound->source_id, AL_GAIN, t_gain); + alSourcePlay(sound->source_id); + sound->disabled = false; + } + continue; + } if (sound->should_delete) { del_list.insert(id); }else{ // If not playing, remove it @@ -490,7 +551,7 @@ public: } return playSoundRaw(buf, loop); } - int playSoundAt(const std::string &name, bool loop, v3f pos, float gain) + int playSoundAt(const std::string &name, bool loop, v3f pos, float gain, bool queue=false) { if (name == "") return 0; @@ -500,10 +561,11 @@ public: <::iterator i = m_sounds_playing.find(id); if (id == m_music_id) m_music_id = 0; @@ -516,6 +578,7 @@ public: } bool soundExists(int sound) { + JMutexAutoLock lock(m_mutex); return (m_sounds_playing.count(sound) != 0); }