Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added CallOnRemove on entity to stop emitting sound on entity deletion #2952

Closed
wants to merge 4 commits into from
Closed

Added CallOnRemove on entity to stop emitting sound on entity deletion #2952

wants to merge 4 commits into from

Conversation

cryx3001
Copy link

@cryx3001 cryx3001 commented Dec 22, 2023

This will fix the issue where props do not stop their current emitting sounds when deleted.
Having a table to store every sounds is useful when you have to delete two or more sounds playing simultaneously. If we had something like :

ent:CallOnRemove("E2_EmitSound_stop_all", function()
    ent:StopSound(snd)
end

It would not have worked since CallOnRemove overwrites the last call with the same identifier.

By the way, even though the rule on indentation is to use Tab instead of Space, I used Space since this is the indentation used in the EmitSound function declaration.

Fixes #2815

Copy link
Member

@Denneisk Denneisk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to do something like this before but never got around to it since I had some efficiency considerations, but this is probably fine.

lua/entities/gmod_wire_expression2/core/sound.lua Outdated Show resolved Hide resolved
lua/entities/gmod_wire_expression2/core/sound.lua Outdated Show resolved Hide resolved
@Denneisk
Copy link
Member

Otherwise good first contribution, thanks for making this.

Copy link
Contributor

@thegrb93 thegrb93 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

E2_emitting_sounds table can grow indefinitely. Need to find a way to limit its size or have it only contain playing sounds

@Denneisk
Copy link
Member

E2_emitting_sounds table can grow indefinitely. Need to find a way to limit its size or have it only contain playing sounds

There's not really any way to know if a sound is still playing unfortunately. I believe emitSound was always meant to be limited to a single sound, though.

@cryx3001
Copy link
Author

There's not really any way to know if a sound is still playing unfortunately. I believe emitSound was always meant to be limited to a single sound, though.

You can actually play a sound multiple times or even different sounds simultaneously. This will work

@name emitSoundTest
@inputs Ent:entity

Ent:emitSound("ambient/bird1.wav")
Ent:emitSound("ambient/bird1.wav")
...
Ent:emitSound("ambient/bird1.wav")
Ent:emitSound("ambient/bird2.wav")

And since we can't get a kind of length property on the sound, I guess the best idea to reduce the size of the table is to use a key, value pair like "sound" -> "number of times played"

@Denneisk
Copy link
Member

This is a bit more ideal.

@thegrb93
Copy link
Contributor

thegrb93 commented Dec 23, 2023

You don't need to count the number of times a sound is played and StopSound that many times. I'm pretty sure one StopSound for each sound path will do.

@cryx3001
Copy link
Author

You don't need to count the number of times a sound is played and StopSound that many times. I'm pretty sure one StopSound for each sound path will do.

I forgot to mention this, but actually no it does not work. If I take this script

@name emitSoundTest
@inputs Ent:entity

Ent:emitSound("ambient/bird1.wav")
Ent:emitSound("ambient/bird1.wav")

and edit the CallOnRemove to call ent:StopSound(snd) once per sound :

ent:CallOnRemove("E2_EmitSound_stop_all", function()
    local emitting_sounds = ent:GetVar("E2_emitting_sounds")
    if not emitting_sounds then return end
    for snd, count in pairs(emitting_sounds) do
        ent:StopSound(snd)
    end
end)

It will only stop one of these two sounds playing, the second one will not stop upon deletion

@Denneisk
Copy link
Member

You don't need to count the number of times a sound is played and StopSound that many times. I'm pretty sure one StopSound for each sound path will do.

Unfortunately cryx is right, I discovered this in my own testing.

@thegrb93
Copy link
Contributor

Yeesh, then we need to limit the counter to however many is the max sounds source-engine will play. If someone runs emitsound a million times, they can make a lagspike calling Stopsound that many times.

@thegrb93
Copy link
Contributor

See if 32 is a good limit?

@@ -304,7 +304,7 @@ local function EmitSound(e2, ent, snd, level, pitch, volume)
end

local emitting_sounds = ent:GetVar("E2_emitting_sounds", {})
emitting_sounds[snd] = (emitting_sounds[snd] or 0) + 1
emitting_sounds[snd] = math.min((emitting_sounds[snd] or 0) + 1, 32)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to ensure you can't emit sounds past this point as well.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this limit should be applied on the total number of sounds played, not specifically for each sound.
I could play bird1.wav, bird2.wav, ..., bird9999999.wav and still cause the lagspike no? I guess we should work with a queue to remove the oldest sound played if needed.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In which case you should go back to the array of strings.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah

Copy link
Contributor

@thegrb93 thegrb93 Jan 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a function that returns if a sound loops? If we can just keep track of the looping sounds playing, we'll know what needs to get cleaned up when the entity is deleted.

@thegrb93
Copy link
Contributor

thegrb93 commented Jan 5, 2024

I made this. Not sure how well it works.

local timerLinkedList = {
    makeNode = function(prev, next, time, func)
        local node = {time = time, func = func}
        if prev then prev.next = node end
        if next then node.next = next end
        return node
    end,
    __index = {
        add = function(self, time, func)
            if self.length==0 then
                self.root = timerLinkedList.makeNode(nil, nil, time, func)
            elseif time<=self.root.time then
                self.root = timerLinkedList.makeNode(nil, self.root, time, func)
            else
                local prevnode = self.root
                local node = prevnode.next
                while node and time>node.time do
                    prevnode = node
                    node = node.next
                end
                timerLinkedList.makeNode(prevnode, node, time, func)
            end
            self.length = self.length + 1
        end,
        delete = function(self)
            while self.root do
                self.root.func()
                self.root = self.root.next
            end
        end,
        pop = function(self)
            self.root = self.root.next
            self.length = self.length - 1
            return self.length==0
        end,
        think = function(self, t)
            if self.length==0 then return true end
            while t>self.root.time do
                self.root.func()
                if self:pop() then return true end
            end
        end
    },
    __call = function(t)
        return setmetatable({length = 0}, t)
    end
}
setmetatable(timerLinkedList, timerLinkedList)

local soundcleaner_timers = setmetatable({},{__index=function(t,k) local r=timerLinkedList() t[k]=r return r end})

local function soundcleaner_cleanup(ent)
    soundcleaner_timers[ent] = nil
    if next(soundcleaner_timers)==nil then
        hook.Remove("Think","Wire_E2_CleanupEmitSounds")
    end
end

local function soundcleaner_think()
    local curtime = CurTime()
    for ent, timer in pairs(soundcleaner_timers) do
        if timer:think(curtime) then
            soundcleaner_cleanup(ent)
        end
    end
end

local function soundcleaner_soundPlayed(ent, sound)
    local timer = soundcleaner_timers[ent]
    if timer.length == 32 then return end

    local duration = SoundLoops(sound) and math.huge or SoundDuration(sound)

    timer:add(CurTime()+duration+0.001, function()
        ent:SoundSound(sound)
    end)
    hook.Add("Think","Wire_E2_CleanupEmitSounds", soundcleaner_think)
end

local function soundcleaner_soundStopped(ent, sound)
    local timer = rawget(soundcleaner_timers, ent)
    if timer and timer:pop() then
        soundcleaner_cleanup(ent)
    end
end

local function soundcleaner_entityDeleted(ent)
    soundcleaner_timers[ent]:delete()
    soundcleaner_cleanup(ent)
end


-- in EmitSound function
soundcleaner_soundPlayed(ent, snd)
ent:CallOnRemove("E2_EmitSound_stop_all", soundcleaner_entityDeleted)

Copy link

github-actions bot commented Feb 4, 2024

This pull request has been marked as stale as there haven't been any changes in the past month. It will be closed in 15 days.

@github-actions github-actions bot added the Stale label Feb 4, 2024
@Denneisk
Copy link
Member

Denneisk commented Feb 7, 2024

I think an array of strings would be sufficient if limited to 32. A whole class and linked list is too much complexity for this task.

@github-actions github-actions bot removed the Stale label Feb 8, 2024
Copy link

github-actions bot commented Mar 9, 2024

This pull request has been marked as stale as there haven't been any changes in the past month. It will be closed in 15 days.

@github-actions github-actions bot added the Stale label Mar 9, 2024
@github-actions github-actions bot closed this Apr 24, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

E2 emitSound does not stop when target entity removed
3 participants