- Reworked the SMPTE timecode handling with Plorkyeran's help. It does now handle dropframe timecodes as well; the ms->SMPTE handling has been tested and seems reasonably correct, while the reverse conversion remains untested and unused. The Adobe Encore export filter will now use dropframe timecodes properly (previously it would play pretend with wallclock hours/minutes/seconds and incorrect frame numbers).
- Changed the SubtitleFormat::AskForFPS dialog box; removed the "PAL/NTSC only" choice and added a "show SMPTE dropframe" parameter instead. Also added 50fps as a choice. - While I was at it, reworked the TranStation export filter so it actually looks ahead to see if the next line will overlap with the current, and if so, move the end time of the current line backwards one frame, which fixes #767 Originally committed to SVN as r2920.
This commit is contained in:
parent
7215f354b9
commit
ffa5a2021d
8 changed files with 255 additions and 110 deletions
|
@ -39,6 +39,7 @@
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include <wx/regex.h>
|
#include <wx/regex.h>
|
||||||
|
#include <math.h>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include "ass_time.h"
|
#include "ass_time.h"
|
||||||
|
@ -288,9 +289,18 @@ int AssTime::GetTimeCentiseconds() { return (time % 1000)/10; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
FractionalTime::FractionalTime (wxString separator, double numerator, double denominator) {
|
///////
|
||||||
num = numerator;
|
// Constructor
|
||||||
den = denominator;
|
FractionalTime::FractionalTime (wxString separator, int numerator, int denominator, bool dropframe) {
|
||||||
|
drop = dropframe;
|
||||||
|
if (drop) {
|
||||||
|
// no dropframe for any other framerates
|
||||||
|
num = 30000;
|
||||||
|
den = 1001;
|
||||||
|
} else {
|
||||||
|
num = numerator;
|
||||||
|
den = denominator;
|
||||||
|
}
|
||||||
sep = separator;
|
sep = separator;
|
||||||
|
|
||||||
// fractions < 1 are not welcome here
|
// fractions < 1 are not welcome here
|
||||||
|
@ -300,20 +310,23 @@ FractionalTime::FractionalTime (wxString separator, double numerator, double den
|
||||||
throw _T("FractionalTime: no separator specified");
|
throw _T("FractionalTime: no separator specified");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///////
|
||||||
|
// Destructor
|
||||||
FractionalTime::~FractionalTime () {
|
FractionalTime::~FractionalTime () {
|
||||||
sep.Clear();
|
sep.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t FractionalTime::ToMillisecs (wxString _text) {
|
///////
|
||||||
|
// SMPTE text string to milliseconds conversion
|
||||||
|
int FractionalTime::ToMillisecs (wxString _text) {
|
||||||
wxString text = _text;
|
wxString text = _text;
|
||||||
wxString re_str = _T("");
|
wxString re_str = _T("");
|
||||||
wxString sep_e = _T("\\") + sep; // escape this just in case it may be a reserved regex character
|
|
||||||
text.Trim(false);
|
text.Trim(false);
|
||||||
text.Trim(true);
|
text.Trim(true);
|
||||||
long h=0,m=0,s=0,ms=0,f=0;
|
long h=0,m=0,s=0,ms=0,f=0;
|
||||||
|
|
||||||
// hour minute second fraction
|
// hour minute second fraction
|
||||||
re_str << _T("(\\d+)") << sep_e << _T("(\\d+)") << sep_e << _T("(\\d+)") << sep_e << _T("(\\d+)");
|
re_str << _T("(\\d+)") << sep << _T("(\\d+)") << sep << _T("(\\d+)") << sep << _T("(\\d+)");
|
||||||
|
|
||||||
wxRegEx re(re_str, wxRE_ADVANCED);
|
wxRegEx re(re_str, wxRE_ADVANCED);
|
||||||
if (!re.IsValid())
|
if (!re.IsValid())
|
||||||
|
@ -321,32 +334,106 @@ int64_t FractionalTime::ToMillisecs (wxString _text) {
|
||||||
if (!re.Matches(text))
|
if (!re.Matches(text))
|
||||||
return 0; // FIXME: throw here too?
|
return 0; // FIXME: throw here too?
|
||||||
|
|
||||||
re.GetMatch(text, 1).ToLong(&h);
|
re.GetMatch(text,1).ToLong(&h);
|
||||||
re.GetMatch(text, 2).ToLong(&m);
|
re.GetMatch(text,2).ToLong(&m);
|
||||||
re.GetMatch(text, 3).ToLong(&s);
|
re.GetMatch(text,3).ToLong(&s);
|
||||||
re.GetMatch(text, 4).ToLong(&f);
|
re.GetMatch(text,4).ToLong(&f);
|
||||||
// FIXME: find out how to do this in a sane way
|
|
||||||
//if ((double)f >= ((double)num/(double)den) // overflow?
|
|
||||||
// f = (num/den - 1);
|
|
||||||
ms = long((1000.0 / (num/den)) * (double)f);
|
|
||||||
|
|
||||||
return (int64_t)((h * 3600000) + (m * 60000) + (s * 1000) + ms);
|
int msecs_f = 0;
|
||||||
|
int fn = 0;
|
||||||
|
// dropframe? do silly things
|
||||||
|
if (drop) {
|
||||||
|
fn += h * frames_per_period * 6;
|
||||||
|
fn += (m % 10) * frames_per_period;
|
||||||
|
|
||||||
|
if (m > 0) {
|
||||||
|
fn += 1800;
|
||||||
|
m--;
|
||||||
|
|
||||||
|
fn += m * 1798; // two timestamps dropped per minute after the first
|
||||||
|
fn += s * 30 + f - 2;
|
||||||
|
}
|
||||||
|
else { // minute is evenly divisible by 10, keep first two timestamps
|
||||||
|
fn += s * 30;
|
||||||
|
fn += f;
|
||||||
|
}
|
||||||
|
|
||||||
|
msecs_f = (fn * num) / den;
|
||||||
|
}
|
||||||
|
// no dropframe, may or may not sync with wallclock time
|
||||||
|
// (see comment in FromMillisecs for an explanation of why it's done like this)
|
||||||
|
else {
|
||||||
|
int fps_approx = floor((double(num)/double(den))+0.5);
|
||||||
|
fn += h * 3600 * fps_approx;
|
||||||
|
fn += m * 60 * fps_approx;
|
||||||
|
fn += s * fps_approx;
|
||||||
|
fn += f;
|
||||||
|
|
||||||
|
msecs_f = (fn * num) / den;
|
||||||
|
}
|
||||||
|
|
||||||
|
return msecs_f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///////
|
||||||
|
// SMPTE text string to AssTime conversion
|
||||||
AssTime FractionalTime::ToAssTime (wxString _text) {
|
AssTime FractionalTime::ToAssTime (wxString _text) {
|
||||||
AssTime time;
|
AssTime time;
|
||||||
time.SetMS((int)ToMillisecs(_text));
|
time.SetMS((int)ToMillisecs(_text));
|
||||||
return time;
|
return time;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///////
|
||||||
|
// AssTime to SMPTE text string conversion
|
||||||
wxString FractionalTime::FromAssTime(AssTime time) {
|
wxString FractionalTime::FromAssTime(AssTime time) {
|
||||||
return FromMillisecs((int64_t)time.GetMS());
|
return FromMillisecs(time.GetMS());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///////
|
||||||
|
// Milliseconds to SMPTE text string conversion
|
||||||
wxString FractionalTime::FromMillisecs(int64_t msec) {
|
wxString FractionalTime::FromMillisecs(int64_t msec) {
|
||||||
int h = msec / 3600000;
|
int h=0, m=0, s=0, f=0; // hours, minutes, seconds, fractions
|
||||||
int m = (msec % 3600000)/60000;
|
int fn = (msec*(int64_t)num) / (1000*den); // frame number
|
||||||
int s = (msec % 60000)/1000;
|
|
||||||
int f = int((msec % 1000) * ((num/den) / 1000.0));
|
// dropframe?
|
||||||
|
if (drop) {
|
||||||
|
fn += 2 * (fn / (30 * 60)) - 2 * (fn / (30 * 60 * 10));
|
||||||
|
h = fn / (30 * 60 * 60);
|
||||||
|
m = (fn / (30 * 60)) % 60;
|
||||||
|
s = (fn / 30) % 60;
|
||||||
|
f = fn % 30;
|
||||||
|
}
|
||||||
|
// no dropframe; h/m/s may or may not sync to wallclock time
|
||||||
|
else {
|
||||||
|
/*
|
||||||
|
This is truly the dumbest shit. What we're trying to ensure here
|
||||||
|
is that non-integer framerates are desynced from the wallclock
|
||||||
|
time by a correct amount of time. For example, in the
|
||||||
|
NTSC-without-dropframe case, 3600*num/den would be 107892
|
||||||
|
(when truncated to int), which is quite a good approximation of
|
||||||
|
how a long an hour is when counted in 30000/1001 frames per second.
|
||||||
|
Unfortunately, that's not what we want, since frame numbers will
|
||||||
|
still range from 00 to 29, meaning that we're really getting _30_
|
||||||
|
frames per second and not 29.97 and the full hour will be off by
|
||||||
|
almost 4 seconds (108000 frames versus 107892).
|
||||||
|
|
||||||
|
DEATH TO SMPTE
|
||||||
|
*/
|
||||||
|
int fps_approx = floor((double(num)/double(den))+0.5);
|
||||||
|
int frames_per_h = 3600*fps_approx;
|
||||||
|
int frames_per_m = 60*fps_approx;
|
||||||
|
int frames_per_s = fps_approx;
|
||||||
|
while (fn >= frames_per_h) {
|
||||||
|
h++; fn -= frames_per_h;
|
||||||
|
}
|
||||||
|
while (fn >= frames_per_m) {
|
||||||
|
m++; fn -= frames_per_m;
|
||||||
|
}
|
||||||
|
while (fn >= frames_per_s) {
|
||||||
|
s++; fn -= frames_per_s;
|
||||||
|
}
|
||||||
|
f = fn;
|
||||||
|
}
|
||||||
|
|
||||||
return wxString::Format(_T("%02i") + sep + _T("%02i") + sep + _T("%02i") + sep + _T("%02i"),h,m,s,f);
|
return wxString::Format(_T("%02i") + sep + _T("%02i") + sep + _T("%02i") + sep + _T("%02i"),h,m,s,f);
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,21 +78,28 @@ bool operator <= (AssTime &t1, AssTime &t2);
|
||||||
bool operator >= (AssTime &t1, AssTime &t2);
|
bool operator >= (AssTime &t1, AssTime &t2);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/////////////////////////////
|
/////////////////////////////
|
||||||
// Class for that annoying SMPTE format timecodes stuff
|
// Class for that annoying SMPTE format timecodes stuff
|
||||||
class FractionalTime {
|
class FractionalTime {
|
||||||
private:
|
private:
|
||||||
int64_t time; // milliseconds, like in AssTime
|
int time; // milliseconds, like in AssTime
|
||||||
double num, den; // numerator/denominator
|
int num, den; // numerator/denominator
|
||||||
wxString sep; // separator; someone might have separators of more than one character :V
|
bool drop; // EVIL
|
||||||
|
wxString sep; // separator; someone might have separators of more than one character :V
|
||||||
|
|
||||||
|
// A period is roughly 10 minutes and is used for the dropframe stuff;
|
||||||
|
// SMPTE dropframe timecodes drops 18 timestamps per 18000, hence the number 17982.
|
||||||
|
static const int frames_per_period = 17982;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// dumb assumption? I give no fuck
|
// dumb assumption? I give no fuck
|
||||||
FractionalTime(wxString separator, double numerator=30.0, double denominator=1.0);
|
// NOTE: separator can be a regex! at least if you only plan on doing SMPTE->somethingelse.
|
||||||
|
FractionalTime(wxString separator, int numerator=30, int denominator=1, bool dropframe=false);
|
||||||
~FractionalTime();
|
~FractionalTime();
|
||||||
|
|
||||||
AssTime ToAssTime(wxString fractime);
|
AssTime ToAssTime(wxString fractime);
|
||||||
int64_t ToMillisecs(wxString fractime);
|
int ToMillisecs(wxString fractime);
|
||||||
|
|
||||||
wxString FromAssTime(AssTime time);
|
wxString FromAssTime(AssTime time);
|
||||||
wxString FromMillisecs(int64_t msec);
|
wxString FromMillisecs(int64_t msec);
|
||||||
|
|
|
@ -279,11 +279,13 @@ wxString SubtitleFormat::GetWildcards(int mode) {
|
||||||
|
|
||||||
/////////////////////////////////
|
/////////////////////////////////
|
||||||
// Ask the user to enter the FPS
|
// Ask the user to enter the FPS
|
||||||
double SubtitleFormat::AskForFPS(bool palNtscOnly) {
|
SubtitleFormat::FPSRational SubtitleFormat::AskForFPS(bool showSMPTE) {
|
||||||
wxArrayString choices;
|
wxArrayString choices;
|
||||||
|
FPSRational fps_rat;
|
||||||
|
fps_rat.smpte_dropframe = false; // ensure it's false by default
|
||||||
|
|
||||||
// Video FPS
|
// Video FPS
|
||||||
bool vidLoaded = !palNtscOnly && VFR_Output.IsLoaded();
|
bool vidLoaded = VFR_Output.IsLoaded();
|
||||||
if (vidLoaded) {
|
if (vidLoaded) {
|
||||||
wxString vidFPS;
|
wxString vidFPS;
|
||||||
if (VFR_Output.GetFrameRateType() == VFR) vidFPS = _T("VFR");
|
if (VFR_Output.GetFrameRateType() == VFR) vidFPS = _T("VFR");
|
||||||
|
@ -292,49 +294,73 @@ double SubtitleFormat::AskForFPS(bool palNtscOnly) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Standard FPS values
|
// Standard FPS values
|
||||||
if (!palNtscOnly) {
|
choices.Add(_("15.000 FPS"));
|
||||||
choices.Add(_("15.000 FPS"));
|
choices.Add(_("23.976 FPS (Decimated NTSC)"));
|
||||||
choices.Add(_("23.976 FPS (Decimated NTSC)"));
|
choices.Add(_("24.000 FPS (FILM)"));
|
||||||
choices.Add(_("24.000 FPS (FILM)"));
|
|
||||||
}
|
|
||||||
choices.Add(_("25.000 FPS (PAL)"));
|
choices.Add(_("25.000 FPS (PAL)"));
|
||||||
choices.Add(_("29.970 FPS (NTSC)"));
|
choices.Add(_("29.970 FPS (NTSC)"));
|
||||||
if (!palNtscOnly) {
|
if (showSMPTE)
|
||||||
choices.Add(_("30.000 FPS"));
|
choices.Add(_("29.970 FPS (NTSC with SMPTE dropframe)"));
|
||||||
choices.Add(_("59.940 FPS (NTSC x2)"));
|
choices.Add(_("30.000 FPS"));
|
||||||
choices.Add(_("60.000 FPS"));
|
choices.Add(_("50.000 FPS (PAL x2)"));
|
||||||
choices.Add(_("119.880 FPS (NTSC x4)"));
|
choices.Add(_("59.940 FPS (NTSC x2)"));
|
||||||
choices.Add(_("120.000 FPS"));
|
choices.Add(_("60.000 FPS"));
|
||||||
}
|
choices.Add(_("119.880 FPS (NTSC x4)"));
|
||||||
|
choices.Add(_("120.000 FPS"));
|
||||||
|
|
||||||
// Ask
|
// Ask
|
||||||
int choice = wxGetSingleChoiceIndex(_("Please choose the appropriate FPS for the subtitles:"),_("FPS"),choices);
|
int choice = wxGetSingleChoiceIndex(_("Please choose the appropriate FPS for the subtitles:"),_("FPS"),choices);
|
||||||
if (choice == -1) return 0.0;
|
if (choice == -1) {
|
||||||
|
fps_rat.num = 0;
|
||||||
|
fps_rat.den = 0;
|
||||||
|
|
||||||
// PAL/NTSC choice
|
return fps_rat;
|
||||||
if (palNtscOnly) {
|
|
||||||
if (choice == 0) return 25.0;
|
|
||||||
else return 30.0 / 1.001;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get FPS from choice
|
// Get FPS from choice
|
||||||
if (vidLoaded) choice--;
|
if (vidLoaded) choice--;
|
||||||
switch (choice) {
|
// dropframe was displayed, that means all choices >4 are bumped up by 1
|
||||||
case -1: return -1.0; break; // VIDEO
|
if (showSMPTE) {
|
||||||
case 0: return 15.0; break;
|
switch (choice) {
|
||||||
case 1: return 24.0 / 1.001; break;
|
case -1: fps_rat.num = -1; fps_rat.den = 1; break; // VIDEO
|
||||||
case 2: return 24.0; break;
|
case 0: fps_rat.num = 15; fps_rat.den = 1; break;
|
||||||
case 3: return 25.0; break;
|
case 1: fps_rat.num = 24000; fps_rat.den = 1001; break;
|
||||||
case 4: return 30.0 / 1.001; break;
|
case 2: fps_rat.num = 24; fps_rat.den = 1; break;
|
||||||
case 5: return 30.0; break;
|
case 3: fps_rat.num = 25; fps_rat.den = 1; break;
|
||||||
case 6: return 60.0 / 1.001; break;
|
case 4: fps_rat.num = 30000; fps_rat.den = 1001; break;
|
||||||
case 7: return 60.0; break;
|
case 5: fps_rat.num = 30000; fps_rat.den = 1001; fps_rat.smpte_dropframe = true; break;
|
||||||
case 8: return 120.0 / 1.001; break;
|
case 6: fps_rat.num = 30; fps_rat.den = 1; break;
|
||||||
case 9: return 120.0; break;
|
case 7: fps_rat.num = 50; fps_rat.den = 1; break;
|
||||||
|
case 8: fps_rat.num = 60000; fps_rat.den = 1001; break;
|
||||||
|
case 9: fps_rat.num = 60; fps_rat.den = 1; break;
|
||||||
|
case 10: fps_rat.num = 120000; fps_rat.den = 1001; break;
|
||||||
|
case 11: fps_rat.num = 120; fps_rat.den = 1; break;
|
||||||
|
}
|
||||||
|
return fps_rat;
|
||||||
|
} else {
|
||||||
|
// dropframe wasn't displayed
|
||||||
|
switch (choice) {
|
||||||
|
case -1: fps_rat.num = -1; fps_rat.den = 1; break; // VIDEO
|
||||||
|
case 0: fps_rat.num = 15; fps_rat.den = 1; break;
|
||||||
|
case 1: fps_rat.num = 24000; fps_rat.den = 1001; break;
|
||||||
|
case 2: fps_rat.num = 24; fps_rat.den = 1; break;
|
||||||
|
case 3: fps_rat.num = 25; fps_rat.den = 1; break;
|
||||||
|
case 4: fps_rat.num = 30000; fps_rat.den = 1001; break;
|
||||||
|
case 5: fps_rat.num = 30; fps_rat.den = 1; break;
|
||||||
|
case 6: fps_rat.num = 50; fps_rat.den = 1; break;
|
||||||
|
case 7: fps_rat.num = 60000; fps_rat.den = 1001; break;
|
||||||
|
case 8: fps_rat.num = 60; fps_rat.den = 1; break;
|
||||||
|
case 9: fps_rat.num = 120000; fps_rat.den = 1001; break;
|
||||||
|
case 10: fps_rat.num = 120; fps_rat.den = 1; break;
|
||||||
|
}
|
||||||
|
return fps_rat;
|
||||||
}
|
}
|
||||||
|
|
||||||
// fubar
|
// fubar
|
||||||
return 0.0;
|
fps_rat.num = 0;
|
||||||
|
fps_rat.den = 0;
|
||||||
|
|
||||||
|
return fps_rat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -65,6 +65,12 @@ private:
|
||||||
static bool loaded;
|
static bool loaded;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
struct FPSRational {
|
||||||
|
int num;
|
||||||
|
int den;
|
||||||
|
bool smpte_dropframe;
|
||||||
|
};
|
||||||
|
|
||||||
std::list<AssEntry*> *Line;
|
std::list<AssEntry*> *Line;
|
||||||
|
|
||||||
void CreateCopy();
|
void CreateCopy();
|
||||||
|
@ -81,7 +87,7 @@ protected:
|
||||||
void LoadDefault(bool defline=true);
|
void LoadDefault(bool defline=true);
|
||||||
AssFile *GetAssFile() { return assFile; }
|
AssFile *GetAssFile() { return assFile; }
|
||||||
int AddLine(wxString data,wxString group,int lasttime,int &version,wxString *outgroup=NULL);
|
int AddLine(wxString data,wxString group,int lasttime,int &version,wxString *outgroup=NULL);
|
||||||
double AskForFPS(bool palNtscOnly=false);
|
FPSRational AskForFPS(bool showSMPTE=false);
|
||||||
|
|
||||||
virtual wxString GetName()=0;
|
virtual wxString GetName()=0;
|
||||||
virtual wxArrayString GetReadWildcards();
|
virtual wxArrayString GetReadWildcards();
|
||||||
|
|
|
@ -70,8 +70,8 @@ bool EncoreSubtitleFormat::CanWriteFile(wxString filename) {
|
||||||
// Write file
|
// Write file
|
||||||
void EncoreSubtitleFormat::WriteFile(wxString _filename,wxString encoding) {
|
void EncoreSubtitleFormat::WriteFile(wxString _filename,wxString encoding) {
|
||||||
// Get FPS
|
// Get FPS
|
||||||
double fps = AskForFPS(true);
|
FPSRational fps_rat = AskForFPS(true);
|
||||||
if (fps <= 0.0) return;
|
if (fps_rat.num <= 0 || fps_rat.den <= 0) return;
|
||||||
|
|
||||||
// Open file
|
// Open file
|
||||||
TextFileWriter file(_filename,encoding);
|
TextFileWriter file(_filename,encoding);
|
||||||
|
@ -88,14 +88,14 @@ void EncoreSubtitleFormat::WriteFile(wxString _filename,wxString encoding) {
|
||||||
using std::list;
|
using std::list;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
// Encore wants ; instead of : if we're dealing with NTSC
|
// Encore wants ; instead of : if we're dealing with NTSC dropframe stuff
|
||||||
FractionalTime fp(fps > 26.0 ? _T(";") : _T(":"), fps);
|
FractionalTime ft(fps_rat.smpte_dropframe ? _T(";") : _T(":"), fps_rat.num, fps_rat.den, fps_rat.smpte_dropframe);
|
||||||
|
|
||||||
for (list<AssEntry*>::iterator cur=Line->begin();cur!=Line->end();cur++) {
|
for (list<AssEntry*>::iterator cur=Line->begin();cur!=Line->end();cur++) {
|
||||||
AssDialogue *current = AssEntry::GetAsDialogue(*cur);
|
AssDialogue *current = AssEntry::GetAsDialogue(*cur);
|
||||||
if (current && !current->Comment) {
|
if (current && !current->Comment) {
|
||||||
// Time stamps
|
// Time stamps
|
||||||
wxString timeStamps = wxString::Format(_T("%i "),++i) + fp.FromAssTime(current->Start) + _T(" ") + fp.FromAssTime(current->End);
|
wxString timeStamps = wxString::Format(_T("%i "),++i) + ft.FromAssTime(current->Start) + _T(" ") + ft.FromAssTime(current->End);
|
||||||
|
|
||||||
// Write
|
// Write
|
||||||
file.WriteLineToFile(timeStamps + current->Text);
|
file.WriteLineToFile(timeStamps + current->Text);
|
||||||
|
|
|
@ -110,6 +110,7 @@ void MicroDVDSubtitleFormat::ReadFile(wxString filename,wxString forceEncoding)
|
||||||
|
|
||||||
// Loop
|
// Loop
|
||||||
bool isFirst = true;
|
bool isFirst = true;
|
||||||
|
FPSRational fps_rat;
|
||||||
double fps = 0.0;
|
double fps = 0.0;
|
||||||
while (file.HasMoreLines()) {
|
while (file.HasMoreLines()) {
|
||||||
wxString line = file.ReadLineFromFile();
|
wxString line = file.ReadLineFromFile();
|
||||||
|
@ -133,9 +134,9 @@ void MicroDVDSubtitleFormat::ReadFile(wxString filename,wxString forceEncoding)
|
||||||
|
|
||||||
// If it wasn't an fps line, ask the user for it
|
// If it wasn't an fps line, ask the user for it
|
||||||
if (fps <= 0.0) {
|
if (fps <= 0.0) {
|
||||||
fps = AskForFPS();
|
fps_rat = AskForFPS();
|
||||||
if (fps == 0.0) return;
|
if (fps_rat.num == 0) return;
|
||||||
else if (fps > 0.0) cfr.SetCFR(fps);
|
else if (fps_rat.num > 0) cfr.SetCFR(double(fps_rat.num)/double(fps_rat.den));
|
||||||
else rate = &VFR_Output;
|
else rate = &VFR_Output;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -172,9 +173,10 @@ void MicroDVDSubtitleFormat::WriteFile(wxString filename,wxString encoding) {
|
||||||
// Set FPS
|
// Set FPS
|
||||||
FrameRate cfr;
|
FrameRate cfr;
|
||||||
FrameRate *rate = 𝔠
|
FrameRate *rate = 𝔠
|
||||||
double fps = AskForFPS();
|
FPSRational fps_rat = AskForFPS();
|
||||||
if (fps == 0.0) return;
|
if (fps_rat.num == 0 || fps_rat.den == 0) return;
|
||||||
else if (fps > 0.0) cfr.SetCFR(fps);
|
double fps = double(fps_rat.num) / double(fps_rat.den);
|
||||||
|
if (fps > 0.0) cfr.SetCFR(fps);
|
||||||
else rate = &VFR_Output;
|
else rate = &VFR_Output;
|
||||||
|
|
||||||
// Convert file
|
// Convert file
|
||||||
|
|
|
@ -74,8 +74,8 @@ bool TranStationSubtitleFormat::CanWriteFile(wxString filename) {
|
||||||
// Write file
|
// Write file
|
||||||
void TranStationSubtitleFormat::WriteFile(wxString _filename,wxString encoding) {
|
void TranStationSubtitleFormat::WriteFile(wxString _filename,wxString encoding) {
|
||||||
// Get FPS
|
// Get FPS
|
||||||
double fps = AskForFPS(true);
|
FPSRational fps_rat = AskForFPS(true);
|
||||||
if (fps <= 0.0) return;
|
if (fps_rat.num <= 0 || fps_rat.den <= 0) return;
|
||||||
|
|
||||||
// Open file
|
// Open file
|
||||||
TextFileWriter file(_filename,encoding);
|
TextFileWriter file(_filename,encoding);
|
||||||
|
@ -89,50 +89,22 @@ void TranStationSubtitleFormat::WriteFile(wxString _filename,wxString encoding)
|
||||||
|
|
||||||
// Write lines
|
// Write lines
|
||||||
using std::list;
|
using std::list;
|
||||||
|
AssDialogue *current = NULL;
|
||||||
|
AssDialogue *next = NULL;
|
||||||
for (list<AssEntry*>::iterator cur=Line->begin();cur!=Line->end();cur++) {
|
for (list<AssEntry*>::iterator cur=Line->begin();cur!=Line->end();cur++) {
|
||||||
AssDialogue *current = AssEntry::GetAsDialogue(*cur);
|
if (next)
|
||||||
|
current = next;
|
||||||
|
next = AssEntry::GetAsDialogue(*cur);
|
||||||
|
|
||||||
if (current && !current->Comment) {
|
if (current && !current->Comment) {
|
||||||
// Get line data
|
|
||||||
AssStyle *style = GetAssFile()->GetStyle(current->Style);
|
|
||||||
int valign = 0;
|
|
||||||
wxChar *halign = _T(" "); // default is centered
|
|
||||||
wxChar *type = _T("N"); // no special style
|
|
||||||
if (style) {
|
|
||||||
if (style->alignment >= 4) valign = 4;
|
|
||||||
if (style->alignment >= 7) valign = 9;
|
|
||||||
if (style->alignment == 1 || style->alignment == 4 || style->alignment == 7) halign = _T("L");
|
|
||||||
if (style->alignment == 3 || style->alignment == 6 || style->alignment == 9) halign = _T("R");
|
|
||||||
if (style->italic) type = _T("I");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hack: If an italics-tag (\i1) appears anywhere in the line,
|
|
||||||
// make it all italics
|
|
||||||
if (current->Text.Find(_T("\\i1")) != wxNOT_FOUND)type = _T("I");
|
|
||||||
|
|
||||||
// Write header
|
|
||||||
AssTime start = current->Start;
|
|
||||||
AssTime end = current->End;
|
|
||||||
// Subtract half a frame duration from end time, since it is inclusive
|
|
||||||
// and we otherwise run the risk of having two lines overlap in a
|
|
||||||
// frame, when they should run right into each other.
|
|
||||||
end.SetMS(end.GetMS() - (int)(500.0/fps));
|
|
||||||
FractionalTime ft(_T(":"),fps);
|
|
||||||
wxString header = wxString::Format(_T("SUB[%i%s%s "),valign,halign,type) + ft.FromAssTime(start) + _T(">") + ft.FromAssTime(end) + _T("]");
|
|
||||||
file.WriteLineToFile(header);
|
|
||||||
|
|
||||||
// Process text
|
|
||||||
wxString lineEnd = _T("\r\n");
|
|
||||||
current->StripTags();
|
|
||||||
current->Text.Replace(_T("\\h"),_T(" "),true);
|
|
||||||
current->Text.Replace(_T("\\n"),lineEnd,true);
|
|
||||||
current->Text.Replace(_T("\\N"),lineEnd,true);
|
|
||||||
while (current->Text.Replace(lineEnd+lineEnd,lineEnd,true));
|
|
||||||
|
|
||||||
// Write text
|
// Write text
|
||||||
file.WriteLineToFile(current->Text);
|
file.WriteLineToFile(ConvertLine(current,&fps_rat,(next && !next->Comment) ? next->Start.GetMS() : -1));
|
||||||
file.WriteLineToFile(_T(""));
|
file.WriteLineToFile(_T(""));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// flush last line
|
||||||
|
if (next && !next->Comment)
|
||||||
|
file.WriteLineToFile(ConvertLine(next,&fps_rat,-1));
|
||||||
|
|
||||||
// Every file must end with this line
|
// Every file must end with this line
|
||||||
file.WriteLineToFile(_T("SUB["));
|
file.WriteLineToFile(_T("SUB["));
|
||||||
|
@ -140,3 +112,45 @@ void TranStationSubtitleFormat::WriteFile(wxString _filename,wxString encoding)
|
||||||
// Clean up
|
// Clean up
|
||||||
ClearCopy();
|
ClearCopy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wxString TranStationSubtitleFormat::ConvertLine(AssDialogue *current, FPSRational *fps_rat, int nextl_start) {
|
||||||
|
// Get line data
|
||||||
|
AssStyle *style = GetAssFile()->GetStyle(current->Style);
|
||||||
|
int valign = 0;
|
||||||
|
wxChar *halign = _T(" "); // default is centered
|
||||||
|
wxChar *type = _T("N"); // no special style
|
||||||
|
if (style) {
|
||||||
|
if (style->alignment >= 4) valign = 4;
|
||||||
|
if (style->alignment >= 7) valign = 9;
|
||||||
|
if (style->alignment == 1 || style->alignment == 4 || style->alignment == 7) halign = _T("L");
|
||||||
|
if (style->alignment == 3 || style->alignment == 6 || style->alignment == 9) halign = _T("R");
|
||||||
|
if (style->italic) type = _T("I");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hack: If an italics-tag (\i1) appears anywhere in the line,
|
||||||
|
// make it all italics
|
||||||
|
if (current->Text.Find(_T("\\i1")) != wxNOT_FOUND)type = _T("I");
|
||||||
|
|
||||||
|
// Write header
|
||||||
|
AssTime start = current->Start;
|
||||||
|
AssTime end = current->End;
|
||||||
|
|
||||||
|
// Subtract one frame if the end time of the current line is equal to the
|
||||||
|
// start of next one, since the end timestamp is inclusive and the lines
|
||||||
|
// would overlap if left as is.
|
||||||
|
if (nextl_start > 0 && end.GetMS() == nextl_start)
|
||||||
|
end.SetMS(end.GetMS() - ((1000*fps_rat->den)/fps_rat->num));
|
||||||
|
|
||||||
|
FractionalTime ft(_T(":"), fps_rat->num, fps_rat->den, fps_rat->smpte_dropframe);
|
||||||
|
wxString header = wxString::Format(_T("SUB[%i%s%s "),valign,halign,type) + ft.FromAssTime(start) + _T(">") + ft.FromAssTime(end) + _T("]\r\n");
|
||||||
|
|
||||||
|
// Process text
|
||||||
|
wxString lineEnd = _T("\r\n");
|
||||||
|
current->StripTags();
|
||||||
|
current->Text.Replace(_T("\\h"),_T(" "),true);
|
||||||
|
current->Text.Replace(_T("\\n"),lineEnd,true);
|
||||||
|
current->Text.Replace(_T("\\N"),lineEnd,true);
|
||||||
|
while (current->Text.Replace(lineEnd+lineEnd,lineEnd,true));
|
||||||
|
|
||||||
|
return header + current->Text;
|
||||||
|
}
|
|
@ -45,6 +45,9 @@
|
||||||
//////////////////////
|
//////////////////////
|
||||||
// TranStation writer
|
// TranStation writer
|
||||||
class TranStationSubtitleFormat : public SubtitleFormat {
|
class TranStationSubtitleFormat : public SubtitleFormat {
|
||||||
|
private:
|
||||||
|
wxString ConvertLine(AssDialogue *line, FPSRational *fps_rat, int nextl_start);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
wxString GetName();
|
wxString GetName();
|
||||||
wxArrayString GetWriteWildcards();
|
wxArrayString GetWriteWildcards();
|
||||||
|
|
Loading…
Reference in a new issue