you can add as much songs as you want, with the script you wrote say you add 10 songs, ur gonna have to make the same loop 10 times
The same loop 10 times? Do you mean 10 while true do loops? Also what’s the problem with just looping through a table and music play and ending them?
No, what I mean is you’re gonna have to copy it and paste it multiple times just to make a playlist of 10 songs.
Then just make a table and make a for i,v in pair loop. End of discussion. No need for module
k told you from the beginning if you don’t think it’s useful then flag it
You was just saying a multiple times that “this method sucks! My module is better”
Horrible model I don’t see why it can even be good horrible Only 4 lines of scirpt?
Changing the location where the audio plays.
Everyone, Calm down…
Okay, This Topic became off-topic quite quickly.
I am a tad bit confused on why everyone insists on posting how irrelevant and useless the module is, I just see this as a starting point for a piece of software which has potential, instead of calling his code dumb
or useless
try and promote something to make it awesome!
When it comes to something as generic as this module, I think it comes down to a users experience, i’ve been on roblox for a while now. Things like DataStore2, I find a waste of time (Just me, I know its good.) but thats because I have the mindset and skills to understand and create a unique interpretation of a DataStore handler.
These are generic modules which help people who don’t really understand studio to the fullest, one might understand how to require modules, but not manipulate sound objects or create a virtual queue with them.
Feedback
About OOP, I am 100% agreeing, but I would include a bit of ECS if you could, allowing you to essentially wrap these Sound objects into Components that the Module itself can manipulate to achieve awesome things.
And on top of that great suggestion from above, I suggest which could be an awesome addition to this module if you put the time into it. Sound Effects, these are not really used and honestly could go hand in hand with an Sound Wrapper, Imagine being able to control variables on a sound object that you normally don’t get! Exciting!
https://developer.roblox.com/en-us/api-reference/class/PitchShiftSoundEffect
https://developer.roblox.com/en-us/api-reference/class/CompressorSoundEffect
https://developer.roblox.com/en-us/api-reference/class/TremoloSoundEffect
https://developer.roblox.com/en-us/api-reference/class/FlangeSoundEffect
https://developer.roblox.com/en-us/api-reference/class/ReverbSoundEffect
https://developer.roblox.com/en-us/api-reference/class/EchoSoundEffect
https://developer.roblox.com/en-us/api-reference/class/DistortionSoundEffect
https://developer.roblox.com/en-us/api-reference/class/ChorusSoundEffect
https://developer.roblox.com/en-us/api-reference/class/EqualizerSoundEffect
This literally never happened in my first resource and it got down, so why?
Also I think there should just be a function to return the sound instance instead. More customizable.
Not going to lie, trying to throw someone elses work down just because yours didn’t turn out so good is really bad thinking. There is no debate for that.
As cluttered with useless arguments that this post has, it’s as simple as what @AsynchronousMatrix said. Please contribute to the resource if you think it’s bad.
It’s as simple as linking something useful, or just not bothering with the post at all if you don’t want to contribute. We’re in the resources category, so let’s all take the time to help and appreciate the module.
This is my personal rewrite because seeing the other complaints in the post
@wShadowRBLX You are free to use this and update it, no need to credit me, I’ve tried implementing what @AsynchronousMatrix said about OOP and ECS (keep in mind i dont know whats ecs nor i use it but based on my understanding its just wrapping on another object?)
local function InstanceTypeof(i: any) return if typeof(i) == "Instance" then i.ClassName else typeof(i) end
local function InvalidArgumentType(argnumber: number, funcname: string, expected: string, got: string)
return string.format("invalid argument #%d to '%s' (%s expected, got %s)", argnumber, funcname, expected, got)
end
local Melody = {}
function Melody:__index(k)
if Melody[k] then
return Melody[k]
end
return self.__sound[k]
end
function Melody:__newindex(k, v)
if self[k] then
self[k] = v
return
end
self.__sound[k] = v
return
end
function Melody.__tostring()
return "Melody"
end
function Melody:AddSoundIdToQueue(id: string | number)
assert(InstanceTypeof(id) == "string" or InstanceTypeof(id) == "number", InvalidArgumentType(1, "AddSoundIdToQueue", "string or number", InstanceTypeof(id)))
if typeof(id) == "string" then
table.insert(self.__queue, id)
else
table.insert(self.__queue, "rbxassetid://"..id)
end
return
end
function Melody:RemoveSoundIdFromQueue(id: string | number): boolean
assert(InstanceTypeof(id) == "string" or InstanceTypeof(id) == "number", InvalidArgumentType(1, "RemoveSoundIdFromQueue", "string or number", InstanceTypeof(id)))
local searchquery = "rbxassetid://"
if typeof(id) == "string" then
searchquery = id
else
searchquery ..= id
end
local index = table.find(self.__queue, searchquery)
if index then
table.remove(self.__queue, index)
return true
end
return false
end
function Melody:RemoveCurrentSoundIdFromQueue(skipsong: boolean?)
skipsong = if typeof(skipsong) == "boolean" then skipsong else true
self.__sound:Stop()
self:RemoveSoundIdFromQueue(self.__sound.SoundId)
print(skipsong)
if skipsong and #self.__queue > 0 then
self.__sound.SoundId = self.__queue[1]
self.__sound:Play()
end
return
end
function Melody:GetQueue()
return self.__queue
end
function Melody:SkipCurrentSong()
return self:RemoveCurrentSoundIdFromQueue(true)
end
function Melody:Play()
if #self.__queue == 0 then
return error("queue is empty", 2)
end
self.__sound.SoundId = self.__queue[1]
return self.__sound:Play()
end
function Melody:Destroy()
self.__sound:Destroy()
table.remove(self.__queue)
self.__sound = nil
self.__queue = nil
return
end
local function new(ids: {string|number}?, parent: Instance?)
local queue = {}
if typeof(ids) == "table" then
for i, v in ipairs(ids) do
if typeof(v) == "string" then
table.insert(queue, v)
continue
elseif typeof(v) == "number" then
table.insert(queue, "rbxassetid://"..v)
continue
end
end
end
local sound = Instance.new("Sound")
local newmelody = setmetatable({
__sound = sound;
__queue = queue;
}, Melody)
sound.Ended:Connect(function(id: string)
if sound.Looped then return end
newmelody:RemoveSoundIdFromQueue(id)
sound.SoundId = newmelody.__queue[1]
sound:Play()
end)
if parent then sound.Parent = parent end
return newmelody
end
return setmetatable({new = new}, {__call = new})
I did not extensively test this! You should make a test suite yourself, below is that I used for testing it (in Studio)
11:45:24.209 > _G.Melody = require(game.ServerStorage.Melody).new({8172854902; 7063614050}, workspace) - Studio
11:45:27.375 > _G.Melody:Play() - Studio
11:45:30.406 > print(_G.Melody:GetQueue()) - Studio
11:45:30.408 ▼ {
[1] = "rbxassetid://8172854902",
[2] = "rbxassetid://7063614050"
} - Server
11:45:38.114 > _G.Melody:SkipCurrentSong() - Studio
11:45:38.116 true - Server - Melody:55
11:45:47.277 > print(_G.Melody:GetQueue()) - Studio
11:45:47.279 ▼ {
[1] = "rbxassetid://7063614050"
} - Server
11:45:55.346 > _G.Melody:SkipCurrentSong() - Studio
11:45:55.348 true - Server - Melody:55
11:45:59.993 > _G.Melody:Play() - Studio
11:45:59.995 _G.Melody:Play():1: queue is empty - Server - Melody:73
11:45:59.996 Stack Begin - Studio
11:45:59.997 Script 'ServerStorage.Melody', Line 73 - function Play - Studio - Melody:73
11:45:59.999 Script '_G.Melody:Play()', Line 1 - Studio
11:46:00.000 Stack End - Studio
The way this works is that it wraps on the Sound instance type and adds in the functionality of what Melody provides, this means it’s essentially the same as the Sound object with Melody’s functionalities (so you can reference whatever on the original Sound object and it will return as expected unless the module overwrote it with it’s own which it overwrote the Play() and Destroy() functions)
Lastly, I made this because of boredom, so if you ask why would I go far as rewriting someone’s stuff, that’s the awnser
Thanks, I’ve modified it, added more functions, and added the functionality of looping the playlist so you don’t play a couple of songs and then stop.
I’ve read the updated version and it seems like you do not understand the concept of Object Oriented Programming (shortened, OOP) which is used in the rewrite, I have fixed the changes you made but you need to learn how to use OOP and understand the concept of it, search around the DevForum, I’ll link a topic I can find below
--made by @xshadowsgamer2018
--rewritten by @ItzEthanPlayz_YT
local function InstanceTypeof(i: any) return if typeof(i) == "Instance" then i.ClassName else typeof(i) end
local function InvalidArgumentType(argnumber: number, funcname: string, expected: string, got: string)
return string.format("invalid argument #%d to '%s' (%s expected, got %s)", argnumber, funcname, expected, got)
end
local Melody = {}
function Melody:__index(k)
if Melody[k] then
return Melody[k]
end
return self.__sound[k]
end
function Melody:__newindex(k, v)
if self[k] then
self[k] = v
return
end
self.__sound[k] = v
return
end
function Melody.__tostring()
return "Melody"
end
function Melody:AddSoundIdToQueue(id: string | number)
assert(InstanceTypeof(id) == "string" or InstanceTypeof(id) == "number", InvalidArgumentType(1, "AddSoundIdToQueue", "string or number", InstanceTypeof(id)))
if typeof(id) == "string" then
table.insert(self.__queue, id)
else
table.insert(self.__queue, "rbxassetid://"..id)
end
return
end
function Melody:RemoveSoundIdFromQueue(id: string | number): boolean
assert(InstanceTypeof(id) == "string" or InstanceTypeof(id) == "number", InvalidArgumentType(1, "RemoveSoundIdFromQueue", "string or number", InstanceTypeof(id)))
local searchquery = "rbxassetid://"
if typeof(id) == "string" then
searchquery = id
else
searchquery ..= id
end
local index = table.find(self.__queue, searchquery)
if index then
table.remove(self.__queue, index)
return true
end
return false
end
function Melody:RemoveCurrentSoundIdFromQueue(skipsong: boolean?)
skipsong = if typeof(skipsong) == "boolean" then skipsong else true
self.__sound:Stop()
self:RemoveSoundIdFromQueue(self.__sound.SoundId)
print(skipsong)
if skipsong and #self.__queue > 0 then
self.__sound.SoundId = self.__queue[1]
self.__sound:Play()
end
return
end
function Melody:GetQueue()
return self.__queue
end
function Melody:SkipCurrentSong()
return self:RemoveCurrentSoundIdFromQueue(true)
end
function Melody:Play()
if #self.__queue == 0 then
return error("queue is empty", 2)
end
self.__sound.SoundId = self.__queue[1]
return self.__sound:Play()
end
function Melody:Pause()
return self.__sound:Pause()
end
function Melody:Stop()
return self.__sound:Stop()
end
function Melody:Destroy()
local sound = self.__sound
table.remove(self.__queue)
self.__sound = nil
self.__queue = nil
self.__endedHandlerEvent:Disconnect()
self.__queueindex = nil
return sound:Destroy()
end
function Melody:GetCurrentSoundId()
return self.__sound.SoundId
end
function Melody:GetCurrentQueueIndex()
return self.__queueindex
end
function Melody:GetOriginalSound()
return self.__sound
end
local function new(ids: {string|number}?, parent: Instance?)
local queue = {}
if typeof(ids) == "table" then
for i, v in ipairs(ids) do
if typeof(v) == "string" then
table.insert(queue, v)
continue
elseif typeof(v) == "number" then
table.insert(queue, "rbxassetid://"..v)
continue
end
end
end
local sound = Instance.new("Sound")
local newmelody = setmetatable({
__sound = sound;
__queue = queue;
__queueindex = 1;
}, Melody)
--// had to use rawset here to prevent something like " __endedHandlerEvent is not a valid member of Sound "Sound" "
rawset(newmelody, "__endedHandlerEvent", sound.Ended:Connect(function(id: string)
if sound.Looped then return end
if newmelody.__queueindex == #queue then
newmelody.__queueindex = 1
sound.SoundId = newmelody.__queue[newmelody.__queueindex]
sound:Play()
else
newmelody.__queueindex += 1
sound.SoundId = newmelody.__queue[newmelody.__queueindex]
sound:Play()
end
end))
if parent then sound.Parent = parent end
return newmelody
end
return setmetatable({new = new}, {__call = new})
Also again, this is wrapped around the original sound object, having a function that gets the original object wouldn’t really make sense but I decided to rename the GetSound
function to GetOriginalSound
to clear out the fact that when you make a Melody object, it already has every function and properties a normal Sound instance provides so you can set it things from the Sound instance directly instead of using GetOriginalSound
and setting properties from the returned original sound instance
EDIT: There was a error when you create a new sound, I fixed that
Just to add- _G is not good and shouldn’t be used in scripts.
There’s nothing wrong with _G. People make it out to be a memory eating variable but it does the same as a module.
You might get crazy from _G variables, here’s what I mean
Let’s say you want to wait for multiple variables of _G, along the lines of
while not _G.amongusnotfunny do
task.wait()
end
while not _G.THISISNOTFUNNYANYMORESTOP do
task.wait()
end
while not _G.STOPTHISMADNESS do
task.wait()
end
And etc. You will go along with these lines right? While modules is just require
and boom your variables are here.
I’ve only used _G only in the command bar for easier testing, have you seen any usage of _G in my scripts or production games?
When I tested Melody, I did everything from the command bar, not via a LuaSourceContainer, it’s obvious that I would need to store the created object somewhere to test it because what’s the point of testing if you can’t test the functionality, right?
Well yeah, that’s the only useful use right now
I know this topic is dead but I can see its useful for someone that is lazy like me most of the times because I can pop in it and do 4 lines of code and there you go and also for those that are saying “This module is useless” there are free models that are music players like this one (kind of) : rickroll music player - Roblox so don’t say this module is “useless” and that stuff, also @AsynchronousMatrix is right about this arguing.