Sounds: Various little improvements (#12486)

Use SimpleSoundSpec where reasonable (OpenAL)
Ensure the sound IDs do not underflow or get overwritten -> loop in u16
Proper use of an enum.
This commit is contained in:
SmallJoker 2022-07-09 22:32:24 +02:00 committed by GitHub
parent 051181fa6e
commit e51f474613
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 112 additions and 108 deletions

@ -51,10 +51,8 @@ public:
// playSound functions return -1 on failure, otherwise a handle to the // playSound functions return -1 on failure, otherwise a handle to the
// sound. If name=="", call should be ignored without error. // sound. If name=="", call should be ignored without error.
virtual int playSound(const std::string &name, bool loop, float volume, virtual int playSound(const SimpleSoundSpec &spec) = 0;
float fade = 0.0f, float pitch = 1.0f) = 0; virtual int playSoundAt(const SimpleSoundSpec &spec, const v3f &pos) = 0;
virtual int playSoundAt(const std::string &name, bool loop, float volume, v3f pos,
float pitch = 1.0f) = 0;
virtual void stopSound(int sound) = 0; virtual void stopSound(int sound) = 0;
virtual bool soundExists(int sound) = 0; virtual bool soundExists(int sound) = 0;
virtual void updateSoundPosition(int sound, v3f pos) = 0; virtual void updateSoundPosition(int sound, v3f pos) = 0;
@ -62,15 +60,6 @@ public:
virtual float getSoundGain(int id) = 0; virtual float getSoundGain(int id) = 0;
virtual void step(float dtime) = 0; virtual void step(float dtime) = 0;
virtual void fadeSound(int sound, float step, float gain) = 0; virtual void fadeSound(int sound, float step, float gain) = 0;
int playSound(const SimpleSoundSpec &spec)
{
return playSound(spec.name, spec.loop, spec.gain, spec.fade, spec.pitch);
}
int playSoundAt(const SimpleSoundSpec &spec, const v3f &pos)
{
return playSoundAt(spec.name, spec.loop, spec.gain, pos, spec.pitch);
}
}; };
class DummySoundManager : public ISoundManager class DummySoundManager : public ISoundManager
@ -88,16 +77,9 @@ public:
{ {
} }
void setListenerGain(float gain) {} void setListenerGain(float gain) {}
int playSound(const std::string &name, bool loop, float volume, float fade,
float pitch) int playSound(const SimpleSoundSpec &spec) { return -1; }
{ int playSoundAt(const SimpleSoundSpec &spec, const v3f &pos) { return -1; }
return 0;
}
int playSoundAt(const std::string &name, bool loop, float volume, v3f pos,
float pitch)
{
return 0;
}
void stopSound(int sound) {} void stopSound(int sound) {}
bool soundExists(int sound) { return false; } bool soundExists(int sound) { return false; }
void updateSoundPosition(int sound, v3f pos) {} void updateSoundPosition(int sound, v3f pos) {}

@ -322,7 +322,7 @@ private:
OnDemandSoundFetcher *m_fetcher; OnDemandSoundFetcher *m_fetcher;
ALCdevice *m_device; ALCdevice *m_device;
ALCcontext *m_context; ALCcontext *m_context;
int m_next_id; u16 m_last_used_id = 0; // only access within getFreeId() !
std::unordered_map<std::string, std::vector<SoundBuffer*>> m_buffers; std::unordered_map<std::string, std::vector<SoundBuffer*>> m_buffers;
std::unordered_map<int, PlayingSound*> m_sounds_playing; std::unordered_map<int, PlayingSound*> m_sounds_playing;
struct FadeState { struct FadeState {
@ -342,8 +342,7 @@ public:
OpenALSoundManager(SoundManagerSingleton *smg, OnDemandSoundFetcher *fetcher): OpenALSoundManager(SoundManagerSingleton *smg, OnDemandSoundFetcher *fetcher):
m_fetcher(fetcher), m_fetcher(fetcher),
m_device(smg->m_device.get()), m_device(smg->m_device.get()),
m_context(smg->m_context.get()), m_context(smg->m_context.get())
m_next_id(1)
{ {
infostream << "Audio: Initialized: OpenAL " << std::endl; infostream << "Audio: Initialized: OpenAL " << std::endl;
} }
@ -379,6 +378,22 @@ public:
infostream << "Audio: Deinitialized." << std::endl; infostream << "Audio: Deinitialized." << std::endl;
} }
u16 getFreeId()
{
u16 startid = m_last_used_id;
while (!isFreeId(++m_last_used_id)) {
if (m_last_used_id == startid)
return 0;
}
return m_last_used_id;
}
inline bool isFreeId(int id) const
{
return id > 0 && m_sounds_playing.find(id) == m_sounds_playing.end();
}
void step(float dtime) void step(float dtime)
{ {
doFades(dtime); doFades(dtime);
@ -437,7 +452,7 @@ public:
<< std::endl; << std::endl;
assert(buf); assert(buf);
PlayingSound *sound = new PlayingSound; PlayingSound *sound = new PlayingSound;
assert(sound);
warn_if_error(alGetError(), "before createPlayingSoundAt"); warn_if_error(alGetError(), "before createPlayingSoundAt");
alGenSources(1, &sound->source_id); alGenSources(1, &sound->source_id);
alSourcei(sound->source_id, AL_BUFFER, buf->buffer_id); alSourcei(sound->source_id, AL_BUFFER, buf->buffer_id);
@ -463,28 +478,17 @@ public:
{ {
assert(buf); assert(buf);
PlayingSound *sound = createPlayingSound(buf, loop, volume, pitch); PlayingSound *sound = createPlayingSound(buf, loop, volume, pitch);
if(!sound) if (!sound)
return -1; return -1;
int id = m_next_id++;
m_sounds_playing[id] = sound;
return id;
}
int playSoundRawAt(SoundBuffer *buf, bool loop, float volume, const v3f &pos, int handle = getFreeId();
float pitch) m_sounds_playing[handle] = sound;
{ return handle;
assert(buf);
PlayingSound *sound = createPlayingSoundAt(buf, loop, volume, pos, pitch);
if(!sound)
return -1;
int id = m_next_id++;
m_sounds_playing[id] = sound;
return id;
} }
void deleteSound(int id) void deleteSound(int id)
{ {
std::unordered_map<int, PlayingSound*>::iterator i = m_sounds_playing.find(id); auto i = m_sounds_playing.find(id);
if(i == m_sounds_playing.end()) if(i == m_sounds_playing.end())
return; return;
PlayingSound *sound = i->second; PlayingSound *sound = i->second;
@ -580,39 +584,46 @@ public:
alListenerf(AL_GAIN, gain); alListenerf(AL_GAIN, gain);
} }
int playSound(const std::string &name, bool loop, float volume, float fade, float pitch) int playSound(const SimpleSoundSpec &spec)
{ {
maintain(); maintain();
if (name.empty()) if (spec.name.empty())
return 0; return 0;
SoundBuffer *buf = getFetchBuffer(name); SoundBuffer *buf = getFetchBuffer(spec.name);
if(!buf){ if(!buf){
infostream << "OpenALSoundManager: \"" << name << "\" not found." infostream << "OpenALSoundManager: \"" << spec.name << "\" not found."
<< std::endl; << std::endl;
return -1; return -1;
} }
int handle = -1; int handle = -1;
if (fade > 0) { if (spec.fade > 0) {
handle = playSoundRaw(buf, loop, 0.0f, pitch); handle = playSoundRaw(buf, spec.loop, 0.0f, spec.pitch);
fadeSound(handle, fade, volume); fadeSound(handle, spec.fade, spec.gain);
} else { } else {
handle = playSoundRaw(buf, loop, volume, pitch); handle = playSoundRaw(buf, spec.loop, spec.gain, spec.pitch);
} }
return handle; return handle;
} }
int playSoundAt(const std::string &name, bool loop, float volume, v3f pos, float pitch) int playSoundAt(const SimpleSoundSpec &spec, const v3f &pos)
{ {
maintain(); maintain();
if (name.empty()) if (spec.name.empty())
return 0; return 0;
SoundBuffer *buf = getFetchBuffer(name); SoundBuffer *buf = getFetchBuffer(spec.name);
if(!buf){ if (!buf) {
infostream << "OpenALSoundManager: \"" << name << "\" not found." infostream << "OpenALSoundManager: \"" << spec.name << "\" not found."
<< std::endl; << std::endl;
return -1; return -1;
} }
return playSoundRawAt(buf, loop, volume, pos, pitch);
PlayingSound *sound = createPlayingSoundAt(buf, spec.loop, spec.gain, pos, spec.pitch);
if (!sound)
return -1;
int handle = getFreeId();
m_sounds_playing[handle] = sound;
return handle;
} }
void stopSound(int sound) void stopSound(int sound)
@ -624,8 +635,9 @@ public:
void fadeSound(int soundid, float step, float gain) void fadeSound(int soundid, float step, float gain)
{ {
// Ignore the command if step isn't valid. // Ignore the command if step isn't valid.
if (step == 0) if (step == 0 || soundid < 0)
return; return;
float current_gain = getSoundGain(soundid); float current_gain = getSoundGain(soundid);
step = gain - current_gain > 0 ? abs(step) : -abs(step); step = gain - current_gain > 0 ? abs(step) : -abs(step);
if (m_sounds_fading.find(soundid) != m_sounds_fading.end()) { if (m_sounds_fading.find(soundid) != m_sounds_fading.end()) {

@ -4498,7 +4498,7 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
if ((s.ftype == f_TabHeader) && if ((s.ftype == f_TabHeader) &&
(s.fid == event.GUIEvent.Caller->getID())) { (s.fid == event.GUIEvent.Caller->getID())) {
if (!s.sound.empty() && m_sound_manager) if (!s.sound.empty() && m_sound_manager)
m_sound_manager->playSound(s.sound, false, 1.0f); m_sound_manager->playSound(SimpleSoundSpec(s.sound, false, 1.0f));
s.send = true; s.send = true;
acceptInput(); acceptInput();
s.send = false; s.send = false;
@ -4543,7 +4543,7 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
if (s.ftype == f_Button || s.ftype == f_CheckBox) { if (s.ftype == f_Button || s.ftype == f_CheckBox) {
if (!s.sound.empty() && m_sound_manager) if (!s.sound.empty() && m_sound_manager)
m_sound_manager->playSound(s.sound, false, 1.0f); m_sound_manager->playSound(SimpleSoundSpec(s.sound, false, 1.0f));
s.send = true; s.send = true;
if (s.is_exit) { if (s.is_exit) {
@ -4568,7 +4568,7 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
} }
} }
if (!s.sound.empty() && m_sound_manager) if (!s.sound.empty() && m_sound_manager)
m_sound_manager->playSound(s.sound, false, 1.0f); m_sound_manager->playSound(SimpleSoundSpec(s.sound, false, 1.0f));
s.send = true; s.send = true;
acceptInput(quit_mode_no); acceptInput(quit_mode_no);
@ -4586,7 +4586,7 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
s.fdefault = L""; s.fdefault = L"";
} else if (s.ftype == f_Unknown || s.ftype == f_HyperText) { } else if (s.ftype == f_Unknown || s.ftype == f_HyperText) {
if (!s.sound.empty() && m_sound_manager) if (!s.sound.empty() && m_sound_manager)
m_sound_manager->playSound(s.sound, false, 1.0f); m_sound_manager->playSound(SimpleSoundSpec(s.sound, false, 1.0f));
s.send = true; s.send = true;
acceptInput(); acceptInput();
s.send = false; s.send = false;

@ -820,12 +820,12 @@ void Client::handleCommand_PlaySound(NetworkPacket* pkt)
s32 server_id; s32 server_id;
SimpleSoundSpec spec; SimpleSoundSpec spec;
u8 type; // 0=local, 1=positional, 2=object SoundLocation type; // 0=local, 1=positional, 2=object
v3f pos; v3f pos;
u16 object_id; u16 object_id;
bool ephemeral = false; bool ephemeral = false;
*pkt >> server_id >> spec.name >> spec.gain >> type >> pos >> object_id >> spec.loop; *pkt >> server_id >> spec.name >> spec.gain >> (u8 &)type >> pos >> object_id >> spec.loop;
try { try {
*pkt >> spec.fade; *pkt >> spec.fade;
@ -836,22 +836,20 @@ void Client::handleCommand_PlaySound(NetworkPacket* pkt)
// Start playing // Start playing
int client_id = -1; int client_id = -1;
switch(type) { switch(type) {
case 0: // local case SoundLocation::Local:
client_id = m_sound->playSound(spec); client_id = m_sound->playSound(spec);
break; break;
case 1: // positional case SoundLocation::Position:
client_id = m_sound->playSoundAt(spec, pos); client_id = m_sound->playSoundAt(spec, pos);
break; break;
case 2: case SoundLocation::Object:
{ // object {
ClientActiveObject *cao = m_env.getActiveObject(object_id); ClientActiveObject *cao = m_env.getActiveObject(object_id);
if (cao) if (cao)
pos = cao->getPosition(); pos = cao->getPosition();
client_id = m_sound->playSoundAt(spec, pos); client_id = m_sound->playSoundAt(spec, pos);
break; break;
} }
default:
break;
} }
if (client_id != -1) { if (client_id != -1) {

@ -1056,7 +1056,7 @@ void read_server_sound_params(lua_State *L, int index,
if(!lua_isnil(L, -1)){ if(!lua_isnil(L, -1)){
v3f p = read_v3f(L, -1)*BS; v3f p = read_v3f(L, -1)*BS;
params.pos = p; params.pos = p;
params.type = ServerPlayingSound::SSP_POSITIONAL; params.type = SoundLocation::Position;
} }
lua_pop(L, 1); lua_pop(L, 1);
lua_getfield(L, index, "object"); lua_getfield(L, index, "object");
@ -1065,7 +1065,7 @@ void read_server_sound_params(lua_State *L, int index,
ServerActiveObject *sao = ObjectRef::getobject(ref); ServerActiveObject *sao = ObjectRef::getobject(ref);
if(sao){ if(sao){
params.object = sao->getId(); params.object = sao->getId();
params.type = ServerPlayingSound::SSP_OBJECT; params.type = SoundLocation::Object;
} }
} }
lua_pop(L, 1); lua_pop(L, 1);

@ -268,30 +268,32 @@ int ModApiClient::l_sound_play(lua_State *L)
SimpleSoundSpec spec; SimpleSoundSpec spec;
read_soundspec(L, 1, spec); read_soundspec(L, 1, spec);
SoundLocation type = SoundLocation::Local;
float gain = 1.0f; float gain = 1.0f;
float pitch = 1.0f; v3f position;
bool looped = false;
s32 handle;
if (lua_istable(L, 2)) { if (lua_istable(L, 2)) {
getfloatfield(L, 2, "gain", gain); getfloatfield(L, 2, "gain", gain);
getfloatfield(L, 2, "pitch", pitch); getfloatfield(L, 2, "pitch", spec.pitch);
getboolfield(L, 2, "loop", looped); getboolfield(L, 2, "loop", spec.loop);
lua_getfield(L, 2, "pos"); lua_getfield(L, 2, "pos");
if (!lua_isnil(L, -1)) { if (!lua_isnil(L, -1)) {
v3f pos = read_v3f(L, -1) * BS; position = read_v3f(L, -1) * BS;
type = SoundLocation::Position;
lua_pop(L, 1); lua_pop(L, 1);
handle = sound->playSoundAt(
spec.name, looped, gain * spec.gain, pos, pitch);
lua_pushinteger(L, handle);
return 1;
} }
} }
handle = sound->playSound(spec.name, looped, gain * spec.gain, spec.fade, pitch); spec.gain *= gain;
lua_pushinteger(L, handle);
s32 handle;
if (type == SoundLocation::Local)
handle = sound->playSound(spec);
else
handle = sound->playSoundAt(spec, position);
lua_pushinteger(L, handle);
return 1; return 1;
} }

@ -138,21 +138,27 @@ void *ServerThread::run()
v3f ServerPlayingSound::getPos(ServerEnvironment *env, bool *pos_exists) const v3f ServerPlayingSound::getPos(ServerEnvironment *env, bool *pos_exists) const
{ {
if(pos_exists) *pos_exists = false; if (pos_exists)
switch(type){ *pos_exists = false;
case SSP_LOCAL:
switch (type ){
case SoundLocation::Local:
return v3f(0,0,0); return v3f(0,0,0);
case SSP_POSITIONAL: case SoundLocation::Position:
if(pos_exists) *pos_exists = true; if (pos_exists)
*pos_exists = true;
return pos; return pos;
case SSP_OBJECT: { case SoundLocation::Object:
if(object == 0) {
return v3f(0,0,0); if (object == 0)
ServerActiveObject *sao = env->getActiveObject(object); return v3f(0,0,0);
if(!sao) ServerActiveObject *sao = env->getActiveObject(object);
return v3f(0,0,0); if (!sao)
if(pos_exists) *pos_exists = true; return v3f(0,0,0);
return sao->getBasePosition(); } if (pos_exists)
*pos_exists = true;
return sao->getBasePosition();
}
} }
return v3f(0,0,0); return v3f(0,0,0);
} }
@ -2071,9 +2077,9 @@ s32 Server::playSound(ServerPlayingSound &params, bool ephemeral)
{ {
// Find out initial position of sound // Find out initial position of sound
bool pos_exists = false; bool pos_exists = false;
v3f pos = params.getPos(m_env, &pos_exists); const v3f pos = params.getPos(m_env, &pos_exists);
// If position is not found while it should be, cancel sound // If position is not found while it should be, cancel sound
if(pos_exists != (params.type != ServerPlayingSound::SSP_LOCAL)) if(pos_exists != (params.type != SoundLocation::Local))
return -1; return -1;
// Filter destination clients // Filter destination clients

@ -99,11 +99,7 @@ struct MediaInfo
// Combines the pure sound (SimpleSoundSpec) with positional information // Combines the pure sound (SimpleSoundSpec) with positional information
struct ServerPlayingSound struct ServerPlayingSound
{ {
enum Type { SoundLocation type = SoundLocation::Local;
SSP_LOCAL,
SSP_POSITIONAL,
SSP_OBJECT
} type = SSP_LOCAL;
float gain = 1.0f; // for amplification of the base sound float gain = 1.0f; // for amplification of the base sound
float max_hear_distance = 32 * BS; float max_hear_distance = 32 * BS;

@ -59,3 +59,11 @@ struct SimpleSoundSpec
float pitch = 1.0f; float pitch = 1.0f;
bool loop = false; bool loop = false;
}; };
// The order must not be changed. This is sent over the network.
enum class SoundLocation : u8 {
Local,
Position,
Object
};