I’m trying to make an audio go through multiple different songs. The issue I’m having is that no matter what I try or how much I search for a solution, it seems like it only changes the song after the initial one plays only once. I was originally using the new roblox audio api with audioplayer, but then realized .Ended doesn’t work for it so I switched back to a normal sound, and with all the variations of my code I’ve tried none of them have worked. Here’s the current version of my code.
music = script.Parent.Parent.Music.Song
music:Play()
local function nextSong()
print("in")
--Audio 1
if music.SoundId == "rbxassetid://17403235328" then
print("in1")
music.SoundId = "rbxassetid://17403230356"
music.Loaded:wait()
music:Play()
--Audio 2
elseif music.SoundId == "rbxassetid://17403230356" then
print("in2")
music.SoundId = "rbxassetid://17403222196"
music.Loaded:wait()
music:Play()
--Audio 3
elseif music.SoundId == "rbxassetid://17403222196" then
print("in3")
music.SoundId = "rbxassetid://17403235328"
music.Loaded:wait()
music:Play()
end
end
while true do
task.wait(.1)
music.Ended:Wait()
nextSong()
end
Does the second song play as expected? Is it possible that the next song finishes playback before the next call to music.Ended:Wait()? I’m curious if changing the sound ID resets music.TimePosition back to 0.
Sorry for the very late reply, but the reason I’m not doing this is because I have another unrelated script that when an object is clicked, it checks if the sound’s volume is 0, and if so, sets the volume to 1, and also does the opposite. With multiple sounds I don’t really know how I would set or check the volumes.
Instead of having this on an infinite loop, you could connect the Sound.Ended event within the nextSong function and then have it re-call the function after it concludes.
Here’s an example revision that is also more scalable if you want to add more audios to the list (I can explain more of the specifics about how it works, if interested):
Example Revision
local parentObject = script.Parent
local grandparentObject = parentObject.Parent
local Music = grandparentObject:WaitForChild("Music")
local Song = Music:WaitForChild("Song")
local audioIds = {
[1] = 17403235328,
[2] = 17403230356,
[3] = 17403222196,
[4] = 17403235328
}
local function assignSoundId(number)
Song.SoundId = "rbxassetid://"..number
end
local function nextSong()
local currentSoundId = string.match(Song.SoundId, "%d+")
if currentSoundId ~= nil then
currentSoundId = tonumber(currentSoundId)
end
local currentAudioNumber = table.find(audioIds, currentSoundId)
if currentAudioNumber then
local nextAudioTrack = audioIds[currentAudioNumber + 1]
if nextAudioTrack then
assignSoundId(nextAudioTrack)
else
assignSoundId(audioIds[1])
end
else
assignSoundId(audioIds[1])
end
warn("Now playing: "..Song.SoundId)
Song:Play()
Song.Ended:Wait()
print("Song ended")
nextSong()
end
nextSong()
On a side note, AudioPlayer.Ended isn’t released yet but it’ll likely be released soon, given that it’s now listed on the Roblox Creator Documentation site along with the fact that there have been several pending changes to the Audio API within recent release notes.
It seems to work, (at least from my short time testing it), but the audio doesn’t automatically play after changing the id? Also, if it’s not too much trouble, a little bit of explanation would be helpful because I do want to get better at coding (like what table.find, and string.match(Song.SoundId, “%d+”) do)
When playtesting, keep an eye on the Output to see if there’s a noticeable delay between any of the print statements.
For reference:
Does it print “Song Ended” and then takes a while for it to print “Now playing: (New SoundId here)”?
or
Does the song audibly end / it sounds like the audio has stopped playing and then it takes a while for “Song Ended” to appear in the Output?
If it’s the latter (the second one), then maybe the original audio file has very quiet sounds / extra silence added to the end of it, or something similar.
Try selecting the Sound object in the Explorer while it’s playing to see when it’s getting close to the end of the audio, as that will help confirm whether or not there’s any delay between “Sound Ended” printing and when the audio actually ends.
Sure! I’ll explain it in one of my next replies so that you can review what I asked / mentioned above while I write out an explanation.
Third (optional), the place in the string to start looking at (which defaults to 1, meaning that it will start looking from the very beginning of the string).
In this case, we are looking through the current SoundId for the Sound object, which is stored as a string in the format of rbxassetid://NumberGoesHere. However, if we only want to look for the number in the SoundId so that we can compare it to the numbers stored in the audioIds table, we can use a character class.
The %d character class is specifically for numbers. When used with string.match(), it will return the first number that was found in the string. If we want to retrieve every single number it found in the string, we can append a magic character to the end of it. In this case, that’s the +, which, as described on that Roblox Creator Documentation page, is supposed to “Match 1 or more of the preceding character class”.
So, when we use that as the string pattern on the SoundId, it will ignore everything else except the number at the end. Instead of returning "rbxassetid://100", for example, it’d just return "100".
Because string.match() returns a string, we need to convert it to a number in order to use it in table.find(). We can use tonumber() on the string to do so. However, if no number was found in the original string.match() check, then currentSoundId would be nil.
Now, we can use table.find() with the number that was retrieved from the SoundId. Like string.match(), it also accepts 3 different values:
First, the table to look through (which is the one called audioIds at the top of the script).
Second, the value to look for inside of the table (which is the number from the SoundId).
Third (optional) the place in the table to start looking at (which defaults to 1, meaning that it will start looking from the very beginning of the table).
If it finds that value in the table, then table.find() will return a number that basically says what spot (index) that value can be found at in the table. Here’s an example:
local tableExample = {
[1] = 100,
[2] = 200,
[3] = 300
}
local numberToLookFor = 200
local tableCheck = table.find(tableExample, numberToLookFor)
print(tableCheck) -- 2
When applying that example to the audioIds table, this effectively tells us which song is currently being played.
Once we look through the table, we need to make sure it found the number, so we’re checking if currentAudioNumber returned something that wasn’t false or nil (when checking something in a conditional statement like if variablesGoesHere then, stuff like numbers, strings, etc. return a “truthy value” which means that the condition passes and the code continues running).
Then, we want to get ready to play the next song. Because we know what spot in the table that song was at (let’s just say it was spot number 3 out of 4), we can add 1 to currentAudioNumber and then look through the audioIds table at that spot. So that means that it would basically be saying audioIds[4], and it’ll find whatever value was stored in that spot / index of the table.
If it found a value, we call the function assignSoundId, sending through the value that was stored in the table so that it can update the SoundId property. However, if it didn’t find a value (which, in this case, would mean that nothing was stored in the next spot), then we know that we’ve reached the end of the list.
So if we’re currently playing audio 4 out of 4, and then the script tries to look for spot number 5 in the audioIds table, it’ll return nil. As a result, the if nextAudioTrack then condition will not be met, and it’ll default to checking the next condition.
If the first condition wasn’t met, then we default to calling the assignSoundId function with spot number 1 from the table, which essentially restarts the playlist from the very beginning, playing the first audio and then the entire process repeats after the song stops playing.