From ebcdf0ce463b56bf4f8527d49fa0cf20c6246e57 Mon Sep 17 00:00:00 2001 From: Karl Blomster Date: Sun, 10 May 2009 03:50:58 +0000 Subject: [PATCH] Move the SMPTE timecode stuff out of the AssTime class and put it in a class of its own, FractionalTime. The AssTime to SMPTE part is tested and verified to work (in fact it looks like I unintentionally fixed an overlap bug in the TranStation export filter), while the SMPTE to AssTime part is completely untested (but it's currently not used anywhere so it's not like it matters). Originally committed to SVN as r2909. --- aegisub/src/ass_time.cpp | 76 ++++++++++++++++++--- aegisub/src/ass_time.h | 23 ++++++- aegisub/src/subtitle_format_encore.cpp | 9 +-- aegisub/src/subtitle_format_transtation.cpp | 4 +- 4 files changed, 97 insertions(+), 15 deletions(-) diff --git a/aegisub/src/ass_time.cpp b/aegisub/src/ass_time.cpp index c178e6112..17905c426 100644 --- a/aegisub/src/ass_time.cpp +++ b/aegisub/src/ass_time.cpp @@ -38,6 +38,7 @@ // Includes #include "config.h" +#include #include #include #include "ass_time.h" @@ -244,15 +245,6 @@ wxString AssTime::GetSRTFormated () { } -/////////////////// -// SMPTE formatted -wxString AssTime::GetSMPTE(double fps) -{ - int f = int(GetTimeMiliseconds() * fps / 1000.0); - return wxString::Format(_T("%02i:%02i:%02i:%02i"),GetTimeHours(),GetTimeMinutes(),GetTimeSeconds(),f); -} - - ////////////////////// // AssTime comparison bool operator < (AssTime &t1, AssTime &t2) { @@ -292,3 +284,69 @@ int AssTime::GetTimeMinutes() { return (time % 3600000)/60000; } int AssTime::GetTimeSeconds() { return (time % 60000)/1000; } int AssTime::GetTimeMiliseconds() { return (time % 1000); } int AssTime::GetTimeCentiseconds() { return (time % 1000)/10; } + + + + +FractionalTime::FractionalTime (wxString separator, double numerator, double denominator) { + num = numerator; + den = denominator; + sep = separator; + + // fractions < 1 are not welcome here + if ((num <= 0 || den <= 0) || (num < den)) + throw _T("FractionalTime: nonsensical enumerator or denominator"); + if (sep.IsEmpty()) + throw _T("FractionalTime: no separator specified"); +} + +FractionalTime::~FractionalTime () { + sep.Clear(); +} + +int64_t FractionalTime::ToMillisecs (wxString _text) { + wxString text = _text; + 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(true); + long h=0,m=0,s=0,ms=0,f=0; + + // hour minute second fraction + re_str << _T("(\\d+)") << sep_e << _T("(\\d+)") << sep_e << _T("(\\d+)") << sep_e << _T("(\\d+)"); + + wxRegEx re(re_str, wxRE_ADVANCED); + if (!re.IsValid()) + throw _T("FractionalTime: regex failure"); + if (!re.Matches(text)) + return 0; // FIXME: throw here too? + + re.GetMatch(text, 1).ToLong(&h); + re.GetMatch(text, 2).ToLong(&m); + re.GetMatch(text, 3).ToLong(&s); + 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); +} + +AssTime FractionalTime::ToAssTime (wxString _text) { + AssTime time; + time.SetMS((int)ToMillisecs(_text)); + return time; +} + +wxString FractionalTime::FromAssTime(AssTime time) { + return FromMillisecs((int64_t)time.GetMS()); +} + +wxString FractionalTime::FromMillisecs(int64_t msec) { + int h = msec / 3600000; + int m = (msec % 3600000)/60000; + int s = (msec % 60000)/1000; + int f = int((msec % 1000) * ((num/den) / 1000.0)); + return wxString::Format(_T("%02i") + sep + _T("%02i") + sep + _T("%02i") + sep + _T("%02i"),h,m,s,f); +} diff --git a/aegisub/src/ass_time.h b/aegisub/src/ass_time.h index 9f44d1f8d..7e39d7baf 100644 --- a/aegisub/src/ass_time.h +++ b/aegisub/src/ass_time.h @@ -41,6 +41,7 @@ // Headers #include #include +#include ///////////////////////////// @@ -66,7 +67,6 @@ public: void ParseSRT(const wxString text); // Sets value to text-form time, in SRT format wxString GetASSFormated(bool ms=false); // Returns the ASS representation of time wxString GetSRTFormated(); // Returns the SRT representation of time - wxString GetSMPTE(double fps); // Returns the SMPTE timecodes for the given frame rate }; // Comparison operators @@ -76,3 +76,24 @@ bool operator < (AssTime &t1, AssTime &t2); 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 FractionalTime { +private: + int64_t time; // milliseconds, like in AssTime + double num, den; // numerator/denominator + wxString sep; // separator; someone might have separators of more than one character :V + +public: + // dumb assumption? I give no fuck + FractionalTime(wxString separator, double numerator=30.0, double denominator=1.0); + ~FractionalTime(); + + AssTime ToAssTime(wxString fractime); + int64_t ToMillisecs(wxString fractime); + + wxString FromAssTime(AssTime time); + wxString FromMillisecs(int64_t msec); +}; diff --git a/aegisub/src/subtitle_format_encore.cpp b/aegisub/src/subtitle_format_encore.cpp index 9386c4a39..6e97db695 100644 --- a/aegisub/src/subtitle_format_encore.cpp +++ b/aegisub/src/subtitle_format_encore.cpp @@ -87,14 +87,15 @@ void EncoreSubtitleFormat::WriteFile(wxString _filename,wxString encoding) { // Write lines using std::list; int i = 0; + + // Encore wants ; instead of : if we're dealing with NTSC + FractionalTime fp(fps > 26.0 ? _T(";") : _T(":"), fps); + for (list::iterator cur=Line->begin();cur!=Line->end();cur++) { AssDialogue *current = AssEntry::GetAsDialogue(*cur); if (current && !current->Comment) { // Time stamps - wxString timeStamps = wxString::Format(_T("%i "),++i) + current->Start.GetSMPTE(fps) + _T(" ") + current->End.GetSMPTE(fps); - - // Convert : to ; if it's NTSC - if (fps > 26.0) timeStamps.Replace(_T(":"),_T(";")); + wxString timeStamps = wxString::Format(_T("%i "),++i) + fp.FromAssTime(current->Start) + _T(" ") + fp.FromAssTime(current->End); // Write file.WriteLineToFile(timeStamps + current->Text); diff --git a/aegisub/src/subtitle_format_transtation.cpp b/aegisub/src/subtitle_format_transtation.cpp index bfd0dafd0..0fa9549a4 100644 --- a/aegisub/src/subtitle_format_transtation.cpp +++ b/aegisub/src/subtitle_format_transtation.cpp @@ -41,6 +41,7 @@ #include "ass_dialogue.h" #include "ass_file.h" #include "ass_style.h" +#include "ass_time.h" #include "subtitle_format_transtation.h" #include "text_file_writer.h" #include @@ -115,7 +116,8 @@ void TranStationSubtitleFormat::WriteFile(wxString _filename,wxString encoding) // 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)); - wxString header = wxString::Format(_T("SUB[%i%s%s "),valign,halign,type) + start.GetSMPTE(fps) + _T(">") + end.GetSMPTE(fps) + _T("]"); + 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