Advice on loading multiple sound effects files at once into an array or list or table etc

G'day! First time thread asking something potentially basic, I'm fairly new to heavy-end software development (I've put together/survived Master's degree stuff in Python in past, and have been on a diet of Lua tutorials + SDK reading) so please bear with me if I've overlooked something! :smiley:

I have a fairly basic(?) setup where in a file for a projectile, I've loaded three premade variations of a sound effect, and placed them all within a table.

When an event triggers that requires one of X number of sound effects to be played, it uses the math.random function and the length of the table to randomly choose to play one of the sound effects:

local pd <const> = playdate
local sfx <const> = pd.sound.sampleplayer

local sound1 = sfx.new("/sound/sfx/SOUNDEFFECT1")
local sound2 = sfx.new("/sound/sfx/SOUNDEFFECT2")
local sound3 = sfx.new("/sound/sfx/SOUNDEFFECT3")
local sfxtable = {sound1, sound 2, sound3}


-- if THING happens, THEN
      sfxtable[math.random(#sfxtable)]:play(1)
-- end

However, I was wondering if there was a better way of loading several files in, instead of having to load each sampleplayer object in separately, one at a time? Is it possible to point to just a directory, and then - assuming all the contained sound files are formatted correctly -, load each sound file as an element within a table/list/array?

I'm at that awkward ''not-beginner-but-not-experienced' level where I'm trying to finish a project as an intermediate stepping stone... to making ALL of the Playdate games

I'm doing this on a Win 11 system, at the mo, but any answers should be similarly applicable to other as well! :slight_smile:

I'd be interested if there's a better way as well. My implementation is similar:

local tmp = snd.sampleplayer.new("sounds/attack")
	table.insert(AudioClips.Human.Attack, tmp)
	tmp = snd.sampleplayer.new("sounds/attack_2")
	table.insert(AudioClips.Human.Attack, tmp)
      ....  MORE OF SAME...

Only difference from yours is that I re-use tmp.

You could use

https://sdk.play.date/inside-playdate/#f-file.listFiles

to return a list of files in your sound effects directory.

Then loop over that table to creating your sound effects table.

Or use the file list table directly she call file player on the item you want to play.

2 Likes

D'oh, thought I was being clever, and totally overlooked the file list function! Thank you for highlighting it, I'm trying to get into the habit from the get-go of making scalable code, so you've helped me learn something new!

For anyone in a similar boat, after a little faffing/help online, I've got a working example, I've not done anything really fancy in terms of error checking, beyond spitting out a debug message, but you can apparently filter out hidden files as well, if your setup happens to make those/it causes an issue.

With the setup below, if you have an audio creation tool/script/batch file etc etc that automagically formats and exports a hefty number of files into one directory, you can reliably use the below example to select from one of any of the contained audio files (or other files, for that matter :thinking:), and in the use-case below, randomly choose a sampler object to play. This ought to work similarly for other purposes too!

local pd <const> = playdate
local sfx <const> = pd.sound.sampleplayer -- using sampleplayer for sound fx

-- Create a table containing the names of all the files contained in a directory
local audioFiles = pd.file.listFiles("/DIRECTORY/TO/YOUR/SOUNDS/")
-- Create a blank table to contain the sampleplayer objects to be played
local audioPlayer = {}


-- Check each filename, and for each correctly loaded file found, create a
-- sample player object and load into the above audioPlayer table.

for key, fileName in ipairs(audioFiles) do
    local player, err = sfx.new("/DIRECTORY/TO/YOUR/SOUNDS/" .. fileName)
    if player then
        table.insert(audioPlayer, player)
    else
        print("Error loading audio file:", error) -- If file not loaded for any reason, print error to debug console
    end
end
1 Like

Adding to the above, you can make life easier by creating a function elsewhere, and calling it as needed following an event, as so:

local pd <const> = playdate
local sfx <const> = pd.sound.sampleplayer

-- TODO, ADD ERROR CHECKING TO SEE IF FILES ARE FINE, AND IF DIRECTORY IS CORRECT TOO, OTHERWISE GAME WILL CRASH


function playRandomDirectorySound(directory)
    local audioFiles = pd.file.listFiles(directory)
    local audioPlayer = {}
    for key, fileName in ipairs(audioFiles) do
        local player, err = sfx.new(directory .. fileName)
        if player then
            table.insert(audioPlayer, player)
        else
            print("Error loading audio file:", error)
        end
    end
return audioPlayer[math.random(#audioPlayer)]:play(1);
end


-- IN NEW FILE

-- SOMETHING HAPPENS
playRandomDirectorySound("PATH/TO/DIRECTORY/")

Make sure you don't forget to format your directory path right, or you'll get a nil return object error, unless you error handle it, which I've been lazy and not done here, hopefully this comes in handy for some peeps :slight_smile:

Some Mac users will have odd files beginning with ._ (dot-underscore) in the sounds folder, so you do want to filter those.

I had to do it in the Daily Driver demo as I grab levels from a folder in a similar way.

2 Likes

You load every sound in a given folder just to play one once. You should either:

  • Load the folder sounds once in a global variable, and play one randomly:
audioPlayers = {}
function playRandomDirectorySound(directory)
    local directoryAudioPlayers = audioPlayers[directory]
    if directoryAudioPlayers == nil then
        directoryAudioPlayers = {}
        local audioFiles = pd.file.listFiles(directory)
        for key, fileName in ipairs(audioFiles) do
            local player, err = sfx.new(directory .. fileName)
            if player then
                table.insert(directoryAudioPlayers, player)
            else
                print("Error loading audio file:", error)
            end
        end
        audioPlayers[directory] = directoryAudioPlayers
    end
    return directoryAudioPlayers[math.random(#directoryAudioPlayers)]:play(1);
end
  • Choose one sound randomly in audioFiles, load it and play it.
function playRandomDirectorySound(directory)
    local audioFiles = pd.file.listFiles(directory)
    local player, err = sfx.new(directory .. audioFiles[math.random(#audioFiles)])
    if player then
        player:play(1)
    else
        print("Error loading audio file:", error)
    end
end
2 Likes

This is much better than my quick attempt, I suspect I would have started seeing performance hitches on loading sounds/overlooked it due to general inexperience on my part, really appreciate this :slight_smile:

I tested the second version of the answer, and it works, though the line

player.play(1)

errored out, and worked when replaced with

player:play(1)

It seems a less resource-intensive option and stills works nicely!

1 Like

Thanks and sorry for the typo :sweat_smile:

1 Like