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;