From 1dedfb18cd62f006c39f76ce298f4a3157607271 Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Thu, 23 Sep 2010 03:06:22 +0000 Subject: [PATCH] Shift timecodes so that frame 0 always starts at time 0, as nothing related to audio supports non-zero start times Originally committed to SVN as r4791. --- aegisub/libaegisub/common/vfr.cpp | 20 +++++++++++++++----- aegisub/tests/libaegisub_vfr.cpp | 18 ++++++++++++++++++ 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/aegisub/libaegisub/common/vfr.cpp b/aegisub/libaegisub/common/vfr.cpp index 43edb5408..d892204e0 100644 --- a/aegisub/libaegisub/common/vfr.cpp +++ b/aegisub/libaegisub/common/vfr.cpp @@ -58,6 +58,14 @@ static void validate_timecodes(std::vector const& timecodes) { std::accumulate(timecodes.begin()+1, timecodes.end(), timecodes.front(), is_increasing); } +/// @brief Shift timecodes so that frame 0 starts at time 0 +/// @param timecodes List of timecodes to normalize +static void normalize_timecodes(std::vector &timecodes) { + if (int front = timecodes.front()) { + std::transform(timecodes.begin(), timecodes.end(), timecodes.begin(), std::bind2nd(std::minus(), front)); + } +} + // A "start,end,fps" line in a v1 timecode file struct TimecodeRange { int start; @@ -183,7 +191,8 @@ Framerate::Framerate(std::vector const& timecodes) : timecodes(timecodes) { validate_timecodes(timecodes); - fps = (timecodes.size() - 1) * 1000. / (timecodes.back() - timecodes.front()); + normalize_timecodes(this->timecodes); + fps = (timecodes.size() - 1) * 1000. / timecodes.back(); last = timecodes.back(); } @@ -212,7 +221,8 @@ Framerate::Framerate(std::string const& filename) : fps(0.) { if (line == "# timecode format v2") { copy(line_iterator(*file, encoding), line_iterator(), back_inserter(timecodes)); validate_timecodes(timecodes); - fps = (timecodes.size() - 1) * 1000. / (timecodes.back() - timecodes.front()); + normalize_timecodes(timecodes); + fps = (timecodes.size() - 1) * 1000. / timecodes.back(); last = timecodes.back(); return; } @@ -266,8 +276,8 @@ int Framerate::FrameAtTime(int ms, Time type) const { if (timecodes.empty()) { return (int)floor(ms * fps / 1000.); } - if (ms < timecodes.front()) { - return (int)floor((ms - timecodes.front()) * fps / 1000.); + if (ms < 0) { + return (int)floor(ms * fps / 1000.); } if (ms > timecodes.back()) { return round((ms - timecodes.back()) * fps / 1000.) + (int)timecodes.size() - 1; @@ -294,7 +304,7 @@ int Framerate::TimeAtFrame(int frame, Time type) const { } if (frame < 0) { - return (int)ceil(frame / fps * 1000.) + timecodes.front(); + return (int)ceil(frame / fps * 1000.); } if (frame >= (signed)timecodes.size()) { return round((frame - timecodes.size() + 1) * 1000. / fps + last); diff --git a/aegisub/tests/libaegisub_vfr.cpp b/aegisub/tests/libaegisub_vfr.cpp index 7f7b2738c..77dd5661b 100644 --- a/aegisub/tests/libaegisub_vfr.cpp +++ b/aegisub/tests/libaegisub_vfr.cpp @@ -376,3 +376,21 @@ TEST(lagi_vfr, load_v1_save_v2_ovr) { ASSERT_NO_THROW(fps.Save("data/vfr/out/v2_100_frames_30_with_override.txt", 100)); EXPECT_TRUE(validate_save("data/vfr/in/v2_100_frames_30_with_override.txt", "data/vfr/out/v2_100_frames_30_with_override.txt")); } + +TEST(lagi_vfr, nonzero_start_time) { + Framerate fps; + + ASSERT_NO_THROW(fps = Framerate(make_vector(5, 10, 20, 30, 40, 50))); + EXPECT_EQ(0, fps.TimeAtFrame(0, EXACT)); + EXPECT_EQ(10, fps.TimeAtFrame(1, EXACT)); + EXPECT_EQ(20, fps.TimeAtFrame(2, EXACT)); + EXPECT_EQ(30, fps.TimeAtFrame(3, EXACT)); + EXPECT_EQ(40, fps.TimeAtFrame(4, EXACT)); + + ASSERT_NO_THROW(fps = Framerate(make_vector(5, -10, 20, 30, 40, 50))); + EXPECT_EQ(0, fps.TimeAtFrame(0, EXACT)); + EXPECT_EQ(30, fps.TimeAtFrame(1, EXACT)); + EXPECT_EQ(40, fps.TimeAtFrame(2, EXACT)); + EXPECT_EQ(50, fps.TimeAtFrame(3, EXACT)); + EXPECT_EQ(60, fps.TimeAtFrame(4, EXACT)); +}