[SOLVED] How to play randomly a sound from a list of sound clips of the same type

Papers, Please - Sound Files is what I have in mind when it comes to voices, not actual speech dialogue.

Like the title stated, I’d assuming I would have a bunch of .WAV files for the voices inside of a folder, let’s call it voices, and then I’d have a game object that would have a sound component, that would be the voice, and inside of the script that would also be in the game object that would call something like

sound.play("go#sound", {delay = 1, gain = 0.5, pan = -1.0, speed = 1.25})

in the documentation. But how would I randomly get the voices files
empty sound slot
and what would I put in the Sound slot then?

Thanks in advance.

You would have one sound component for each voice line and give them IDs like voice1, voice2, etc. Then all you need is a bit of code to pick one of them:

local VOICE_LINES_AMOUNT = 5

-- This will be between 1 and 5, inclusive
local n = math.random(VOICE_LINES_AMOUNT)

local id = "go#voice" .. n
sound.play(id, {delay = 1, gain = 0.5, pan = -1.0, speed = 1.25})

You’ll also want to make sure to call math.randomseed() somewhere to initialize Lua’s RNG:

-- In some init() function
math.randomseed(socket.gettime() * 10000)

-- Some people recommend throwing out the first few random numbers
math.random()
math.random()
math.random()
1 Like

Thank you so much, I’ll try and implement this. :+1: From what I understand, there’s an extension called Dicebag that handles randomness better.

If you also don’t mind explaining the logic, why would you need a sound component for each voice line instead of one sound component that references .wav files? I’m still unclear on that part. Thank you.

The sound component itself can only refer to one sound resource at a time.

You can use a script resource property to refer to multiple sound resources, then set the sound resource on the sound component before playing the sound.

Note: There is an issue with this approach (it may crash) that we’re fixing in the next beta release, which will be released on monday.

5 Likes

Thank you very much for the information.

What would be the syntax for writing that? I’m reading Script properties and am looking at the examples for the documentation. I do see there being a function for resource called

resource.set_sound(path, buffer))

so I was thinking of writing something like

go.property("voice_clip",  resource.set_sound("/voice_clip.wav", 0)))

but how would referring to multiple sound resources work, and what is the argument type because it’s not showing up in the editor. Thank you.

Resource properties are documented here: Script component properties

Although the sound resource is for some reason not documented… I’ll make a note of that. But I believe it would be:

go.property("sound1", resource.sound("sound1.wav"))

function init(self)
    resource.set("#somesound", "sound", self.sound1)
end
2 Likes

set has two parameters
I think set only has two parameters, unless there’s function overloading?

Sorry, it should be go.set()!

1 Like

No problem, thank you for your help. There’s one problem though, the property’s not showing up properly, I don’t know if that’s the glitch or not.
property's not showing up
The code below’s what I wrote inside of book.script which is attached to book.go. I didn’t put it in a function or anything.

go.property("sliding_sound", "assets/heyy-jayy__sliding-book-1/book_sliding_"..math.random(1,4)..".wav")

directory
The only thing I can think of being the issue is the directory? But I’m pretty sure I wrote in the correct directory.

This will not work. It has to be a single string without string concatenation or any pieces of Lua code. The string will be evaluated at build-time not run-time.

In your case you need four resource properties:

go.property("sliding_sound", "assets/heyy-jayy__sliding-book-1/book_sliding_1.wav")
go.property("sliding_sound", "assets/heyy-jayy__sliding-book-1/book_sliding_2.wav")
go.property("sliding_sound", "assets/heyy-jayy__sliding-book-1/book_sliding_3.wav")
go.property("sliding_sound", "assets/heyy-jayy__sliding-book-1/book_sliding_4.wav")

I would probably recommend that you use four sound components. There is no real advantage to using sound resource properties.

2 Likes

Thank you. Is it suppose to be appearing in the editor because it’s not for some reason. I tried giving them different names but they also don’t appear in the editor.
image

I also have a question about setting resources, am reading Sound In Defold and System API documentation

local boom = sys.load_resource("/sounds/boom.wav")
local path = go.get("#sound", "sound")
resource.set_sound(path, boom)

if it’s possible to do that, shouldn’t it be possible to do something like (unless string concatenation also doesn’t work with sys.load_resource

resource.set_sound(go.get("#sliding-sound", "sound"), sys.load_resource("assets/heyy-jayy__sliding-book-1/book_sliding_"..math.random(1,4)..".wav"))

with #sliding-sound's the name of my sound component. However I don’t know how that would work during runtime. For the project idea, I have a game object that’s intended to be spawned via factory, and it’ll eventually get destroyed and be respawned again, so I was hoping to randomise the sound and set it everytime it ‘respawns’. I should probably retitle the thread but I don’t know what to call it.

I think we’re overcomplicating things. Here is my recommendation:

  1. Create a game object with id sounds and add it to your “main” collection. Add your sound components to this game object.

NOTE: This does not have to be the factory spawned game object. It is very useful to have your sound components in a central game object as it makes it easier to add things such as sound gating, randomizations of the same sound, easy muting of all sounds of a certain type etc

Screenshot 2022-10-31 at 07.55.44

  1. In your factory spawned item, add a script and randomly select the sound to use on creation (ie in init):
function init(self)
    self.sound = "sliding-sound" .. math.random(1, 4)
end
  1. Add a helper function in the same script to play the sound:
local function play_sound(self)
    local sound_to_play = "/sounds#" .. self.sound
    sound.play(sound_to_play)
end
  1. Use the helper function to play the sound when needed, for example when receiving a message:
function on_message(self, message_id, message, sender)
    if message_id == hash("foobar") then
        play_sound(self)
    end
end
4 Likes

Thanks Britzl, I probably am overcomplicating things, I’ll try to implement what you’ve said. :rofl:

Since I plan on having minigames and multiple sounds, I’m assuming this sound game object would persist throughout all of the minigames then?

If the sounds are shared between the mini games then sure, you can put the game object in the collection where you are loading and unloading different mini games. You just need to make sure that you are using an absolute url to the game object, for instance “main:/sounds#sliding-sound1” etc (assuming sounds are in a collection with id “main” and in a game object with id “sounds”.

Having the sounds in a central place and in memory all the time will save you from loading the sounds each time you start a mini-game. But this is an optimization that you don’t necessarily have to make now. And it is easy to change later on.

Thank you britzl.

1 Like