if(pcall(function() customsong.SoundId = "rbxassetid://" .. "dummysongid" end)) then customsong:Play() else print("song id is not valid") end
Why doesn't it print "song id is not valid" since the soundid is invalid?
pcall
only returns false if an error occurs. Since giving 'SoundId' an invalid sound does not raise an error, pcall
returns true.
It took me a bit to figure out how to do this, but I tested this and it works:
function IsValidSound(soundId) --either returns false or valid sound local con1, con2, yieldFolder, valid local sound = Instance.new("Sound", workspace) local function cleanup() con1:disconnect() con2:disconnect() yieldFolder.Name = "Different" end yieldFolder = Instance.new("Folder") con1 = game:GetService("LogService").MessageOut:connect(function(msg, msgType) if msgType ~= Enum.MessageType.MessageError then return end if not msg:find(tostring(soundId)) then return end valid = false cleanup() end) con2 = sound.Loaded:connect(function() valid = sound.TimeLength > 0 cleanup() end) sound.SoundId = soundId if sound.IsLoaded then --already loaded cleanup() if sound.TimeLength == 0 then sound:Destroy() return false end return sound end yieldFolder.Changed:wait() --wait for either event to occur yieldFolder:Destroy() if not valid then sound:Destroy() end return valid and sound end
I learned the following:
IsLoaded
will always be true
Note that IsValidSound may not return immediately (it waits for the sound to load if needed) - that is, it may act like a "wait" command, so don't put it in a loop with hundreds of sound IDs to check (if you needed to do that, you'd probably want to use coroutines so each sound can load simultaneously instead of one at a time). In your case you can use it like this:
local sound = IsValidSound("rbxassetid://" .. "dummysongid") if sound then sound:Play() else print("song id is not valid") end
Don't forget to Destroy the sound when you're done (so that you're not left with lots of different Sounds in the workspace). Alternatively, since a sound only needs to load once before its ready to play, you could do:
--customsong defined previously local sound = IsValidSound("rbxassetid://" .. "dummysongid") if sound then customsong.SoundId = sound.SoundId --transfer to your customsong sound:Destroy() --destroy the sound created by IsValidSound customsong:Play() else print("song id is not valid") end
The IsValidSound
is a bit complicated, but in reality it's just doing this:
You cannot use 'coroutine.yield' reliably (Roblox often just resumes it after a brief delay), so I use a 'yieldFolder' instead. The idea is to pause the calling thread until one of the events we set up occur (at which point we change the name of the folder, allowing the calling thread to finish up). Do note that if neither event occur, IsValidSound would yield forever (much like calling wait(1000000000)
). However, based on my testing, that shouldn't happen. If you wanted to be absolutely safe, you could add a 3rd connection that just waits for a certain number of seconds to go by (ex, 5 seconds), and then - if valid
had not been set to a non-nil value by then - set valid
to false
and call cleanup()
.
(Edit)
One potential flaw with the above solution is that it assumes that Roblox will print the sound ID to the Output as an error when the ID is an invalid sound. If they change this behaviour for whatever reason (though I doubt they will), the function will stop working.
HappyWalker found a forum post talking about a function in the MarketplaceService - GetProductInfo - which can help us out. I played around with it a bit and came up with this function:
function IsSound(id) --Unreliable way to determine if something is a sound local success, value = pcall(function() return game:GetService("MarketplaceService"):GetProductInfo(id).AssetTypeId == 3 end) return success and value end
In the case of failure, It is usually faster than "IsValidSound", but it has numerous problems:
At best it can be used to "help out" the IsValidSound function by filtering out IDs that aren't sounds - thereby saving IsValidSound from raising more errors (which may slow down the place if you try to do hundreds per second). It should only be used if you expect the IDs to be invalid, or if you have to go through a lot of them. For the occasional use by players requesting music, IsSound should not be used.
I'm not sure if this works with Sounds but here they found a good way to tell if a decal/texture is valid or not:
https://forum.roblox.com/Forum/ShowPost.aspx?PostID=182215819