aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/lua_api.txt7
-rw-r--r--src/client.cpp1
-rw-r--r--src/client.h1
-rw-r--r--src/network/clientopcodes.cpp2
-rw-r--r--src/network/clientpackethandler.cpp35
-rw-r--r--src/network/networkprotocol.h11
-rw-r--r--src/script/common/c_content.cpp2
-rw-r--r--src/script/common/c_converter.h2
-rw-r--r--src/script/lua_api/l_server.cpp11
-rw-r--r--src/script/lua_api/l_server.h3
-rw-r--r--src/server.cpp64
-rw-r--r--src/server.h6
-rw-r--r--src/sound.h28
-rw-r--r--src/sound_openal.cpp94
14 files changed, 248 insertions, 19 deletions
diff --git a/doc/lua_api.txt b/doc/lua_api.txt
index 479e38a2e..77ffb88e2 100644
--- a/doc/lua_api.txt
+++ b/doc/lua_api.txt
@@ -456,11 +456,13 @@ Examples of sound parameter tables:
-- Play locationless on all clients
{
gain = 1.0, -- default
+ fade = 0.0, -- default, change to a value > 0 to fade the sound in
}
-- Play locationless to one player
{
to_player = name,
gain = 1.0, -- default
+ fade = 0.0, -- default, change to a value > 0 to fade the sound in
}
-- Play locationless to one player, looped
{
@@ -2587,6 +2589,11 @@ These functions return the leftover itemstack.
* `spec` is a `SimpleSoundSpec`
* `parameters` is a sound parameter table
* `minetest.sound_stop(handle)`
+* `minetest.sound_fade(handle, step, gain)`
+ * `handle` is a handle returned by minetest.sound_play
+ * `step` determines how fast a sound will fade.
+ Negative step will lower the sound volume, positive step will increase the sound volume
+ * `gain` the target gain for the fade.
### Timing
* `minetest.after(time, func, ...)`
diff --git a/src/client.cpp b/src/client.cpp
index 3c5a70f21..3269c573a 100644
--- a/src/client.cpp
+++ b/src/client.cpp
@@ -407,6 +407,7 @@ void Client::step(float dtime)
// Step environment
m_env.step(dtime);
+ m_sound->step(dtime);
/*
Get events
diff --git a/src/client.h b/src/client.h
index 7cbfadd50..e8db7de44 100644
--- a/src/client.h
+++ b/src/client.h
@@ -328,6 +328,7 @@ public:
void handleCommand_ItemDef(NetworkPacket* pkt);
void handleCommand_PlaySound(NetworkPacket* pkt);
void handleCommand_StopSound(NetworkPacket* pkt);
+ void handleCommand_FadeSound(NetworkPacket *pkt);
void handleCommand_Privileges(NetworkPacket* pkt);
void handleCommand_InventoryFormSpec(NetworkPacket* pkt);
void handleCommand_DetachedInventory(NetworkPacket* pkt);
diff --git a/src/network/clientopcodes.cpp b/src/network/clientopcodes.cpp
index 1be6e5522..bdcb1dfce 100644
--- a/src/network/clientopcodes.cpp
+++ b/src/network/clientopcodes.cpp
@@ -109,7 +109,7 @@ const ToClientCommandHandler toClientCommandTable[TOCLIENT_NUM_MSG_TYPES] =
{ "TOCLIENT_EYE_OFFSET", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_EyeOffset }, // 0x52
{ "TOCLIENT_DELETE_PARTICLESPAWNER", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_DeleteParticleSpawner }, // 0x53
{ "TOCLIENT_CLOUD_PARAMS", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_CloudParams }, // 0x54
- null_command_handler,
+ { "TOCLIENT_FADE_SOUND", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_FadeSound }, // 0x55
null_command_handler,
null_command_handler,
null_command_handler,
diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp
index defc83f31..a895acc84 100644
--- a/src/network/clientpackethandler.cpp
+++ b/src/network/clientpackethandler.cpp
@@ -755,21 +755,39 @@ void Client::handleCommand_ItemDef(NetworkPacket* pkt)
void Client::handleCommand_PlaySound(NetworkPacket* pkt)
{
+ /*
+ [0] u32 server_id
+ [4] u16 name length
+ [6] char name[len]
+ [ 6 + len] f32 gain
+ [10 + len] u8 type
+ [11 + len] (f32 * 3) pos
+ [23 + len] u16 object_id
+ [25 + len] bool loop
+ [26 + len] f32 fade
+ */
+
s32 server_id;
std::string name;
+
float gain;
u8 type; // 0=local, 1=positional, 2=object
v3f pos;
u16 object_id;
bool loop;
+ float fade = 0;
*pkt >> server_id >> name >> gain >> type >> pos >> object_id >> loop;
+ try {
+ *pkt >> fade;
+ } catch (SerializationError &e) {};
+
// Start playing
int client_id = -1;
switch(type) {
case 0: // local
- client_id = m_sound->playSound(name, loop, gain);
+ client_id = m_sound->playSound(name, loop, gain, fade);
break;
case 1: // positional
client_id = m_sound->playSoundAt(name, loop, gain, pos);
@@ -808,6 +826,21 @@ void Client::handleCommand_StopSound(NetworkPacket* pkt)
}
}
+void Client::handleCommand_FadeSound(NetworkPacket *pkt)
+{
+ s32 sound_id;
+ float step;
+ float gain;
+
+ *pkt >> sound_id >> step >> gain;
+
+ UNORDERED_MAP<s32, int>::iterator i =
+ m_sounds_server_to_client.find(sound_id);
+
+ if (i != m_sounds_server_to_client.end())
+ m_sound->fadeSound(i->second, step, gain);
+}
+
void Client::handleCommand_Privileges(NetworkPacket* pkt)
{
m_privileges.clear();
diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h
index a1a4f5bfa..70cad85d8 100644
--- a/src/network/networkprotocol.h
+++ b/src/network/networkprotocol.h
@@ -153,9 +153,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
PROTOCOL VERSION 31:
Add tile overlay
Stop sending TOSERVER_CLIENT_READY
+ PROTOCOL VERSION 32:
+ Add fading sounds
*/
-#define LATEST_PROTOCOL_VERSION 31
+#define LATEST_PROTOCOL_VERSION 32
// Server's supported network protocol range
#define SERVER_PROTOCOL_VERSION_MIN 24
@@ -620,6 +622,13 @@ enum ToClientCommand
v2f1000 speed
*/
+ TOCLIENT_FADE_SOUND = 0x55,
+ /*
+ s32 sound_id
+ float step
+ float gain
+ */
+
TOCLIENT_SRP_BYTES_S_B = 0x60,
/*
Belonging to AUTH_MECHANISM_LEGACY_PASSWORD and AUTH_MECHANISM_SRP.
diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp
index 5fe5af58d..8696ad7cb 100644
--- a/src/script/common/c_content.cpp
+++ b/src/script/common/c_content.cpp
@@ -680,6 +680,7 @@ void read_server_sound_params(lua_State *L, int index,
if(lua_istable(L, index)){
getfloatfield(L, index, "gain", params.gain);
getstringfield(L, index, "to_player", params.to_player);
+ getfloatfield(L, index, "fade", params.fade);
lua_getfield(L, index, "pos");
if(!lua_isnil(L, -1)){
v3f p = read_v3f(L, -1)*BS;
@@ -712,6 +713,7 @@ void read_soundspec(lua_State *L, int index, SimpleSoundSpec &spec)
} else if(lua_istable(L, index)){
getstringfield(L, index, "name", spec.name);
getfloatfield(L, index, "gain", spec.gain);
+ getfloatfield(L, index, "fade", spec.fade);
} else if(lua_isstring(L, index)){
spec.name = lua_tostring(L, index);
}
diff --git a/src/script/common/c_converter.h b/src/script/common/c_converter.h
index a5fbee765..b0f61a8ca 100644
--- a/src/script/common/c_converter.h
+++ b/src/script/common/c_converter.h
@@ -77,6 +77,8 @@ void setfloatfield(lua_State *L, int table,
const char *fieldname, float value);
void setboolfield(lua_State *L, int table,
const char *fieldname, bool value);
+void setstringfield(lua_State *L, int table,
+ const char *fieldname, const char *value);
v3f checkFloatPos (lua_State *L, int index);
v2f check_v2f (lua_State *L, int index);
diff --git a/src/script/lua_api/l_server.cpp b/src/script/lua_api/l_server.cpp
index 7b723d14c..ea993d7b7 100644
--- a/src/script/lua_api/l_server.cpp
+++ b/src/script/lua_api/l_server.cpp
@@ -455,6 +455,16 @@ int ModApiServer::l_sound_stop(lua_State *L)
return 0;
}
+int ModApiServer::l_sound_fade(lua_State *L)
+{
+ NO_MAP_LOCK_REQUIRED;
+ s32 handle = luaL_checkinteger(L, 1);
+ float step = luaL_checknumber(L, 2);
+ float gain = luaL_checknumber(L, 3);
+ getServer(L)->fadeSound(handle, step, gain);
+ return 0;
+}
+
// is_singleplayer()
int ModApiServer::l_is_singleplayer(lua_State *L)
{
@@ -518,6 +528,7 @@ void ModApiServer::Initialize(lua_State *L, int top)
API_FCT(show_formspec);
API_FCT(sound_play);
API_FCT(sound_stop);
+ API_FCT(sound_fade);
API_FCT(get_player_information);
API_FCT(get_player_privs);
diff --git a/src/script/lua_api/l_server.h b/src/script/lua_api/l_server.h
index 3a4a917c0..251a0ce89 100644
--- a/src/script/lua_api/l_server.h
+++ b/src/script/lua_api/l_server.h
@@ -68,6 +68,9 @@ private:
// sound_stop(handle)
static int l_sound_stop(lua_State *L);
+ // sound_fade(handle, step, gain)
+ static int l_sound_fade(lua_State *L);
+
// get_player_privs(name, text)
static int l_get_player_privs(lua_State *L);
diff --git a/src/server.cpp b/src/server.cpp
index 9ef69cb37..190a1baf2 100644
--- a/src/server.cpp
+++ b/src/server.cpp
@@ -2100,15 +2100,23 @@ s32 Server::playSound(const SimpleSoundSpec &spec,
m_playing_sounds[id] = ServerPlayingSound();
ServerPlayingSound &psound = m_playing_sounds[id];
psound.params = params;
+ psound.spec = spec;
+ float gain = params.gain * spec.gain;
NetworkPacket pkt(TOCLIENT_PLAY_SOUND, 0);
- pkt << id << spec.name << (float) (spec.gain * params.gain)
- << (u8) params.type << pos << params.object << params.loop;
+ pkt << id << spec.name << gain
+ << (u8) params.type << pos << params.object
+ << params.loop << params.fade;
- for(std::vector<u16>::iterator i = dst_clients.begin();
+ // Backwards compability
+ bool play_sound = gain > 0;
+
+ for (std::vector<u16>::iterator i = dst_clients.begin();
i != dst_clients.end(); ++i) {
- psound.clients.insert(*i);
- m_clients.send(*i, 0, &pkt, true);
+ if (play_sound || m_clients.getProtocolVersion(*i) >= 32) {
+ psound.clients.insert(*i);
+ m_clients.send(*i, 0, &pkt, true);
+ }
}
return id;
}
@@ -2132,6 +2140,52 @@ void Server::stopSound(s32 handle)
m_playing_sounds.erase(i);
}
+void Server::fadeSound(s32 handle, float step, float gain)
+{
+ // Get sound reference
+ UNORDERED_MAP<s32, ServerPlayingSound>::iterator i =
+ m_playing_sounds.find(handle);
+ if (i == m_playing_sounds.end())
+ return;
+
+ ServerPlayingSound &psound = i->second;
+ psound.params.gain = gain;
+
+ NetworkPacket pkt(TOCLIENT_FADE_SOUND, 4);
+ pkt << handle << step << gain;
+
+ // Backwards compability
+ bool play_sound = gain > 0;
+ ServerPlayingSound compat_psound = psound;
+ compat_psound.clients.clear();
+
+ NetworkPacket compat_pkt(TOCLIENT_STOP_SOUND, 4);
+ compat_pkt << handle;
+
+ for (UNORDERED_SET<u16>::iterator it = psound.clients.begin();
+ it != psound.clients.end();) {
+ if (m_clients.getProtocolVersion(*it) >= 32) {
+ // Send as reliable
+ m_clients.send(*it, 0, &pkt, true);
+ ++it;
+ } else {
+ compat_psound.clients.insert(*it);
+ // Stop old sound
+ m_clients.send(*it, 0, &compat_pkt, true);
+ psound.clients.erase(it++);
+ }
+ }
+
+ // Remove sound reference
+ if (!play_sound || psound.clients.size() == 0)
+ m_playing_sounds.erase(i);
+
+ if (play_sound && compat_psound.clients.size() > 0) {
+ // Play new sound volume on older clients
+ playSound(compat_psound.spec, compat_psound.params);
+ }
+}
+
void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
std::vector<u16> *far_players, float far_d_nodes)
{
diff --git a/src/server.h b/src/server.h
index 3a082b9a4..5e6211637 100644
--- a/src/server.h
+++ b/src/server.h
@@ -115,6 +115,7 @@ struct ServerSoundParams
u16 object;
float max_hear_distance;
bool loop;
+ float fade;
ServerSoundParams():
gain(1.0),
@@ -123,7 +124,8 @@ struct ServerSoundParams
pos(0,0,0),
object(0),
max_hear_distance(32*BS),
- loop(false)
+ loop(false),
+ fade(0)
{}
v3f getPos(ServerEnvironment *env, bool *pos_exists) const;
@@ -132,6 +134,7 @@ struct ServerSoundParams
struct ServerPlayingSound
{
ServerSoundParams params;
+ SimpleSoundSpec spec;
UNORDERED_SET<u16> clients; // peer ids
};
@@ -231,6 +234,7 @@ public:
// Envlock
s32 playSound(const SimpleSoundSpec &spec, const ServerSoundParams &params);
void stopSound(s32 handle);
+ void fadeSound(s32 handle, float step, float gain);
// Envlock
std::set<std::string> getPlayerEffectivePrivs(const std::string &name);
diff --git a/src/sound.h b/src/sound.h
index 98f7692d5..7bdb6a26b 100644
--- a/src/sound.h
+++ b/src/sound.h
@@ -34,8 +34,8 @@ public:
struct SimpleSoundSpec
{
- SimpleSoundSpec(const std::string &name = "", float gain = 1.0)
- : name(name), gain(gain)
+ SimpleSoundSpec(const std::string &name = "", float gain = 1.0, float fade = 0.0)
+ : name(name), gain(gain), fade(fade)
{
}
@@ -43,13 +43,13 @@ struct SimpleSoundSpec
std::string name;
float gain;
+ float fade;
};
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
@@ -63,16 +63,21 @@ 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, float volume) = 0;
- virtual int playSoundAt(
- const std::string &name, bool loop, float volume, v3f pos) = 0;
+ virtual int playSound(const std::string &name, bool loop, float volume,
+ float fade = 0) = 0;
+ virtual int playSoundAt(const std::string &name, bool loop, float volume,
+ v3f pos) = 0;
virtual void stopSound(int sound) = 0;
virtual bool soundExists(int sound) = 0;
virtual void updateSoundPosition(int sound, v3f pos) = 0;
+ virtual bool updateSoundGain(int id, float gain) = 0;
+ virtual float getSoundGain(int id) = 0;
+ virtual void step(float dtime) = 0;
+ virtual void fadeSound(int sound, float step, float gain) = 0;
int playSound(const SimpleSoundSpec &spec, bool loop)
{
- return playSound(spec.name, loop, spec.gain);
+ return playSound(spec.name, loop, spec.gain, spec.fade);
}
int playSoundAt(const SimpleSoundSpec &spec, bool loop, v3f pos)
{
@@ -93,7 +98,10 @@ public:
}
void updateListener(v3f pos, v3f vel, v3f at, v3f up) {}
void setListenerGain(float gain) {}
- int playSound(const std::string &name, bool loop, float volume) { return 0; }
+ int playSound(const std::string &name, bool loop, float volume, float fade)
+ {
+ return 0;
+ }
int playSoundAt(const std::string &name, bool loop, float volume, v3f pos)
{
return 0;
@@ -101,6 +109,10 @@ public:
void stopSound(int sound) {}
bool soundExists(int sound) { return false; }
void updateSoundPosition(int sound, v3f pos) {}
+ bool updateSoundGain(int id, float gain) { return false; }
+ float getSoundGain(int id) { return 0; }
+ void step(float dtime) { }
+ void fadeSound(int sound, float step, float gain) { }
};
// Global DummySoundManager singleton
diff --git a/src/sound_openal.cpp b/src/sound_openal.cpp
index b9af9e3a9..a425af827 100644
--- a/src/sound_openal.cpp
+++ b/src/sound_openal.cpp
@@ -274,6 +274,19 @@ private:
UNORDERED_MAP<std::string, std::vector<SoundBuffer*> > m_buffers;
UNORDERED_MAP<int, PlayingSound*> m_sounds_playing;
v3f m_listener_pos;
+ struct FadeState {
+ FadeState() {}
+ FadeState(float step, float current_gain, float target_gain):
+ step(step),
+ current_gain(current_gain),
+ target_gain(target_gain) {}
+ float step;
+ float current_gain;
+ float target_gain;
+ };
+
+ UNORDERED_MAP<int, FadeState> m_sounds_fading;
+ float m_fade_delay;
public:
bool m_is_initialized;
OpenALSoundManager(OnDemandSoundFetcher *fetcher):
@@ -281,6 +294,7 @@ public:
m_device(NULL),
m_context(NULL),
m_next_id(1),
+ m_fade_delay(0),
m_is_initialized(false)
{
ALCenum error = ALC_NO_ERROR;
@@ -349,6 +363,11 @@ public:
infostream<<"Audio: Deinitialized."<<std::endl;
}
+ void step(float dtime)
+ {
+ doFades(dtime);
+ }
+
void addBuffer(const std::string &name, SoundBuffer *buf)
{
UNORDERED_MAP<std::string, std::vector<SoundBuffer*> >::iterator i =
@@ -515,6 +534,7 @@ public:
addBuffer(name, buf);
return false;
}
+
bool loadSoundData(const std::string &name,
const std::string &filedata)
{
@@ -541,7 +561,7 @@ public:
alListenerf(AL_GAIN, gain);
}
- int playSound(const std::string &name, bool loop, float volume)
+ int playSound(const std::string &name, bool loop, float volume, float fade)
{
maintain();
if(name == "")
@@ -552,8 +572,16 @@ public:
<<std::endl;
return -1;
}
- return playSoundRaw(buf, loop, volume);
+ int handle = -1;
+ if (fade > 0) {
+ handle = playSoundRaw(buf, loop, 0);
+ fadeSound(handle, fade, volume);
+ } else {
+ handle = playSoundRaw(buf, loop, volume);
+ }
+ return handle;
}
+
int playSoundAt(const std::string &name, bool loop, float volume, v3f pos)
{
maintain();
@@ -567,16 +595,55 @@ public:
}
return playSoundRawAt(buf, loop, volume, pos);
}
+
void stopSound(int sound)
{
maintain();
deleteSound(sound);
}
+
+ void fadeSound(int soundid, float step, float gain)
+ {
+ m_sounds_fading[soundid] = FadeState(step, getSoundGain(soundid), gain);
+ }
+
+ void doFades(float dtime)
+ {
+ m_fade_delay += dtime;
+
+ if (m_fade_delay < 0.1f)
+ return;
+
+ float chkGain = 0;
+ for (UNORDERED_MAP<int, FadeState>::iterator i = m_sounds_fading.begin();
+ i != m_sounds_fading.end();) {
+ if (i->second.step < 0.f)
+ chkGain = -(i->second.current_gain);
+ else
+ chkGain = i->second.current_gain;
+
+ if (chkGain < i->second.target_gain) {
+ i->second.current_gain += (i->second.step * m_fade_delay);
+ i->second.current_gain = rangelim(i->second.current_gain, 0, 1);
+
+ updateSoundGain(i->first, i->second.current_gain);
+ ++i;
+ } else {
+ if (i->second.target_gain <= 0.f)
+ stopSound(i->first);
+
+ m_sounds_fading.erase(i++);
+ }
+ }
+ m_fade_delay = 0;
+ }
+
bool soundExists(int sound)
{
maintain();
return (m_sounds_playing.count(sound) != 0);
}
+
void updateSoundPosition(int id, v3f pos)
{
UNORDERED_MAP<int, PlayingSound*>::iterator i = m_sounds_playing.find(id);
@@ -589,6 +656,29 @@ public:
alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 30.0);
}
+
+ bool updateSoundGain(int id, float gain)
+ {
+ UNORDERED_MAP<int, PlayingSound*>::iterator i = m_sounds_playing.find(id);
+ if (i == m_sounds_playing.end())
+ return false;
+
+ PlayingSound *sound = i->second;
+ alSourcef(sound->source_id, AL_GAIN, gain);
+ return true;
+ }
+
+ float getSoundGain(int id)
+ {
+ UNORDERED_MAP<int, PlayingSound*>::iterator i = m_sounds_playing.find(id);
+ if (i == m_sounds_playing.end())
+ return 0;
+
+ PlayingSound *sound = i->second;
+ ALfloat gain;
+ alGetSourcef(sound->source_id, AL_GAIN, &gain);
+ return gain;
+ }
};
ISoundManager *createOpenALSoundManager(OnDemandSoundFetcher *fetcher)