Better animation of pauses in speech

This commit is contained in:
Daniel Wolf 2016-08-05 19:34:57 +02:00
parent 1c50ece142
commit c65c8b4eb3
3 changed files with 88 additions and 1 deletions

View File

@ -29,3 +29,7 @@ protected:
std::ostream& operator<<(std::ostream& stream, Shape value);
std::istream& operator>>(std::istream& stream, Shape& value);
inline bool isClosed(Shape shape) {
return shape == Shape::A || shape == Shape::X;
}

View File

@ -164,6 +164,36 @@ Timeline<Shape> createTweens(ContinuousTimeline<Shape> shapes) {
return tweens;
}
Timeline<Shape> animatePauses(const ContinuousTimeline<Shape>& shapes) {
Timeline<Shape> result;
// Don't close mouth for short pauses
for (const auto& timedShape : shapes) {
if (timedShape.getValue() != X) continue;
const centiseconds maxPausedOpenMouthDuration = 35cs;
const TimeRange timeRange = timedShape.getTimeRange();
if (timeRange.getLength() <= maxPausedOpenMouthDuration) {
result.set(timeRange, B);
}
}
// Keep mouth open into pause if it just opened
for_each_adjacent(shapes.begin(), shapes.end(), [&](const Timed<Shape>& secondLast, const Timed<Shape>& last, const Timed<Shape>& pause) {
if (pause.getValue() != X) return;
centiseconds lastLength = last.getTimeRange().getLength();
const centiseconds minOpenDuration = 20cs;
if (isClosed(secondLast.getValue()) && !isClosed(last.getValue()) && lastLength < minOpenDuration) {
const centiseconds minSpillDuration = 20cs;
centiseconds spillDuration = std::min(minSpillDuration, pause.getTimeRange().getLength());
result.set(pause.getStart(), pause.getStart() + spillDuration, B);
}
});
return result;
}
ContinuousTimeline<Shape> animate(const BoundedTimeline<Phone> &phones) {
// Convert phones to continuous timeline so that silences aren't skipped when iterating
ContinuousTimeline<optional<Phone>> continuousPhones(phones.getRange(), boost::none);
@ -204,6 +234,12 @@ ContinuousTimeline<Shape> animate(const BoundedTimeline<Phone> &phones) {
shapes.set(it->getTimeRange(), shape);
}
// Animate pauses
Timeline<Shape> pauses = animatePauses(shapes);
for (const auto& pause : pauses) {
shapes.set(pause);
}
// Create inbetweens for smoother animation
Timeline<Shape> tweens = createTweens(shapes);
for (const auto& tween : tweens) {

View File

@ -3,6 +3,7 @@
#include <functional>
#include <memory>
#include <chrono>
#include <deque>
#define UNUSED(x) ((void)(x))
@ -11,4 +12,50 @@ using lambda_unique_ptr = std::unique_ptr<T, std::function<void(T*)>>;
std::string formatDuration(std::chrono::duration<double> seconds);
std::string formatTime(time_t time, const std::string& format);
std::string formatTime(time_t time, const std::string& format);
template<unsigned int n, typename iterator_type>
void for_each_adjacent(
iterator_type begin,
iterator_type end,
std::function<void(const std::deque<std::reference_wrapper<const typename iterator_type::value_type>>&)> f)
{
// Get the first n values
iterator_type it = begin;
using element_type = std::reference_wrapper<const typename iterator_type::value_type>;
std::deque<element_type> values;
for (unsigned i = 0; i < n; ++i) {
if (it == end) return;
values.push_back(std::ref(*it));
if (i < n - 1) ++it;
}
for (; it != end; ++it) {
f(values);
values.pop_front();
values.push_back(*it);
}
}
template<typename iterator_type>
void for_each_adjacent(
iterator_type begin,
iterator_type end,
std::function<void(const typename iterator_type::reference a, const typename iterator_type::reference b)> f)
{
for_each_adjacent<2>(begin, end, [&](const std::deque<std::reference_wrapper<const typename iterator_type::value_type>>& args) {
f(args[0], args[1]);
});
}
template<typename iterator_type>
void for_each_adjacent(
iterator_type begin,
iterator_type end,
std::function<void(const typename iterator_type::reference a, const typename iterator_type::reference b, const typename iterator_type::reference c)> f)
{
for_each_adjacent<3>(begin, end, [&](const std::deque<std::reference_wrapper<const typename iterator_type::value_type>>& args) {
f(args[0], args[1], args[2]);
});
}