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!
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!
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.
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 ), 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
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
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
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
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!