Why do my sounds create errors?

I have a gameobject with three different sounds attached to it as well as this code. The code should play each of the sounds once, in turn, after the set delay and then repeat from the start of the code

function init(self) 
	for i=1,100 do
		sound.play("/sound#creak1", {delay = 10, gain = 0.1})
		sound.stop("/sound#creak1")
		sound.play("/sound#creak2", {delay = 17, gain = 0.1})
		sound.stop("/sound#creak2")
		sound.play("/sound#creak3", {delay = 29, gain = 0.1})
		sound.stop("/sound#creak3")
		i = i + 1
	end
end

however, it keeps coming up with this error


(sorry i couldnt fit the entire error in one photo)
i don’t think all the sound instances should be in use? that’s why i put to stop the sound in the code but i’m not sure what’s going wrong.
if anyone has any advice that would be really appreciated!

We recommend you actually post the text, since that’s much more readable, and it allows others to find issue with similar problems.

The sound instances aren’t destroyed at the same frame, but you need to wait a frame.

With your loop, you create 300 instances.

Also, even if you increased the sound.max_sound_instances, your code would still stop the sound, since it’s called right after the call to play the sound.

Instead, try something like this (untested) code:

local function play_sound(self, index)
    local sound = self.sounds[index]
    
    sound.play(sound.url, sound.options, function (self, message_id, message, sender)
            self.currentsound = self.currentsound + 1
            if self.currentsound > #self.urls then
                self.currentsound = 1
            end
            play_sound(self, self.currentsound)
        end)
end

function init(self)
    self.sounds = {
        {url = "/sound#creak1", options = {delay = 10, gain = 0.1}},
        {url = "/sound#creak2", options = {delay = 17, gain = 0.1}},
        {url = "/sound#creak3", options = {delay = 29, gain = 0.1}},
    }
    self.currentsound = 1
    play_sound(self, self.currentsound)
end

As I said, untested, but I think it should work.

The big difference here is that I specified the callback to be called when the sound is finished. At that time, it will increase the counter, and call play_sound() again.

It is actually 150 since i is incremented at the end of the for loop, effectively increasing it twice per iteration. But this is besides the point. :slight_smile:

The suggestions by @Mathias_Westerdahl look good!

2 Likes

Thankyou for your explanation, but its come back with this error instead now

ERROR:SCRIPT: main/scripts/creaking.script:3: attempt to call field 'play' (a nil value) stack traceback: main/scripts/creaking.script:3: in function play_sound main/scripts/creaking.script:19: in function <main/scripts/creaking.script:12>

Yeah, as you can see, in my untested code I accidentally overwrite the sound module with my new local sound.

Replace it with local soundinfo = .. etc, and it should work better.

Thankyou!
I ended up using this code and it worked.

local function play_sound(self, index)
	local soundinfo = self.sounds[index]
	sound.play(soundinfo.url, soundinfo.options, function (self, message_id, message, sender)
		self.currentsound = self.currentsound + 1
		if self.currentsound > #"self.urls" then
			self.currentsound = 1
		end
		play_sound(self, self.currentsound)
	end)
end

function init(self)
	self.sounds = {
		{url = "/sound#creak1", options = {delay = 10, gain = 10}},
		{url = "/sound#creak2", options = {delay = 17, gain = 10}},
		{url = "/sound#creak3", options = {delay = 29, gain = 10}},
	}
	self.currentsound = 1
	play_sound(self, self.currentsound)
end