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

Add volume L/R controls to AudioStreamPlayer (master) #51667

Closed

Conversation

20kdc
Copy link
Contributor

@20kdc 20kdc commented Aug 14, 2021

This is useful for manual panning.
Manual panning otherwise requires processing the sounds to have separate L and R variants.
Processing this in GDScript is insanely inefficient, so I decided this was the neater solution.

NOTE: Due to inherent interference between Godot 4.x hardware support and my own personal policies, a version of this PR will be made targetting 3.x directly, as the PR is effectively useless to me on 4.x-only.
Testing for this PR has only been performed with the 3.x version.

The 3.x PR will be submitted first so that it can be cross-linked-to, the master PR will be submitted a few seconds later with the link added.

3.x PR: #51666

This is useful for manual panning.
Manual panning otherwise requires processing absolutely all of your sounds to have separate L and R variants.
Processing this in GDScript is insanely inefficient, so I decided this was the neater solution.
doc/classes/AudioStreamPlayer.xml Outdated Show resolved Hide resolved
doc/classes/AudioStreamPlayer.xml Outdated Show resolved Hide resolved
scene/audio/audio_stream_player.cpp Outdated Show resolved Hide resolved
@20kdc
Copy link
Contributor Author

20kdc commented Aug 16, 2021

Would just like to check: Are any of the requested changes not actually performed?

doc/classes/AudioStreamPlayer.xml Outdated Show resolved Hide resolved
@Calinou
Copy link
Member

Calinou commented Aug 17, 2021

@ellenhp Do you think the audio scale L/R should be exposed as linear scales or decibels? Exposing as decibels is more consistent with volume_db, but may be confusing. I have no strong preference for using a linear scale or decibels here.

Would just like to check: Are any of the requested changes not actually performed?

I can see the changes being pushed, so yes 🙂

@ellenhp
Copy link
Contributor

ellenhp commented Aug 17, 2021

I'd recommend using decibels for consistency. Do you know how this change interacts with surround sound mix targets? It seems this should work but it might be a good idea to special-case MIX_TARGET_CENTER, since if I remember right one channel of the center mix target is the center, and the other is LFE which goes directly to a subwoofer (I think?). The spatial audio player nodes show some of the logic that's used to deal with that if I remember right. I'd also recommend maybe considering doing a left-right channel balance instead of letting users throw decibel values directly into the AudioStreamPlayer.

Perhaps a value in the range -1 (full left) to 1 (full right) with zero being balanced, and the default? That's much more intuitive to deal with in the editor in my opinion, and preserving loudness automatically would be really nice for people doing stuff in scripts.

The other thing here is you are going to need to implement ramps to avoid popping.

@Calinou
Copy link
Member

Calinou commented Aug 17, 2021

Perhaps a value in the range -1 (full left) to 1 (full right) with zero being balanced, and the default? That's much more intuitive to deal with in the editor in my opinion, and preserving loudness automatically would be really nice for people doing stuff in scripts.

I agree this would be easier for users to manage too.

@ellenhp
Copy link
Contributor

ellenhp commented Aug 17, 2021

Honestly I'd really prefer we get #51296 in before merging this, because it'll really simplify the code. It removes the need for ramping volume, for starters. That's all handled in the audio server now. I suppose for 3.x the volume ramps will need to be implemented though since #51296 is not a reasonable change to cherry pick IMO.

@20kdc
Copy link
Contributor Author

20kdc commented Aug 17, 2021

Notes before I work out how to make this into a commit:
My assumption on surround is that if what I'm doing here doesn't work, then the original code never worked in the presence of a stereo file (as one might get from stereo music).
My assumption on panning is that this exists explicitly as a bypass for stuff like AudioStreamPlayer2D, and therefore that automating things is probably not the ideal approach (helper function to set them with a specific balance, maybe, but I'd need to know the specific balancing mechanism you had in mind)

Something that would resolve both concerns would be per-mix-target volume pairs + a balance function to mostly-automate if that's what the user wants, but not sure how to design the API for this... or achieve this at all.

@ellenhp
Copy link
Contributor

ellenhp commented Aug 17, 2021

I think that's a reasonable assumption. It's probably fine as-is without handling surround sound in a special way. The only issue IMO is it being difficult to use for dealing with stereo balance. I'd expect any sort of stereo balance control to preserve loudness as a user. I don't feel strongly either way but that's my suggestion. :)

@20kdc
Copy link
Contributor Author

20kdc commented Aug 17, 2021

	// the 2.0 on left/right is critical to preserving loudness.
	// a balance of 0.0 ought to be a no-op, which means a scale of 1.0,1.0
	// therefore, left/right need to be scaled to 2.0 to compensate.
	volume_scale.l = MIN(2.0, MAX(0.0, 1.0 - p_balance));
	volume_scale.r = MIN(2.0, MAX(0.0, 1.0 + p_balance));

I'd like confirmation on this balancing mechanism before I actually commit it to both branches.

@ellenhp
Copy link
Contributor

ellenhp commented Aug 17, 2021

This looks right to me. Unless there's some psycho-acoustic or perceptual stuff that I don't know about, I think it'll sound right too.

…nd ellenhp suggestions, add a quick way of setting loudness-preserving balance
@20kdc
Copy link
Contributor Author

20kdc commented Aug 17, 2021

found the issue with linux builds, turns out I mis-documented a parameter, will fix

@20kdc 20kdc requested review from Calinou and removed request for a team August 20, 2021 18:23
@20kdc
Copy link
Contributor Author

20kdc commented Aug 20, 2021

Um, I didn't expect that to remove the existing request, sorry!
EDIT:
Explaination: I hit "re-request review" assuming that was the correct way to acknowledge that as far as I know (via see review) I've performed all requested changes.

@20kdc
Copy link
Contributor Author

20kdc commented Oct 20, 2021

Just so you know, the only requested change marker still active is outdated, but a merge conflict has appeared since, so I'll have to poke this

@Calinou
Copy link
Member

Calinou commented Oct 20, 2021

Should audio balance be exposed as a property? Right now, it's only a method, which means you can't change it in the editor.

I'm not sure if exposing the individual L/R scale makes sense, as I expect most people to be tweaking the audio balance instead.

@20kdc
Copy link
Contributor Author

20kdc commented Oct 20, 2021

Ok, so, the good news is that by the look of it AudioServer now covers the functionality I wanted, but better: (set_playback_all_bus_volumes_linear).
The bad news: This isn't actually exposed to GDScript.

However obviously this represents a significant divergence from the 3.x solution.
With that in mind, this PR is being closed.

@20kdc 20kdc closed this Oct 20, 2021
@ellenhp
Copy link
Contributor

ellenhp commented Oct 20, 2021 via email

@20kdc
Copy link
Contributor Author

20kdc commented Oct 20, 2021

to me it would be above and beyond enough to be able to supply a specific set of volumes to set_playback_all_bus_volumes_linear from GDScript.

It is important to note that separate left/right volumes was the whole point of the PR. If someone wanted to use whatever Godot's default balancing method is they would likely just build a small "virtual listener box" in 2D.

Right now it's not even possible to block off the left or right channel without processing the sample float-by-float. (in 3.x, anyway, AFAIK.)

@ellenhp
Copy link
Contributor

ellenhp commented Oct 20, 2021 via email

@20kdc
Copy link
Contributor Author

20kdc commented Oct 20, 2021

Eeeh. This bit only matters for the 3.x PR, but: It's mathematically equivalent if willing to pre-process the L/R volumes to get the panning ratio between them, which seems annoying.
Plus someone would have to stop anyone from changing the panning function even if the new one is more realistic to some arbitrary definition of panning, because at that point people are going to start relying on the details (the mathematical equivalence only holds so long as the panning function remains the same).

Anyway, not exposing AudioServer's playback API directly isn't that big of a concern to me.

Ultimately my general idea is that if something wants exact panning control, it should have it. Hypothetical application would be "write an XM player in Godot" kinda thing here - stuff where there's no real reason GDScript shouldn't be able to do it because the real CPU-intensive stuff is all done on the C++ side anyway, so using GDScript avoids having to deal with native compilation across platforms (read: Mac).

That in mind, now that API on the AudioServer exists direct Volume L/R properties are the wrong way to go about this on master to me, because it's less flexible. Hence I've closed this PR - it'd basically be rewritten entirely anyway.

Seeing as the underlying API supports surround sound, whatever API covers it ought to do so as well. If I was doing it I'd just add a method to apply a multiplied PoolFloatArray over the whole lot and a method to clear it.

@ellenhp
Copy link
Contributor

ellenhp commented Oct 20, 2021 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants