-
-
Notifications
You must be signed in to change notification settings - Fork 21.2k
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
Audio: Write to device's buffer directly without temporary buffers. #90013
base: master
Are you sure you want to change the base?
Conversation
@@ -36,7 +36,6 @@ | |||
#include "core/templates/safe_refcount.h" | |||
#include "servers/audio_server.h" | |||
|
|||
#include <mmsystem.h> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe it's needed for MSVC?
@@ -30,11 +30,6 @@ | |||
|
|||
#include "audio_driver_opensl.h" | |||
|
|||
#include <string.h> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably not needed? Android builds always use clang
platform/web/audio_driver_web.h
Outdated
@@ -54,7 +54,8 @@ class AudioDriverWeb : public AudioDriver { | |||
|
|||
int buffer_length = 0; | |||
int mix_rate = 0; | |||
int channel_count = 0; | |||
int channels = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed the name to match other audio drivers.
servers/audio_server.h
Outdated
virtual SpeakerMode get_speaker_mode() const = 0; | ||
virtual int get_total_channels() const = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It could be better to implement new SpeakerMode
values instead of get_total_channels
?
servers/audio_server.cpp
Outdated
|
||
if (k == cs - 1 && stride == (uintptr_t)cs * 2 - 1) { | ||
// Downmix. | ||
_write_sample(p_buffer, idx, (int32_t)(((int64_t)vl2 + (int64_t)vr2) / 2)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe it would be better to pass _write_sample
as an argument or make it a virtual method in AudioDriver
? It looks like all existing drivers don't need it though
How would you suggest testing this? |
Just using AudioStreamPlayer and switching driver in the settings. I checked downsampling by modifying PulseAudio code like this: Patch
diff --git a/drivers/pulseaudio/audio_driver_pulseaudio.cpp b/drivers/pulseaudio/audio_driver_pulseaudio.cpp
index 2e7a3155da1..166ee9472a8 100644
--- a/drivers/pulseaudio/audio_driver_pulseaudio.cpp
+++ b/drivers/pulseaudio/audio_driver_pulseaudio.cpp
@@ -200,6 +200,8 @@ Error AudioDriverPulseAudio::init_output_device() {
return err;
}
+ pa_channel_map_init_mono(&pa_map);
+
if (pa_map.channels <= 8) {
channels = pa_map.channels;
} else { |
Web audio is broken on master too. (#90906) |
case AudioDriver::BUFFER_FORMAT_INTEGER_32: | ||
// (1u << (32 - 1)) - 1 can't be stored in float correctly. | ||
((int32_t *)p_buffer)[p_idx] = (double)p_sample * ((1u << (32 - 1)) - 1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is different from the implementation that was before:
godot/servers/audio_server.cpp
Lines 288 to 291 in 9bc49a6
float l = CLAMP(buf[from + j].left, -1.0, 1.0); | |
int32_t vl = l * ((1 << 20) - 1); | |
int32_t vl2 = (vl < 0 ? -1 : 1) * (ABS(vl) << 11); | |
*dest = vl2; |
But I'm not sure why it was like this, all I can find on the Internet is how to convert from float to 16-bit integer and it's just to multiply a float to match the range of the number (eg. -1.0 is the minimum value that can fit in 16-bit integer and 1.0 is the maximum), so I expect it to be the same with 32-bit integers?
Also, used everywhere
((1u << (*bits* - 1)) - 1)
, though it would be better to use (1u << (*bits* - 1))
for negative numbers and ((1u << (*bits* - 1)) - 1)
for positive ones. But I think it's really hard to hear the difference so kept it like this for a small optimization (which would be good as audio callbacks really should run as fast as possible).
case AudioDriver::BUFFER_FORMAT_INTEGER_24: { | ||
int32_t sample = int32_t(((int8_t *)p_buffer)[p_idx * 3 + 2]) << 16; | ||
sample |= int32_t(((int8_t *)p_buffer)[p_idx * 3 + 1]) << 8; | ||
sample |= int32_t(((int8_t *)p_buffer)[p_idx * 3 + 0]); | ||
|
||
return (float)sample / (1u << (24 - 1)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A sign will probably be wrong, but unsure how to fix that.
case AudioDriver::BUFFER_FORMAT_INTEGER_24: { | ||
int32_t sample = p_sample * ((1u << (24 - 1)) - 1); | ||
int32_t sign = p_sample < 0 ? -1 : 1; | ||
|
||
((int8_t *)p_buffer)[p_idx * 3 + 2] = sign * (ABS(sample) >> 16); | ||
((int8_t *)p_buffer)[p_idx * 3 + 1] = sign * (ABS(sample) >> 8); | ||
((int8_t *)p_buffer)[p_idx * 3 + 0] = sample; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as the above.
const inb = GodotRuntime.heapSub(HEAPF32, p_in_buf, p_in_size); | ||
const input = event.inputBuffer; | ||
const input_channels = input.numberOfChannels; | ||
if (GodotAudio.input) { | ||
const inlen = input.getChannelData(0).length; | ||
for (let ch = 0; ch < 2; ch++) { | ||
for (let ch = 0; ch < input_channels; ch++) { | ||
const data = input.getChannelData(ch); | ||
for (let s = 0; s < inlen; s++) { | ||
inb[s * 2 + ch] = data[s]; | ||
for (let sample = 0; sample < data.length; sample++) { | ||
inb[sample * input_channels + ch] = data[sample]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might not be a good idea to pass all of the channels of the input buffer, but thought that downmixing code should be kept in the same place for all AudioDriver
s.
e86d4df
to
b1d4f24
Compare
Can you expand on this? If you use threaded export audio should work fine. |
|
4320fea
to
620e598
Compare
if env["platform"] == "windows" and not env.msvc: | ||
SConscript("backtrace/SCsub") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved out of Sounds drivers
section
if env["platform"] in ["ios", "macos"]: | ||
SConscript("coreaudio/SCsub") | ||
|
||
if env["platform"] == "linuxbsd": | ||
if env["alsa"]: | ||
SConscript("alsa/SCsub") | ||
if env["pulseaudio"]: | ||
SConscript("pulseaudio/SCsub") | ||
|
||
if env["platform"] == "windows": | ||
if env["xaudio2"]: | ||
SConscript("xaudio2/SCsub") | ||
SConscript("wasapi/SCsub") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's better this way, as no empty files will be compiled. Maybe it would be better to add checks like this to other drivers? Also should I delete checks like #ifdef ALSA_ENABLED
in files?
|
||
int latency = Engine::get_singleton()->get_audio_output_latency(); | ||
buffer_size = closest_power_of_2(latency * mix_rate / 1000); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think power of 2 is required, can't find anything about it online
WAVEFORMATEX wave_format = { 0 }; | ||
WAVEFORMATEX wave_format; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixes g++ warning
if (unlikely(audio_context.input_channels == 0)) { | ||
audio_context.input_channels = godot_audio_get_input_channels(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't really know if there is a better way to initialize input_channels
unsigned int input_position = 0; | ||
unsigned int input_size = 0; | ||
|
||
void audio_server_process(int p_frames, int32_t *p_buffer, bool p_update_mix_time = true); | ||
void audio_server_process(int p_frames, void *p_buffer, bool p_active = true, bool p_update_mix_time = true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not really sure about p_active
, it tells whether or not to process audio or just fill buffer with 0. I think it should be an argument, because AudioDriverOpenSL
and AudioDriverCoreAudio
pass false if callback is unable to lock mutex, it doesn't feel right to have a function like is_active
because of that. input_process
has something simmilar to this, if you pass nullptr
as a buffer (it's needed for AudioDriverWASAPI
), internall buffer will be filled with 0, we can't pass nullptr
in audio_server_process
, as we still need to fill a buffer. It's just a simplification to not call memset
and to not reapeat the same code
Marked as a draft, cause I want to separate some changes into different PRs/commits |
Any updates on this? |
Sadly no, have no time right now :/ |
Also, a little bit of a clean-up.
TODO: