Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 19 additions & 8 deletions doc/classes/AudioStreamPlayer.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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].
</member>
<member name="downmix" type="bool" setter="set_downmix" getter="is_downmix_enabled" default="true">
If [code]true[/code], audio will be downmixed to fit the current [enum AudioServer.SpeakerMode] instead of specifically defined [member output_channels].
</member>
<member name="max_polyphony" type="int" setter="set_max_polyphony" getter="get_max_polyphony" default="1">
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.
</member>
<member name="mix_target" type="int" setter="set_mix_target" getter="get_mix_target" enum="AudioStreamPlayer.MixTarget" default="0">
The mix target channels. Has no effect when two speakers or less are detected (see [enum AudioServer.SpeakerMode]).
<member name="output_channels" type="int" setter="set_output_channels" getter="get_output_channels" enum="AudioStreamPlayer.ChannelFlags" is_bitfield="true" default="3">
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].
</member>
<member name="pitch_scale" type="float" setter="set_pitch_scale" getter="get_pitch_scale" default="1.0">
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.
Expand Down Expand Up @@ -105,14 +109,21 @@
</signal>
</signals>
<constants>
<constant name="MIX_TARGET_STEREO" value="0" enum="MixTarget">
The audio will be played only on the first channel. This is the default.
<constant name="AUDIO_CHANNEL_LEFT" value="1" enum="ChannelFlags" is_bitfield="true">
</constant>
<constant name="AUDIO_CHANNEL_RIGHT" value="2" enum="ChannelFlags" is_bitfield="true">
</constant>
<constant name="AUDIO_CHANNEL_CENTER" value="4" enum="ChannelFlags" is_bitfield="true">
</constant>
<constant name="AUDIO_CHANNEL_LFE" value="8" enum="ChannelFlags" is_bitfield="true">
</constant>
<constant name="AUDIO_CHANNEL_REAR_LEFT" value="16" enum="ChannelFlags" is_bitfield="true">
</constant>
<constant name="AUDIO_CHANNEL_REAR_RIGHT" value="32" enum="ChannelFlags" is_bitfield="true">
</constant>
<constant name="MIX_TARGET_SURROUND" value="1" enum="MixTarget">
The audio will be played on all surround channels.
<constant name="AUDIO_CHANNEL_SIDE_LEFT" value="64" enum="ChannelFlags" is_bitfield="true">
</constant>
<constant name="MIX_TARGET_CENTER" value="2" enum="MixTarget">
The audio will be played on the second channel, which is usually the center.
<constant name="AUDIO_CHANNEL_SIDE_RIGHT" value="128" enum="ChannelFlags" is_bitfield="true">
</constant>
</constants>
</class>
126 changes: 95 additions & 31 deletions scene/audio/audio_stream_player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<AudioStreamPlayer::ChannelFlags> p_channels) {
output_channels = p_channels;
}

AudioStreamPlayer::MixTarget AudioStreamPlayer::get_mix_target() const {
return mix_target;
BitField<AudioStreamPlayer::ChannelFlags> 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) {
Expand All @@ -188,29 +196,76 @@ Vector<AudioFrame> 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;
}

Expand Down Expand Up @@ -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);

Expand All @@ -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() {
Expand Down
26 changes: 18 additions & 8 deletions scene/audio/audio_stream_player.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<ChannelFlags> output_channels = AUDIO_CHANNEL_LEFT | AUDIO_CHANNEL_RIGHT; // Default to stereo

bool downmix = true;

void _set_playing(bool p_enable);
bool _is_active() const;
Expand Down Expand Up @@ -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<ChannelFlags> p_channels);
BitField<ChannelFlags> 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;
Expand All @@ -116,4 +126,4 @@ class AudioStreamPlayer : public Node {
~AudioStreamPlayer();
};

VARIANT_ENUM_CAST(AudioStreamPlayer::MixTarget)
VARIANT_BITFIELD_CAST(AudioStreamPlayer::ChannelFlags);
Loading