I have made a similar module in the past. It has all the features that you might need for controlling the music. I know that not all stuff is optimized, and I have used bad methods, so do take note that you should only use the code as an example rather than a finished product because this is not a finished product. It features playing, preloading, skipping, skipping to the previous song, autoplay, set timestamps, subtitles, etc.
If you are interested in the code, it’s going to be below. Also, I embedded the subtitle reader in my video player module. I actually code most of the stuff related to audio on my audio player, then embed the code into my other modules. I share this because I want to see other people get inspired and create more useful resources like this one. Also, this is a small addition to this tutorial for people seeking to implement other functions other than just playing and stopping; please only use this as an example and not a finished product. The code is not optimized well and can’t be scaled; I made too many functions when I could do it with fewer.
- The whole code:
Disclaimer: the code below was meant to be ran on the client, if you wish to change it to the server you might have to do a little more manual edits.
local Tracks =
{
[1] = {
["Original"] = 13697265197,
["Name"] = "Tomodachi no Kioku ver.b"
},
[2] = {
["Original"] = 13697280194,
["Name"] = "Modokashii Kimochi"
},
[3] = {
["Original"] = 13697288644,
["Name"] = "Hiruyasumi no Okujou"
},
[4] = {
["Original"] = 13697299240,
["Name"] = "Lunch for Two People"
},
[5] = {
["Original"] = 13697306954,
["Name"] = "My Precious Friend"
},
[6] = {
["Original"] = 13697311245,
["Name"] = "Fujimiya-san to Oshaberi"
},
[7] = {
["Original"] = 13697350772,
["Name"] = "Fujimiya-san ni Dokidoki"
},
[8] = {
["Original"] = 13697369399,
["Name"] = "Hase and Kiru"
},
[9] = {
["Original"] = 13697394991,
["Name"] = "After School Fun ver.a"
},
[10] = {
["Original"] = 13697401626,
["Name"] = "Gentle"
},
[11] = {
["Original"] = 13697427739,
["Name"] = "Atarashii Tomodachi"
},
[12] = {
["Original"] = 13697433460,
["Name"] = "Arm-in-Arm"
},
[13] = {
["Original"] = 13697470886,
["Name"] = "Don't Get Along So Well!"
},
[14] = {
["Original"] = 13697474677,
["Name"] = "Yakimoki Hase-kun"
},
[15] = {
["Original"] = 13697480645,
["Name"] = "After School Fun ver.b"
},
[16] = {
["Original"] = 13697488249,
["Name"] = "Gakkou no Kaerimichi"
},
[17] = {
["Original"] = 13697496239,
["Name"] = "Asa no Hikari no Naka de"
},
[18] = {
["Original"] = 13697506835,
["Name"] = "Fujimiya-san no Nikki"
},
[19] = {
["Original"] = 13697512986,
["Name"] = "Kienai Omoi, Itsumademo"
},
[20] = {
["Original"] = 13697521310,
["Name"] = "Fuan na Hibi"
},
[21] = {
["Original"] = 13697526457,
["Name"] = "Kako no Dekigoto"
},
[22] = {
["Original"] = 13697533176,
["Name"] = "Boukyaku no Kanata ni"
},
[23] = {
["Original"] = 13697539583,
["Name"] = "Monday"
},
[24] = {
["Original"] = 13697559668,
["Name"] = "Memory Loss"
},
[25] = {
["Original"] = 13697567690,
["Name"] = "False Memories"
},
[26] = {
["Original"] = 13697571942,
["Name"] = "Tomodachi ni Naritai"
},
[27] = {
["Original"] = 13697576190,
["Name"] = "Odayaka na Toki"
},
[28] = {
["Original"] = 13697586283,
["Name"] = "Emotions, Overflowing"
},
[29] = {
["Original"] = 13697589994,
["Name"] = "Tomodachi no Kioku ver.a"
},
[30] = {
["Original"] = 13690557806,
["Translated"] = {
["Russian"] = 13728422986,
["Subtitles"] = {
["Russian"] = {
{0, ""},
{8, "Опять прийдёт новый рассвет - хочу встречать его с тобой,"},
{15, "Напополам делить секунды, радость и печаль."},
{22, "Бережно переверну страницу книги, где я воспоминания"},
{32, "Собираю и храню."},
{35.8, "Может быть, словами не выходит о чувствах рассказать ничего,"},
{43, "Но, забыв про страх, попытаюсь сделать вперёд шаг!"},
{50, "Протяни ладонь и в небо воспарим"},
{54, "Безграничное и столь прекрасное."},
{58, "По мосту из радуги отправимся"},
{61, "В грядущий день, что с нетерпеньем ждёт."},
{65, "Поборов сомнения, смогу найти"},
{69, "То, чем дорожил давно когда-то ты,"},
{72, "Улыбкою твоей согреюсь вновь и теперь хочу"},
{78, "Любоваться ею всегда!"},
{81, "Друзья на неделю"},
{88, ""}
}
}
},
["Alternative"] = 13697594306, -- Piano
["Subtitles"] = {
{0, ""},
{8, "I want to be with you when the new dawn comes again,"},
{15, "I want to share the seconds, the joys and the sorrows."},
{22, "I'll carefully turn the page of the book where I'll"},
{32, "Collect and keep my memories."},
{35.8, "Maybe words can't tell how I feel,"},
{43, "But forgetting the fear, I'll try to take a step forward!"},
{50, "Hold out your palm and you'll soar into the sky"},
{54, "So boundless and so beautiful"},
{58, "Over the bridge of the rainbow"},
{61, "To the day to come That's waiting impatiently"},
{65, "I'll overcome my doubts, I'll find"},
{69, "What you once treasured"},
{72, "I'll be warmed by your smile and now I want to"},
{78, "Always be able to admire it!"},
{81, "One Week Friends"},
{88, ""}
},
["Name"] = "Niji no Kakera"
},
[31] = {
["Original"] = 13697613040,
["Subtitles"] = {},
["Name"] = "Kanade"
}
}
local TweenService = game:GetService("TweenService")
local currentTrack
local currentVolume = 0.5
local isAutoPlay = false
local isSubtitles = false
local TracksList = {}
local TrackControl = {}
function TrackControl:Play()
if not game.Workspace:FindFirstChild("Tracks") then
local TracksFolder = Instance.new("Folder")
TracksFolder.Name = "Tracks"
TracksFolder.Parent = game.Workspace
for _, v in Tracks do
local Track = Instance.new("Sound")
Track.Volume = 0
Track.SoundId = "rbxassetid://".. v["Original"]
Track.Name = v["Name"]
Track.Parent = TracksFolder
table.insert(TracksList, Track)
local Equalizer = Instance.new("EqualizerSoundEffect")
Equalizer.HighGain = 2
Equalizer.MidGain = 2
Equalizer.LowGain = 5
Equalizer.Parent = Track
end
currentTrack = math.random(1, 29)
game:GetService("ContentProvider"):PreloadAsync(TracksList)
end
TracksList[currentTrack]:Resume()
TweenService:Create(TracksList[currentTrack], TweenInfo.new(0.5, Enum.EasingStyle.Cubic, Enum.EasingDirection.In), {Volume = currentVolume}):Play()
end
function TrackControl:Pause()
if not game.Workspace:FindFirstChild("Tracks") then return end
TweenService:Create(TracksList[currentTrack], TweenInfo.new(0.5, Enum.EasingStyle.Cubic, Enum.EasingDirection.Out), {Volume = 0}):Play()
task.wait(0.5)
TracksList[currentTrack]:Pause()
end
function TrackControl:Rewind()
if not game.Workspace:FindFirstChild("Tracks") then return end
TweenService:Create(TracksList[currentTrack], TweenInfo.new(0.5, Enum.EasingStyle.Cubic, Enum.EasingDirection.Out), {Volume = 0}):Play()
task.wait(0.5)
TracksList[currentTrack]:Stop()
if currentTrack - 1 > table.maxn(TracksList) then currentTrack = 32 else currentTrack -= 1 end
TracksList[currentTrack]:Play()
TweenService:Create(TracksList[currentTrack], TweenInfo.new(0.5, Enum.EasingStyle.Cubic, Enum.EasingDirection.In), {Volume = currentVolume}):Play()
end
function TrackControl:Skip()
if not game.Workspace:FindFirstChild("Tracks") then return end
TweenService:Create(TracksList[currentTrack], TweenInfo.new(0.5, Enum.EasingStyle.Cubic, Enum.EasingDirection.Out), {Volume = 0}):Play()
task.wait(0.5)
TracksList[currentTrack]:Stop()
if currentTrack + 1 > table.maxn(TracksList) then currentTrack = 1 else currentTrack += 1 end
TracksList[currentTrack]:Play()
TweenService:Create(TracksList[currentTrack], TweenInfo.new(0.5, Enum.EasingStyle.Cubic, Enum.EasingDirection.In), {Volume = currentVolume}):Play()
end
function TrackControl:Stamp(newStamp)
if not game.Workspace:FindFirstChild("Tracks") then return end
TracksList[currentTrack].TimePosition = newStamp
end
function TrackControl:Volume(newVolume)
if not game.Workspace:FindFirstChild("Tracks") then return end
currentVolume = newVolume
TweenService:Create(TracksList[currentTrack], TweenInfo.new(0.1, Enum.EasingStyle.Cubic, Enum.EasingDirection.InOut), {Volume = newVolume}):Play()
end
function TrackControl:ChangeTrack(newTrack)
if not game.Workspace:FindFirstChild("Tracks") then return end
TweenService:Create(TracksList[currentTrack], TweenInfo.new(0.5, Enum.EasingStyle.Cubic, Enum.EasingDirection.Out), {Volume = 0}):Play()
task.wait(0.5)
TracksList[currentTrack]:Stop()
currentTrack = newTrack
TracksList[currentTrack]:Play()
TweenService:Create(TracksList[currentTrack], TweenInfo.new(0.5, Enum.EasingStyle.Cubic, Enum.EasingDirection.In), {Volume = currentVolume}):Play()
end
function TrackControl:GetTrack()
if not game.Workspace:FindFirstChild("Tracks") then return end
return TracksList[currentTrack]
end
function TrackControl:ChangeAutoPlay(newValue: boolean)
if not game.Workspace:FindFirstChild("Tracks") then return end
if newValue == true then
isAutoPlay = true
task.spawn(function()
while task.wait() do
if not isAutoPlay then break end
if not TracksList[currentTrack].Playing then TrackControl:Skip() return end
TracksList[currentTrack].Ended:Wait()
TrackControl:Skip()
end
end)
else
isAutoPlay = false
end
end
function TrackControl:ChangeToAlternative(newTrack)
if not game.Workspace:FindFirstChild("Tracks") then return end
if not TracksList[newTrack] then return end
if not Tracks[newTrack]["Alternative"] then return end
if newTrack == currentTrack then
TweenService:Create(TracksList[currentTrack], TweenInfo.new(0.5, Enum.EasingStyle.Cubic, Enum.EasingDirection.Out), {Volume = 0}):Play()
task.wait(0.5)
TracksList[currentTrack]:Stop()
currentTrack = newTrack
TracksList[currentTrack].SoundId = "rbxassetid://".. Tracks[newTrack]["Alternative"]
game:GetService("ContentProvider"):PreloadAsync({TracksList[currentTrack]})
TracksList[currentTrack]:Play()
TweenService:Create(TracksList[currentTrack], TweenInfo.new(0.5, Enum.EasingStyle.Cubic, Enum.EasingDirection.In), {Volume = currentVolume}):Play()
else
TracksList[newTrack].SoundId = "rbxassetid://".. Tracks[newTrack]["Alternative"]
game:GetService("ContentProvider"):PreloadAsync({TracksList[newTrack]})
end
end
function TrackControl:ChangeToLanguage(newTrack, newLanguage)
if not game.Workspace:FindFirstChild("Tracks") then return end
if not TracksList[newTrack] then return end
if not Tracks[newTrack]["Translated"][newLanguage] then return end
if newTrack == currentTrack then
TweenService:Create(TracksList[currentTrack], TweenInfo.new(0.5, Enum.EasingStyle.Cubic, Enum.EasingDirection.Out), {Volume = 0}):Play()
task.wait(0.5)
TracksList[currentTrack]:Stop()
currentTrack = newTrack
TracksList[currentTrack].SoundId = "rbxassetid://".. Tracks[currentTrack]["Translated"][newLanguage]
game:GetService("ContentProvider"):PreloadAsync({TracksList[currentTrack]})
TracksList[currentTrack]:Play()
TweenService:Create(TracksList[currentTrack], TweenInfo.new(0.5, Enum.EasingStyle.Cubic, Enum.EasingDirection.In), {Volume = currentVolume}):Play()
else
TracksList[newTrack].SoundId = "rbxassetid://".. Tracks[newTrack]["Translated"][newLanguage]
game:GetService("ContentProvider"):PreloadAsync({TracksList[newTrack]})
end
end
function TrackControl:EnableSubtitles(newTrack, subtitleContainer, translatedLanguage)
if not game.Workspace:FindFirstChild("Tracks") then return end
if currentTrack == newTrack and TracksList[currentTrack].Playing == true then return end
task.spawn(function()
repeat task.wait() until currentTrack == newTrack and TracksList[currentTrack].Playing == true
if Tracks[newTrack]["Translated"]["Subtitles"][translatedLanguage] then
for i, v in Tracks[newTrack]["Translated"]["Subtitles"][translatedLanguage] do
subtitleContainer.Text = v[2]
if not Tracks[newTrack]["Translated"]["Subtitles"][translatedLanguage][i+1] then return end
task.wait(Tracks[newTrack]["Translated"]["Subtitles"][translatedLanguage][i+1][1] - v[1])
end
else
for i, v in Tracks[newTrack]["Subtitles"] do
subtitleContainer.Text = v[2]
if not Tracks[newTrack]["Subtitles"][i+1] then return end
task.wait(Tracks[newTrack]["Subtitles"][i+1][1] - v[1])
end
end
end)
end
return TrackControl