diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index 6d6a4de968b1..e14f71feeea3 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -420,6 +420,16 @@
Safer override for [member audio/driver/output_latency] in the Web platform, to avoid audio issues especially on mobile devices.
+
+ The channel index to send to the stereo output when [code]override_speaker_channels[/code] has forced there to be a higher number of actual channels.
+
+
+ If [code]true[/code], the number of output channels will be set by [code]override_speaker_channels[/code] instead of the speaker mode of the output device.
+ [b]Note:[/b] This is only implemented in the PulseAudio audio driver on Linux. Other platforms are unaffected by this project setting.
+
+
+ The number of channels to use when [code]override_channels[/code] is set to [code]true[/code].
+
The base strength of the panning effect for all [AudioStreamPlayer2D] nodes. The panning strength can be further scaled on each Node using [member AudioStreamPlayer2D.panning_strength]. A value of [code]0.0[/code] disables stereo panning entirely, leaving only volume attenuation in place. A value of [code]1.0[/code] completely mutes one of the channels if the sound is located exactly to the left (or right) of the listener.
The default value of [code]0.5[/code] is tuned for headphones. When using speakers, you may find lower values to sound better as speakers have a lower stereo separation compared to headphones.
diff --git a/drivers/pulseaudio/audio_driver_pulseaudio.cpp b/drivers/pulseaudio/audio_driver_pulseaudio.cpp
index 0c517f4a5453..87965c7b4f7c 100644
--- a/drivers/pulseaudio/audio_driver_pulseaudio.cpp
+++ b/drivers/pulseaudio/audio_driver_pulseaudio.cpp
@@ -175,7 +175,6 @@ Error AudioDriverPulseAudio::detect_channels(bool input) {
ERR_PRINT("pa_context_get_sink_info_by_name error");
}
}
-
return OK;
}
@@ -200,23 +199,31 @@ Error AudioDriverPulseAudio::init_output_device() {
return err;
}
- switch (pa_map.channels) {
+ print_verbose("PulseAudio: detected " + itos(pa_map.channels) + " output channels");
+ pa_channels = pa_map.channels;
+ if (GLOBAL_GET("audio/driver/override_channels") && (pa_map.channels == 2)) {
+ pa_channels = GLOBAL_GET("audio/driver/override_speaker_channels");
+ pa_channel0 = CLAMP((int)GLOBAL_GET("audio/driver/override_channel_out"), 0, pa_channels - 2);
+ print_verbose("PulseAudio: forcing " + itos(pa_channels) + " output channels, but outputting to " + itos(pa_channel0) + "," + itos(pa_channel0 + 1));
+ }
+
+ switch (pa_channels) {
case 1: // Mono
case 3: // Surround 2.1
case 5: // Surround 5.0
case 7: // Surround 7.0
- channels = pa_map.channels + 1;
+ channels = pa_channels + 1;
break;
case 2: // Stereo
case 4: // Surround 4.0
case 6: // Surround 5.1
case 8: // Surround 7.1
- channels = pa_map.channels;
+ channels = pa_channels;
break;
default:
- WARN_PRINT("PulseAudio: Unsupported number of output channels: " + itos(pa_map.channels));
+ WARN_PRINT("PulseAudio: Unsupported number of output channels: " + itos(pa_channels));
pa_channel_map_init_stereo(&pa_map);
channels = 2;
break;
@@ -226,7 +233,7 @@ Error AudioDriverPulseAudio::init_output_device() {
buffer_frames = closest_power_of_2(tmp_latency * mix_rate / 1000);
pa_buffer_size = buffer_frames * pa_map.channels;
- print_verbose("PulseAudio: detected " + itos(pa_map.channels) + " output channels");
+ print_verbose("PulseAudio: reserving " + itos(channels) + " bus channels");
print_verbose("PulseAudio: audio buffer frames: " + itos(buffer_frames) + " calculated output latency: " + itos(buffer_frames * 1000 / mix_rate) + "ms");
pa_sample_spec spec;
@@ -416,11 +423,10 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) {
for (unsigned int i = 0; i < ad->pa_buffer_size; i++) {
out_ptr[i] = ad->samples_in[i] >> 16;
}
- } else {
+ } else if (ad->channels == ad->pa_map.channels + 1) {
// Uneven amount of channels
unsigned int in_idx = 0;
unsigned int out_idx = 0;
-
for (unsigned int i = 0; i < ad->buffer_frames; i++) {
for (int j = 0; j < ad->pa_map.channels - 1; j++) {
out_ptr[out_idx++] = ad->samples_in[in_idx++] >> 16;
@@ -429,6 +435,17 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) {
uint32_t r = ad->samples_in[in_idx++] >> 16;
out_ptr[out_idx++] = (l + r) / 2;
}
+ } else {
+ // override_channels case
+ unsigned int in_idx = 0;
+ unsigned int out_idx = 0;
+ for (unsigned int i = 0; i < ad->buffer_frames; i++) {
+ in_idx += ad->pa_channel0;
+ for (int j = 0; j < ad->pa_map.channels; j++) {
+ out_ptr[out_idx++] = ad->samples_in[in_idx++] >> 16;
+ }
+ in_idx += ad->channels - ad->pa_map.channels - ad->pa_channel0;
+ }
}
}
diff --git a/drivers/pulseaudio/audio_driver_pulseaudio.h b/drivers/pulseaudio/audio_driver_pulseaudio.h
index ce29a55cdeea..ad4109a42cac 100644
--- a/drivers/pulseaudio/audio_driver_pulseaudio.h
+++ b/drivers/pulseaudio/audio_driver_pulseaudio.h
@@ -53,6 +53,8 @@ class AudioDriverPulseAudio : public AudioDriver {
pa_stream *pa_rec_str = nullptr;
pa_channel_map pa_map = {};
pa_channel_map pa_rec_map = {};
+ int pa_channels = 2;
+ int pa_channel0 = 0;
String output_device_name = "Default";
String new_output_device = "Default";
diff --git a/servers/audio_server.cpp b/servers/audio_server.cpp
index b5df08211a87..178e952c7b80 100644
--- a/servers/audio_server.cpp
+++ b/servers/audio_server.cpp
@@ -216,6 +216,9 @@ void AudioDriverManager::initialize(int p_driver) {
GLOBAL_DEF_RST("audio/driver/enable_input", false);
GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "audio/driver/mix_rate", PROPERTY_HINT_RANGE, "11025,192000,1,or_greater,suffix:Hz"), DEFAULT_MIX_RATE);
GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "audio/driver/mix_rate.web", PROPERTY_HINT_RANGE, "0,192000,1,or_greater,suffix:Hz"), 0); // Safer default output_latency for web (use browser default).
+ GLOBAL_DEF_RST("audio/driver/override_channels", false);
+ GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "audio/driver/override_speaker_channels", PROPERTY_HINT_RANGE, "2,8,1"), 8);
+ GLOBAL_DEF(PropertyInfo(Variant::INT, "audio/driver/override_channel_out", PROPERTY_HINT_RANGE, "0,6,1"), 0);
int failed_driver = -1;