Renamed many symbols named "shapes" to "animation" to make the intent clearer
This commit is contained in:
parent
876fa024ad
commit
b02f37e961
|
@ -28,10 +28,10 @@ Shape getPauseShape(Shape previous, Shape next, centiseconds duration) {
|
|||
return Shape::X;
|
||||
}
|
||||
|
||||
JoiningContinuousTimeline<Shape> animatePauses(const JoiningContinuousTimeline<Shape>& shapes) {
|
||||
JoiningContinuousTimeline<Shape> result(shapes);
|
||||
JoiningContinuousTimeline<Shape> animatePauses(const JoiningContinuousTimeline<Shape>& animation) {
|
||||
JoiningContinuousTimeline<Shape> result(animation);
|
||||
|
||||
for_each_adjacent(shapes.begin(), shapes.end(), [&](const Timed<Shape>& previous, const Timed<Shape>& pause, const Timed<Shape>& next) {
|
||||
for_each_adjacent(animation.begin(), animation.end(), [&](const Timed<Shape>& previous, const Timed<Shape>& pause, const Timed<Shape>& next) {
|
||||
if (pause.getValue() != Shape::X) return;
|
||||
|
||||
result.set(pause.getTimeRange(), getPauseShape(previous.getValue(), next.getValue(), pause.getDuration()));
|
||||
|
|
|
@ -4,4 +4,4 @@
|
|||
#include "ContinuousTimeline.h"
|
||||
|
||||
// Takes an existing animation and modifies the pauses (X shapes) to look better.
|
||||
JoiningContinuousTimeline<Shape> animatePauses(const JoiningContinuousTimeline<Shape>& shapes);
|
||||
JoiningContinuousTimeline<Shape> animatePauses(const JoiningContinuousTimeline<Shape>& animation);
|
||||
|
|
|
@ -12,7 +12,7 @@ using boost::optional;
|
|||
// * When speaking, we anticipate vowels, trying to form their shape before the actual vowel.
|
||||
// So whenever we come across a one-shape vowel, we backtrack a little, spreating that shape to the left.
|
||||
JoiningContinuousTimeline<Shape> animateRough(const ContinuousTimeline<ShapeRule>& shapeRules) {
|
||||
JoiningContinuousTimeline<Shape> shapes(shapeRules.getRange(), Shape::X);
|
||||
JoiningContinuousTimeline<Shape> animation(shapeRules.getRange(), Shape::X);
|
||||
|
||||
Shape referenceShape = Shape::X;
|
||||
// Animate forwards
|
||||
|
@ -21,7 +21,7 @@ JoiningContinuousTimeline<Shape> animateRough(const ContinuousTimeline<ShapeRule
|
|||
const ShapeRule shapeRule = it->getValue();
|
||||
const ShapeSet shapeSet = std::get<ShapeSet>(shapeRule);
|
||||
const Shape shape = getClosestShape(referenceShape, shapeSet);
|
||||
shapes.set(it->getTimeRange(), shape);
|
||||
animation.set(it->getTimeRange(), shape);
|
||||
const auto phone = std::get<optional<Phone>>(shapeRule);
|
||||
const bool anticipateShape = phone && isVowel(*phone) && shapeSet.size() == 1;
|
||||
if (anticipateShape) {
|
||||
|
@ -41,7 +41,7 @@ JoiningContinuousTimeline<Shape> animateRough(const ContinuousTimeline<ShapeRule
|
|||
|
||||
// Overwrite forward-animated shape with backwards-animated, anticipating shape
|
||||
const Shape anticipatingShape = getClosestShape(referenceShape, std::get<ShapeSet>(reverseIt->getValue()));
|
||||
shapes.set(reverseIt->getTimeRange(), anticipatingShape);
|
||||
animation.set(reverseIt->getTimeRange(), anticipatingShape);
|
||||
|
||||
// Make sure the new, backwards-animated shape still resembles the anticipated shape
|
||||
if (getBasicShape(anticipatingShape) != getBasicShape(anticipatedShape)) break;
|
||||
|
@ -53,5 +53,5 @@ JoiningContinuousTimeline<Shape> animateRough(const ContinuousTimeline<ShapeRule
|
|||
referenceShape = anticipateShape ? shape : relax(shape);
|
||||
}
|
||||
|
||||
return shapes;
|
||||
return animation;
|
||||
}
|
||||
|
|
|
@ -29,9 +29,9 @@ ContinuousTimeline<ShapeRule> convertToTargetShapeSet(const ContinuousTimeline<S
|
|||
return result;
|
||||
}
|
||||
|
||||
JoiningContinuousTimeline<Shape> convertToTargetShapeSet(const JoiningContinuousTimeline<Shape>& shapes, const ShapeSet& targetShapeSet) {
|
||||
JoiningContinuousTimeline<Shape> result(shapes);
|
||||
for (const auto& timedShape : shapes) {
|
||||
JoiningContinuousTimeline<Shape> convertToTargetShapeSet(const JoiningContinuousTimeline<Shape>& animation, const ShapeSet& targetShapeSet) {
|
||||
JoiningContinuousTimeline<Shape> result(animation);
|
||||
for (const auto& timedShape : animation) {
|
||||
result.set(timedShape.getTimeRange(), convertToTargetShapeSet(timedShape.getValue(), targetShapeSet));
|
||||
}
|
||||
return result;
|
||||
|
|
|
@ -12,5 +12,5 @@ ShapeSet convertToTargetShapeSet(const ShapeSet& shapes, const ShapeSet& targetS
|
|||
// Replaces each shape in each rule with the closest shape that occurs in the target shape set.
|
||||
ContinuousTimeline<ShapeRule> convertToTargetShapeSet(const ContinuousTimeline<ShapeRule>& shapeRules, const ShapeSet& targetShapeSet);
|
||||
|
||||
// Replaces each shape in the specified timeline with the closest shape that occurs in the target shape set.
|
||||
JoiningContinuousTimeline<Shape> convertToTargetShapeSet(const JoiningContinuousTimeline<Shape>& shapes, const ShapeSet& targetShapeSet);
|
||||
// Replaces each shape in the specified animation with the closest shape that occurs in the target shape set.
|
||||
JoiningContinuousTimeline<Shape> convertToTargetShapeSet(const JoiningContinuousTimeline<Shape>& animation, const ShapeSet& targetShapeSet);
|
||||
|
|
|
@ -85,8 +85,8 @@ JoiningContinuousTimeline<Shape> retime(const JoiningContinuousTimeline<Shape>&
|
|||
return targetShapes;
|
||||
}
|
||||
|
||||
JoiningContinuousTimeline<Shape> retime(const JoiningContinuousTimeline<Shape>& shapes, TimeRange sourceRange, TimeRange targetRange) {
|
||||
const auto sourceShapes = JoiningContinuousTimeline<Shape>(sourceRange, Shape::X, shapes);
|
||||
JoiningContinuousTimeline<Shape> retime(const JoiningContinuousTimeline<Shape>& animation, TimeRange sourceRange, TimeRange targetRange) {
|
||||
const auto sourceShapes = JoiningContinuousTimeline<Shape>(sourceRange, Shape::X, animation);
|
||||
return retime(sourceShapes, targetRange);
|
||||
}
|
||||
|
||||
|
@ -96,10 +96,10 @@ enum class MouthState {
|
|||
Open
|
||||
};
|
||||
|
||||
JoiningContinuousTimeline<Shape> optimizeTiming(const JoiningContinuousTimeline<Shape>& shapes) {
|
||||
JoiningContinuousTimeline<Shape> optimizeTiming(const JoiningContinuousTimeline<Shape>& animation) {
|
||||
// Identify segments with idle, closed, and open mouth shapes
|
||||
JoiningContinuousTimeline<MouthState> segments(shapes.getRange(), MouthState::Idle);
|
||||
for (const auto& timedShape : shapes) {
|
||||
JoiningContinuousTimeline<MouthState> segments(animation.getRange(), MouthState::Idle);
|
||||
for (const auto& timedShape : animation) {
|
||||
const Shape shape = timedShape.getValue();
|
||||
const MouthState mouthState = shape == Shape::X ? MouthState::Idle : shape == Shape::A ? MouthState::Closed : MouthState::Open;
|
||||
segments.set(timedShape.getTimeRange(), mouthState);
|
||||
|
@ -112,7 +112,7 @@ JoiningContinuousTimeline<Shape> optimizeTiming(const JoiningContinuousTimeline<
|
|||
|
||||
// Make sure all open and closed segments are long enough to register visually.
|
||||
// We don't care about idle shapes at this point.
|
||||
JoiningContinuousTimeline<Shape> result(shapes.getRange(), Shape::X);
|
||||
JoiningContinuousTimeline<Shape> result(animation.getRange(), Shape::X);
|
||||
// ... we're filling the result timeline from right to left, so `resultStart` points to the earliest shape already written
|
||||
centiseconds resultStart = result.getRange().getEnd();
|
||||
for (auto segmentIt = segments.rbegin(); segmentIt != segments.rend(); ++segmentIt) {
|
||||
|
@ -122,7 +122,7 @@ JoiningContinuousTimeline<Shape> optimizeTiming(const JoiningContinuousTimeline<
|
|||
if (segmentTargetEnd - segmentIt->getStart() >= minSegmentDuration) {
|
||||
// The segment is long enough; we don't have to extend it to the left.
|
||||
const TimeRange targetRange(segmentIt->getStart(), segmentTargetEnd);
|
||||
const auto retimedSegment = retime(shapes, segmentIt->getTimeRange(), targetRange);
|
||||
const auto retimedSegment = retime(animation, segmentIt->getTimeRange(), targetRange);
|
||||
for (const auto& timedShape : retimedSegment) {
|
||||
result.set(timedShape);
|
||||
}
|
||||
|
@ -148,7 +148,7 @@ JoiningContinuousTimeline<Shape> optimizeTiming(const JoiningContinuousTimeline<
|
|||
size_t remainingShortSegmentCount = std::distance(shortSegmentIt, end);
|
||||
const centiseconds segmentDuration = (resultStart - shortSegmentsTargetStart) / remainingShortSegmentCount;
|
||||
const TimeRange segmentTargetRange(resultStart - segmentDuration, resultStart);
|
||||
const auto retimedSegment = retime(shapes, shortSegmentIt->getTimeRange(), segmentTargetRange);
|
||||
const auto retimedSegment = retime(animation, shortSegmentIt->getTimeRange(), segmentTargetRange);
|
||||
for (const auto& timedShape : retimedSegment) {
|
||||
result.set(timedShape);
|
||||
}
|
||||
|
|
|
@ -5,4 +5,4 @@
|
|||
|
||||
// Changes the timing of an existing animation to reduce jitter and to make sure all shapes register visually.
|
||||
// In some cases, shapes may be omitted.
|
||||
JoiningContinuousTimeline<Shape> optimizeTiming(const JoiningContinuousTimeline<Shape>& shapes);
|
||||
JoiningContinuousTimeline<Shape> optimizeTiming(const JoiningContinuousTimeline<Shape>& animation);
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
#include "tweening.h"
|
||||
#include "animationRules.h"
|
||||
|
||||
JoiningContinuousTimeline<Shape> insertTweens(const JoiningContinuousTimeline<Shape>& shapes) {
|
||||
JoiningContinuousTimeline<Shape> insertTweens(const JoiningContinuousTimeline<Shape>& animation) {
|
||||
centiseconds minTweenDuration = 4_cs;
|
||||
centiseconds maxTweenDuration = 8_cs;
|
||||
|
||||
JoiningContinuousTimeline<Shape> result(shapes);
|
||||
JoiningContinuousTimeline<Shape> result(animation);
|
||||
|
||||
for (auto first = shapes.begin(), second = std::next(shapes.begin());
|
||||
first != shapes.end() && second != shapes.end();
|
||||
for (auto first = animation.begin(), second = std::next(animation.begin());
|
||||
first != animation.end() && second != animation.end();
|
||||
++first, ++second)
|
||||
{
|
||||
auto pair = getTween(first->getValue(), second->getValue());
|
||||
|
|
|
@ -4,4 +4,4 @@
|
|||
#include "ContinuousTimeline.h"
|
||||
|
||||
// Takes an existing animation and inserts inbetween shapes for smoother results.
|
||||
JoiningContinuousTimeline<Shape> insertTweens(const JoiningContinuousTimeline<Shape>& shapes);
|
||||
JoiningContinuousTimeline<Shape> insertTweens(const JoiningContinuousTimeline<Shape>& animation);
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#include "EnumConverter.h"
|
||||
#include <set>
|
||||
|
||||
// The classic Hanna-Barbera mouth shapes A-F phus the common supplements G-H
|
||||
// The classic Hanna-Barbera mouth shapes A-F plus the common supplements G-H
|
||||
// For reference, see http://sunewatts.dk/lipsync/lipsync/article_02.php
|
||||
// For visual examples, see https://flic.kr/s/aHsj86KR4J. Their shapes "BMP".."L" map to A..H.
|
||||
enum class Shape {
|
||||
|
|
|
@ -7,5 +7,5 @@
|
|||
class Exporter {
|
||||
public:
|
||||
virtual ~Exporter() {}
|
||||
virtual void exportShapes(const boost::filesystem::path& inputFilePath, const JoiningContinuousTimeline<Shape>& shapes, const ShapeSet& targetShapeSet, std::ostream& outputStream) = 0;
|
||||
virtual void exportAnimation(const boost::filesystem::path& inputFilePath, const JoiningContinuousTimeline<Shape>& animation, const ShapeSet& targetShapeSet, std::ostream& outputStream) = 0;
|
||||
};
|
||||
|
|
|
@ -25,17 +25,17 @@ string escapeJsonString(const string& s) {
|
|||
return result;
|
||||
}
|
||||
|
||||
void JsonExporter::exportShapes(const boost::filesystem::path& inputFilePath, const JoiningContinuousTimeline<Shape>& shapes, const ShapeSet& targetShapeSet, std::ostream& outputStream) {
|
||||
void JsonExporter::exportAnimation(const boost::filesystem::path& inputFilePath, const JoiningContinuousTimeline<Shape>& animation, const ShapeSet& targetShapeSet, std::ostream& outputStream) {
|
||||
// Export as JSON.
|
||||
// I'm not using a library because the code is short enough without one and it lets me control the formatting.
|
||||
outputStream << "{\n";
|
||||
outputStream << " \"metadata\": {\n";
|
||||
outputStream << " \"soundFile\": \"" << escapeJsonString(inputFilePath.string()) << "\",\n";
|
||||
outputStream << " \"duration\": " << formatDuration(shapes.getRange().getDuration()) << "\n";
|
||||
outputStream << " \"duration\": " << formatDuration(animation.getRange().getDuration()) << "\n";
|
||||
outputStream << " },\n";
|
||||
outputStream << " \"mouthCues\": [\n";
|
||||
bool isFirst = true;
|
||||
for (auto& timedShape : dummyShapeIfEmpty(shapes, targetShapeSet)) {
|
||||
for (auto& timedShape : dummyShapeIfEmpty(animation, targetShapeSet)) {
|
||||
if (!isFirst) outputStream << ",\n";
|
||||
isFirst = false;
|
||||
outputStream << " { \"start\": " << formatDuration(timedShape.getStart())
|
||||
|
|
|
@ -4,5 +4,5 @@
|
|||
|
||||
class JsonExporter : public Exporter {
|
||||
public:
|
||||
void exportShapes(const boost::filesystem::path& inputFilePath, const JoiningContinuousTimeline<Shape>& shapes, const ShapeSet& targetShapeSet, std::ostream& outputStream) override;
|
||||
void exportAnimation(const boost::filesystem::path& inputFilePath, const JoiningContinuousTimeline<Shape>& animation, const ShapeSet& targetShapeSet, std::ostream& outputStream) override;
|
||||
};
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
#include "TsvExporter.h"
|
||||
#include "targetShapeSet.h"
|
||||
|
||||
void TsvExporter::exportShapes(const boost::filesystem::path& inputFilePath, const JoiningContinuousTimeline<Shape>& shapes, const ShapeSet& targetShapeSet, std::ostream& outputStream) {
|
||||
void TsvExporter::exportAnimation(const boost::filesystem::path& inputFilePath, const JoiningContinuousTimeline<Shape>& animation, const ShapeSet& targetShapeSet, std::ostream& outputStream) {
|
||||
UNUSED(inputFilePath);
|
||||
|
||||
// Output shapes with start times
|
||||
for (auto& timedShape : shapes) {
|
||||
for (auto& timedShape : animation) {
|
||||
outputStream << formatDuration(timedShape.getStart()) << "\t" << timedShape.getValue() << "\n";
|
||||
}
|
||||
|
||||
// Output closed mouth with end time
|
||||
outputStream << formatDuration(shapes.getRange().getEnd()) << "\t" << convertToTargetShapeSet(Shape::X, targetShapeSet) << "\n";
|
||||
outputStream << formatDuration(animation.getRange().getEnd()) << "\t" << convertToTargetShapeSet(Shape::X, targetShapeSet) << "\n";
|
||||
}
|
||||
|
|
|
@ -4,6 +4,6 @@
|
|||
|
||||
class TsvExporter : public Exporter {
|
||||
public:
|
||||
void exportShapes(const boost::filesystem::path& inputFilePath, const JoiningContinuousTimeline<Shape>& shapes, const ShapeSet& targetShapeSet, std::ostream& outputStream) override;
|
||||
void exportAnimation(const boost::filesystem::path& inputFilePath, const JoiningContinuousTimeline<Shape>& animation, const ShapeSet& targetShapeSet, std::ostream& outputStream) override;
|
||||
};
|
||||
|
||||
|
|
|
@ -6,15 +6,15 @@
|
|||
using std::string;
|
||||
using boost::property_tree::ptree;
|
||||
|
||||
void XmlExporter::exportShapes(const boost::filesystem::path& inputFilePath, const JoiningContinuousTimeline<Shape>& shapes, const ShapeSet& targetShapeSet, std::ostream& outputStream) {
|
||||
void XmlExporter::exportAnimation(const boost::filesystem::path& inputFilePath, const JoiningContinuousTimeline<Shape>& animation, const ShapeSet& targetShapeSet, std::ostream& outputStream) {
|
||||
ptree tree;
|
||||
|
||||
// Add metadata
|
||||
tree.put("rhubarbResult.metadata.soundFile", inputFilePath.string());
|
||||
tree.put("rhubarbResult.metadata.duration", formatDuration(shapes.getRange().getDuration()));
|
||||
tree.put("rhubarbResult.metadata.duration", formatDuration(animation.getRange().getDuration()));
|
||||
|
||||
// Add mouth cues
|
||||
for (auto& timedShape : dummyShapeIfEmpty(shapes, targetShapeSet)) {
|
||||
for (auto& timedShape : dummyShapeIfEmpty(animation, targetShapeSet)) {
|
||||
ptree& mouthCueElement = tree.add("rhubarbResult.mouthCues.mouthCue", timedShape.getValue());
|
||||
mouthCueElement.put("<xmlattr>.start", formatDuration(timedShape.getStart()));
|
||||
mouthCueElement.put("<xmlattr>.end", formatDuration(timedShape.getEnd()));
|
||||
|
|
|
@ -4,5 +4,5 @@
|
|||
|
||||
class XmlExporter : public Exporter {
|
||||
public:
|
||||
void exportShapes(const boost::filesystem::path& inputFilePath, const JoiningContinuousTimeline<Shape>& shapes, const ShapeSet& targetShapeSet, std::ostream& outputStream) override;
|
||||
void exportAnimation(const boost::filesystem::path& inputFilePath, const JoiningContinuousTimeline<Shape>& animation, const ShapeSet& targetShapeSet, std::ostream& outputStream) override;
|
||||
};
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
#include "targetShapeSet.h"
|
||||
|
||||
// Makes sure there is at least one mouth shape
|
||||
std::vector<Timed<Shape>> dummyShapeIfEmpty(const JoiningTimeline<Shape>& shapes, const ShapeSet& targetShapeSet) {
|
||||
std::vector<Timed<Shape>> dummyShapeIfEmpty(const JoiningTimeline<Shape>& animation, const ShapeSet& targetShapeSet) {
|
||||
std::vector<Timed<Shape>> result;
|
||||
std::copy(shapes.begin(), shapes.end(), std::back_inserter(result));
|
||||
std::copy(animation.begin(), animation.end(), std::back_inserter(result));
|
||||
if (result.empty()) {
|
||||
// Add zero-length empty mouth
|
||||
result.push_back(Timed<Shape>(0_cs, 0_cs, convertToTargetShapeSet(Shape::X, targetShapeSet)));
|
||||
|
|
|
@ -4,4 +4,4 @@
|
|||
#include "Timeline.h"
|
||||
|
||||
// Makes sure there is at least one mouth shape
|
||||
std::vector<Timed<Shape>> dummyShapeIfEmpty(const JoiningTimeline<Shape>& shapes, const ShapeSet& targetShapeSet);
|
||||
std::vector<Timed<Shape>> dummyShapeIfEmpty(const JoiningTimeline<Shape>& animation, const ShapeSet& targetShapeSet);
|
||||
|
|
|
@ -163,7 +163,7 @@ int main(int argc, char *argv[]) {
|
|||
|
||||
// Export animation
|
||||
unique_ptr<Exporter> exporter = createExporter(exportFormat.getValue());
|
||||
exporter->exportShapes(inputFilePath, animation, targetShapeSet, std::cout);
|
||||
exporter->exportAnimation(inputFilePath, animation, targetShapeSet, std::cout);
|
||||
|
||||
logging::info("Exiting application normally.");
|
||||
} catch (...) {
|
||||
|
|
Loading…
Reference in New Issue