Skip to content

Commit

Permalink
Merge branch 'feature/69-moho' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
DanielSWolf committed Jul 3, 2019
2 parents 6f0e3ef + c7925d6 commit 460378b
Show file tree
Hide file tree
Showing 9 changed files with 161 additions and 4 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Unreleased

* **Added** switch data file exporter for Moho (formerly Anime Studio) and OpenToonz ([issue #69](https://github.com/DanielSWolf/rhubarb-lip-sync/issues/69))
* **Improved** animation rule for OW sound: animating it as E-F rather than F.

## Version 1.9.1
Expand Down
39 changes: 37 additions & 2 deletions README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ Rhubarb Lip Sync allows you to quickly create 2D mouth animation from voice reco
Rhubarb Lip Sync integrates with the following applications:

* *Adobe After Effects* (see <<afterEffects,below>>)
* *Moho* and *OpenToonz* (see <<moho,below>>)
* *Spine* by Esoteric Software (see <<spine,below>>)
* *Vegas Pro* by Magix (see <<vegas,below>>)
* *Visionaire Studio* (see https://www.visionaire-studio.net/forum/thread/mouth-animation-using-rhubarb-lip-sync[external link])
Expand All @@ -44,6 +45,13 @@ You can use Rhubarb Lip Sync to animate dialog right from Adobe After Effects. F

image:img/after-effects.png[]

[[moho]]
=== Moho and OpenToonz

Rhubarb Lip Sync can create .dat switch data files, which are understood by Moho and OpenToonz. You can set the frame rate using the `--datFrameRate` option; to control the shape names, use the `--datUsePrestonBlair` flag. For more details, see <<options>>.

image:img/moho.png[]

[[spine]]
=== Spine by Esoteric Software

Expand Down Expand Up @@ -123,7 +131,7 @@ Rhubarb Lip Sync is a command-line tool that is currently available for Windows

The following command-line options are the most common:

[cols="2,5"]
[cols="2,5a"]
|===
| Option | Description

Expand All @@ -136,7 +144,7 @@ The following command-line options are the most common:
_Default value: ``pocketSphinx``_

| `-f` _<format>_, `--exportFormat` _<format>_
| The export format. Options: `tsv` (tab-separated values, see <<tsv,details>>), `xml` (see <<xml,details>>), `json` (see <<json,details>>).
| The export format. Options: `tsv` (tab-separated values, see <<tsv,details>>), `xml` (see <<xml,details>>), `json` (see <<json,details>>), `dat` (see <<moho>>).

_Default value: ``tsv``_

Expand All @@ -161,6 +169,33 @@ _Default value: ``GHX``_

| `-h`, `--help`
| Displays usage information and exits.

| `--datFrameRate` _number_
| Only valid when using the `dat` export format. Controls the frame rate for the output file.

_Default value: 24_

| `--datUsePrestonBlair`
| Only valid when using the `dat` export format. Uses Preston Blair mouth shapes names instead of the default alphabetical ones. This applies the following mapping:

!===
! Alphabetic name ! Preston Blair name

! A ! MBP
! B ! etc
! C ! E
! D ! AI
! E ! O
! F ! U
! G ! FV
! H ! L
! X ! rest
!===

CAUTION: This mapping is only applied when exporting, _after_ the recording has been animated. To control which mouth shapes to use, use the <<extendedShapes,`extendedShapes`>> option _with the alphabetic names_.

TIP: For optimal results, make sure your mouth drawings follow the guidelines in the <<mouth-shapes>> section. This is easier if you stick to the alphabetic names instead of the Preston Blair names. The only situation where you _need_ to use the Preston Blair names is when you're using OpenToonz, because OpenToonz only supports the Preston Blair names.

|===

==== Advanced command-line options ====
Expand Down
Binary file added img/moho.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions rhubarb/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,8 @@ target_link_libraries(rhubarb-core

# ... rhubarb-exporters
add_library(rhubarb-exporters
src/exporters/DatExporter.cpp
src/exporters/DatExporter.h
src/exporters/Exporter.h
src/exporters/exporterTools.cpp
src/exporters/exporterTools.h
Expand Down
72 changes: 72 additions & 0 deletions rhubarb/src/exporters/DatExporter.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#include "DatExporter.h"
#include "animation/targetShapeSet.h"
#include <boost/lexical_cast.hpp>

using std::chrono::duration;
using std::chrono::duration_cast;
using std::string;

DatExporter::DatExporter(const ShapeSet& targetShapeSet, double frameRate, bool convertToPrestonBlair) :
frameRate(frameRate),
convertToPrestonBlair(convertToPrestonBlair),
prestonBlairShapeNames {
{ Shape::A, "MBP" },
{ Shape::B, "etc" },
{ Shape::C, "E" },
{ Shape::D, "AI" },
{ Shape::E, "O" },
{ Shape::F, "U" },
{ Shape::G, "FV" },
{ Shape::H, "L" },
{ Shape::X, "rest" },
}
{
// Animation works with a fixed frame rate of 100.
// Downsampling to much less than 25 fps may result in dropped frames.
// Upsampling to more than 100 fps doesn't make sense.
const double minFrameRate = 24.0;
const double maxFrameRate = 100.0;

if (frameRate < minFrameRate || frameRate > maxFrameRate) {
throw std::runtime_error(fmt::format("Frame rate must be between {} and {} fps.", minFrameRate, maxFrameRate));
}

if (convertToPrestonBlair) {
for (Shape shape : targetShapeSet) {
if (prestonBlairShapeNames.find(shape) == prestonBlairShapeNames.end()) {
throw std::runtime_error(fmt::format("Mouth shape {} cannot be converted to Preston Blair shape names."));
}
}
}
}

void DatExporter::exportAnimation(const ExporterInput& input, std::ostream& outputStream) {
outputStream << "MohoSwitch1" << "\n";

// Output shapes with start times
int lastFrameNumber = 0;
for (auto& timedShape : input.animation) {
const int frameNumber = toFrameNumber(timedShape.getStart());
if (frameNumber == lastFrameNumber) continue;

const string shapeName = toString(timedShape.getValue());
outputStream << frameNumber << " " << shapeName << "\n";
lastFrameNumber = frameNumber;
}

// Output closed mouth with end time
int frameNumber = toFrameNumber(input.animation.getRange().getEnd());
if (frameNumber == lastFrameNumber) ++frameNumber;
const string shapeName = toString(convertToTargetShapeSet(Shape::X, input.targetShapeSet));
outputStream << frameNumber << " " << shapeName << "\n";
}

string DatExporter::toString(Shape shape) const {
return convertToPrestonBlair
? prestonBlairShapeNames.at(shape)
: boost::lexical_cast<std::string>(shape);
}

int DatExporter::toFrameNumber(centiseconds time) const {
return 1 + static_cast<int>(frameRate * duration_cast<duration<double>>(time).count());
}
21 changes: 21 additions & 0 deletions rhubarb/src/exporters/DatExporter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#pragma once

#include "Exporter.h"
#include "core/Shape.h"
#include <map>
#include <string>

// Exporter for Moho's switch data file format
class DatExporter : public Exporter {
public:
DatExporter(const ShapeSet& targetShapeSet, double frameRate, bool convertToPrestonBlair);
void exportAnimation(const ExporterInput& input, std::ostream& outputStream) override;

private:
int toFrameNumber(centiseconds time) const;
std::string toString(Shape shape) const;

double frameRate;
bool convertToPrestonBlair;
std::map<Shape, std::string> prestonBlairShapeNames;
};
1 change: 1 addition & 0 deletions rhubarb/src/rhubarb/ExportFormat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ string ExportFormatConverter::getTypeName() {

EnumConverter<ExportFormat>::member_data ExportFormatConverter::getMemberData() {
return member_data {
{ ExportFormat::Dat, "dat" },
{ ExportFormat::Tsv, "tsv" },
{ ExportFormat::Xml, "xml" },
{ ExportFormat::Json, "json" }
Expand Down
1 change: 1 addition & 0 deletions rhubarb/src/rhubarb/ExportFormat.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "tools/EnumConverter.h"

enum class ExportFormat {
Dat,
Tsv,
Xml,
Json
Expand Down
28 changes: 26 additions & 2 deletions rhubarb/src/rhubarb/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "tools/textFiles.h"
#include "lib/rhubarbLib.h"
#include "ExportFormat.h"
#include "exporters/DatExporter.h"
#include "exporters/TsvExporter.h"
#include "exporters/XmlExporter.h"
#include "exporters/JsonExporter.h"
Expand Down Expand Up @@ -82,8 +83,15 @@ unique_ptr<Recognizer> createRecognizer(RecognizerType recognizerType) {
}
}

unique_ptr<Exporter> createExporter(ExportFormat exportFormat) {
unique_ptr<Exporter> createExporter(
ExportFormat exportFormat,
const ShapeSet& targetShapeSet,
double datFrameRate,
bool datUsePrestonBlair
) {
switch (exportFormat) {
case ExportFormat::Dat:
return make_unique<DatExporter>(targetShapeSet, datFrameRate, datUsePrestonBlair);
case ExportFormat::Tsv:
return make_unique<TsvExporter>();
case ExportFormat::Xml:
Expand Down Expand Up @@ -172,6 +180,16 @@ int main(int platformArgc, char* platformArgv[]) {
false, string(), "string", cmd
);

tclap::SwitchArg datUsePrestonBlair(
"", "datUsePrestonBlair", "Only for dat exporter: uses the Preston Blair mouth shape names.",
cmd, false
);

tclap::ValueArg<double> datFrameRate(
"", "datFrameRate", "Only for dat exporter: the desired frame rate.",
false, 24.0, "number", cmd
);

auto exportFormats = vector<ExportFormat>(ExportFormatConverter::get().getValues());
tclap::ValuesConstraint<ExportFormat> exportFormatConstraint(exportFormats);
tclap::ValueArg<ExportFormat> exportFormat(
Expand Down Expand Up @@ -222,6 +240,13 @@ int main(int platformArgc, char* platformArgv[]) {
path inputFilePath(inputFileName.getValue());
ShapeSet targetShapeSet = getTargetShapeSet(extendedShapes.getValue());

unique_ptr<Exporter> exporter = createExporter(
exportFormat.getValue(),
targetShapeSet,
datFrameRate.getValue(),
datUsePrestonBlair.getValue()
);

logging::log(StartEntry(inputFilePath));
logging::debugFormat("Command line: {}",
join(args | transformed([](string arg) { return fmt::format("\"{}\"", arg); }), " "));
Expand All @@ -246,7 +271,6 @@ int main(int platformArgc, char* platformArgv[]) {
logging::info("Done animating.");

// Export animation
unique_ptr<Exporter> exporter = createExporter(exportFormat.getValue());
optional<boost::filesystem::ofstream> outputFile;
if (outputFileName.isSet()) {
outputFile = boost::in_place(outputFileName.getValue());
Expand Down

0 comments on commit 460378b

Please sign in to comment.