Skip to content

Commit

Permalink
Merge pull request #72233 from TokageItLab/audio-blending
Browse files Browse the repository at this point in the history
Implement blending audio feature to AnimationTree
  • Loading branch information
akien-mga committed Jan 28, 2023
2 parents 6a252c1 + 7533088 commit e5752fd
Show file tree
Hide file tree
Showing 22 changed files with 561 additions and 210 deletions.
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

0 comments on commit e5752fd

Please sign in to comment.