Scripting Helpers is winding down operations and is now read-only. More info→
Ad
Log in to vote
2

How do I test whether a soundId in a script is valid?

Asked by 7 years ago
            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?

0
There's no real way to check whether or not a property is valid or not. If you put in a song ID, it'll take it as proper parameters. Add a conditional checking if the pitch of the song is at least 0.1. That way, you'll know whether or not something is playing. DepressionSensei 315 — 7y
0
You will need to test this but you might be able to use, http://wiki.roblox.com/index.php?title=API:Class/Sound/IsLoaded User#5423 17 — 7y
1
@DepressionSensei and @kingdom5: I found a way chess123mate 5873 — 7y
0
very niceu DepressionSensei 315 — 7y
0
@chess123mate Thanks for the help! It works :) AbandonedRick 112 — 7y

2 answers

Log in to vote
3
Answered by 7 years ago
Edited 7 years ago

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:

  • The sound has to be fresh (that is, you cannot re-use an old Sound), or else IsLoaded will always be true
  • The sound has to be parented into the game somewhere (I didn't test putting it in places other than the workspace, but that should be fine)
  • Sometimes invalid sounds will load immediately (but have a TimeLength of 0), other times they will never load
  • Calling IsValidSound 100x takes several seconds (if the particular soundIds haven't been loaded before and if they are all invalid) due to the errors in the output

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:

  • Get the sound loading
  • If it loads immediately, check the TimeLength property to see if it's a valid sound or not
  • Otherwise, wait until Roblox prints out an error saying that the song is invalid or wait until the song loads. If it loads, check the TimeLength property to determine whether it's valid or not.

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:

  • There is no guarantee that it is a valid sound (I don't know - is it possible to upload an invalid sound file to Roblox? If so, IsSound may return 'true' on a sound file that is invalid)
  • Unlike in IsValidSound, the sound itself isn't loaded, so you end up losing time when the 'id' is actually a sound you wish to play
  • The MarketplaceService errors if you ask it for too much information. When it does this, IsSound has little choice but to return false (I tried trying again, but this made things worse). I ran into this when asking it for hundreds of ids within a few seconds. Not only did the requests take a long time to return, they resulted in errors. (Specifically, I was scanning a section of IDs that had 2 valid sounds and it didn't always find them.)
  • Sometimes, even when sending just a single request, it would take > 5 (perhaps even 10) seconds to respond

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.

0
Thanks for the help! It works :) AbandonedRick 112 — 7y
Ad
Log in to vote
0
Answered by 7 years ago
Edited 7 years ago

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

0
Nice find! I've updated my answer with a function based on what they were talking about. chess123mate 5873 — 7y

Answer this question