diff --git a/doc/classes/AudioStreamPlayer.xml b/doc/classes/AudioStreamPlayer.xml
index dae089ea9f10..9ee74bb1eb13 100644
--- a/doc/classes/AudioStreamPlayer.xml
+++ b/doc/classes/AudioStreamPlayer.xml
@@ -66,11 +66,15 @@
The target bus name. All sounds from this node will be playing on this bus.
[b]Note:[/b] At runtime, if no bus with the given name exists, all sounds will fall back on [code]"Master"[/code]. See also [method AudioServer.get_bus_name].
+
+ If [code]true[/code], audio will be downmixed to fit the current [enum AudioServer.SpeakerMode] instead of specifically defined [member output_channels].
+
The maximum number of sounds this node can play at the same time. Calling [method play] after this value is reached will cut off the oldest sounds.
-
- The mix target channels. Has no effect when two speakers or less are detected (see [enum AudioServer.SpeakerMode]).
+
+ The audio channels the stream should output to. Defaults to Stereo.
+ [b]Note:[/b] Set [member downmix] to [code]false[/code] to only output if selected channel is available in the current [enum AudioServer.SpeakerMode].
The audio's pitch and tempo, as a multiplier of the [member stream]'s sample rate. A value of [code]2.0[/code] doubles the audio's pitch, while a value of [code]0.5[/code] halves the pitch.
@@ -105,14 +109,21 @@
-
- The audio will be played only on the first channel. This is the default.
+
+
+
+
+
+
+
+
+
+
+
-
- The audio will be played on all surround channels.
+
-
- The audio will be played on the second channel, which is usually the center.
+
diff --git a/scene/audio/audio_stream_player.cpp b/scene/audio/audio_stream_player.cpp
index 6fbfd25451b5..557d3449fbf1 100644
--- a/scene/audio/audio_stream_player.cpp
+++ b/scene/audio/audio_stream_player.cpp
@@ -157,12 +157,20 @@ bool AudioStreamPlayer::is_autoplay_enabled() const {
return internal->autoplay;
}
-void AudioStreamPlayer::set_mix_target(MixTarget p_target) {
- mix_target = p_target;
+void AudioStreamPlayer::set_output_channels(BitField p_channels) {
+ output_channels = p_channels;
}
-AudioStreamPlayer::MixTarget AudioStreamPlayer::get_mix_target() const {
- return mix_target;
+BitField AudioStreamPlayer::get_output_channels() const {
+ return output_channels;
+}
+
+void AudioStreamPlayer::set_downmix(bool p_enable) {
+ downmix = p_enable;
+}
+
+bool AudioStreamPlayer::is_downmix_enabled() const {
+ return downmix;
}
void AudioStreamPlayer::_set_playing(bool p_enable) {
@@ -188,29 +196,76 @@ Vector AudioStreamPlayer::_get_volume_vector() {
}
float volume_linear = Math::db_to_linear(internal->volume_db);
-
- // Set the volume vector up according to the speaker mode and mix target.
- // TODO do we need to scale the volume down when we output to more channels?
- if (AudioServer::get_singleton()->get_speaker_mode() == AudioServer::SPEAKER_MODE_STEREO) {
- volume_vector.write[0] = AudioFrame(volume_linear, volume_linear);
- } else {
- switch (mix_target) {
- case MIX_TARGET_STEREO: {
- volume_vector.write[0] = AudioFrame(volume_linear, volume_linear);
- } break;
- case MIX_TARGET_SURROUND: {
- // TODO Make sure this is right.
- volume_vector.write[0] = AudioFrame(volume_linear, volume_linear);
- volume_vector.write[1] = AudioFrame(volume_linear, /* LFE= */ 1.0f);
- volume_vector.write[2] = AudioFrame(volume_linear, volume_linear);
- volume_vector.write[3] = AudioFrame(volume_linear, volume_linear);
- } break;
- case MIX_TARGET_CENTER: {
- // TODO Make sure this is right.
- volume_vector.write[1] = AudioFrame(volume_linear, /* LFE= */ 1.0f);
- } break;
+ const int speaker_mode = AudioServer::get_singleton()->get_speaker_mode();
+
+ float left = output_channels.has_flag(AUDIO_CHANNEL_LEFT) * volume_linear;
+ float right = output_channels.has_flag(AUDIO_CHANNEL_RIGHT) * volume_linear;
+ float center = output_channels.has_flag(AUDIO_CHANNEL_CENTER) * volume_linear;
+ float lfe = output_channels.has_flag(AUDIO_CHANNEL_LFE) * volume_linear;
+ float rear_left = output_channels.has_flag(AUDIO_CHANNEL_REAR_LEFT) * volume_linear;
+ float rear_right = output_channels.has_flag(AUDIO_CHANNEL_REAR_RIGHT) * volume_linear;
+ float side_left = output_channels.has_flag(AUDIO_CHANNEL_SIDE_LEFT) * volume_linear;
+ float side_right = output_channels.has_flag(AUDIO_CHANNEL_SIDE_RIGHT) * volume_linear;
+
+ if (downmix) {
+ //Adjust the channel volumes based on the speaker mode.
+ switch (speaker_mode) {
+ case AudioServer::SPEAKER_MODE_STEREO:
+ left += 0.707 * center + // Center contributes equally to both front channels.
+ 0.5 * lfe + // LFE contributes equally to both front channels, and is attenuated to prevent overpowering the mix.
+ 0.707 * side_left + // Side contributes partially to the front channel.
+ 0.5 * rear_left; // Rear contributes less to the front channel.
+
+ right += 0.707 * center +
+ 0.5 * lfe +
+ 0.707 * side_right +
+ 0.5 * rear_right;
+ break;
+
+ case AudioServer::SPEAKER_SURROUND_31:
+ left += 0.707 * side_left + // Side contributes partially to the front channel.
+ 0.5 * rear_left; // Rear contributes less to the front channel.
+
+ right += 0.707 * side_right +
+ 0.5 * rear_right;
+ break;
+
+ case AudioServer::SPEAKER_SURROUND_51:
+ left += 0.707 * side_left; // Side contributes partially to the front channel.
+ rear_left += 0.707 * side_left; // Side also contributes to the rear channel.
+
+ right += 0.707 * side_right;
+ rear_right += 0.707 * side_right;
+ break;
+
+ default:
+ break;
}
+
+ // Clamp all channels to prevent clipping.
+ left = CLAMP(left, 0.0f, volume_linear);
+ right = CLAMP(right, 0.0f, volume_linear);
+ center = CLAMP(center, 0.0f, volume_linear);
+ lfe = CLAMP(lfe, 0.0f, volume_linear);
+ rear_left = CLAMP(rear_left, 0.0f, volume_linear);
+ rear_right = CLAMP(rear_right, 0.0f, volume_linear);
+ }
+
+ // Set the volume vector up according to the speaker mode.
+ volume_vector.write[0] = AudioFrame(left, right);
+
+ if (speaker_mode >= AudioServer::SPEAKER_SURROUND_31) {
+ volume_vector.write[1] = AudioFrame(center, lfe);
+ }
+
+ if (speaker_mode >= AudioServer::SPEAKER_SURROUND_51) {
+ volume_vector.write[2] = AudioFrame(rear_left, rear_right);
}
+
+ if (speaker_mode >= AudioServer::SPEAKER_SURROUND_71) {
+ volume_vector.write[3] = AudioFrame(side_left, side_right);
+ }
+
return volume_vector;
}
@@ -260,8 +315,11 @@ void AudioStreamPlayer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_autoplay", "enable"), &AudioStreamPlayer::set_autoplay);
ClassDB::bind_method(D_METHOD("is_autoplay_enabled"), &AudioStreamPlayer::is_autoplay_enabled);
- ClassDB::bind_method(D_METHOD("set_mix_target", "mix_target"), &AudioStreamPlayer::set_mix_target);
- ClassDB::bind_method(D_METHOD("get_mix_target"), &AudioStreamPlayer::get_mix_target);
+ ClassDB::bind_method(D_METHOD("set_output_channels", "output_channels"), &AudioStreamPlayer::set_output_channels);
+ ClassDB::bind_method(D_METHOD("get_output_channels"), &AudioStreamPlayer::get_output_channels);
+
+ ClassDB::bind_method(D_METHOD("set_downmix", "enable"), &AudioStreamPlayer::set_downmix);
+ ClassDB::bind_method(D_METHOD("is_downmix_enabled"), &AudioStreamPlayer::is_downmix_enabled);
ClassDB::bind_method(D_METHOD("set_playing", "enable"), &AudioStreamPlayer::_set_playing);
@@ -284,16 +342,22 @@ void AudioStreamPlayer::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playing", PROPERTY_HINT_ONESHOT, "", PROPERTY_USAGE_EDITOR), "set_playing", "is_playing");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autoplay"), "set_autoplay", "is_autoplay_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stream_paused", PROPERTY_HINT_NONE, ""), "set_stream_paused", "get_stream_paused");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "mix_target", PROPERTY_HINT_ENUM, "Stereo,Surround,Center"), "set_mix_target", "get_mix_target");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "output_channels", PROPERTY_HINT_FLAGS, "Front Left,Front Right,Front Center,Low Frequency,Rear Left,Rear Right,Side Left,Side Right"), "set_output_channels", "get_output_channels");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "downmix", PROPERTY_HINT_NONE, ""), "set_downmix", "is_downmix_enabled");
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_polyphony", PROPERTY_HINT_NONE, ""), "set_max_polyphony", "get_max_polyphony");
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "bus", PROPERTY_HINT_ENUM, ""), "set_bus", "get_bus");
ADD_PROPERTY(PropertyInfo(Variant::INT, "playback_type", PROPERTY_HINT_ENUM, "Default,Stream,Sample"), "set_playback_type", "get_playback_type");
ADD_SIGNAL(MethodInfo("finished"));
- BIND_ENUM_CONSTANT(MIX_TARGET_STEREO);
- BIND_ENUM_CONSTANT(MIX_TARGET_SURROUND);
- BIND_ENUM_CONSTANT(MIX_TARGET_CENTER);
+ BIND_BITFIELD_FLAG(AUDIO_CHANNEL_LEFT);
+ BIND_BITFIELD_FLAG(AUDIO_CHANNEL_RIGHT);
+ BIND_BITFIELD_FLAG(AUDIO_CHANNEL_CENTER);
+ BIND_BITFIELD_FLAG(AUDIO_CHANNEL_LFE);
+ BIND_BITFIELD_FLAG(AUDIO_CHANNEL_REAR_LEFT);
+ BIND_BITFIELD_FLAG(AUDIO_CHANNEL_REAR_RIGHT);
+ BIND_BITFIELD_FLAG(AUDIO_CHANNEL_SIDE_LEFT);
+ BIND_BITFIELD_FLAG(AUDIO_CHANNEL_SIDE_RIGHT);
}
AudioStreamPlayer::AudioStreamPlayer() {
diff --git a/scene/audio/audio_stream_player.h b/scene/audio/audio_stream_player.h
index 8b19f9032645..16a72c163471 100644
--- a/scene/audio/audio_stream_player.h
+++ b/scene/audio/audio_stream_player.h
@@ -42,16 +42,23 @@ class AudioStreamPlayer : public Node {
GDCLASS(AudioStreamPlayer, Node);
public:
- enum MixTarget {
- MIX_TARGET_STEREO,
- MIX_TARGET_SURROUND,
- MIX_TARGET_CENTER
+ enum ChannelFlags {
+ AUDIO_CHANNEL_LEFT = 1 << 0,
+ AUDIO_CHANNEL_RIGHT = 1 << 1,
+ AUDIO_CHANNEL_CENTER = 1 << 2,
+ AUDIO_CHANNEL_LFE = 1 << 3,
+ AUDIO_CHANNEL_REAR_LEFT = 1 << 4,
+ AUDIO_CHANNEL_REAR_RIGHT = 1 << 5,
+ AUDIO_CHANNEL_SIDE_LEFT = 1 << 6,
+ AUDIO_CHANNEL_SIDE_RIGHT = 1 << 7,
};
private:
AudioStreamPlayerInternal *internal = nullptr;
- MixTarget mix_target = MIX_TARGET_STEREO;
+ BitField output_channels = AUDIO_CHANNEL_LEFT | AUDIO_CHANNEL_RIGHT; // Default to stereo
+
+ bool downmix = true;
void _set_playing(bool p_enable);
bool _is_active() const;
@@ -100,8 +107,11 @@ class AudioStreamPlayer : public Node {
void set_autoplay(bool p_enable);
bool is_autoplay_enabled() const;
- void set_mix_target(MixTarget p_target);
- MixTarget get_mix_target() const;
+ void set_output_channels(BitField p_channels);
+ BitField get_output_channels() const;
+
+ void set_downmix(bool p_enable);
+ bool is_downmix_enabled() const;
void set_stream_paused(bool p_pause);
bool get_stream_paused() const;
@@ -116,4 +126,4 @@ class AudioStreamPlayer : public Node {
~AudioStreamPlayer();
};
-VARIANT_ENUM_CAST(AudioStreamPlayer::MixTarget)
+VARIANT_BITFIELD_CAST(AudioStreamPlayer::ChannelFlags);