Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement blending audio feature to AnimationTree #72233

Merged
merged 1 commit into from
Jan 28, 2023
Merged
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
15 changes: 15 additions & 0 deletions doc/classes/Animation.xml
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,13 @@
[param stream] is the [AudioStream] resource to play. [param start_offset] is the number of seconds cut off at the beginning of the audio stream, while [param end_offset] is at the ending.
</description>
</method>
<method name="audio_track_is_use_blend" qualifiers="const">
<return type="bool" />
<param index="0" name="track_idx" type="int" />
<description>
Returns [code]true[/code] if the track at [code]idx[/code] will be blended with other animations.
</description>
</method>
<method name="audio_track_set_key_end_offset">
<return type="void" />
<param index="0" name="track_idx" type="int" />
Expand Down Expand Up @@ -131,6 +138,14 @@
Sets the stream of the key identified by [param key_idx] to value [param stream]. The [param track_idx] must be the index of an Audio Track.
</description>
</method>
<method name="audio_track_set_use_blend">
<return type="void" />
<param index="0" name="track_idx" type="int" />
<param index="1" name="enable" type="bool" />
<description>
Sets whether the track will be blended with other animations. If [code]true[/code], the audio playback volume changes depending on the blend value.
</description>
</method>
<method name="bezier_track_get_key_in_handle" qualifiers="const">
<return type="Vector2" />
<param index="0" name="track_idx" type="int" />
Expand Down
4 changes: 4 additions & 0 deletions doc/classes/AnimationPlayer.xml
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,10 @@
<member name="assigned_animation" type="String" setter="set_assigned_animation" getter="get_assigned_animation">
If playing, the the current animation's key, otherwise, the animation last played. When set, this changes the animation, but will not play it unless already playing. See also [member current_animation].
</member>
<member name="audio_max_polyphony" type="int" setter="set_audio_max_polyphony" getter="get_audio_max_polyphony" default="32">
The number of possible simultaneous sounds for each of the assigned AudioStreamPlayers.
For example, if this value is [code]32[/code] and the animation has two audio tracks, the two [AudioStreamPlayer]s assigned can play simultaneously up to [code]32[/code] voices each.
</member>
<member name="autoplay" type="String" setter="set_autoplay" getter="get_autoplay" default="&quot;&quot;">
The key of the animation to play when the scene loads.
</member>
Expand Down
4 changes: 4 additions & 0 deletions doc/classes/AnimationTree.xml
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@
<member name="anim_player" type="NodePath" setter="set_animation_player" getter="get_animation_player" default="NodePath(&quot;&quot;)">
The path to the [AnimationPlayer] used for animating.
</member>
<member name="audio_max_polyphony" type="int" setter="set_audio_max_polyphony" getter="get_audio_max_polyphony" default="32">
The number of possible simultaneous sounds for each of the assigned AudioStreamPlayers.
For example, if this value is [code]32[/code] and the animation has two audio tracks, the two [AudioStreamPlayer]s assigned can play simultaneously up to [code]32[/code] voices each.
</member>
<member name="process_callback" type="int" setter="set_process_callback" getter="get_process_callback" enum="AnimationTree.AnimationProcessCallback" default="1">
The process mode of this [AnimationTree]. See [enum AnimationProcessCallback] for available modes.
</member>
Expand Down
6 changes: 6 additions & 0 deletions doc/classes/AudioStreamPlayer.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@
Returns the [AudioStreamPlayback] object associated with this [AudioStreamPlayer].
</description>
</method>
<method name="has_stream_playback">
<return type="bool" />
<description>
Returns whether the [AudioStreamPlayer] can return the [AudioStreamPlayback] object or not.
</description>
</method>
<method name="play">
<return type="void" />
<param index="0" name="from_position" type="float" default="0.0" />
Expand Down
6 changes: 6 additions & 0 deletions doc/classes/AudioStreamPlayer2D.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@
Returns the [AudioStreamPlayback] object associated with this [AudioStreamPlayer2D].
</description>
</method>
<method name="has_stream_playback">
<return type="bool" />
<description>
Returns whether the [AudioStreamPlayer] can return the [AudioStreamPlayback] object or not.
</description>
</method>
<method name="play">
<return type="void" />
<param index="0" name="from_position" type="float" default="0.0" />
Expand Down
6 changes: 6 additions & 0 deletions doc/classes/AudioStreamPlayer3D.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@
Returns the [AudioStreamPlayback] object associated with this [AudioStreamPlayer3D].
</description>
</method>
<method name="has_stream_playback">
<return type="bool" />
<description>
Returns whether the [AudioStreamPlayer] can return the [AudioStreamPlayback] object or not.
</description>
</method>
<method name="play">
<return type="void" />
<param index="0" name="from_position" type="float" default="0.0" />
Expand Down
50 changes: 43 additions & 7 deletions editor/animation_track_editor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1954,6 +1954,10 @@ void AnimationTrackEdit::_notification(int p_what) {
get_theme_icon(SNAME("TrackDiscrete"), SNAME("EditorIcons")),
get_theme_icon(SNAME("TrackCapture"), SNAME("EditorIcons"))
};
Ref<Texture2D> blend_icon[2] = {
get_theme_icon(SNAME("UseBlendEnable"), SNAME("EditorIcons")),
get_theme_icon(SNAME("UseBlendDisable"), SNAME("EditorIcons")),
};

int ofs = get_size().width - timeline->get_buttons_width();

Expand Down Expand Up @@ -1982,6 +1986,11 @@ void AnimationTrackEdit::_notification(int p_what) {
if (!animation->track_is_compressed(track) && animation->track_get_type(track) == Animation::TYPE_VALUE) {
draw_texture(update_icon, update_mode_rect.position);
}
if (animation->track_get_type(track) == Animation::TYPE_AUDIO) {
Ref<Texture2D> use_blend_icon = blend_icon[animation->audio_track_is_use_blend(track) ? 0 : 1];
Vector2 use_blend_icon_pos = update_mode_rect.position + (update_mode_rect.size - use_blend_icon->get_size()) / 2;
draw_texture(use_blend_icon, use_blend_icon_pos);
}
// Make it easier to click.
update_mode_rect.position.y = 0;
update_mode_rect.size.y = get_size().height;
Expand All @@ -1990,13 +1999,12 @@ void AnimationTrackEdit::_notification(int p_what) {
update_mode_rect.size.x += hsep / 2;

if (!read_only) {
if (animation->track_get_type(track) == Animation::TYPE_VALUE) {
if (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_AUDIO) {
draw_texture(down_icon, Vector2(ofs, int(get_size().height - down_icon->get_height()) / 2));
update_mode_rect.size.x += down_icon->get_width();
} else if (animation->track_get_type(track) == Animation::TYPE_BEZIER) {
Ref<Texture2D> bezier_icon = get_theme_icon(SNAME("EditBezier"), SNAME("EditorIcons"));
update_mode_rect.size.x += down_icon->get_width();

update_mode_rect = Rect2();
} else {
update_mode_rect = Rect2();
Expand Down Expand Up @@ -2439,7 +2447,11 @@ String AnimationTrackEdit::get_tooltip(const Point2 &p_pos) const {
}

if (update_mode_rect.has_point(p_pos)) {
return TTR("Update Mode (How this property is set)");
if (animation->track_get_type(track) == Animation::TYPE_AUDIO) {
return TTR("Use Blend");
} else {
return TTR("Update Mode (How this property is set)");
}
}

if (interp_mode_rect.has_point(p_pos)) {
Expand Down Expand Up @@ -2641,9 +2653,14 @@ void AnimationTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
menu->connect("id_pressed", callable_mp(this, &AnimationTrackEdit::_menu_selected));
}
menu->clear();
menu->add_icon_item(get_theme_icon(SNAME("TrackContinuous"), SNAME("EditorIcons")), TTR("Continuous"), MENU_CALL_MODE_CONTINUOUS);
menu->add_icon_item(get_theme_icon(SNAME("TrackDiscrete"), SNAME("EditorIcons")), TTR("Discrete"), MENU_CALL_MODE_DISCRETE);
menu->add_icon_item(get_theme_icon(SNAME("TrackCapture"), SNAME("EditorIcons")), TTR("Capture"), MENU_CALL_MODE_CAPTURE);
if (animation->track_get_type(track) == Animation::TYPE_AUDIO) {
menu->add_icon_item(get_theme_icon(SNAME("UseBlendEnable"), SNAME("EditorIcons")), TTR("Use Blend"), MENU_USE_BLEND_ENABLED);
menu->add_icon_item(get_theme_icon(SNAME("UseBlendDisable"), SNAME("EditorIcons")), TTR("Don't Use Blend"), MENU_USE_BLEND_DISABLED);
} else {
menu->add_icon_item(get_theme_icon(SNAME("TrackContinuous"), SNAME("EditorIcons")), TTR("Continuous"), MENU_CALL_MODE_CONTINUOUS);
menu->add_icon_item(get_theme_icon(SNAME("TrackDiscrete"), SNAME("EditorIcons")), TTR("Discrete"), MENU_CALL_MODE_DISCRETE);
menu->add_icon_item(get_theme_icon(SNAME("TrackCapture"), SNAME("EditorIcons")), TTR("Capture"), MENU_CALL_MODE_CAPTURE);
}
menu->reset_size();

Vector2 popup_pos = get_screen_position() + update_mode_rect.position + Vector2(0, update_mode_rect.size.height);
Expand All @@ -2662,7 +2679,7 @@ void AnimationTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
menu->add_icon_item(get_theme_icon(SNAME("InterpRaw"), SNAME("EditorIcons")), TTR("Nearest"), MENU_INTERPOLATION_NEAREST);
menu->add_icon_item(get_theme_icon(SNAME("InterpLinear"), SNAME("EditorIcons")), TTR("Linear"), MENU_INTERPOLATION_LINEAR);
menu->add_icon_item(get_theme_icon(SNAME("InterpCubic"), SNAME("EditorIcons")), TTR("Cubic"), MENU_INTERPOLATION_CUBIC);
// Check is angle property.
// Check whether it is angle property.
AnimationPlayerEditor *ape = AnimationPlayerEditor::get_singleton();
if (ape) {
AnimationPlayer *ap = ape->get_player();
Expand Down Expand Up @@ -3055,6 +3072,16 @@ void AnimationTrackEdit::_menu_selected(int p_index) {
emit_signal(SNAME("delete_request"));

} break;
case MENU_USE_BLEND_ENABLED:
case MENU_USE_BLEND_DISABLED: {
bool use_blend = p_index == MENU_USE_BLEND_ENABLED;
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Change Animation Use Blend"));
undo_redo->add_do_method(animation.ptr(), "audio_track_set_use_blend", track, use_blend);
undo_redo->add_undo_method(animation.ptr(), "audio_track_set_use_blend", track, animation->audio_track_is_use_blend(track));
undo_redo->commit_action();
queue_redraw();
} break;
}
}

Expand Down Expand Up @@ -3488,6 +3515,9 @@ void AnimationTrackEditor::_animation_track_remove_request(int p_track, Ref<Anim
if (p_from_animation->track_get_type(idx) == Animation::TYPE_VALUE) {
undo_redo->add_undo_method(p_from_animation.ptr(), "value_track_set_update_mode", idx, p_from_animation->value_track_get_update_mode(idx));
}
if (animation->track_get_type(idx) == Animation::TYPE_AUDIO) {
undo_redo->add_undo_method(animation.ptr(), "audio_track_set_use_blend", idx, animation->audio_track_is_use_blend(idx));
}

undo_redo->commit_action();
}
Expand Down Expand Up @@ -5618,6 +5648,9 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
if (tc.track_type == Animation::TYPE_VALUE) {
tc.update_mode = animation->value_track_get_update_mode(idx);
}
if (tc.track_type == Animation::TYPE_AUDIO) {
tc.use_blend = animation->audio_track_is_use_blend(idx);
}
tc.loop_wrap = animation->track_get_interpolation_loop_wrap(idx);
tc.enabled = animation->track_is_enabled(idx);
for (int i = 0; i < animation->track_get_key_count(idx); i++) {
Expand Down Expand Up @@ -5662,6 +5695,9 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
if (track_clipboard[i].track_type == Animation::TYPE_VALUE) {
undo_redo->add_do_method(animation.ptr(), "value_track_set_update_mode", base_track, track_clipboard[i].update_mode);
}
if (track_clipboard[i].track_type == Animation::TYPE_AUDIO) {
undo_redo->add_do_method(animation.ptr(), "audio_track_set_use_blend", base_track, track_clipboard[i].use_blend);
}

for (int j = 0; j < track_clipboard[i].keys.size(); j++) {
undo_redo->add_do_method(animation.ptr(), "track_insert_key", base_track, track_clipboard[i].keys[j].time, track_clipboard[i].keys[j].value, track_clipboard[i].keys[j].transition);
Expand Down
5 changes: 4 additions & 1 deletion editor/animation_track_editor.h
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,9 @@ class AnimationTrackEdit : public Control {
MENU_KEY_INSERT,
MENU_KEY_DUPLICATE,
MENU_KEY_ADD_RESET,
MENU_KEY_DELETE
MENU_KEY_DELETE,
MENU_USE_BLEND_ENABLED,
MENU_USE_BLEND_DISABLED,
};

AnimationTimelineEdit *timeline = nullptr;
Expand Down Expand Up @@ -566,6 +568,7 @@ class AnimationTrackEditor : public VBoxContainer {
Animation::LoopMode loop_mode = Animation::LOOP_PINGPONG;
bool loop_wrap = false;
bool enabled = false;
bool use_blend = false;

struct Key {
float time = 0;
Expand Down
1 change: 1 addition & 0 deletions editor/icons/UseBlendDisable.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions editor/icons/UseBlendEnable.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 12 additions & 5 deletions scene/2d/audio_stream_player_2d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,10 @@ void AudioStreamPlayer2D::_notification(int p_what) {
_update_panning();
}

if (setplay.get() >= 0 && stream.is_valid()) {
if (setplayback.is_valid() && setplay.get() >= 0) {
active.set();
Ref<AudioStreamPlayback> new_playback = stream->instantiate_playback();
ERR_FAIL_COND_MSG(new_playback.is_null(), "Failed to instantiate playback.");
AudioServer::get_singleton()->start_playback_stream(new_playback, _get_actual_bus(), volume_vector, setplay.get(), pitch_scale);
stream_playbacks.push_back(new_playback);
AudioServer::get_singleton()->start_playback_stream(setplayback, _get_actual_bus(), volume_vector, setplay.get(), pitch_scale);
setplayback.unref();
setplay.set(-1);
}

Expand Down Expand Up @@ -255,7 +253,11 @@ void AudioStreamPlayer2D::play(float p_from_pos) {
if (stream->is_monophonic() && is_playing()) {
stop();
}
Ref<AudioStreamPlayback> stream_playback = stream->instantiate_playback();
ERR_FAIL_COND_MSG(stream_playback.is_null(), "Failed to instantiate playback.");

stream_playbacks.push_back(stream_playback);
setplayback = stream_playback;
setplay.set(p_from_pos);
active.set();
set_physics_process_internal(true);
Expand Down Expand Up @@ -390,6 +392,10 @@ bool AudioStreamPlayer2D::get_stream_paused() const {
return false;
}

bool AudioStreamPlayer2D::has_stream_playback() {
return !stream_playbacks.is_empty();
}

Ref<AudioStreamPlayback> AudioStreamPlayer2D::get_stream_playback() {
ERR_FAIL_COND_V_MSG(stream_playbacks.is_empty(), Ref<AudioStreamPlayback>(), "Player is inactive. Call play() before requesting get_stream_playback().");
return stream_playbacks[stream_playbacks.size() - 1];
Expand Down Expand Up @@ -458,6 +464,7 @@ void AudioStreamPlayer2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_panning_strength", "panning_strength"), &AudioStreamPlayer2D::set_panning_strength);
ClassDB::bind_method(D_METHOD("get_panning_strength"), &AudioStreamPlayer2D::get_panning_strength);

ClassDB::bind_method(D_METHOD("has_stream_playback"), &AudioStreamPlayer2D::has_stream_playback);
ClassDB::bind_method(D_METHOD("get_stream_playback"), &AudioStreamPlayer2D::get_stream_playback);

ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), "set_stream", "get_stream");
Expand Down
2 changes: 2 additions & 0 deletions scene/2d/audio_stream_player_2d.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ class AudioStreamPlayer2D : public Node2D {

SafeFlag active{ false };
SafeNumeric<float> setplay{ -1.0 };
Ref<AudioStreamPlayback> setplayback;

Vector<AudioFrame> volume_vector;

Expand Down Expand Up @@ -129,6 +130,7 @@ class AudioStreamPlayer2D : public Node2D {
void set_panning_strength(float p_panning_strength);
float get_panning_strength() const;

bool has_stream_playback();
Ref<AudioStreamPlayback> get_stream_playback();

AudioStreamPlayer2D();
Expand Down
24 changes: 17 additions & 7 deletions scene/3d/audio_stream_player_3d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -284,14 +284,12 @@ void AudioStreamPlayer3D::_notification(int p_what) {
volume_vector = _update_panning();
}

if (setplay.get() >= 0 && stream.is_valid()) {
if (setplayback.is_valid() && setplay.get() >= 0) {
active.set();
Ref<AudioStreamPlayback> new_playback = stream->instantiate_playback();
ERR_FAIL_COND_MSG(new_playback.is_null(), "Failed to instantiate playback.");
HashMap<StringName, Vector<AudioFrame>> bus_map;
bus_map[_get_actual_bus()] = volume_vector;
AudioServer::get_singleton()->start_playback_stream(new_playback, bus_map, setplay.get(), actual_pitch_scale, linear_attenuation, attenuation_filter_cutoff_hz);
stream_playbacks.push_back(new_playback);
AudioServer::get_singleton()->start_playback_stream(setplayback, bus_map, setplay.get(), actual_pitch_scale, linear_attenuation, attenuation_filter_cutoff_hz);
setplayback.unref();
setplay.set(-1);
}

Expand Down Expand Up @@ -580,14 +578,21 @@ void AudioStreamPlayer3D::play(float p_from_pos) {
if (stream->is_monophonic() && is_playing()) {
stop();
}
Ref<AudioStreamPlayback> stream_playback = stream->instantiate_playback();
ERR_FAIL_COND_MSG(stream_playback.is_null(), "Failed to instantiate playback.");

stream_playbacks.push_back(stream_playback);
setplayback = stream_playback;
setplay.set(p_from_pos);
active.set();
set_physics_process_internal(true);
}

void AudioStreamPlayer3D::seek(float p_seconds) {
stop();
play(p_seconds);
if (is_playing()) {
stop();
play(p_seconds);
}
}

void AudioStreamPlayer3D::stop() {
Expand Down Expand Up @@ -783,6 +788,10 @@ bool AudioStreamPlayer3D::get_stream_paused() const {
return false;
}

bool AudioStreamPlayer3D::has_stream_playback() {
return !stream_playbacks.is_empty();
}

Ref<AudioStreamPlayback> AudioStreamPlayer3D::get_stream_playback() {
ERR_FAIL_COND_V_MSG(stream_playbacks.is_empty(), Ref<AudioStreamPlayback>(), "Player is inactive. Call play() before requesting get_stream_playback().");
return stream_playbacks[stream_playbacks.size() - 1];
Expand Down Expand Up @@ -875,6 +884,7 @@ void AudioStreamPlayer3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_panning_strength", "panning_strength"), &AudioStreamPlayer3D::set_panning_strength);
ClassDB::bind_method(D_METHOD("get_panning_strength"), &AudioStreamPlayer3D::get_panning_strength);

ClassDB::bind_method(D_METHOD("has_stream_playback"), &AudioStreamPlayer3D::has_stream_playback);
ClassDB::bind_method(D_METHOD("get_stream_playback"), &AudioStreamPlayer3D::get_stream_playback);

ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), "set_stream", "get_stream");
Expand Down
2 changes: 2 additions & 0 deletions scene/3d/audio_stream_player_3d.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ class AudioStreamPlayer3D : public Node3D {

SafeFlag active{ false };
SafeNumeric<float> setplay{ -1.0 };
Ref<AudioStreamPlayback> setplayback;

AttenuationModel attenuation_model = ATTENUATION_INVERSE_DISTANCE;
float volume_db = 0.0;
Expand Down Expand Up @@ -188,6 +189,7 @@ class AudioStreamPlayer3D : public Node3D {
void set_panning_strength(float p_panning_strength);
float get_panning_strength() const;

bool has_stream_playback();
Ref<AudioStreamPlayback> get_stream_playback();

AudioStreamPlayer3D();
Expand Down
Loading