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);