Added --extendedShapes command-line parameter
This commit is contained in:
parent
9712483a75
commit
a8df4ac4f5
|
@ -229,6 +229,8 @@ add_library(rhubarb-animation
|
||||||
src/animation/shapeRule.cpp
|
src/animation/shapeRule.cpp
|
||||||
src/animation/shapeRule.h
|
src/animation/shapeRule.h
|
||||||
src/animation/shapeShorthands.h
|
src/animation/shapeShorthands.h
|
||||||
|
src/animation/targetShapeSet.cpp
|
||||||
|
src/animation/targetShapeSet.h
|
||||||
src/animation/timingOptimization.cpp
|
src/animation/timingOptimization.cpp
|
||||||
src/animation/timingOptimization.h
|
src/animation/timingOptimization.h
|
||||||
src/animation/tweening.cpp
|
src/animation/tweening.cpp
|
||||||
|
@ -298,6 +300,7 @@ add_library(rhubarb-exporters
|
||||||
)
|
)
|
||||||
target_include_directories(rhubarb-exporters PUBLIC "src/exporters")
|
target_include_directories(rhubarb-exporters PUBLIC "src/exporters")
|
||||||
target_link_libraries(rhubarb-exporters
|
target_link_libraries(rhubarb-exporters
|
||||||
|
rhubarb-animation
|
||||||
rhubarb-core
|
rhubarb-core
|
||||||
rhubarb-time
|
rhubarb-time
|
||||||
)
|
)
|
||||||
|
|
|
@ -11,10 +11,6 @@ Shape getBasicShape(Shape shape);
|
||||||
// Returns the mouth shape that results from relaxing the specified shape.
|
// Returns the mouth shape that results from relaxing the specified shape.
|
||||||
Shape relax(Shape shape);
|
Shape relax(Shape shape);
|
||||||
|
|
||||||
// A set of mouth shapes that can be used to represent a certain sound.
|
|
||||||
// The actual selection will be performed based on similarity with the previous or next shape.
|
|
||||||
using ShapeSet = std::set<Shape>;
|
|
||||||
|
|
||||||
// Gets the shape from a non-empty set of shapes that most closely resembles a reference shape.
|
// Gets the shape from a non-empty set of shapes that most closely resembles a reference shape.
|
||||||
Shape getClosestShape(Shape reference, ShapeSet shapes);
|
Shape getClosestShape(Shape reference, ShapeSet shapes);
|
||||||
|
|
||||||
|
|
|
@ -5,16 +5,23 @@
|
||||||
#include "pauseAnimation.h"
|
#include "pauseAnimation.h"
|
||||||
#include "tweening.h"
|
#include "tweening.h"
|
||||||
#include "timingOptimization.h"
|
#include "timingOptimization.h"
|
||||||
|
#include "targetShapeSet.h"
|
||||||
|
|
||||||
JoiningContinuousTimeline<Shape> animate(const BoundedTimeline<Phone> &phones) {
|
JoiningContinuousTimeline<Shape> animate(const BoundedTimeline<Phone> &phones, const ShapeSet& targetShapeSet) {
|
||||||
// Create timeline of shape rules
|
// Create timeline of shape rules
|
||||||
const ContinuousTimeline<ShapeRule> shapeRules = getShapeRules(phones);
|
ContinuousTimeline<ShapeRule> shapeRules = getShapeRules(phones);
|
||||||
|
|
||||||
|
// Modify shape rules to only contain allowed shapes -- plus X, which is needed for pauses and will be replaced later
|
||||||
|
ShapeSet targetShapeSetPlusX = targetShapeSet;
|
||||||
|
targetShapeSetPlusX.insert(Shape::X);
|
||||||
|
shapeRules = convertToTargetShapeSet(shapeRules, targetShapeSetPlusX);
|
||||||
|
|
||||||
// Animate in multiple steps
|
// Animate in multiple steps
|
||||||
JoiningContinuousTimeline<Shape> animation = animateRough(shapeRules);
|
JoiningContinuousTimeline<Shape> animation = animateRough(shapeRules);
|
||||||
animation = optimizeTiming(animation);
|
animation = optimizeTiming(animation);
|
||||||
animation = animatePauses(animation);
|
animation = animatePauses(animation);
|
||||||
animation = insertTweens(animation);
|
animation = insertTweens(animation);
|
||||||
|
animation = convertToTargetShapeSet(animation, targetShapeSet);
|
||||||
|
|
||||||
for (const auto& timedShape : animation) {
|
for (const auto& timedShape : animation) {
|
||||||
logTimedEvent("shape", timedShape);
|
logTimedEvent("shape", timedShape);
|
||||||
|
|
|
@ -3,5 +3,6 @@
|
||||||
#include "Phone.h"
|
#include "Phone.h"
|
||||||
#include "Shape.h"
|
#include "Shape.h"
|
||||||
#include "ContinuousTimeline.h"
|
#include "ContinuousTimeline.h"
|
||||||
|
#include "targetShapeSet.h"
|
||||||
|
|
||||||
JoiningContinuousTimeline<Shape> animate(const BoundedTimeline<Phone>& phones);
|
JoiningContinuousTimeline<Shape> animate(const BoundedTimeline<Phone>& phones, const ShapeSet& targetShapeSet);
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
#include "targetShapeSet.h"
|
||||||
|
|
||||||
|
Shape convertToTargetShapeSet(Shape shape, const ShapeSet& targetShapeSet) {
|
||||||
|
if (targetShapeSet.find(shape) != targetShapeSet.end()) {
|
||||||
|
return shape;
|
||||||
|
}
|
||||||
|
Shape basicShape = getBasicShape(shape);
|
||||||
|
if (targetShapeSet.find(basicShape) == targetShapeSet.end()) {
|
||||||
|
throw std::invalid_argument(fmt::format("Target shape set must contain basic shape {}.", basicShape));
|
||||||
|
}
|
||||||
|
return basicShape;
|
||||||
|
}
|
||||||
|
|
||||||
|
ShapeSet convertToTargetShapeSet(const ShapeSet& shapes, const ShapeSet& targetShapeSet) {
|
||||||
|
ShapeSet result;
|
||||||
|
for (Shape shape : shapes) {
|
||||||
|
result.insert(convertToTargetShapeSet(shape, targetShapeSet));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ContinuousTimeline<ShapeRule> convertToTargetShapeSet(const ContinuousTimeline<ShapeRule>& shapeRules, const ShapeSet& targetShapeSet) {
|
||||||
|
ContinuousTimeline<ShapeRule> result(shapeRules);
|
||||||
|
for (const auto& timedShapeRule : shapeRules) {
|
||||||
|
ShapeRule rule = timedShapeRule.getValue();
|
||||||
|
std::get<ShapeSet>(rule) = convertToTargetShapeSet(std::get<ShapeSet>(rule), targetShapeSet);
|
||||||
|
result.set(timedShapeRule.getTimeRange(), rule);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
JoiningContinuousTimeline<Shape> convertToTargetShapeSet(const JoiningContinuousTimeline<Shape>& shapes, const ShapeSet& targetShapeSet) {
|
||||||
|
JoiningContinuousTimeline<Shape> result(shapes);
|
||||||
|
for (const auto& timedShape : shapes) {
|
||||||
|
result.set(timedShape.getTimeRange(), convertToTargetShapeSet(timedShape.getValue(), targetShapeSet));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Shape.h"
|
||||||
|
#include "shapeRule.h"
|
||||||
|
|
||||||
|
// Returns the closest shape to the specified one that occurs in the target shape set.
|
||||||
|
Shape convertToTargetShapeSet(Shape shape, const ShapeSet& targetShapeSet);
|
||||||
|
|
||||||
|
// Replaces each shape in the specified set with the closest shape that occurs in the target shape set.
|
||||||
|
ShapeSet convertToTargetShapeSet(const ShapeSet& shapes, const ShapeSet& targetShapeSet);
|
||||||
|
|
||||||
|
// 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);
|
|
@ -1,12 +1,35 @@
|
||||||
#include "Shape.h"
|
#include "Shape.h"
|
||||||
|
|
||||||
using std::string;
|
using std::string;
|
||||||
|
using std::set;
|
||||||
|
|
||||||
ShapeConverter& ShapeConverter::get() {
|
ShapeConverter& ShapeConverter::get() {
|
||||||
static ShapeConverter converter;
|
static ShapeConverter converter;
|
||||||
return converter;
|
return converter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
set<Shape> ShapeConverter::getBasicShapes() {
|
||||||
|
static const set<Shape> result = [] {
|
||||||
|
set<Shape> result;
|
||||||
|
for (int i = 0; i <= static_cast<int>(Shape::LastBasicShape); ++i) {
|
||||||
|
result.insert(static_cast<Shape>(i));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
set<Shape> ShapeConverter::getExtendedShapes() {
|
||||||
|
static const set<Shape> result = [] {
|
||||||
|
set<Shape> result;
|
||||||
|
for (int i = static_cast<int>(Shape::LastBasicShape) + 1; i < static_cast<int>(Shape::EndSentinel); ++i) {
|
||||||
|
result.insert(static_cast<Shape>(i));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
string ShapeConverter::getTypeName() {
|
string ShapeConverter::getTypeName() {
|
||||||
return "Shape";
|
return "Shape";
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "EnumConverter.h"
|
#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 phus the common supplements G-H
|
||||||
// For reference, see http://sunewatts.dk/lipsync/lipsync/article_02.php
|
// For reference, see http://sunewatts.dk/lipsync/lipsync/article_02.php
|
||||||
|
@ -14,6 +15,7 @@ enum class Shape {
|
||||||
D, // Mouth wide open (vowels like f[a]ther, b[a]t, wh[y])
|
D, // Mouth wide open (vowels like f[a]ther, b[a]t, wh[y])
|
||||||
E, // Rounded mouth (vowels like [o]ff)
|
E, // Rounded mouth (vowels like [o]ff)
|
||||||
F, // Puckered lips (y[ou], b[o]y, [w]ay)
|
F, // Puckered lips (y[ou], b[o]y, [w]ay)
|
||||||
|
LastBasicShape = F,
|
||||||
|
|
||||||
// Extended shapes
|
// Extended shapes
|
||||||
|
|
||||||
|
@ -27,6 +29,8 @@ enum class Shape {
|
||||||
class ShapeConverter : public EnumConverter<Shape> {
|
class ShapeConverter : public EnumConverter<Shape> {
|
||||||
public:
|
public:
|
||||||
static ShapeConverter& get();
|
static ShapeConverter& get();
|
||||||
|
std::set<Shape> getBasicShapes();
|
||||||
|
std::set<Shape> getExtendedShapes();
|
||||||
protected:
|
protected:
|
||||||
std::string getTypeName() override;
|
std::string getTypeName() override;
|
||||||
member_data getMemberData() override;
|
member_data getMemberData() override;
|
||||||
|
@ -38,4 +42,9 @@ std::istream& operator>>(std::istream& stream, Shape& value);
|
||||||
|
|
||||||
inline bool isClosed(Shape shape) {
|
inline bool isClosed(Shape shape) {
|
||||||
return shape == Shape::A || shape == Shape::X;
|
return shape == Shape::A || shape == Shape::X;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A set of mouth shapes.
|
||||||
|
// This may be used to represent all shapes that can be used to represent a certain sound.
|
||||||
|
// Alternatively, it can represent all shapes the user wants to allow as program output.
|
||||||
|
using ShapeSet = std::set<Shape>;
|
||||||
|
|
|
@ -7,5 +7,5 @@
|
||||||
class Exporter {
|
class Exporter {
|
||||||
public:
|
public:
|
||||||
virtual ~Exporter() {}
|
virtual ~Exporter() {}
|
||||||
virtual void exportShapes(const boost::filesystem::path& inputFilePath, const JoiningContinuousTimeline<Shape>& shapes, std::ostream& outputStream) = 0;
|
virtual void exportShapes(const boost::filesystem::path& inputFilePath, const JoiningContinuousTimeline<Shape>& shapes, const ShapeSet& targetShapeSet, std::ostream& outputStream) = 0;
|
||||||
};
|
};
|
||||||
|
|
|
@ -25,7 +25,7 @@ string escapeJsonString(const string& s) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void JsonExporter::exportShapes(const boost::filesystem::path& inputFilePath, const JoiningContinuousTimeline<Shape>& shapes, std::ostream& outputStream) {
|
void JsonExporter::exportShapes(const boost::filesystem::path& inputFilePath, const JoiningContinuousTimeline<Shape>& shapes, const ShapeSet& targetShapeSet, std::ostream& outputStream) {
|
||||||
// Export as JSON.
|
// Export as JSON.
|
||||||
// I'm not using a library because the code is short enough without one and it lets me control the formatting.
|
// I'm not using a library because the code is short enough without one and it lets me control the formatting.
|
||||||
outputStream << "{\n";
|
outputStream << "{\n";
|
||||||
|
@ -35,7 +35,7 @@ void JsonExporter::exportShapes(const boost::filesystem::path& inputFilePath, co
|
||||||
outputStream << " },\n";
|
outputStream << " },\n";
|
||||||
outputStream << " \"mouthCues\": [\n";
|
outputStream << " \"mouthCues\": [\n";
|
||||||
bool isFirst = true;
|
bool isFirst = true;
|
||||||
for (auto& timedShape : dummyShapeIfEmpty(shapes)) {
|
for (auto& timedShape : dummyShapeIfEmpty(shapes, targetShapeSet)) {
|
||||||
if (!isFirst) outputStream << ",\n";
|
if (!isFirst) outputStream << ",\n";
|
||||||
isFirst = false;
|
isFirst = false;
|
||||||
outputStream << " { \"start\": " << formatDuration(timedShape.getStart())
|
outputStream << " { \"start\": " << formatDuration(timedShape.getStart())
|
||||||
|
|
|
@ -4,5 +4,5 @@
|
||||||
|
|
||||||
class JsonExporter : public Exporter {
|
class JsonExporter : public Exporter {
|
||||||
public:
|
public:
|
||||||
void exportShapes(const boost::filesystem::path& inputFilePath, const JoiningContinuousTimeline<Shape>& shapes, std::ostream& outputStream) override;
|
void exportShapes(const boost::filesystem::path& inputFilePath, const JoiningContinuousTimeline<Shape>& shapes, const ShapeSet& targetShapeSet, std::ostream& outputStream) override;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include "TsvExporter.h"
|
#include "TsvExporter.h"
|
||||||
|
#include "targetShapeSet.h"
|
||||||
|
|
||||||
void TsvExporter::exportShapes(const boost::filesystem::path& inputFilePath, const JoiningContinuousTimeline<Shape>& shapes, std::ostream& outputStream) {
|
void TsvExporter::exportShapes(const boost::filesystem::path& inputFilePath, const JoiningContinuousTimeline<Shape>& shapes, const ShapeSet& targetShapeSet, std::ostream& outputStream) {
|
||||||
UNUSED(inputFilePath);
|
UNUSED(inputFilePath);
|
||||||
|
|
||||||
// Output shapes with start times
|
// Output shapes with start times
|
||||||
|
@ -9,5 +10,5 @@ void TsvExporter::exportShapes(const boost::filesystem::path& inputFilePath, con
|
||||||
}
|
}
|
||||||
|
|
||||||
// Output closed mouth with end time
|
// Output closed mouth with end time
|
||||||
outputStream << formatDuration(shapes.getRange().getEnd()) << "\t" << Shape::X << "\n";
|
outputStream << formatDuration(shapes.getRange().getEnd()) << "\t" << convertToTargetShapeSet(Shape::X, targetShapeSet) << "\n";
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,6 @@
|
||||||
|
|
||||||
class TsvExporter : public Exporter {
|
class TsvExporter : public Exporter {
|
||||||
public:
|
public:
|
||||||
void exportShapes(const boost::filesystem::path& inputFilePath, const JoiningContinuousTimeline<Shape>& shapes, std::ostream& outputStream) override;
|
void exportShapes(const boost::filesystem::path& inputFilePath, const JoiningContinuousTimeline<Shape>& shapes, const ShapeSet& targetShapeSet, std::ostream& outputStream) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
using std::string;
|
using std::string;
|
||||||
using boost::property_tree::ptree;
|
using boost::property_tree::ptree;
|
||||||
|
|
||||||
void XmlExporter::exportShapes(const boost::filesystem::path& inputFilePath, const JoiningContinuousTimeline<Shape>& shapes, std::ostream& outputStream) {
|
void XmlExporter::exportShapes(const boost::filesystem::path& inputFilePath, const JoiningContinuousTimeline<Shape>& shapes, const ShapeSet& targetShapeSet, std::ostream& outputStream) {
|
||||||
ptree tree;
|
ptree tree;
|
||||||
|
|
||||||
// Add metadata
|
// Add metadata
|
||||||
|
@ -14,7 +14,7 @@ void XmlExporter::exportShapes(const boost::filesystem::path& inputFilePath, con
|
||||||
tree.put("rhubarbResult.metadata.duration", formatDuration(shapes.getRange().getDuration()));
|
tree.put("rhubarbResult.metadata.duration", formatDuration(shapes.getRange().getDuration()));
|
||||||
|
|
||||||
// Add mouth cues
|
// Add mouth cues
|
||||||
for (auto& timedShape : dummyShapeIfEmpty(shapes)) {
|
for (auto& timedShape : dummyShapeIfEmpty(shapes, targetShapeSet)) {
|
||||||
ptree& mouthCueElement = tree.add("rhubarbResult.mouthCues.mouthCue", timedShape.getValue());
|
ptree& mouthCueElement = tree.add("rhubarbResult.mouthCues.mouthCue", timedShape.getValue());
|
||||||
mouthCueElement.put("<xmlattr>.start", formatDuration(timedShape.getStart()));
|
mouthCueElement.put("<xmlattr>.start", formatDuration(timedShape.getStart()));
|
||||||
mouthCueElement.put("<xmlattr>.end", formatDuration(timedShape.getEnd()));
|
mouthCueElement.put("<xmlattr>.end", formatDuration(timedShape.getEnd()));
|
||||||
|
|
|
@ -4,5 +4,5 @@
|
||||||
|
|
||||||
class XmlExporter : public Exporter {
|
class XmlExporter : public Exporter {
|
||||||
public:
|
public:
|
||||||
void exportShapes(const boost::filesystem::path& inputFilePath, const JoiningContinuousTimeline<Shape>& shapes, std::ostream& outputStream) override;
|
void exportShapes(const boost::filesystem::path& inputFilePath, const JoiningContinuousTimeline<Shape>& shapes, const ShapeSet& targetShapeSet, std::ostream& outputStream) override;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
#include "exporterTools.h"
|
#include "exporterTools.h"
|
||||||
|
#include "targetShapeSet.h"
|
||||||
|
|
||||||
// Makes sure there is at least one mouth shape
|
// Makes sure there is at least one mouth shape
|
||||||
std::vector<Timed<Shape>> dummyShapeIfEmpty(const JoiningTimeline<Shape>& shapes) {
|
std::vector<Timed<Shape>> dummyShapeIfEmpty(const JoiningTimeline<Shape>& shapes, const ShapeSet& targetShapeSet) {
|
||||||
std::vector<Timed<Shape>> result;
|
std::vector<Timed<Shape>> result;
|
||||||
std::copy(shapes.begin(), shapes.end(), std::back_inserter(result));
|
std::copy(shapes.begin(), shapes.end(), std::back_inserter(result));
|
||||||
if (result.empty()) {
|
if (result.empty()) {
|
||||||
// Add zero-length empty mouth
|
// Add zero-length empty mouth
|
||||||
result.push_back(Timed<Shape>(0_cs, 0_cs, Shape::X));
|
result.push_back(Timed<Shape>(0_cs, 0_cs, convertToTargetShapeSet(Shape::X, targetShapeSet)));
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,4 +4,4 @@
|
||||||
#include "Timeline.h"
|
#include "Timeline.h"
|
||||||
|
|
||||||
// Makes sure there is at least one mouth shape
|
// Makes sure there is at least one mouth shape
|
||||||
std::vector<Timed<Shape>> dummyShapeIfEmpty(const JoiningTimeline<Shape>& shapes);
|
std::vector<Timed<Shape>> dummyShapeIfEmpty(const JoiningTimeline<Shape>& shapes, const ShapeSet& targetShapeSet);
|
||||||
|
|
|
@ -13,11 +13,12 @@ using std::unique_ptr;
|
||||||
JoiningContinuousTimeline<Shape> animateAudioClip(
|
JoiningContinuousTimeline<Shape> animateAudioClip(
|
||||||
const AudioClip& audioClip,
|
const AudioClip& audioClip,
|
||||||
optional<u32string> dialog,
|
optional<u32string> dialog,
|
||||||
|
const ShapeSet& targetShapeSet,
|
||||||
int maxThreadCount,
|
int maxThreadCount,
|
||||||
ProgressSink& progressSink)
|
ProgressSink& progressSink)
|
||||||
{
|
{
|
||||||
BoundedTimeline<Phone> phones = recognizePhones(audioClip, dialog, maxThreadCount, progressSink);
|
BoundedTimeline<Phone> phones = recognizePhones(audioClip, dialog, maxThreadCount, progressSink);
|
||||||
JoiningContinuousTimeline<Shape> result = animate(phones);
|
JoiningContinuousTimeline<Shape> result = animate(phones, targetShapeSet);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,8 +33,9 @@ unique_ptr<AudioClip> createWaveAudioClip(path filePath) {
|
||||||
JoiningContinuousTimeline<Shape> animateWaveFile(
|
JoiningContinuousTimeline<Shape> animateWaveFile(
|
||||||
path filePath,
|
path filePath,
|
||||||
optional<u32string> dialog,
|
optional<u32string> dialog,
|
||||||
|
const ShapeSet& targetShapeSet,
|
||||||
int maxThreadCount,
|
int maxThreadCount,
|
||||||
ProgressSink& progressSink)
|
ProgressSink& progressSink)
|
||||||
{
|
{
|
||||||
return animateAudioClip(*createWaveAudioClip(filePath), dialog, maxThreadCount, progressSink);
|
return animateAudioClip(*createWaveAudioClip(filePath), dialog, targetShapeSet, maxThreadCount, progressSink);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,15 +5,18 @@
|
||||||
#include "AudioClip.h"
|
#include "AudioClip.h"
|
||||||
#include "ProgressBar.h"
|
#include "ProgressBar.h"
|
||||||
#include <boost/filesystem.hpp>
|
#include <boost/filesystem.hpp>
|
||||||
|
#include "targetShapeSet.h"
|
||||||
|
|
||||||
JoiningContinuousTimeline<Shape> animateAudioClip(
|
JoiningContinuousTimeline<Shape> animateAudioClip(
|
||||||
const AudioClip& audioClip,
|
const AudioClip& audioClip,
|
||||||
boost::optional<std::u32string> dialog,
|
boost::optional<std::u32string> dialog,
|
||||||
|
const ShapeSet& targetShapeSet,
|
||||||
int maxThreadCount,
|
int maxThreadCount,
|
||||||
ProgressSink& progressSink);
|
ProgressSink& progressSink);
|
||||||
|
|
||||||
JoiningContinuousTimeline<Shape> animateWaveFile(
|
JoiningContinuousTimeline<Shape> animateWaveFile(
|
||||||
boost::filesystem::path filePath,
|
boost::filesystem::path filePath,
|
||||||
boost::optional<std::u32string> dialog,
|
boost::optional<std::u32string> dialog,
|
||||||
|
const ShapeSet& targetShapeSet,
|
||||||
int maxThreadCount,
|
int maxThreadCount,
|
||||||
ProgressSink& progressSink);
|
ProgressSink& progressSink);
|
||||||
|
|
18
src/main.cpp
18
src/main.cpp
|
@ -22,6 +22,7 @@
|
||||||
#include "JsonExporter.h"
|
#include "JsonExporter.h"
|
||||||
#include <boost/iostreams/stream.hpp>
|
#include <boost/iostreams/stream.hpp>
|
||||||
#include <boost/iostreams/device/null.hpp>
|
#include <boost/iostreams/device/null.hpp>
|
||||||
|
#include "targetShapeSet.h"
|
||||||
|
|
||||||
using std::exception;
|
using std::exception;
|
||||||
using std::string;
|
using std::string;
|
||||||
|
@ -81,6 +82,18 @@ unique_ptr<Exporter> createExporter(ExportFormat exportFormat) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ShapeSet getTargetShapeSet(const string& extendedShapesString) {
|
||||||
|
// All basic shapes are mandatory
|
||||||
|
ShapeSet result(ShapeConverter::get().getBasicShapes());
|
||||||
|
|
||||||
|
// Add any extended shapes
|
||||||
|
for (char ch : extendedShapesString) {
|
||||||
|
Shape shape = ShapeConverter::get().parse(string(1, ch));
|
||||||
|
result.insert(shape);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
auto pausableStderrSink = addPausableStdErrSink(logging::Level::Warn);
|
auto pausableStderrSink = addPausableStdErrSink(logging::Level::Warn);
|
||||||
pausableStderrSink->pause();
|
pausableStderrSink->pause();
|
||||||
|
@ -96,6 +109,7 @@ int main(int argc, char *argv[]) {
|
||||||
tclap::ValueArg<string> logFileName("", "logFile", "The log file path.", false, string(), "string", cmd);
|
tclap::ValueArg<string> logFileName("", "logFile", "The log file path.", false, string(), "string", cmd);
|
||||||
tclap::SwitchArg quietMode("q", "quiet", "Suppresses all output to stderr except for error messages.", cmd, false);
|
tclap::SwitchArg quietMode("q", "quiet", "Suppresses all output to stderr except for error messages.", cmd, false);
|
||||||
tclap::ValueArg<int> maxThreadCount("", "threads", "The maximum number of worker threads to use.", false, getProcessorCoreCount(), "number", cmd);
|
tclap::ValueArg<int> maxThreadCount("", "threads", "The maximum number of worker threads to use.", false, getProcessorCoreCount(), "number", cmd);
|
||||||
|
tclap::ValueArg<string> extendedShapes("", "extendedShapes", "All extended, optional shapes to use.", false, "GHX", "string", cmd);
|
||||||
tclap::ValueArg<string> dialogFile("d", "dialogFile", "A file containing the text of the dialog.", false, string(), "string", cmd);
|
tclap::ValueArg<string> dialogFile("d", "dialogFile", "A file containing the text of the dialog.", false, string(), "string", cmd);
|
||||||
auto exportFormats = vector<ExportFormat>(ExportFormatConverter::get().getValues());
|
auto exportFormats = vector<ExportFormat>(ExportFormatConverter::get().getValues());
|
||||||
tclap::ValuesConstraint<ExportFormat> exportFormatConstraint(exportFormats);
|
tclap::ValuesConstraint<ExportFormat> exportFormatConstraint(exportFormats);
|
||||||
|
@ -120,6 +134,7 @@ int main(int argc, char *argv[]) {
|
||||||
throw std::runtime_error("Thread count must be 1 or higher.");
|
throw std::runtime_error("Thread count must be 1 or higher.");
|
||||||
}
|
}
|
||||||
path inputFilePath(inputFileName.getValue());
|
path inputFilePath(inputFileName.getValue());
|
||||||
|
ShapeSet targetShapeSet = getTargetShapeSet(extendedShapes.getValue());
|
||||||
|
|
||||||
// Set up log file
|
// Set up log file
|
||||||
if (logFileName.isSet()) {
|
if (logFileName.isSet()) {
|
||||||
|
@ -140,6 +155,7 @@ int main(int argc, char *argv[]) {
|
||||||
animation = animateWaveFile(
|
animation = animateWaveFile(
|
||||||
inputFilePath,
|
inputFilePath,
|
||||||
dialogFile.isSet() ? readUtf8File(path(dialogFile.getValue())) : boost::optional<u32string>(),
|
dialogFile.isSet() ? readUtf8File(path(dialogFile.getValue())) : boost::optional<u32string>(),
|
||||||
|
targetShapeSet,
|
||||||
maxThreadCount.getValue(),
|
maxThreadCount.getValue(),
|
||||||
progressBar);
|
progressBar);
|
||||||
}
|
}
|
||||||
|
@ -147,7 +163,7 @@ int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
// Export animation
|
// Export animation
|
||||||
unique_ptr<Exporter> exporter = createExporter(exportFormat.getValue());
|
unique_ptr<Exporter> exporter = createExporter(exportFormat.getValue());
|
||||||
exporter->exportShapes(inputFilePath, animation, std::cout);
|
exporter->exportShapes(inputFilePath, animation, targetShapeSet, std::cout);
|
||||||
|
|
||||||
logging::info("Exiting application normally.");
|
logging::info("Exiting application normally.");
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
|
|
Loading…
Reference in New Issue