How to load and play multiple sounds at once?

Hi,

I’m using this code to load sounds dynamically at runtime.

if message_id == hash("loadsound") then
	pprint(message)
	local wav = sys.load_resource("/main/bundled_resources/common/"..message.sounddirectory.."/"..message.soundfilename..".wav") --sounds/clock-ticking.wav")
	print("Loaded:"..message.soundfilename..".wav")
	-- get resource path to the sound component
	local resource_path = go.get("/"..message.soundbank.."#"..message.soundbank, "sound")
	-- update the resource with the loaded wav file
	resource.set_sound(resource_path, wav)
	print("new sound ready!")
end

But I’m not getting multiple sounds to play at once when called likely in the same frame. I’m sure it’s some weird thing where I’m not addressing or properly maintaining two sound banks/buffers/objects but I’m not sure much else so far…

So I just figured this out to some degree but I’m not sure to explain why this is the solution.

In the set up for the game objects that are individually addressed for each sound channel the “placeholder” sound effect field set in Sound must be different.

I’m not sure I’m describing this great but initially you set up an object and attach sounds to it and those sounds are addressable, but in order to compile the game pre-set sounds must be attached to each one (e.g. a wav file) and that might be like “lasersound.wav” but if each individual sound channel you intend to use is all set to lasersound.wav at design time it won’t play multiple effects. So you would need sounditem1 = lasersound1.wav sounditem2 = lasersound2.wav and so on. Even though you’ll be replacing the actual files dynamically at run time, they need to be different at design time.

At least once I did this I got simultaneous playback of two different sounds.

This is correct. The sound component has a reference to a sound resource. If two sound components refer to the same sound resource (ie same file from your assets) it will not be loaded twice. The components will refer to the exact same data.

And resource.set_sound() will change the data in the sound resource referenced by a sound component. So with this in mind it makes sense that the two sound components both will use the new sound.

2 Likes

What I did was create a 3 second silent wav file and I named the 8 instances to each of my “banks” so I have a silent wave file attached to the game object at design time titled:

backgroundmusic
ambient1
ambient2
VoiceOver
effect1
effect2
effect3
effect4

since each are distinct sounds when I replace them with the runtime file the problem is solved. I might eventually replace them with a voice over that says “error: new file not loaded.” for feedback but for now it’s all good.

2 Likes

Hmm interesting, was stuck for a while with the same problem until I found this post. I understand why it works like that but is this how you want it to work @britzl ? In my opinion ideally sound components should be distinguished by their id, not by their sound file loaded?

If sound component is planned to load buffer programmatically, maybe it can be allowed to skip specifying sound file?

Or more generally I think if user decides to swap audio file later and two components reference the same file, maybe it makes senso to have a different type of reference?

Letting a user swap an audio file on the component is an advanced feature.
If that’s how they want to do it, then by all means.
It’s not the default behavior, nor is it something we recommend.

Fair @Mathias_Westerdahl, I thought its more common to use sound components this way. Maybe its just me who prefers programmatic approaches but I also saw people on the forum trying to load audio resources dynamically into pre-defined “slots”.

In docs Sound in Defold there is official example of how to do this:

The resource path to the sound (hash). You can use the resource path to change the sound using resource.set_sound(path, buffer). Example:

But the caveat that “sound resource must be unique, otherwise even if user thinks he has unique components they become the same and play the same sounds” is not mentioned?

What I’m saying is, yeah perhaps its an advanced use-case, but maybe its also good to have this caveat stated in docs, or even better allow sound resource does not need to be unique, making it a good mix of “keep it simple” but “be more friendly for flexible cases” as sooner or later more people would stumble on this?

What are you referring to here? That the resource path has to be unique? That’s because it’s a file path. File paths are unique.

Apparently if I have “music_1.sound” and “music_2.sound” both reference “empty.wav” and then I set “file.wav” in “music_1.sound”, “music_2.sound” will be switched to “file.wav” also, even thought its a separate sound component? I found it confusing

That doesn’t seem correct.

Did you use the resource.set_sound() function? If so, with what arguments?

yep, resource.set_sound(go.get(url, ‘sound’), buf)

Thats what @britzl said also?

This is correct. The sound component has a reference to a sound resource. If two sound components refer to the same sound resource (ie same file from your assets) it will not be loaded twice. The components will refer to the exact same data. And resource.set_sound() will change the data in the sound resource referenced by a sound component. So with this in mind it makes sense that the two sound components both will use the new sound.

Well, it’s the resource (a.k.a “the file”) that you’re replacing the contents for.
The sound components still point to “empty.wav”, but you just replaced that file.

1 Like

Aha, am I using it incorrectly? Can you think of a way to not create 20 copies of “empty.wav” for 20 placeholder “slots” “.sound” components to achieve this advanced use-case?

Well, it depends on what you wanted to do? :slight_smile:

Perhaps it’s better you create your own thread, asking for advice on how to solve your particular case.
I’m not even sure what the OP tried to do. :thinking:

I’m happy to create a new thread but IMO its very much in-line with original post. I think me and original poster stumbled on the same problem and same use-case, and because this thread answered my question, so that’s why I follow up here :sweat_smile:

Here

Idea: Have pre-defined “slots” and then create new sounds at run-time

Problem: .sound MUST have sound pre-specified. I tried setting “empty.wav” in all of them. Swapped new different files into effect1 and effect2, played both of them, but they play the same sound instead of the ones I wanted. Walk-around is to create multiple copies of “empty.wav”.

I understand why it happens, but it still feels more like a walk-around :grin:

Well, it is seems you still have a problem, so I’m not sure it solved it for you? :thinking:

Also, I’ I’m not entirely sure what the OP tried to do.

Normally, what we recommend is that you create:

  • One sound component per sound file: beep.sound → beep.wav, boop.sound → boop.wav and so on
  • One sound.script that handles the sound playing logic.
  • A game object that contains all the sound components and the script

This way, all the sounds gets loaded when the game object is loaded.

I’m not exactly sure about your actual use case, but this this is our usual recommended approach.

If this doesn’t work for you, you need to specify more precisely what doesn’t work (and why), so that we can help.

Aha sorry for not being clear, I don’t have a problem anymore as this post solved my problem by creating copies of empty placeholder, I just follow up on this as I wanted to share that I was confused with this too and haven’t found my answers in docs and because I still “feel” solution is a walk-around.

Yep that is my setup also!

My use-case is keep my project code-centric, meaning sounds are not pre-defined but swaped at run-time. Confusion comes from a fact that two .sound components have separate id, but play same sound even though I swapped the buffer separately per id, because original sound reference was the same. You see what I mean here? :sweat_smile:

I thought sound components are separate as they are literally separate components! But they end up playing same sound just because orignal “empty.wav” reference was the same. Even tho you swapped new buffers. And if you are not aware of that you end up thinking that its something wrong with the code haha

So in my mind real trouble would come when you for some reason decide to have different .sound placeholdes throughout your project, that you plan to swap buffers later, so you create empty.wav for all and swap buffers later. So you need to manually keep track that all of them have separate “empty-copy-copy-copy.wav”..

I understand what you’re saying. But you didn’t though. You swapped the resource itself.
You didn’t swap the sound id’s for the component.

They are separate, but they may have different settings (looping etc). The sound file resource is still unique though. That’s what the resource.* Lua module is for: adding / manipulating resource files.

Not sure why it’s better to load these files programmatically. Seems like it’s more of a hassle tbh, but, as I mentioned, you can.

Also I want you to note that the sound property of the sound component is a resource property.
You can get it and set it to another component, which is a variant to the behavior we’re discussing here:

  • Load main-sounds.collection, which has laser.sound → laser.wav
  • Load theme.collection, which has “theme_laser.sound” → jinglebells.wav
  • Set the themed sounds over the placeholders:
local theme_laser = go.get(theme_laser_url, "sound") -- /path/to/jinglebells.wavc
go.set(laser_placeholder, "sound", theme_laser)
  • unload the theme.collection

I mean before I thought that I was swapping a new file but now after reading the post I understand that you swap the buffer of the resource :rofl: but happy you clarified it!

Aha so you are suggesting that instead of swapping buffer of the resource its better to have collection of pre-defined resources and then load and unload that collection? Will try!

I generally like to do things programmatically as I find it faster and prefer flexibility to tinkering with UI :slight_smile: at least for me its more intuitive to write a line of code than manually create pre-defined components… but I guess it would cause performance issues to create many things at run time?

Last question, but still for my own hassle haha, if I choose a programmatic approach, is there a third option? Maybe create a new resource at run-time instead of loading from a collection?

One benefit of doing it our preferred way is so that the build system knows which resources to include.

You can also create the collection file with components using separate scripts. Our formats are text, so you can create the setup offline, but still keep it “idiomatic”.

1 Like