Music tracker script?

Hello,

I’m currently working on a music playing system and I’m trying to make a visual tracker for the currently playing song. I’m working on the script that moves the tracker along the line according to the length of the song, but I don’t know how to go about it, HELP. The song is a sound object in the Workspace that gets updated to play a different song from a playlist stored in a table. I want the tracker to move along the line according to the length of the song, and to reset when a new song starts playing.

  • Here is the song playing script:
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local updateSong = ReplicatedStorage.UpdateSong
local sound = {game.Workspace:WaitForChild("BackgroundMusic")}
local ContentProvider = game:GetService("ContentProvider")

local playlist = { 
	["Barbie and 12 Dancing Princesses Theme Song - Barbie"] = "rbxassetid://4614145040",
	["Brandenburg Concerto #3, Allegro - Johann Sebastian Bach"] = "rbxassetid://1848052559", 
	["Follow Your Dreams (Piano) - Sheryn Regis"] = "rbxassetid://1839680736", 
	["Gymnopdie No 1 - Erik Satie"] = "rbxassetid://6163865135", 
	["Lavender Blue - Dilly Dilly"] = "rbxassetid://1847584954", 
	["Moonlight Sonata (Solo Piano) - Beethoven"] = "rbxassetid://1838577407", 
	["Once Upon A December (Piano) - Emile Pandolfi"] = "rbxassetid://313981815", 
	["Prelude No. 4 - Debussy"] = "rbxassetid://515792175", 
	["Swan Lake - Barbie"] = "rbxassetid://6072424146",
	["Symphony No 6 Pastorale In F Major, Op 68 - Beethoven"] = "rbxassetid://1844267333"} --Playlist

local function getKeyFromValue(tab, input)--tab is the table and input is the value that you're looking for the key.
	for key, value in pairs(tab) do
		if value == input then
			return key
		end
	end
end



Players.PlayerAdded:Connect(function(player)--Updating the song playing for when a player joins.
	updateSong:FireClient(player, getKeyFromValue(playlist, sound[1].SoundId), sound[1])
end)

while true do
	for i, v in pairs(playlist) do
		sound[1].SoundId = v
		ContentProvider:PreloadAsync(sound, print("Loaded Song")) --Loading buttons volume image
		updateSong:FireAllClients(i, sound[1])--This is where the server must update everyone's GUI's
		sound[1]:Play()
		sound[1].Ended:Wait()
	end
end
  • Here is the current song and song length local script:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local updateSong = ReplicatedStorage.UpdateSong
local TFM = require(ReplicatedStorage.TFM)
local songNameLabel = script.Parent:WaitForChild("SongNameLabel")
local songLengthEnd = script.Parent:WaitForChild("SongLengthEnd")

updateSong.OnClientEvent:Connect(function(songName, songLength)
	songNameLabel.Text = songName
	songLengthEnd.Text = TFM:Convert(math.floor(songLength), 'Default', true)
end)
  • Here is the explorer showing the two gui’s the line and the tracker:

image

  • Here is a really embarrasing atempt at making the tracker work:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
local TFM = require(ReplicatedStorage.TFM)

local backgroundMusic = game.Workspace:WaitForChild("BackgroundMusic")

local pointer = script.Parent
local songLengthLine = script.Parent.Parent:WaitForChild("SongLengthLine")
local startPosition = UDim2.new(0.389, 0,0.287, 0)
local endPosition = UDim2.new(0.61, 0,0.285, 0)
local lineLength = 0.227
local oldTimePosition = backgroundMusic.TimePosition -- initial 

--Script

RunService.RenderStepped:Connect(function()
	local incrament = 0.227/backgroundMusic.TimeLength
	wait(incrament)
	local pos = backgroundMusic.TimePosition
	if pos ~= oldTimePosition then
		oldTimePosition = pos
		pointer.Position = pointer.Position + UDim2.new(incrament,0,0,0)
	end
end)

Ask in replies if you want to see other facets of my system.

3 Likes

Technically, you could just set the tracker’s X scale position to TimePosition/TimeLength if the tracker’s parent is the bar behind it.

1 Like

We can simplify this greatly.

So we have all the info we need:

  • We have the current time position of the song
  • We also have the time length of the song

With this data, we can perform some simple arithmetic to figure out where to place the slider element.

local MAX_X = 1 --//Max X scaled position for the slider

local function getSliderX(song)
    local scale = song.TimePosition / song.TimeLength

    return MAX_X * scale
end

Then you can call the function passing through the sound that’s playing, and use its returned X value to position the slider’s X scaled position on screen (assuming you have it placed within like a line or something that determines how it should be positioned, like in your image). :slight_smile:

I did this:

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
local TFM = require(ReplicatedStorage.TFM)

local backgroundMusic = game.Workspace:WaitForChild("BackgroundMusic")

local pointer = script.Parent
local songLengthLine = script.Parent.Parent:WaitForChild("SongLengthLine")
local startPosition = UDim2.new(0.389, 0,0.287, 0)
local endPosition = UDim2.new(0.61, 0,0.285, 0)
local lineLength = 0.227
local oldTimePosition = backgroundMusic.TimePosition -- initial 
local MAX_X = 0.61
--Script

local function getSliderX(song)
	local scale = song.TimePosition / song.TimeLength

	return MAX_X * scale
end

RunService.RenderStepped:Connect(function()
	pointer.Position.X = getSliderX(backgroundMusic)

end)

But output is showing:

You have to set it through UDim2.new() - You’re assigning to X via:

RunService.RenderStepped:Connect(function()
    pointer.Position.X = getSliderX(backgroundMusic) -- Attempting to assign to X as an int
end)

UDim2 - Datatype

1 Like

I have another problem now. The pointer starts all the on the left side of the script instead of the beggining of the slider:

Script:

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
local TFM = require(ReplicatedStorage.TFM)

local backgroundMusic = game.Workspace:WaitForChild("BackgroundMusic")

local pointer = script.Parent
local songLengthLine = script.Parent.Parent:WaitForChild("SongLengthLine")
local startPosition = UDim2.new(0.389, 0,0.287, 0)
local endPosition = UDim2.new(0.61, 0,0.285, 0)
local lineLength = 0.227
local oldTimePosition = backgroundMusic.TimePosition -- initial 
local MAX_X = 0.61
--Script

local function getSliderX(song)
	local scale = song.TimePosition / song.TimeLength

	return MAX_X * scale
end

RunService.RenderStepped:Connect(function()
	if backgroundMusic.TimePosition == 0 then
		
	end
	pointer.Position = UDim2.new(getSliderX(backgroundMusic),0,0.285,0)
end)

That’s probably because the frame the slider is in is not centered with the brown line you have.

To fix it, you could put the slider directly in the brown line instead.

1 Like

I cannot promise this will work precisely. I’m not too familiar with your game structure to know much about what the UI looks like during UI Assembly/Editing. I’ve done a crude UI based on a timer bar to try solve this. I’ll update this or add more posts when I understand more of your game’s UI handling and song handling.

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
local TFM = require(ReplicatedStorage.TFM)

local backgroundMusic = game.Workspace:WaitForChild("BackgroundMusic")

local pointer = script.Parent
local songLengthLine = script.Parent.Parent
local oldTimePosition = 0 -- Since this is the initial, why is this not as
-- local MAX_X = 0.61 -- Remove this, have the Pointer as a child of SongLengthLine so you can use 0 to 1 X Scale.
--Script

local function ConvertTimeToPosition(song)
	return (1 / song.TimeLength) * song.TimePosition
end

RunService.RenderStepped:Connect(function()
	pointer.Position = UDim2.new(ConvertTimeToPosition(backgroundMusic), 0, 1, 0) -- change the 1 to reflect what your Y Scale position is for your pointer.

end)

This is what I used to try achieve your problem, so you are aware of how different our work really is.

UI I used to test:

UI structure:
image

UI script (prototyping/rudimentary):
image

Song Used: Nightcore - Anima Libera - Roblox

1 Like

Additionally, due to the odd journey away from my primary project this took me, I created two examples of a Music Player (that may not be efficient) which are both local client only.

They are the same for the most part but with two different methods of Audio Track Input.

The first one allows any track to be inputted (and forces numerical input in the text box) with the ability to recognize specific tracks through the use of a Track Data modulescript in ReplicatedStorage.Assets. The second one loops over each track in the Track Data modulescript and creates selectable buttons in the track list for the user to choose from.
Both examples are concealable with Play/Stop and a Hide/Show button.

These aren’t meant to be used entirely for drag and drop purposes due to their fairly haphazard construction in pursuit of trying to help the Thread Creator solve their problem. I just thought it’d be good to create these two examples in case anyone else came here and was stuck.

Since the first example only uses Track Data for caching, you do not need to add additional track data to play a song. Though to use the second example with custom songs, you will need to add additional entries in the TrackData module script (below).

Adding Track Data

image

If you wish to add track data for either Audio Player example, navigate to ReplicatedStorage.Assets and open up the TrackData module script. Each track follows the specific arrangement as shown above. To add a specific Track you don’t need to know the Track Artist (cannot be nil field), you only need the Track ID (and preferably the Track Title so you can find it easily).

Below is one of the above example entries that you can modify. You will also need to add a comma after the last entry’s “}” in order to prevent an error.

	['6821414415'] = {
		Title = "Nightcore - Anima Libera",
		Artist = "Twisterase (Remix), Dj Raaban (Original)",
		BPM = 0
	},
2 Likes