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::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;
|
||||
}
|
||||
|
||||
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) {
|
||||
|
|
49
src/tools.h
49
src/tools.h
|
@ -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]);
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue