a personal module I made for myself. (so don’t expect it to be perfect)
it takes your sound and bpm, and then allows you to set callbacks for every time a beat or step happens, useful for rhythm games and such.
Code
local timing = {}
timing.__index = timing
local runService = game:FindService("RunService")
function timing.new(soundInstance: Sound)
return setmetatable({
bpm = 60,
beat = 0,
step = 0, -- beat * 4
prevBeat = 0,
prevStep = 0, -- beat * 4
soundInstance = soundInstance,
playing = false,
}, timing)
end
function timing:setBPM(bpm)
if type(bpm) ~= "number" then
return warn("inputted bpm isn't a number!!") -- why would you even do this anyway LOL
end
self.bpm = bpm
end
function timing:Play()
self.playing = true
self.soundInstance:Play()
end
function timing:Stop()
self.playing = false
self.soundInstance:Stop()
end
function timing:Update()
local connection = runService.RenderStepped:Connect(function(deltaTime)
if not self.playing then return warn("no sound playing!! can't update.") end
local songPosition = self.soundInstance.TimePosition * 1000
local beat = (songPosition / 1000) * (self.bpm / 60)
local roundedBeat = math.round(beat)
local step = beat * 4
local roundedStep = math.round(step)
if self.prevBeat ~= roundedBeat then
task.spawn(self.beatCallback, roundedBeat)
end
if self.prevStep ~= roundedStep then
task.spawn(self.stepCallback, roundedStep)
end
self.beat = roundedBeat
self.step = roundedStep
self.prevBeat = self.beat
self.prevStep = self.step
end)
end
function timing:SetBeatCallback(callback)
if type(callback) ~= "function" then
return warn("callback isn't a function!!") -- again, why
end
self.beatCallback = callback
end
function timing:SetStepCallback(callback)
if type(callback) ~= "function" then
return warn("callback isn't a function!!") -- AGAIN, WHY????
end
self.stepCallback = callback
end
return timing
Example
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local TweenService = game:GetService("TweenService")
local Timer = require(ReplicatedStorage.timer)
local Frame = script.Parent.Frame
local Scale = Frame.UIScale
local NewTimer = Timer.new(script.Parent.Song)
NewTimer:setBPM(115)
NewTimer:Update()
NewTimer:SetBeatCallback(function(beat)
Scale.Scale = 1.25
TweenService:Create(Scale, TweenInfo.new(0.45, Enum.EasingStyle.Exponential, Enum.EasingDirection.Out), {
Scale = 1
}):Play()
end)
NewTimer:SetStepCallback(function(step)
Frame.TextLabel.Text = `BPM: {NewTimer.bpm}\n Beat: {NewTimer.beat}\n Step: {step}`
end)
NewTimer:Play()