Simple Volumetric Audio by Rylvns

umm… I did the EDIT version and…

  23:40:21.185  Lord_BradyRocks's Place Number: 1001 auto-recovery file was created  -  Studio
  23:40:23.737  [ACTION ITEM] Experience doesn't have permissions for sound asset 16491133121. Click here to grant experience permission to asset.  -  Studio
  23:40:23.737  Failed to load sound rbxassetid://16491133121: User is not authorized to access Asset. (x5)  -  Studio
  23:40:24.099  [ACTION ITEM] Experience doesn't have permissions for sound asset 16491133121. Click here to grant experience permission to asset.  -  Studio
  23:40:24.099  Failed to load sound rbxassetid://16491133121: User is not authorized to access Asset. (x5)  -  Studio

is the sound open source etc…
:slight_smile:

1 Like

The sound was uploaded to my profile so that’s why you can’t hear it, you can replace it with a Roblox free sound if you want, I will instead upload it to the game assets but I don’t think this will actually solve the issue.

1 Like

ummm, maybe just use a roblox free sound in the demo… I dont know…

1 Like

First off, I love this! I’ve needed something like this for so long and it works perfectly. I’m mainly replying however because I’ve actually taken your original script and modified it to be a bunch more optimized for games with larger workspaces.

  • No longer orients through workspace every heartbeat
  • All sounds in the workspace before the game begins must be tagged with “TrackSound”
  • Any instances added or removed from the workspace fire a set of functions that will check and see if it is a sound needed to be added to the ray casts of the volumetric audio
-- by Rylen
-- 2/23/24 @ 10:46 PM

local RunService = game:GetService("RunService");
local Players = game:GetService("Players");
local Player = Players.LocalPlayer;
local Camera = workspace.CurrentCamera;
local CollectionService = game:GetService("CollectionService");

local Sounds = {};

local Params = RaycastParams.new();
Params.BruteForceAllSlow = false;
Params.IgnoreWater = true;
Params.RespectCanCollide = false;
Params.FilterType = Enum.RaycastFilterType.Exclude;

function Lerp(Start, Goal, Alpha)
    return Start + (Goal - Start) * Alpha;
end;

local function InitializeSound(Object)
    Sounds[Object] = {
        ["MaxVolume"] = Object.Volume,
        ["MaxAudibleDistance"] = Object:GetAttribute("MaxAudibleDistance") or 50,
    };
    local EqualizerSoundEffect = Instance.new("EqualizerSoundEffect", Object);
    EqualizerSoundEffect.Name = "Equalizer";
    EqualizerSoundEffect.Enabled = true;
    EqualizerSoundEffect.HighGain = 0;
    EqualizerSoundEffect.MidGain = 0;
    EqualizerSoundEffect.LowGain = 0;
    EqualizerSoundEffect.Parent = Object;
end;

-- Detect whether a sound is being added or removed from the workspace (Fired with connections below) --
local function OnSoundAdded(Object)
    if Object:IsA("Sound") and Object.Parent:IsA("BasePart") then
        if not Sounds[Object] then
            InitializeSound(Object);
        end
    end
end

local function OnSoundRemoved(Object)
    if Sounds[Object] then
        Sounds[Object] = nil;
    end
end

-- Fire above functions whenever something is added to the workspace --
workspace.DescendantAdded:Connect(OnSoundAdded);
workspace.DescendantRemoving:Connect(OnSoundRemoved);

-- SET UP PRE-EXISTING SOUNDS --
for _, Object in pairs(CollectionService:GetTagged("TrackSound")) do
    OnSoundAdded(Object)
end

RunService.Heartbeat:Connect(function()
    for Sound, Properties in pairs(Sounds) do
        local Part = Sound.Parent;

        local MaxVolume = Properties.MaxVolume;
        local Distance = (Camera.CFrame.Position - Part.Position).Magnitude;

        if Distance < Properties.MaxAudibleDistance then
            Params.FilterDescendantsInstances = (function()
                local Results = {Part};
                for _, User in pairs(Players:GetPlayers()) do
                    if User.Character then
                        for _, Object in pairs(User.Character:GetDescendants()) do
                            if Object:IsA("BasePart") then
                                table.insert(Results, Object);
                            end
                        end
                    end
                end
                return Results;
            end)();

            local RaycastResults = workspace:Raycast(Camera.CFrame.Position, Part.Position - Camera.CFrame.Position, Params);
            if RaycastResults then
                local MuffleFactor = 1 - (1 - math.min(Distance / Properties.MaxAudibleDistance, 1));
                Sound.Equalizer.HighGain = Lerp(Sound.Equalizer.HighGain, -80 * MuffleFactor, 0.25);
                Sound.Equalizer.MidGain = Lerp(Sound.Equalizer.MidGain, -80 * MuffleFactor, 0.25);
                Sound.Equalizer.LowGain = Lerp(Sound.Equalizer.LowGain, -20 * MuffleFactor, 0.25);
            else
                Sound.Equalizer.HighGain = Lerp(Sound.Equalizer.HighGain, 0, 0.15);
                Sound.Equalizer.MidGain = Lerp(Sound.Equalizer.MidGain, 0, 0.15);
                Sound.Equalizer.LowGain = Lerp(Sound.Equalizer.LowGain, 0, 0.15);
            end

            Sound.Volume = MaxVolume * (1 - (Distance / Properties.MaxAudibleDistance));
        end
    end;
end);

Again, thank you for making this, and I hope this helps anyone who may want to use this in games with a large workspace!

3 Likes

Hey! This system is great! I do have a few suggestions from looking at the source code and some adjustments from another similar system called “3d sound system”
Link: 3D Sound System - Realistic frequency dampening

Suggestions:
Use the multiraycast module for better and more accurate detection.
Link:MultiRaycast - Easy & optimized parallel raycast

Add sound dampening like 3d sound system to enhance immersion.
(Fork it from them lol)

Control sound volume(volumetric) behind a wall by firing a ray cast though the wall to detect how far away it is and / it by thickness of the wall to produce the perfect volumetric calculation.

These suggestions are just stuff I would be glad to see!

Thank you for your time.

2 Likes

Woah! Amazing work!
Thank you for this optimization for others, I will put it in the main message for them to see.

1 Like

Thank you for these suggestions, I will definitely place these in for the next version!!

I made a video for the next version. Trying to solve sound bounces.

4 Likes

Very nice! The use of rays seems much better now
Good luck on the project

1 Like

Preview of Version-2aa, recoded:

I will explain what this does tmr. (2am edt)

4 Likes

Hello, Any updates on the new version?

Not much since the video, I’ve just started school and I’m thinking exactly how this will work.
A clone of the main sound source will be parented to a pair of attachments which are: 5 closest to the player, and 5 farthest from the player (editable). The sounds will have reverb and the other sound effects that roblox offers. Using this will allow the sound bounces which I wanna add. If you have questions I’m sure I have an answer. If you’re a developer maybe we could work on this together.

Are you using some sort of vector map module? It could help.

Yes im a scripter

I’m using a chunk system for the attachments which can be edited; in the video I show this. I added this so that attachments arn’t so close together and for “performance” to control the amount of attachments.

I suggest placing attachments in a set “Chunk” Style and then using spatial query for wall detection

1 Like

What is a spatial query? How can I do a spatial query? Does it help with performance? Whats a set “Chunk” style? !!

Spatial Query Documentation

Essentially, Its a a imaginary Vector3 based shape that has functions like “GetPartsInPart”

Its a solution to hitbox or detection methods that use parts (which costs performance). Instead you can just check without instancing anything new.

Hitbox Module Using Spatial Query:
Hitbox.rbxm (8.8 KB)

In most cases, Yes. Vastly. This is because of cuts on memory usage, Math, Creation and deletion, and the new method which actually has decent code (Compared to old functions)

Imagine a box, Lets say i divide this box into sections called “Chunks”. Each chunk is its own independent existence and has its own calculation.

You can split up the box into cubes or spheres. Whatever suits your needs but the idea is that using vectormapping you can split up the cost of operation a ton. By essentially getting rid of something called “Junk Calculation”

“Junk Calculation” is when roblox was about to calculate something that didnt need to be and it has to clean it up. This happens the bigger or more “Heavy” something is.

1 Like

If you dont mind giving me the code for this demo, I can add vector mapping so you dont have to go through that trouble.

1 Like

i really love it! this system works perfectly overall, but i’ve noticed errors.

the functionality seems to be working just fine despite the error.
it only happens in my game, not in your demo place.
if you have any ideas about why this might be happening or how to resolve it, I’d really appreciate your insight.
but again, the system is fantastic, and I’m really enjoying using it. :+1:

I’d recommend using the optimized version. But your error is caused by your sound’s parent not being a basepart. You can add an if statement to check if it is a basepart and skip it to remove this issue.