You've already forked godot
mirror of
https://github.com/godotengine/godot.git
synced 2025-11-04 12:00:25 +00:00
Implement blending audio feature to AnimationTree
This commit is contained in:
@@ -431,6 +431,17 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim, Node *p_root_ov
|
||||
}
|
||||
}
|
||||
|
||||
if (a->track_get_type(i) == Animation::TYPE_AUDIO) {
|
||||
if (!node_cache->audio_anim.has(a->track_get_path(i).get_concatenated_names())) {
|
||||
TrackNodeCache::AudioAnim aa;
|
||||
aa.object = (Object *)child;
|
||||
aa.audio_stream.instantiate();
|
||||
aa.audio_stream->set_polyphony(audio_max_polyphony);
|
||||
|
||||
node_cache->audio_anim[a->track_get_path(i).get_concatenated_names()] = aa;
|
||||
}
|
||||
}
|
||||
|
||||
node_cache->last_setup_pass = setup_pass;
|
||||
}
|
||||
}
|
||||
@@ -820,52 +831,40 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
|
||||
if (!nc->node || is_stopping) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (p_seeked) {
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (!can_call) {
|
||||
continue; // To avoid spamming the preview in editor.
|
||||
}
|
||||
if (p_seeked && !can_call) {
|
||||
continue; // To avoid spamming the preview in editor.
|
||||
}
|
||||
#endif // TOOLS_ENABLED
|
||||
int idx = a->track_find_key(i, p_time);
|
||||
if (idx < 0) {
|
||||
continue;
|
||||
HashMap<StringName, TrackNodeCache::AudioAnim>::Iterator E = nc->audio_anim.find(a->track_get_path(i).get_concatenated_names());
|
||||
ERR_CONTINUE(!E); //should it continue, or create a new one?
|
||||
|
||||
TrackNodeCache::AudioAnim *aa = &E->value;
|
||||
Node *asp = Object::cast_to<Node>(aa->object);
|
||||
if (!asp) {
|
||||
continue;
|
||||
}
|
||||
aa->length = a->get_length();
|
||||
aa->time = p_time;
|
||||
aa->loop = a->get_loop_mode() != Animation::LOOP_NONE;
|
||||
aa->backward = backward;
|
||||
if (aa->accum_pass != accum_pass) {
|
||||
ERR_CONTINUE(cache_update_audio_size >= NODE_CACHE_UPDATE_MAX);
|
||||
cache_update_audio[cache_update_audio_size++] = aa;
|
||||
aa->accum_pass = accum_pass;
|
||||
}
|
||||
|
||||
HashMap<int, TrackNodeCache::PlayingAudioStreamInfo> &map = aa->playing_streams;
|
||||
// Find stream.
|
||||
int idx = -1;
|
||||
if (p_seeked) {
|
||||
idx = a->track_find_key(i, p_time);
|
||||
// Discard previous stream when seeking.
|
||||
if (map.has(idx)) {
|
||||
aa->audio_stream_playback->stop_stream(map[idx].index);
|
||||
map.erase(idx);
|
||||
}
|
||||
|
||||
Ref<AudioStream> stream = a->audio_track_get_key_stream(i, idx);
|
||||
if (!stream.is_valid()) {
|
||||
nc->node->call(SNAME("stop"));
|
||||
nc->audio_playing = false;
|
||||
playing_caches.erase(nc);
|
||||
} else {
|
||||
float start_ofs = a->audio_track_get_key_start_offset(i, idx);
|
||||
start_ofs += p_time - a->track_get_key_time(i, idx);
|
||||
float end_ofs = a->audio_track_get_key_end_offset(i, idx);
|
||||
float len = stream->get_length();
|
||||
|
||||
if (start_ofs > len - end_ofs) {
|
||||
nc->node->call(SNAME("stop"));
|
||||
nc->audio_playing = false;
|
||||
playing_caches.erase(nc);
|
||||
continue;
|
||||
}
|
||||
|
||||
nc->node->call(SNAME("set_stream"), stream);
|
||||
nc->node->call(SNAME("play"), start_ofs);
|
||||
|
||||
nc->audio_playing = true;
|
||||
playing_caches.insert(nc);
|
||||
if (len && end_ofs > 0) { //force an end at a time
|
||||
nc->audio_len = len - start_ofs - end_ofs;
|
||||
} else {
|
||||
nc->audio_len = 0;
|
||||
}
|
||||
|
||||
nc->audio_start = p_time;
|
||||
}
|
||||
|
||||
} else {
|
||||
//find stuff to play
|
||||
List<int> to_play;
|
||||
if (p_started) {
|
||||
int first_key = a->track_find_key(i, p_prev_time, Animation::FIND_MODE_EXACT);
|
||||
@@ -875,55 +874,47 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
|
||||
}
|
||||
a->track_get_key_indices_in_range(i, p_time, p_delta, &to_play, p_looped_flag);
|
||||
if (to_play.size()) {
|
||||
int idx = to_play.back()->get();
|
||||
idx = to_play.back()->get();
|
||||
}
|
||||
}
|
||||
if (idx < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Ref<AudioStream> stream = a->audio_track_get_key_stream(i, idx);
|
||||
if (!stream.is_valid()) {
|
||||
nc->node->call(SNAME("stop"));
|
||||
nc->audio_playing = false;
|
||||
playing_caches.erase(nc);
|
||||
} else {
|
||||
float start_ofs = a->audio_track_get_key_start_offset(i, idx);
|
||||
float end_ofs = a->audio_track_get_key_end_offset(i, idx);
|
||||
float len = stream->get_length();
|
||||
// Play stream.
|
||||
Ref<AudioStream> stream = a->audio_track_get_key_stream(i, idx);
|
||||
if (stream.is_valid()) {
|
||||
double start_ofs = a->audio_track_get_key_start_offset(i, idx);
|
||||
double end_ofs = a->audio_track_get_key_end_offset(i, idx);
|
||||
double len = stream->get_length();
|
||||
|
||||
nc->node->call(SNAME("set_stream"), stream);
|
||||
nc->node->call(SNAME("play"), start_ofs);
|
||||
|
||||
nc->audio_playing = true;
|
||||
playing_caches.insert(nc);
|
||||
if (len && end_ofs > 0) { //force an end at a time
|
||||
nc->audio_len = len - start_ofs - end_ofs;
|
||||
} else {
|
||||
nc->audio_len = 0;
|
||||
}
|
||||
|
||||
nc->audio_start = p_time;
|
||||
}
|
||||
} else if (nc->audio_playing) {
|
||||
bool loop = a->get_loop_mode() != Animation::LOOP_NONE;
|
||||
|
||||
bool stop = false;
|
||||
|
||||
if (!loop) {
|
||||
if ((p_time < nc->audio_start && !backward) || (p_time > nc->audio_start && backward)) {
|
||||
stop = true;
|
||||
}
|
||||
} else if (nc->audio_len > 0) {
|
||||
float len = nc->audio_start > p_time ? (a->get_length() - nc->audio_start) + p_time : p_time - nc->audio_start;
|
||||
|
||||
if (len > nc->audio_len) {
|
||||
stop = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (stop) {
|
||||
//time to stop
|
||||
nc->node->call(SNAME("stop"));
|
||||
nc->audio_playing = false;
|
||||
playing_caches.erase(nc);
|
||||
if (aa->object->call(SNAME("get_stream")) != aa->audio_stream) {
|
||||
aa->object->call(SNAME("set_stream"), aa->audio_stream);
|
||||
aa->audio_stream_playback.unref();
|
||||
if (!playing_audio_stream_players.has(asp)) {
|
||||
playing_audio_stream_players.push_back(asp);
|
||||
}
|
||||
}
|
||||
if (!aa->object->call(SNAME("is_playing"))) {
|
||||
aa->object->call(SNAME("play"));
|
||||
}
|
||||
if (!aa->object->call(SNAME("has_stream_playback"))) {
|
||||
aa->audio_stream_playback.unref();
|
||||
continue;
|
||||
}
|
||||
if (aa->audio_stream_playback.is_null()) {
|
||||
aa->audio_stream_playback = aa->object->call(SNAME("get_stream_playback"));
|
||||
}
|
||||
|
||||
TrackNodeCache::PlayingAudioStreamInfo pasi;
|
||||
pasi.index = aa->audio_stream_playback->play_stream(stream, start_ofs);
|
||||
pasi.start = p_time;
|
||||
if (len && end_ofs > 0) { // Force an end at a time.
|
||||
pasi.len = len - start_ofs - end_ofs;
|
||||
} else {
|
||||
pasi.len = 0;
|
||||
}
|
||||
map[idx] = pasi;
|
||||
}
|
||||
|
||||
} break;
|
||||
@@ -1223,6 +1214,53 @@ void AnimationPlayer::_animation_update_transforms() {
|
||||
ERR_CONTINUE(ba->accum_pass != accum_pass);
|
||||
ba->object->set_indexed(ba->bezier_property, ba->bezier_accum);
|
||||
}
|
||||
|
||||
for (int i = 0; i < cache_update_audio_size; i++) {
|
||||
TrackNodeCache::AudioAnim *aa = cache_update_audio[i];
|
||||
|
||||
ERR_CONTINUE(aa->accum_pass != accum_pass);
|
||||
|
||||
// Audio ending process.
|
||||
LocalVector<int> erase_list;
|
||||
for (const KeyValue<int, TrackNodeCache::PlayingAudioStreamInfo> &K : aa->playing_streams) {
|
||||
TrackNodeCache::PlayingAudioStreamInfo pasi = K.value;
|
||||
|
||||
bool stop = false;
|
||||
if (!aa->audio_stream_playback->is_stream_playing(pasi.index)) {
|
||||
stop = true;
|
||||
}
|
||||
if (!aa->loop) {
|
||||
if (!aa->backward) {
|
||||
if (aa->time < pasi.start) {
|
||||
stop = true;
|
||||
}
|
||||
} else if (aa->backward) {
|
||||
if (aa->time > pasi.start) {
|
||||
stop = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (pasi.len > 0) {
|
||||
double len = 0.0;
|
||||
if (!aa->backward) {
|
||||
len = pasi.start > aa->time ? (aa->length - pasi.start) + aa->time : aa->time - pasi.start;
|
||||
} else {
|
||||
len = pasi.start < aa->time ? (aa->length - aa->time) + pasi.start : pasi.start - aa->time;
|
||||
}
|
||||
if (len > pasi.len) {
|
||||
stop = true;
|
||||
}
|
||||
}
|
||||
if (stop) {
|
||||
// Time to stop.
|
||||
aa->audio_stream_playback->stop_stream(pasi.index);
|
||||
erase_list.push_back(K.key);
|
||||
}
|
||||
}
|
||||
for (uint32_t erase_idx = 0; erase_idx < erase_list.size(); erase_idx++) {
|
||||
aa->playing_streams.erase(erase_list[erase_idx]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationPlayer::_animation_process(double p_delta) {
|
||||
@@ -1238,6 +1276,7 @@ void AnimationPlayer::_animation_process(double p_delta) {
|
||||
cache_update_size = 0;
|
||||
cache_update_prop_size = 0;
|
||||
cache_update_bezier_size = 0;
|
||||
cache_update_audio_size = 0;
|
||||
|
||||
AnimationData *prev_from = playback.current.from;
|
||||
_animation_process2(p_delta, started);
|
||||
@@ -1675,6 +1714,7 @@ void AnimationPlayer::play(const StringName &p_name, double p_custom_blend, floa
|
||||
}
|
||||
|
||||
if (get_current_animation() != p_name) {
|
||||
_clear_audio_streams();
|
||||
_stop_playing_caches(false);
|
||||
}
|
||||
|
||||
@@ -1856,6 +1896,7 @@ void AnimationPlayer::_node_removed(Node *p_node) {
|
||||
}
|
||||
|
||||
void AnimationPlayer::clear_caches() {
|
||||
_clear_audio_streams();
|
||||
_stop_playing_caches(true);
|
||||
|
||||
node_cache_map.clear();
|
||||
@@ -1867,10 +1908,19 @@ void AnimationPlayer::clear_caches() {
|
||||
cache_update_size = 0;
|
||||
cache_update_prop_size = 0;
|
||||
cache_update_bezier_size = 0;
|
||||
cache_update_audio_size = 0;
|
||||
|
||||
emit_signal(SNAME("caches_cleared"));
|
||||
}
|
||||
|
||||
void AnimationPlayer::_clear_audio_streams() {
|
||||
for (int i = 0; i < playing_audio_stream_players.size(); i++) {
|
||||
playing_audio_stream_players[i]->call(SNAME("stop"));
|
||||
playing_audio_stream_players[i]->call(SNAME("set_stream"), Ref<AudioStream>());
|
||||
}
|
||||
playing_audio_stream_players.clear();
|
||||
}
|
||||
|
||||
void AnimationPlayer::set_active(bool p_active) {
|
||||
if (active == p_active) {
|
||||
return;
|
||||
@@ -1950,6 +2000,15 @@ AnimationPlayer::AnimationMethodCallMode AnimationPlayer::get_method_call_mode()
|
||||
return method_call_mode;
|
||||
}
|
||||
|
||||
void AnimationPlayer::set_audio_max_polyphony(int p_audio_max_polyphony) {
|
||||
ERR_FAIL_COND(p_audio_max_polyphony < 0 || p_audio_max_polyphony > 128);
|
||||
audio_max_polyphony = p_audio_max_polyphony;
|
||||
}
|
||||
|
||||
int AnimationPlayer::get_audio_max_polyphony() const {
|
||||
return audio_max_polyphony;
|
||||
}
|
||||
|
||||
void AnimationPlayer::set_movie_quit_on_finish_enabled(bool p_enabled) {
|
||||
movie_quit_on_finish = p_enabled;
|
||||
}
|
||||
@@ -1978,6 +2037,7 @@ void AnimationPlayer::_set_process(bool p_process, bool p_force) {
|
||||
}
|
||||
|
||||
void AnimationPlayer::_stop_internal(bool p_reset, bool p_keep_state) {
|
||||
_clear_audio_streams();
|
||||
_stop_playing_caches(p_reset);
|
||||
Playback &c = playback;
|
||||
c.blend.clear();
|
||||
@@ -2198,6 +2258,9 @@ void AnimationPlayer::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_method_call_mode", "mode"), &AnimationPlayer::set_method_call_mode);
|
||||
ClassDB::bind_method(D_METHOD("get_method_call_mode"), &AnimationPlayer::get_method_call_mode);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_audio_max_polyphony", "max_polyphony"), &AnimationPlayer::set_audio_max_polyphony);
|
||||
ClassDB::bind_method(D_METHOD("get_audio_max_polyphony"), &AnimationPlayer::get_audio_max_polyphony);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_movie_quit_on_finish_enabled", "enabled"), &AnimationPlayer::set_movie_quit_on_finish_enabled);
|
||||
ClassDB::bind_method(D_METHOD("is_movie_quit_on_finish_enabled"), &AnimationPlayer::is_movie_quit_on_finish_enabled);
|
||||
|
||||
@@ -2223,6 +2286,7 @@ void AnimationPlayer::_bind_methods() {
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playback_active", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_active", "is_active");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "speed_scale", PROPERTY_HINT_RANGE, "-64,64,0.01"), "set_speed_scale", "get_speed_scale");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "method_call_mode", PROPERTY_HINT_ENUM, "Deferred,Immediate"), "set_method_call_mode", "get_method_call_mode");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "audio_max_polyphony", PROPERTY_HINT_RANGE, "1,127,1"), "set_audio_max_polyphony", "get_audio_max_polyphony");
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "movie_quit_on_finish"), "set_movie_quit_on_finish_enabled", "is_movie_quit_on_finish_enabled");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user