Loop Issues & Thread Duplication

Howdy everyone. So I’m having issues with thread duplication. This program runs perfectly fine the first time I run it. It is an automated sound-light system. It’s all calibrated to respond to the sound.

First song Played:

Second song Played:

EDIT: The problem I see is where the sound names are being called multiple times.
Now, the amount of true’s might look concerning, but that’s just this:

ffc(script.Parent,"RemoteEvent2").OnServerEvent:Connect(function(plr,var) soundPlaying = var; print(soundPlaying) end)

This is where I think the issue is:

while true do -- Delay time divided by 2 (math.random() only permits whole numbers)
    -- UPDATE: Lights update faster than the motors.
    ffc(sounds,selSound):Play();

    repeat wait() until soundPlaybackLoudness ~= nil;
    repeat wait() until soundPlaying == false;

    soundPlaying = true;

    wait(math.random(1,2)/2);

    selSound = soundFind();
end

If anyone is able to help, that’d be much appreciated. Thank you.

1 Like

There’s a lack of code and structure to go off of to determine a cause. Are you able to post the entire code segment you’re working with, while truncating any irrelevant lines? The way the code is provided as is right now yields no actual issues.

As a personal nitpick while you address the above (if you ever plan to), you should validate all in one line instead of many, for the sake of progressing when your conditions are met rather than having multiple loops unnecessarily.

repeat wait() until soundPlaybackLoudness ~= nil and soundPlaying == false

Note: I am not sure how to determine what is irrelevant in this scenario because I have no idea what’s causing the problem.

Server:

-- //: Created by snorebear | 3/24/2019
print('Sound Volume/Light Brightness Converter by snorebear Initialized.');

local Core = {};

local game = game;
local workspace = workspace;
local wfc = game.WaitForChild;
local ffc = game.FindFirstChild;
local rtype = game.IsA;

game:GetService("Lighting").Ambient = Color3.new(0,0,0);

local util = wfc(script,"Util");

-- //: Random Sound Module - MAY BE REMOVED
local randomSound = ffc(util,"RandomSound") and require(ffc(util,"RandomSound")) or nil;
if randomSound then randomSound:Init(wfc(script.Parent,"Sounds"):GetChildren()); end

local sounds = wfc(script.Parent,"Sounds");
local lights = wfc(workspace,"MLights"); -- Change this string ("MLights") if you change the model name.
local lazers = wfc(workspace,"Lazers"); -- Change this string ("Lazers") if you change the model name.

-- //: Setup
local motors = {};
local motorSpeed = 1;

local function setupMotors(obj)
	local base = wfc(wfc(wfc(obj,"Body"),"Pan"),"PanBase");
	local part = wfc(wfc(obj,"Base"),"Base");
	
	local panMotor = Instance.new("Motor", obj);
	panMotor.Name = "PanMotor";
	panMotor.Part0 = base;
	panMotor.Part1 = part;
	panMotor.C0 = CFrame.new(2.2,0,0) * CFrame.fromEulerAnglesXYZ(math.rad(90), math.rad(90), 0); -- Anchor point 1 relative offset.
	panMotor.C1 = CFrame.new(0,0,0) * CFrame.fromEulerAnglesXYZ(math.rad(90),0, 0); -- Anchor point 2 relative offset.
	base.Parent.Weld.Disabled = false;
	
	base = wfc(wfc(wfc(obj,"Body"),"Head"),"HeadBase");
	part = wfc(wfc(wfc(obj,"Body"),"Pan"),"TiltBase");
	
	local tiltMotor = Instance.new("Motor", obj);
	tiltMotor.Name = "TiltMotor";
	tiltMotor.Part0 = base;
	tiltMotor.Part1 = part;
	tiltMotor.C0 = CFrame.new(0,-0.01,0) * CFrame.fromEulerAnglesXYZ(math.rad(-180), math.rad(0), math.rad(90));
	tiltMotor.C1 = CFrame.new(0,0,0) * CFrame.fromEulerAnglesXYZ(math.rad(-90), math.rad(0), 0); -- CFrame.new(-1.4,0,0) :: Distance from TiltBase to HeadBase.
	base.Parent.Weld.Disabled = false;
	
	panMotor.MaxVelocity = math.rad(10);
	tiltMotor.MaxVelocity = math.rad(10);
	panMotor.DesiredAngle = math.rad(0);
	tiltMotor.DesiredAngle = math.rad(0);
	
	table.insert(motors,{"panMotor",panMotor,obj}); table.insert(motors,{"tiltMotor",tiltMotor,obj});
end
for i_,v in pairs(lights:GetChildren()) do setupMotors(v); end

-- //: Selecting a Sound
local prevIndices = {};

local function soundFind(var)
	if randomSound ~= nil then
		local var = randomSound:findRandomSound();
		
		if prevIndices[var] then local var2 = soundFind(); return var2 end
		table.insert(prevIndices,var);
		
		return var;
	end
end

local selSound = soundFind();
local soundPlaybackLoudness;
local soundPlaying
ffc(script.Parent,"RemoteEvent2").OnServerEvent:Connect(function(plr,var) soundPlaying = var; print(soundPlaying) end)
ffc(script.Parent,"RemoteEvent").OnServerEvent:Connect(function(plr,var) soundPlaybackLoudness = var*2; end)
ffc(script.Parent,"RemoteFunction").OnServerInvoke = function(plr) return selSound end
repeat wait() until soundPlaybackLoudness ~= nil;

-- //: Core
local function calculatePanTilt()
	local mult = 2;
	if soundPlaybackLoudness > 600 then mult = 5 end
	local vol = soundPlaybackLoudness/1000*mult;
	if vol > 1 then vol = 1 end
	local tilt,pan;
	
	tilt = math.random(-math.floor(vol*120),math.floor(vol*120));
	pan = math.random(-math.floor(vol*65),math.floor(vol*65));
	
	return tilt,pan;
end

local function calculateBrightness()
	local vol = soundPlaybackLoudness/1000;
	if vol > 1 then vol = 1 end
	local rv;
	
	rv = vol;
	
	return rv;
end

local function calculateColor()
	local vol = soundPlaybackLoudness/1000;
	if vol > 1 then vol = 1 end
	local c3 = Color3.fromRGB;
	
	local presets = function()
		local colorscheme = math.floor(vol*255);
		local r,g,b = 0,0,0;
		
		if soundPlaybackLoudness >= 600 then
			r = colorscheme*2;
			g = 0;
			b = 0;
		elseif soundPlaybackLoudness < 500 and soundPlaybackLoudness >= 200 then
			r = colorscheme*2;
			g = colorscheme*1.5;
			b = 0;
		elseif soundPlaybackLoudness > 50 and soundPlaybackLoudness < 200 then
			r = 0;
			g = colorscheme*1.5;
			b = 0;
		else
			r = 0;
			g = colorscheme*2;
			b = colorscheme*2;
		end
		
		return r,g,b;
	end
	--math.floor(vol*255)+(math.random(1,255)-vol);
	
	return c3(presets());
end

local function pan(angle)
	for i=1,#motors do
		if motors[i][3].Name == "CPSSa" then
			angle = -angle;
		end
		if motors[i][1] == "panMotor" then
			local panMotor = motors[i][2];
			panMotor.DesiredAngle = math.rad(angle);
			panMotor.MaxVelocity = math.rad(math.abs(math.deg(panMotor.CurrentAngle) -  math.deg(panMotor.DesiredAngle)))/(60 * motorSpeed);
		end
	end
end

local function tilt(angle)
	for i=1,#motors do
		if motors[i][3].Name == "CPSSa" then
			angle = -angle;
		end
		if motors[i][1] == "tiltMotor" then
			local tiltMotor = motors[i][2];
			tiltMotor.DesiredAngle = math.rad(angle);
			tiltMotor.MaxVelocity = math.rad(math.abs(math.deg(tiltMotor.CurrentAngle) -  math.deg(tiltMotor.DesiredAngle)))/(60 * motorSpeed);
		end
	end
end

spawn(function()
	while wait(0.01) do
		if selSound == nil or soundPlaybackLoudness == nil then repeat wait() until selSound ~= nil or soundPlaybackLoudness ~= nil end
		
		local color = calculateColor();
		local brightness = calculateBrightness();
		
		for _,v in pairs(lights:GetChildren()) do
			if rtype(v,"Model") and ffc(v,"Body") then
				local head = wfc(ffc(v,"Body"),"Head");
				
				local partsToChange = {
					ffc(head,"Beam"),
					ffc(head,"Lens")
				};
				
				for i=1,#partsToChange do
					if (ffc(partsToChange[i],"SpotLight") or ffc(partsToChange[i],"PointLight") or ffc(partsToChange[i],"SurfaceLight")) then
						local light = ffc(partsToChange[i],"SpotLight") and ffc(partsToChange[i],"SpotLight") or ffc(partsToChange[i],"PointLight") and ffc(partsToChange[i],"PointLight") or ffc(partsToChange[i],"SurfaceLight") and ffc(partsToChange[i],"SurfaceLight");
						
						light.Enabled = true;
						light.Color = color;
						light.Brightness = brightness;
					end
					if ffc(partsToChange[i],"light") then
						local light = ffc(partsToChange[i],"light");
						
						light.Enabled = true;
						light.Color = ColorSequence.new(color);
						light.LightEmission = brightness;
					end
				end
			end
		end
	end
end)
spawn(function()
	while true do
		if selSound == nil or soundPlaybackLoudness == nil then repeat wait() until selSound ~= nil or soundPlaybackLoudness ~= nil end
		
		local var = math.random(1,2)/3;
		wait(var);
		
		motorSpeed = var;
		local pan1,tilt1 = calculatePanTilt();
		spawn(function()
			pan(pan1);
			tilt(tilt1);
		end)
	end
end)

while true do -- Delay time divided by 2 (math.random() only permits whole numbers)
	-- UPDATE: Lights update faster than the motors.
	ffc(sounds,selSound):Play();
	
	repeat wait() until soundPlaybackLoudness ~= nil;
	repeat wait() until soundPlaying == false;
	
	soundPlaying = true;
	
	wait(math.random(1,2)/2);
	
	selSound = soundFind();
end

Client:

local selSound = workspace["Light Controller"]["RemoteFunction"]:InvokeServer();
if selSound == nil then repeat wait() until selSound ~= nil; end

local handle = function()
	if workspace["Light Controller"]["Sounds"][selSound].Playing == false then 
		workspace["Light Controller"]["RemoteEvent2"]:FireServer(workspace["Light Controller"]["Sounds"][selSound].Playing);
		selSound = workspace["Light Controller"]["RemoteFunction"]:InvokeServer();
		if selSound == nil then repeat wait(0.05); until selSound ~= nil; end
		print(selSound);
	else
		workspace["Light Controller"]["RemoteEvent2"]:FireServer(true);
	end
end

spawn(function()
	while wait(math.random(1,2)/3) do
		handle();
	end
end)

while wait(0.01) do
	if selSound == nil then repeat wait() until selSound ~= nil; end
	workspace["Light Controller"]["RemoteEvent"]:FireServer(workspace["Light Controller"]["Sounds"][selSound].PlaybackLoudness);
end

As far as I can tell, judging from the way your thread is supposedly duplicating, I can only see one root cause and that’s in the only loop that invokes a function with a print statement in it.

I’m going to go ahead and assume that the issue is somewhere within this chunk of code. I’m not quite sure what this is or why anything here is necessary (the loop, the interval, the purpose of any of this code, so on). Perhaps code may not be enough and a barebones repro with the affected code and assembly would be neat to have, for a hands-on look. Only as a last resort, though - don’t think that’s needed right now.

In the future, it’d probably be helpful to both yourself and collaborators if you gave your variables explicit names. “handle” doesn’t really say much. Handle what? Lights? Sounds? Funny noises? I notice this is also an issue with your object naming in general as well.

image

2 Likes

I messed around with a lot of stuff and moved quite a few things to try and locate the source of the duplication. As per the “handle” function, that is also the reason. I just can’t seem to find as to why it does this.

Is it the print output from print(selSound); that you’re looking at and deciding there’s a problem?

Maybe you are getting some overlap here? (with calls to handle):

spawn(function()
	while wait(math.random(1,2)/3) do
		handle();
	end
end)

Maybe the args to random need adjustments?

Sorry it’s hard to tell. With a working version I’d probably insert a ton of prints until I could wrap my head around it all. :smiley:

1 Like

I tried that and unfortunately it isn’t. Thank you for the help however.

1 Like

Looks like most of it shouldn’t be an issue. What about the soundFind function? Have you looked at that as a potential culprit? Maybe there’s a quick way to handle the recursion bit differently and also make sure everything you’re doing with the table is working as expected so that can be ruled out. That’s the only other area I see that has a high chance of hiding an issue.

The problem is, if the soundFind function is the culprit, then why does the whole loop run multiple times? It should be a simple callback. I’ll take a look at the table however.

EDIT: I know the whole loop runs because of the print statements and the sound bugs out (as seen when playing the same sound over and over before it’s completed).

There may not be anything at all wrong with the code. It might be that sounds are not resetting correctly (docs talk about Play jumping to the last time value set in a script rather than 0). To make sure the sounds reset, try doing this:

while true do -- Delay time divided by 2 (math.random() only permits whole numbers)
	-- UPDATE: Lights update faster than the motors.
	ffc(sounds,selSound):Stop();
	ffc(sounds,selSound):Play();
	-- ...
1 Like

It’s still slightly buggy but that seems to have fixed it mainly. The sound skips once maximum. Appreciate the help. Never would have thought of that being the cause.

1 Like

Feels like a hacky solution, but it may be on the right track (i.e., something to do with the sound object behavior and how it’s working with your code). Have you had any trouble with more than one song playing at a time?

Anyway, glad I could contribute something that feels like progress :laughing:. Hopefully someone will be able to give you a definitive answer to this problem. I thought @colbert2677 was on the right track with the handle code. Seems like a prime candidate. It may still turn out to be adding to the bugginess.

1 Like

Your solution works to an extent. Sometimes the sound still bugs (as I stated above), but I made a janky fix that just uses a counter system. I.e. it counts up to a maximum number and subtracts it every time the function finishes. This prevents multiple sounds loading in. For now, the product functions in the best state I can think of. It’s just that odd fix that will concern me in the future. However, the solution will work.