Better animation of pauses in speech
This commit is contained in:
parent
1c50ece142
commit
c65c8b4eb3
|
@ -29,3 +29,7 @@ protected:
|
||||||
std::ostream& operator<<(std::ostream& stream, Shape value);
|
std::ostream& operator<<(std::ostream& stream, Shape value);
|
||||||
|
|
||||||
std::istream& operator>>(std::istream& stream, Shape& value);
|
std::istream& operator>>(std::istream& stream, Shape& value);
|
||||||
|
|
||||||
|
inline bool isClosed(Shape shape) {
|
||||||
|
return shape == Shape::A || shape == Shape::X;
|
||||||
|
}
|
|
@ -164,6 +164,36 @@ Timeline<Shape> createTweens(ContinuousTimeline<Shape> shapes) {
|
||||||
return tweens;
|
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) {
|
ContinuousTimeline<Shape> animate(const BoundedTimeline<Phone> &phones) {
|
||||||
// Convert phones to continuous timeline so that silences aren't skipped when iterating
|
// Convert phones to continuous timeline so that silences aren't skipped when iterating
|
||||||
ContinuousTimeline<optional<Phone>> continuousPhones(phones.getRange(), boost::none);
|
ContinuousTimeline<optional<Phone>> continuousPhones(phones.getRange(), boost::none);
|
||||||
|
@ -204,6 +234,12 @@ ContinuousTimeline<Shape> animate(const BoundedTimeline<Phone> &phones) {
|
||||||
shapes.set(it->getTimeRange(), shape);
|
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
|
// Create inbetweens for smoother animation
|
||||||
Timeline<Shape> tweens = createTweens(shapes);
|
Timeline<Shape> tweens = createTweens(shapes);
|
||||||
for (const auto& tween : tweens) {
|
for (const auto& tween : tweens) {
|
||||||
|
|
47
src/tools.h
47
src/tools.h
|
@ -3,6 +3,7 @@
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <deque>
|
||||||
|
|
||||||
#define UNUSED(x) ((void)(x))
|
#define UNUSED(x) ((void)(x))
|
||||||
|
|
||||||
|
@ -12,3 +13,49 @@ using lambda_unique_ptr = std::unique_ptr<T, std::function<void(T*)>>;
|
||||||
std::string formatDuration(std::chrono::duration<double> seconds);
|
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]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue