Sound: Set gain per voice

Hey,

With sound objects in the engine, we can have up to 32 voices active at any given time, which allows us to have the same same sound playing multiple times without having to duplicate the object. This is really useful for emitters; an example being torches on walls.

Using kinematic triggers to resolve the players distance from a sound emitter is also a really useful tool, however the issue comes if the player is in range of two emitters at the same time which are using the same sound. Setting the gain of the sound object takes effect on ALL active voices.

It would be ideal if it was possible to set the gain for a specific voice. Perhaps when you send a play_sound message, you’re able to provide an arbitrary ID, which is assigned to just the voice that is initiated from that call. When calling set_gain, providing that ID again effects only the voice with that assigned ID.

Practically speaking, it is possible to just use one voice and just have the highest emitter distance win, but due to the way messages come in, this can lead to slight hiccups in the volume as a lower emitter might be the only one to send in collision responses in a specific frame. To get this method perfect, a lot of extra monitoring needs to be done, which is why I think the above case or something similar would be more elegant.

2 Likes

You could do this yourself by having a script responsible for all sound playback, kind of like the sound gate example in the docs

1 Like

Interesting idea with the kinematic objects to measure distance to a sound source and adjusting gain based on distance. But how would it lead to hiccups? I need to test something like this myself tomorrow at the office.

How would this work without duplicating sound objects for each “voice” you wanted to control the volume for? I imagine you can set the gain for a specific voice with play_sound but then there’s no way to adjust that specific voice, correct?

For some reason, I had it in my head that it’s possible for a dispatched message to not be delivered in the same frame, but I think I might have been confused with cascading messages (ie reactive message chains)?

If I am correct about messages possibly not arriving on the same frame, then having two triggers using the same sound object (to prevent duplicates of the same sound object) then the volume might flicker as the two distances of the triggers are calculated at different times.

You are correct. This could happen if you post a lot of messages. The engine will try to clear the message queue several times each frame, but theoretically it might happen that a message is processed one frame later. During normal use this should however never happen.

1 Like

I suppose a solution would be to have the module dedicated to handling the sound gather any data given in from triggers every time they come in, but only act on it every 10~ frames, that way any possible delay would be negated (at least you’d hope so within 10 frames).

The immediate issue that comes from this is that slight added delay being added might be noticeable to the ear, but we could solve that by instead of simply sending set_gain as-is, we instead have the same sound module gradient the transition of gain so it fades smoothly. This would remove any noticeable sudden change in audio, as tiny as it might be.

To clarify the case of messages not being delivered the same frame, this only happen under very rare circumstances. It’s not dependent on the amount of messages you post, but on the depth. Consider a message being sent as a result of another message being received (i.e. msg.post happening in the scope of on_message), we call that a message chain of depth 1. Depending on where in the frame you are posting messages, you would have to create a message chain of depth ~100 for them to lag to the next frame. E.g. this should not cause messages to stall to the next frame, but it will certainly stall the game update and make various buffers run out of available space:

function update(self, dt)
    for i=1,1000000000 do
         msg.post(...)
    end
end
1 Like

@Ragnar_Svensson That’s what I was trying to refer to with this line, so I guess I was getting mixed up! :slight_smile:

Ah that’s right, sorry! :slight_smile:

Absolutely fine, I appreciate the clarification! I wasn’t sure if messages were guaranteed same-frame. Good to know they are (on a base level).

Regarding message depth, see “Message chains” here: http://www.defold.com/manuals/message-passing/#anchor-at