Jump to content

[Question]Using getSoundLength for timer


kieran

Recommended Posts

Hi, I am trying to make a script that will play a random sound from a table and then continue to play sounds, but I have a problem with the timer, all sounds play at once instead of one after the other...

songs = {}
SongNum = 2

songs[1] = "sounds/1.mp3"
songs[2] = "sounds/2.mp3"

--Login Sounds
function PlayLoginSound()
	randomSong = math.random (1, SongNum)--Get a random song from table
	sound = playSound(tostring(songs[randomSong]), true) --Play the song
	if (sound) then
		setSoundVolume(sound, 0.2) --Set the sound volume to 20%
		else 
		outputDebugString("error")
	end
		soundLength = getSoundLength ( sound )
	if (soundLength) then 
		setTimer(nextSound, soundLength, 1)
	end
end

addEventHandler ( "onClientResourceStart", resourceRoot, PlayLoginSound)

function nextSound()
	soundLength = getSoundLength ( sound )
	if (soundLength) then 
	sound = playSound(tostring(songs[randomSong]), true) 
		setSoundVolume(sound, 0.2) 
		setTimer(nextSound, soundLength, 1)
	end
end

I have also tried converting "soundLength" to a number.

 

I am basically trying to set a timer, then trigger a function to play the next sound...  But both start at the same time, so it isn't a problem with 2 timers being active at once, I will kill timers after I get them starting one at a time, thanks for help.

Edited by kieran
Link to comment

I just found out getSoundLength returns the length in seconds, say my song was exactly 2 minutes and 32 milliseconds long, it would return 120.32

The problem is I can't do "getSoundLength*100" because this would return a decimal value!

 

So is there any way I can get the numbers before a decimal point?

Link to comment

You can try to do 'getSoundLength*100' and use this to round it up to an integer:

--http://lua-users.org/wiki/FormattingNumbers
function round(val, decimal)
  if (decimal) then
    return math.floor( (val * 10^decimal) + 0.5) / (10^decimal)
  else
    return math.floor(val+0.5)
  end
end

So something like:

setTimer(nextSound, round(soundLength), 1)

You can also do like this until you get an integer:

setTimer(nextSound, round(soundLength, 2), 1)

The '2' can be higher, depends which value for it returns an integer of the passed number. Experiment a bit with it.

Link to comment

@koragg Thanks I solved it, it returns a whole number after I used math.floor.....  But, it still doesn't set the timer to the sounds length, here is what I attempted to do, but it plays songs on top of each other....

songs = {}
SongNum = 8

songs[1] = "sounds/1.mp3"
songs[2] = "sounds/2.mp3"
songs[3] = "sounds/3.mp3"
songs[4] = "sounds/4.mp3"
songs[5] = "sounds/5.mp3"
songs[6] = "sounds/6.mp3"
songs[7] = "sounds/7.mp3"
songs[8] = "sounds/8.mp3"


function PlayLoginSound()
	randomSong = math.random (1, SongNum)
	sound = playSound(tostring(songs[randomSong]), true) 
	if (sound) then
		setSoundVolume(sound, 0.5) 
		else 
		return
	end
		soundLength = math.floor(getSoundLength ( sound )) 
		--outputChatBox(""..soundLength)  <-This shown me it returned a whole number
	if (soundLength) then 
		setTimer(nextSound, soundLength*100, 1)
	end
end

addEventHandler ( "onClientResourceStart", resourceRoot, PlayLoginSound)


--Perhaps my problem lays here? I don't know why, it should be triggered on a timer.....
function nextSound()
		soundLength = math.floor(getSoundLength ( sound )) 
	if (soundLength) then 
	sound = playSound(tostring(songs[randomSong]), true) 
		setSoundVolume(sound, 0.8) 
		setTimer(nextSound, soundLength*100, 0)
	end
end

function StopLoginSound()
	stopSound(sound)
end

addEvent("stoploginsound", true)
addEventHandler("stoploginsound", root, StopLoginSound)

I know the problem MUST lay in my timer, I just don't know what the problem is xD

 

Update

Okay it works, it plays same song though.....  Forgot 1 secong = 1000 ms and not 100 ms, how can I get it to actually play random songs?

Edited by kieran
Code was outdated, sorry
Link to comment

Hey sorry for reviving this topic, but I have came across a problem (again) :/

My killTimer will not work for the event triggered on login!

It stops the sound, but it plays it after the timer is done....  I have tried setting element data on login and returning if the player has that data, but it is the exact same.....   Any help would be nice, been trying to get this work for the best part of 2 days now...

Server

Spoiler

--Stop login sound
function PlayLogin()
	triggerClientEvent(source, "stoploginsound", source)
end
addEventHandler("onPlayerLogin", root, PlayLogin)

 

Client

Spoiler

songs = {}
SongNum = 8

songs[1] = "sounds/1.mp3"
songs[2] = "sounds/2.mp3"
songs[3] = "sounds/3.mp3"
songs[4] = "sounds/4.mp3"
songs[5] = "sounds/5.mp3"
songs[6] = "sounds/6.mp3"
songs[7] = "sounds/7.mp3"
songs[8] = "sounds/8.mp3"

--Login Sounds
function PlayLoginSound()
	randomSong = math.random (1, SongNum)
	sound = playSound(tostring(songs[randomSong]), false) --Play the song
	if (sound) then
		setSoundVolume(sound, 0.5) -- set the sound volume to 50%
		else 
		return
	end
		soundLength = math.floor(getSoundLength ( sound )) 
		outputChatBox(""..soundLength)
	if (soundLength) then 
		setTimer(nextSound, soundLength*1000, 1)
	end
end

addEventHandler("onClientResourceStart", root, PlayLoginSound)


function nextSound()
	if isTimer ( nextSong ) then 
	killTimer ( nextSong ) 
	end
	randomSong = math.random (1, SongNum)
	sound = playSound(tostring(songs[randomSong]), false) 
		setSoundVolume(sound, 0.5) 
	soundLength = math.floor(getSoundLength ( sound )) 
	if (soundLength) then 
		nextSong = setTimer(nextSound, soundLength*1000, 0)
	end
end

function StopLoginSound()
	stopSound(sound)
	if isTimer ( nextSong ) then 
	killTimer ( nextSong )
	removeEventHandler("stoploginsound", root, StopLoginSound)
	end
end

addEvent("stoploginsound", true)
addEventHandler("stoploginsound", root, StopLoginSound)

 

 

Link to comment
nextSong = setTimer(nextSound, soundLength*1000, 0)

https://wiki.multitheftauto.com/wiki/SetTimer

  • timesToExecute: The number of times you want the timer to execute, or 0 for infinite repetitions.

The 0 at the end should be a 1 if you want to play it only once. Otherwise when the timer ends/gets killed it will get created again, and like this forever.

Remember that when calling the 'nextSound' function from inside of it at line 42 it's OK to have the last argument set to 1, because every time the 'nextSound' gets triggered it will create a new timer. And since the timer that triggers the function is inside of it, even with '1' instead of '0' it will repeat with another song. I think :D Give it a go, I don't see any errors in your code.

Link to comment

@koragg Nope, still keeps timer on after the server event is triggered, changed timer to 1 and added a return at the end as it was being triggered 6 times? :/

Honestly at a  loss here....  I know that most languages can only get code inside functions and use it on that function, for example if you set a local variable in visual studio and tried using it on another form with c# it wouldn't be able to get it as it isn't passed, is there maybe a similar issue with timers...?  Because I tried this and it returned "Timer not found." to chat box...

Updated code (Client)

Spoiler

--Login Sounds
function PlayLoginSound()
	randomSong = math.random (1, SongNum)
	sound = playSound(tostring(songs[randomSong]), false) --Play the song
	if (sound) then
		setSoundVolume(sound, 0.5) -- set the sound volume to 50%
		else 
		return
	end
		soundLength = math.floor(getSoundLength ( sound )) 
		outputChatBox(""..soundLength)
	if (soundLength) then 
		setTimer(nextSound, soundLength*1000, 1)
		addEventHandler("stoploginsound", root, StopLoginSound)
	end
end

addEventHandler("onClientResourceStart", root, PlayLoginSound)


function nextSound()
	if isTimer ( nextSong ) then 
	killTimer ( nextSong ) 
	end
	randomSong = math.random (1, SongNum)
	sound = playSound(tostring(songs[randomSong]), false) 
		setSoundVolume(sound, 0.5) 
	soundLength = math.floor(getSoundLength ( sound )) 
	if (soundLength) then 
		nextSong = setTimer(nextSound, soundLength*1000, 1)
		return
	end
end

function StopLoginSound()
	stopSound(sound)
	if isTimer ( nextSong ) then 
	killTimer ( nextSong )
	outputChatBox("Timer stopped.")
	else
	outputChatBox("Timer not found.")
	end
	removeEventHandler("stoploginsound", root, StopLoginSound)
end

addEvent("stoploginsound", true)

 

Would it be possible to set data on the localPlayer to the timer even though the player isn't spawned?  Only way I can think of to pass it to next function.

Edited by kieran
Link to comment

Hm, I guess you can set element data server-side when the player logs in. Something like this:

function onPlayerLogin()
  setElementData(source, "loggedIn", true)
end
addEventHandler("onPlayerLogin", root, onPlayerLogin)

And then on the client you can add this so that it checks for that data every frame:

function StopLoginSound()
    local loggedIn = getElementData(localPlayer, "loggedIn")
    if loggedIn == true then
      stopSound(sound)
      if isTimer ( nextSong ) then 
          killTimer ( nextSong )
      end
    end
end
addEventHandler("onClientRender", root, StopLoginSound)

 

Link to comment
8 hours ago, Randomly said:

attempt to call global "isSoundFinished" (a nil value)

Looks as if this is global, my script is with my login panel, it is client side, not global... 

I have even tried removing the event handler from when the player renders so when the first timer is done, it SHOULD remove event handler and then add it again when playing the next song, in an attempt to get the timer from that function, but nothing! 

And to start with I don't think you quite get my issue.....  The timer itself is not being found when the event is triggered.  Not the sound, it stops the sound, but my timer is still active, this means that even though the sound goes off, the timer is still active, meaning it will repeat the whole process again....   Is there any way to set data to a timer (not a sound element) or create a variable that will carry it to a function?  And another useful piece of information, even though my timer is within my function (before the last event in snippet I sent) it can not find it there either, so is there a problem with isTimer?

Spoiler

--Login Sounds
function PlayLoginSound()
	randomSong = math.random (1, SongNum)
	sound = playSound(tostring(songs[randomSong]), false) --Play the song
	if (sound) then
		setSoundVolume(sound, 0.5) -- set the sound volume to 50%
		else 
		return
	end
		soundLength = math.floor(getSoundLength ( sound )) 
		outputChatBox(""..soundLength)
	if (soundLength) then 
		setTimer(nextSound, soundLength*1000, 1)
		addEventHandler("stoploginsound", root, StopLoginSound)
	end
end

addEventHandler("onClientResourceStart", root, PlayLoginSound)


function nextSound()
	killTimer ( nextSong )-- CAN'T FIND THE TIMER
	if isTimer ( nextSong ) then --SO THIS WON'T WORK
	killTimer ( nextSong )
	end
	randomSong = math.random (1, SongNum)
	sound = playSound(tostring(songs[randomSong]), false) 
		setSoundVolume(sound, 0.5) 
		removeEventHandler("stoploginsound", root, StopLoginSound)
	soundLength = math.floor(getSoundLength ( sound )) 
	setElementDimension(sound, 0)
	if (soundLength) then 
		nextSong = setTimer(nextSound, soundLength*1000, 1)--IS THERE A WAY TO SET THIS TO BE A VARIABLE OUTSIDE THE FUNCTION?
	end
	addEventHandler("stoploginsound", root, StopLoginSound)
end
--CAN'T PUT A TIMER HERE, NEED SOMETHING TO TRIGGER IT, SO WOULDN'T WORK.

function StopLoginSound()
	stopSound(sound)
	killTimer ( nextSong )
	if isTimer ( nextSong ) then 
	killTimer ( nextSong )
	end
	removeEventHandler("stoploginsound", root, StopLoginSound)
end

addEvent("stoploginsound", true)

 

Okay source of the problem was found, turns out I was looking at the wrong function as Timer not found. was returned to chat when I was using code above, so looks as if the isTimer does not register a timer at all.....   Any way around this?

Edit:  I have update the code snippet, my debugscript 3 returns "Bad argument @ 'killTimer' [Expected lua-timer at argument 1, got nil]" for BOTH functions.

Edited by kieran
Link to comment

I don't understand what you mean, isSoundFinished couldn't find the sound as I was trying to check it outside the function, I think it needs a local variable with it, and yes, you seen the code, my timers play the next song from the list, they have the variable "nextSong" as I say, it can not find it as I am trying to check a timer outside the function I defined the variable....   I don't know if that's because my script is client side and the timer only runs for client or not....

And isSoundFinished wouldn't be much use, I am trying to get the local variable from line 34, but as this timer is in a separate function to the one I use to kill it, it can not detect it, at least I think that's what is going on.....

So here is something I think could work, can you add events with no handlers and then use triggerEvent?  Or do you need the handlers to handle these events?

If you can't, then can you nest functions?  I have tried but not succeeded...

Link to comment

Try to add "local nextSong" at the very top of your script above all other functions (if you haven't done it already, I'm from phone and I'm sleepy so forgive me xd). And yes you can nest functions but no i got no idea how it works (just seen it done once somewhere). Oh and I'm pretty sure you need to add both an event as well as a handler before triggering it. 

Edited by koragg
Good night xd
Link to comment

Still same result @koragg

Spoiler

	Login_window = {}
	local nextSong

songs = {}
SongNum = 8

songs[1] = "sounds/1.mp3"
songs[2] = "sounds/2.mp3"
songs[3] = "sounds/3.mp3"
songs[4] = "sounds/4.mp3"
songs[5] = "sounds/5.mp3"
songs[6] = "sounds/6.mp3"
songs[7] = "sounds/7.mp3"
songs[8] = "sounds/8.mp3"

--Login Sounds
function PlayLoginSound()
	randomSong = math.random (1, SongNum)
	sound = playSound(tostring(songs[randomSong]), false) --Play the song
	if (sound) then
		setSoundVolume(sound, 0.5) -- set the sound volume to 50%
		else 
		return
	end
		soundLength = math.floor(getSoundLength ( sound )) 
		outputChatBox(""..soundLength)
	if (soundLength) then 
		setTimer(nextSound, soundLength*1000, 1)
		addEventHandler("stoploginsound", root, StopLoginSound)
	end
end

addEventHandler("onClientResourceStart", root, PlayLoginSound)

--function StartLoginMusic()
--addEventHandler ( "onClientResourceStart", resourceRoot, PlayLoginSound)


function nextSound()
	if isTimer ( nextSong ) then 
	killTimer ( nextSong )
	end
	randomSong = math.random (1, SongNum)
	sound = playSound(tostring(songs[randomSong]), false) 
		setSoundVolume(sound, 0.5) 
		removeEventHandler("stoploginsound", root, StopLoginSound)
	soundLength = math.floor(getSoundLength ( sound )) 
	setElementDimension(sound, 0)
	if (soundLength) then 
		nextSong = setTimer(nextSound, soundLength*1000, 1)
	end
end

function StopLoginSound()
	stopSound(sound)
	killTimer ( nextSong )--Says it not there
	if isTimer ( nextSong ) then 
	killTimer ( nextSong )
	end
end

addEvent("stoploginsound", true)

 

 

Link to comment
9 hours ago, Randomly said:

Have you managed to fix it? Because i was helping from phone the whole time but if you really want to solve this, i can debug it on my PC

Not managed yet, driving me crazy, the whole problem is that it can't find the timer, I know you need to make a timer or something as a global variable (setting element data would do that as it shares it with server) but with the timer I have it in one function, trying to cancel it in another, and when I loop the  song, I am trying to cancel the timer before it starts (so it's not really necessary on the loop.)

Bbut the problem is the last function, it can't find "nextSong" since I put it in an if and set the timer there.

But if you would debug on your PC would be appreciated :) it's just the pesky timer. -_-

Edited by kieran
Link to comment
songs = {}
SongNum = 5

songs[1] = "sounds/1.mp3"
songs[2] = "sounds/2.mp3"
songs[3] = "sounds/3.mp3"
songs[4] = "sounds/4.mp3"
songs[5] = "sounds/5.mp3"

--Login Sounds
function PlayLoginSound()
	local randomSong = math.random (1, SongNum)--Get a random song from table
	sound = playSound(tostring(songs[randomSong]), true) --Play the song
	if (sound) then
		soundLength = getSoundLength ( sound )
		setSoundVolume(sound, 0.2) --Set the sound volume to 20%
		if (soundLength) then 
			setTimer(nextSound, soundLength*1000, 1)
		end
	else 
		outputDebugString("error")
	end
end

addEventHandler ( "onClientResourceStart", resourceRoot, PlayLoginSound)



function nextSound()
	destroyElement(sound) -- This is a new line
	local randomSong = math.random (1, SongNum) -- This is a new line too
	sound = playSound(tostring(songs[randomSong]), true) 
	soundLength = getSoundLength ( sound ) -- This function returns the lenght in seconds
	if (sound) then
		setSoundVolume(sound, 0.2) 
		if (soundLength) then
			setTimer(nextSound, soundLength*1000, 1) -- setTimer uses miliseconds. 1 second = 1000 miliseconds
		end
	end
end

Here it is. It is working. It was a tiny mistake.

Edit: I would expand the code with a check so it can't play the same song twice.

Edited by Randomly
+ idea
Link to comment

It is not fixed :( I don't think I have been explaining it well...  I want to trigger an event to stop the timer when my player logs in.

It works the exact same as before, the problem is I can not get the timer from "nextSound"  when I try to kill it on my custom event.

Client

Spoiler

function nextSound()
	--First of all it can not find the element, this is why I MUST use stopSound.
	if isTimer ( nextSong ) then 
	killTimer ( nextSong )
	end
	randomSong = math.random (1, SongNum) 
	sound = playSound(tostring(songs[randomSong]), false) 
		setSoundVolume(sound, 0.5) 
	soundLength = math.floor(getSoundLength ( sound )) 
	--setElementDimension(sound, 0)
	if (soundLength) then 
		nextSong = setTimer(nextSound, soundLength*1000, 1)--HERE is the problem, This timer is inside this function. (look down in code)
	end
end

function StopLoginSound()
	stopSound(sound)--Since playSound is there for the WHOLE script, in functions or not, I can stop it here.
	if isTimer ( nextSong ) then --From the timer I set above, you will see it is in a SEPERATE FUNCTION. It can not find it, I want to make it so it can find the timer across functions.
	killTimer ( nextSong )
	end
end

addEvent("stoploginsound", true)

 

Server

Spoiler

--Stop sound on login
function PlayLogin()
	triggerClientEvent(source, "stoploginsound", source)--Look at the client code.
end
addEventHandler("onPlayerLogin", root, PlayLogin)

 

I will try explain why it can not find the timer, I know why it can't, the timer can only be used in the function it is currently in, this is the reason I wanted to nest a function and add a handler to stop it.  Finally, I am using 2 functions, one to make the timer, one to stop it.

I want to make it so it can be used across functions when set inside a function.

One idea I have is put the timer in a table and get the table when I try to stop it, how would I do that?

Also read comments, they should give a better idea of what I am trying.

Edited by kieran
Link to comment
12 minutes ago, kieran said:

It is not fixed :( I don't think I have been explaining it well...  I want to trigger an event to stop the timer when my player logs in.

It works the exact same as before, the problem is I can not get the timer from "nextSound"  when I try to kill it on my custom event.

Client

  Reveal hidden contents


 function nextSound()	--First of all it can not find the element, this is why I MUST use stopSound.	if isTimer ( nextSong ) then 	killTimer ( nextSong )	end	randomSong = math.random (1, SongNum) 	sound = playSound(tostring(songs[randomSong]), false) 		setSoundVolume(sound, 0.5) 	soundLength = math.floor(getSoundLength ( sound )) 	--setElementDimension(sound, 0)	if (soundLength) then 		nextSong = setTimer(nextSound, soundLength*1000, 1)--HERE is the problem, This timer is inside this function. (look down in code)	endendfunction StopLoginSound()	stopSound(sound)--Since playSound is there for the WHOLE script, in functions or not, I can stop it here.	if isTimer ( nextSong ) then --From the timer I set above, you will see it is in a SEPERATE FUNCTION. It can not find it, I want to make it so it can find the timer across functions.	killTimer ( nextSong )	endendaddEvent("stoploginsound", true)

 

Server

  Reveal hidden contents


 --Stop sound on loginfunction PlayLogin()	triggerClientEvent(source, "stoploginsound", source)--Look at the client code.endaddEventHandler("onPlayerLogin", root, PlayLogin)

 

I will try explain why it can not find the timer, I know why it can't, the timer can only be used in the function it is currently in, this is the reason I wanted to nest a function and add a handler to stop it.  Finally, I am using 2 functions, one to make the timer, one to stop it.

I want to make it so it can be used across functions when set inside a function.

One idea I have is put the timer in a table and get the table when I try to stop it, how would I do that?

Also read comments, they should give a better idea of what I am trying.

Oh... I get it. Okay. I'll look into that too.

Link to comment
  • Moderators

I'm not sure if that's what you mean, but I made this code and I haven't tested it. Here it is:


addEvent("stoploginsound", true)

local songs = {
	"sounds/1.mp3", "sounds/2.mp3", "sounds/3.mp3", "sounds/4.mp3",
	"sounds/5.mp3", "sounds/6.mp3", "sounds/7.mp3", "sounds/8.mp3"
}
local playingSound

function PlayLoginSound()
	playRandomSong_orStop()
	
	addEventHandler( "onClientSoundStopped", resourceRoot, onSoundStopped )
	addEventHandler( "stoploginsound", root, playRandomSong_orStop )
end
addEventHandler ( "onClientResourceStart", resourceRoot, PlayLoginSound)

function onSoundStopped ( reason )
	if ( reason == "finished" ) then
		--outputChatBox ( "end of sound" )
		destroyElement( source )
		playRandomSong_orStop()
	end
end

function playRandomSong_orStop( stop )
	if stop then
		destroyElement( playingSound )
		playingSound = nil
		return
	end
	local rndSong = math.random( #songs )
	playingSound = playSound( tostring(songs[rndSong]) ) --Play the song
	setSoundVolume(sound, 0.5) --Set the sound volume to 20%
end

-- server-side

function PlayLogin()
	triggerClientEvent(source, "stoploginsound", source, true)
end
addEventHandler("onPlayerLogin", root, PlayLogin)

 

Link to comment
8 minutes ago, DNL291 said:

I'm not sure if that's what you mean, but I made this code and I haven't tested it. Here it is:


addEvent("stoploginsound", true)local songs = {	"sounds/1.mp3", "sounds/2.mp3", "sounds/3.mp3", "sounds/4.mp3",	"sounds/5.mp3", "sounds/6.mp3", "sounds/7.mp3", "sounds/8.mp3"}local playingSoundfunction PlayLoginSound()	playRandomSong_orStop()		addEventHandler( "onClientSoundStopped", resourceRoot, onSoundStopped )	addEventHandler( "stoploginsound", root, playRandomSong_orStop )endaddEventHandler ( "onClientResourceStart", resourceRoot, PlayLoginSound)function onSoundStopped ( reason )	if ( reason == "finished" ) then		--outputChatBox ( "end of sound" )		destroyElement( source )		playRandomSong_orStop()	endendfunction playRandomSong_orStop( stop )	if stop then		destroyElement( playingSound )		playingSound = nil		return	end	local rndSong = math.random( #songs )	playingSound = playSound( tostring(songs[rndSong]) ) --Play the song	setSoundVolume(sound, 0.5) --Set the sound volume to 20%end-- server-sidefunction PlayLogin()	triggerClientEvent(source, "stoploginsound", source, true)endaddEventHandler("onPlayerLogin", root, PlayLogin)

 

Sorry, but no, I will explain, I am using a timer in the function that loops the music, that timer is INSIDE the function, so it can get the sound length and set the timer to repeat the process.

The problem is, on my custom event, it can not find the timer as it is INSIDE a different function, I want it so that I can use the timer across more than one function, but still get the sound length...   Basically I am looping a timer in a function, but wish to cancel it with a custom event.

Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...