New Audio API [Beta]: Elevate Sound and Voice in Your Experiences

Will there be an option to choose which AudioEmitters can be ignored by an AudioListener? It would be useful to avoid feedback loops (for in-game microphones that pick up everything), but not only.

Also, will there be a way to set more than 1 AudioInteractionGroup for the new Instances?

Will there be an option to choose which AudioEmitters can be ignored by an AudioListener?

Currently this can be controlled by the AudioInteractionGroup property – listeners will only hear emitters that are in the same group.

will there be a way to set more than 1 AudioInteractionGroup for the new Instances?

This is something we’ve discussed but don’t yet have any firm plans on – for now, in order to have audio emitted and heard by multiple listeners, you’d also need multiple emitters (each one in a different interaction group).

The good news is that those extra wires and emitters are pretty lightweight, but I understand it can contribute to an overall mess of disorganized, wire-spaghetti.

It would be more convenient if these behaved like CollisionGroups, where you can define cross-group interactions; but CollisionGroups had some limitations (e.x. maximum count) that dissuaded us from reusing them directly.

1 Like

Is there a way to get the position of a sound playing through AudioListener? Not the AudioListener’s parent but the sound’s parent.

We actually just added two methods: AudioEmitter:GetInteractingListeners() and AudioListener:GetInteractingEmitters(). These can be used to list all the listeners that would hear a given emitter, or all the emitters that can be heard-by a given listener

Combining these and the helper method from above, you could

  1. list all the emitters heard by a listener with listener:GetInteractingEmitters(),
  2. for a particular emitter in the list of emitters, use getCFrameFrom(emitter) to get its position

Does that help?

1 Like

Thank you for the follow up. I played with this a little and it seems very useful in altering listeners and emitters in groups.

I was wondering if you guys were planning on creating an event for when an AudioListener picks up any sound, it provides the AudioEmitter with it possibly, something like AudioListener.SoundDetected:Connect(someAudioEmitter: AudioEmitter)

Adding an event like that directly would come with a passive performance penalty, since the stream being produced by the AudioListener is already mixed; we’d have to comb back through the graph to unpack it/determine where it came from.

But, you can implement something similar with AudioAnalyzers if you don’t mind the overhead – a rough idea would be

  1. wire up your AudioListener, as well as the inputs of each AudioEmitter to several AudioAnalyzers
  2. periodically check the listener-analyzer’s PeakLevel property; whenever it crosses a threshold, get the listener’s interacting emitters
  3. for each emitter, check whether its analyzer also has PeakLevel above a threshold

If both the Listener-analyzer and the Emitter-analyzer have crossed a volume threshold at about the same time, this indicates that the emitter is partially-responsible for whatever the listener heard

2 Likes

Ah, performance didn’t really cross my mind there.

Thanks for giving me a good solution. I’ll be sure to try it.

1 Like

I’m encountering an issue where I hear a short pop or click when I create an AudioPlayer, set its AssetId, and immediately call :Play().

This is the code I’m using:

while true do
	local audioPlayer = Instance.new("AudioPlayer", workspace)
	audioPlayer.AssetId = "rbxassetid://7003103879"
	audioPlayer:Play()
	
	audioPlayer.Ended:Connect(function()
		audioPlayer:Destroy()
	end)
	
	task.wait(0.1)
end

Observations

  • The issue occurs consistently when I use Bluetooth headphones.
  • When I use my laptop speakers, the issue does not seem to occur.
  • I haven’t tested it with wired headphones.
  • The problem is not limited to this specific AssetId; it also happens with other audio assets.
  • The issue persists regardless of whether the AudioPlayer is connected to an emitter or not.
2 Likes

Interesting; I hear the same thing – will check it out

2 Likes

Currently im having an issue and im not sure if its a client server authority issue or something else.

But for my needs under VoiceChatService I disable “EnableDefaultVoice”

On the server on player join im creating an AudioDeviceInput parented to the player instance… And on Character Added I am creating an audio emitter parented to the character.

From there on the client I create an Audio Output Device and put it in the client workspace camera. Depending on the state my game is in I either from the client will wire the server created AudioDeviceInput to the client created AudioOutputDevice (this works letting me hear every player).

But from the client when I try to wire the Server Created Audio Input Device to the server created AudioEmitter it will connect the wire, but no audio can be heard.

Any ideas why the client works with AudioInputDevice → AudioOutputDevice but not AudioInputDevice → AudioEmitter?

AudioEmitters need a corresponding AudioListener to hear them; If you have

AudioInputDevice → AudioEmitter

You may also need AudioListener → AudioDeviceOutput somewhere else in the scene

Ah so I saw in the example Place that is linked in the first post, that there is an:

AudioListner → AudioDeviceOutput in the client workspace camera.

So if im understanding correctly, by including that the audio listener in the camera would pick up the other player characters AudioEmitter and play the sound back to local player?

1 Like

I want to report a bug.

So when you create any AudioApi related instances using Instance.new() such as “AudioReverb” and then you :Destroy() it afterward. It doesn’t properly clear the memory usage despite using :Destroy() so it causes “Memory Leak.”

Reproducing this is easy

local CreatedInstances = {}
while task.wait() do
	-- Create 5 new instances on every loop
	for p=1,5 do
		local NewInstance = Instance.new("AudioReverb") --> Any class related to AudioApi
		NewInstance.Parent = script
		
		table.insert(CreatedInstances,NewInstance)
	end
	
	-- Destroy them after
	for i,v in CreatedInstances do
		CreatedInstances[i] = nil
		v:Destroy()
	end
end

Run this in server script. It will create new AudioReverb instances then destroy it in a loop.

Then if you go to developer console (F9) > memory > server > PlaceMemory > Sound, you will see the graph skyrockets like crazy!

I hope you are able to fix this :pray:

2 Likes

Hey @mastawba; I gave this script a try, and what I observed is that memory usage can climb for a while before garbage collection kicks in and knocks it back down – I was not able to reproduce a truly unbounded leak, though – what was the highest you saw memory usage go?

1 Like

Using the code I provided in an new empty baseplate with UseAudioApi true and EnableDefaultVoice true. For 30 seconds the memory reached over 15,924 megabytes. That is way over than Roblox server’s maximum memory of estimated 6,300 megabytes.

I’ve also noticed that this also works in Roblox server not just in Roblox studio. I have a test game here memory leak test - Roblox

1 Like

Ah I had tested the code in a script with Client RunContext, and that does not leak; changing that to Server, I am seeing similar. Yikes – will look into what’s causing that.

2 Likes

Hey @JOSIMA33, a fix for these clicking noises should be going out in v638 – thanks for the report!

The problem occurs when creating an AudioPlayer and immediately playing it, so as a temporary workaround, creating AudioPlayers in advance, before playing them, avoids this. Even a single-frame wait is long enough:

while true do
	local audioPlayer = Instance.new("AudioPlayer", workspace)
    task.wait() -- waiting here avoids the click
	audioPlayer.AssetId = "rbxassetid://7003103879"
	audioPlayer:Play()
	
	audioPlayer.Ended:Connect(function()
		audioPlayer:Destroy()
	end)
	
	task.wait(0.1)
end

you could also pool & reuse AudioPlayers instead of calling Instance.new/Instance:Destroy,

local pool : {AudioPlayer} = {}
local function createAudioPlayer() : AudioPlayer
    if #pool == 0 then
        return Instance.new("AudioPlayer", workspace)
    else
        return table.remove(pool)
    end
end

local function destroyAudioPlayer(audioPlayer : AudioPlayer)
    audioPlayer.AssetId = ""
    table.insert(pool, audioPlayer)
end

which only encounters the click the first few times, when the pool is empty.

@mastawba there’s a fix currently in-review for the server-side memory leak – hopefully should also be in v638 :sweat: – thank you for reporting!

3 Likes

I’d like to thank you for creating something this awesome.
Here’s some stuff I was able to create

Summary




https://ko-fi.com/indirecta/shop

Do you plan to add an audio instance that could be used for vocoding?
You’d specify a frequency band or apply a parametric eq, and it’d use the resulting audio data to manipulate another input (that could also be mixed using eqs and ducked over the background to only modulate voice)

3 Likes

I wonder. Will the old sounds be deprecated?

I don’t know if this has been answered before, but will audioEmitters ever work with volumetric audio?

Volumetric audio was a so much great feature. I am kinda confused why AudioEmitters don’t use it.

1 Like