diff --git a/app/audio/audiovisualwaveform.h b/app/audio/audiovisualwaveform.h index be488169f..c7071e115 100644 --- a/app/audio/audiovisualwaveform.h +++ b/app/audio/audiovisualwaveform.h @@ -60,7 +60,7 @@ public: * * Starting at `start`, writes samples over anything in the buffer, expanding it if necessary. */ - void OverwriteSamples(SampleBufferPtr samples, int sample_rate, const rational& start = rational()); + void OverwriteSamples(SampleBufferPtr samples, int sample_rate, const rational& start = 0); /** * @brief Replaces sums at a certain range in this visual waveform @@ -81,7 +81,7 @@ public: * * Maximum length of `sums` to overwrite with. */ - void OverwriteSums(const AudioVisualWaveform& sums, const rational& dest, const rational& offset = rational(), const rational &length = rational()); + void OverwriteSums(const AudioVisualWaveform& sums, const rational& dest, const rational& offset = 0, const rational &length = 0); void Shift(const rational& from, const rational& to); diff --git a/app/common/rational.cpp b/app/common/rational.cpp index f8702acb6..c5b537af3 100644 --- a/app/common/rational.cpp +++ b/app/common/rational.cpp @@ -7,24 +7,28 @@ namespace olive { rational rational::fromDouble(const double &flt, bool* ok) { + if (qIsNaN(flt)) { + // Return NaN rational + if (ok) *ok = false; + return rational(0, 0); + } + // Use FFmpeg function for the time being AVRational r = av_d2q(flt, INT_MAX); + if (r.den == 0) { // If den == 0, we were unable to convert to a rational if (ok) { *ok = false; } - - return rational(); } else { // Otherwise, assume we received a real rational if (ok) { *ok = true; } - - return r; } + return r; } rational rational::fromString(const QString &str, bool* ok) @@ -37,32 +41,32 @@ rational rational::fromString(const QString &str, bool* ok) case 2: return rational(elements.at(0).toLongLong(ok), elements.at(1).toLongLong(ok)); default: + // Returns NaN with ok set to false if (ok) { *ok = false; } - return rational(); + return rational(0, 0); } } -//Function: print number to cout - -void rational::print(std::ostream &out) const -{ - out << this->numer_ << "/" << this->denom_; -} - //Function: ensures denom >= 0 void rational::fix_signs() { + // Normalize so that denominator is always positive and only numerator is positive if (denom_ < 0) { denom_ = -denom_; numer_ = -numer_; } - if (numer_ == intType(0) || denom_ == intType(0)) { + // Normalize to 0/1 if numerator is zero + if (numer_ == intType(0)) { + denom_ = intType(1); + } + + // Normalize to 0/0 (aka NaN) if denominator is zero + if (denom_ == intType(0)) { numer_ = intType(0); - denom_ = intType(0); } } @@ -107,7 +111,7 @@ double rational::toDouble() const if (denom_ != 0) { return static_cast(numer_) / static_cast(denom_); } else { - return static_cast(0); + return qSNaN(); } } @@ -137,6 +141,11 @@ rational rational::flipped() const } bool rational::isNull() const +{ + return numerator() == 0; +} + +bool rational::isNaN() const { return denominator() == 0; } @@ -170,15 +179,21 @@ const rational& rational::operator=(const rational &rhs) const rational& rational::operator+=(const rational &rhs) { - if (!rhs.isNull()) { - if (isNull()) { - numer_ = rhs.numer_; - denom_ = rhs.denom_; - } else { - numer_ = (numer_ * rhs.denom_) + (rhs.numer_ * denom_); - denom_ = denom_ * rhs.denom_; + if (!isNaN()) { + if (rhs.isNaN()) { + // Set to NaN + denom_ = 0; fix_signs(); - reduce(); + } else if (!rhs.isNull()) { + if (isNull()) { + numer_ = rhs.numer_; + denom_ = rhs.denom_; + } else { + numer_ = (numer_ * rhs.denom_) + (rhs.numer_ * denom_); + denom_ = denom_ * rhs.denom_; + fix_signs(); + reduce(); + } } } @@ -187,15 +202,21 @@ const rational& rational::operator+=(const rational &rhs) const rational& rational::operator-=(const rational &rhs) { - if (!rhs.isNull()) { - if (isNull()) { - numer_ = -rhs.numer_; - denom_ = rhs.denom_; - } else { - numer_ = (numer_ * rhs.denom_) - (rhs.numer_ * denom_); - denom_ = denom_ * rhs.denom_; + if (!isNaN()) { + if (rhs.isNaN()) { + // Set to NaN + denom_ = 0; fix_signs(); - reduce(); + } else if (!rhs.isNull()) { + if (isNull()) { + numer_ = -rhs.numer_; + denom_ = rhs.denom_; + } else { + numer_ = (numer_ * rhs.denom_) - (rhs.numer_ * denom_); + denom_ = denom_ * rhs.denom_; + fix_signs(); + reduce(); + } } } @@ -204,19 +225,36 @@ const rational& rational::operator-=(const rational &rhs) const rational& rational::operator/=(const rational &rhs) { - numer_ = numer_ * rhs.denom_; - denom_ = denom_ * rhs.numer_; - fix_signs(); - reduce(); + if (!isNaN()) { + if (rhs.isNaN()) { + // Set to NaN + denom_ = 0; + fix_signs(); + } else { + numer_ = numer_ * rhs.denom_; + denom_ = denom_ * rhs.numer_; + fix_signs(); + reduce(); + } + } + return *this; } const rational& rational::operator*=(const rational &rhs) { - numer_ = numer_ * rhs.numer_; - denom_ = denom_ * rhs.denom_; - fix_signs(); - reduce(); + if (!isNaN()) { + if (rhs.isNaN()) { + denom_ = 0; + fix_signs(); + } else { + numer_ = numer_ * rhs.numer_; + denom_ = denom_ * rhs.denom_; + fix_signs(); + reduce(); + } + } + return *this; } @@ -254,6 +292,10 @@ rational rational::operator*(const rational &rhs) const bool rational::operator<(const rational &rhs) const { + if (isNaN() || rhs.isNaN()) { + return false; + } + if (isNull() && rhs.isNull()) { return false; } @@ -283,6 +325,10 @@ bool rational::operator<(const rational &rhs) const bool rational::operator<=(const rational &rhs) const { + if (isNaN() || rhs.isNaN()) { + return false; + } + if (isNull() && rhs.isNull()) { return true; } @@ -322,40 +368,16 @@ bool rational::operator>=(const rational &rhs) const bool rational::operator==(const rational &rhs) const { + if (isNaN() || rhs.isNaN()) { + return false; + } + return (numer_ == rhs.numer_ && denom_ == rhs.denom_); } bool rational::operator!=(const rational &rhs) const { - return (numer_ != rhs.numer_) || (denom_ != rhs.denom_); -} - -//Unary operators - -const rational& rational::operator++() -{ - numer_ += denom_; - return *this; -} - -rational rational::operator++(int) -{ - rational tmp = *this; - numer_ += denom_; - return tmp; -} - -const rational& rational::operator--() -{ - numer_ -= denom_; - return *this; -} - -rational rational::operator--(int) -{ - rational tmp; - numer_ -= denom_; - return tmp; + return !(*this == rhs); } const rational& rational::operator+() const @@ -417,8 +439,4 @@ uint qHash(const rational &r, uint seed) QDebug operator<<(QDebug debug, const olive::rational &r) { return debug.space() << r.toDouble(); - /* - debug.nospace() << r.numerator() << "/" << r.denominator(); - return debug.space(); - */ } diff --git a/app/common/rational.h b/app/common/rational.h index 8dd3327fa..3a3d19411 100644 --- a/app/common/rational.h +++ b/app/common/rational.h @@ -34,13 +34,9 @@ class rational public: //constructors rational(const intType &numerator = 0) : - numer_(numerator) + numer_(numerator), + denom_(1) { - if (numer_ == 0) { - denom_ = 0; - } else { - denom_ = 1; - } } rational(const intType &numerator, const intType &denominator) : @@ -87,10 +83,6 @@ public: bool operator!=(const rational &rhs) const; //Unary operators - const rational& operator++(); //prefix - rational operator++(int); //postfix - const rational& operator--(); //prefix - rational operator--(int); //postfix const rational& operator+() const; rational operator-() const; bool operator!() const; @@ -108,11 +100,13 @@ public: // Produce "flipped" version rational flipped() const; - // Returns whether the rational is null or not + // Returns whether the rational is valid but equal to zero or not + // + // A NaN is always a null, but a null is not always a NaN bool isNull() const; - //Function: print number to cout - void print(std::ostream &out = std::cout) const; + // Returns whether this rational is not a valid number + bool isNaN() const; //IO friend std::ostream& operator<<(std::ostream &out, const rational &value); diff --git a/app/node/output/track/track.cpp b/app/node/output/track/track.cpp index 941f80fc0..aefa8b262 100644 --- a/app/node/output/track/track.cpp +++ b/app/node/output/track/track.cpp @@ -40,6 +40,9 @@ const QString Track::kMutedInput = QStringLiteral("muted_in"); Track::Track() : track_type_(Track::kNone), + track_length_(0), + midop_track_length_(0), + preop_track_length_(0), index_(-1), locked_(false) { @@ -276,7 +279,7 @@ void Track::InputDisconnectedEvent(const QString &input, int element, const Node if (next) { UpdateInOutFrom(blocks_.indexOf(next)); } else if (blocks_.isEmpty()) { - SetLengthInternal(rational()); + SetLengthInternal(0); } else { SetLengthInternal(blocks_.last()->out()); } diff --git a/app/node/output/track/tracklist.cpp b/app/node/output/track/tracklist.cpp index 4986cf583..e91321539 100644 --- a/app/node/output/track/tracklist.cpp +++ b/app/node/output/track/tracklist.cpp @@ -31,6 +31,7 @@ namespace olive { TrackList::TrackList(Sequence *parent, const Track::Type &type, const QString &track_input) : QObject(parent), track_input_(track_input), + total_length_(0), type_(type) { } diff --git a/app/node/output/viewer/viewer.cpp b/app/node/output/viewer/viewer.cpp index e8c16848e..afe60e4fa 100644 --- a/app/node/output/viewer/viewer.cpp +++ b/app/node/output/viewer/viewer.cpp @@ -37,6 +37,9 @@ const uint64_t ViewerOutput::kVideoParamEditMask = VideoParamEdit::kWidthHeight #define super Node ViewerOutput::ViewerOutput(bool create_default_streams) : + last_length_(0), + video_length_(0), + audio_length_(0), video_frame_cache_(this), audio_playback_cache_(this), video_cache_enabled_(true), @@ -226,7 +229,7 @@ void ViewerOutput::InvalidateCache(const TimeRange& range, const QString& from, if ((video_cache_enabled_ && (from == kTextureInput || from == kVideoParamsInput)) || (audio_cache_enabled_ && (from == kSamplesInput || from == kAudioParamsInput))) { - TimeRange invalidated_range(qMax(rational(), range.in()), + TimeRange invalidated_range(qMax(rational(0), range.in()), qMin(GetLength(), range.out())); if (invalidated_range.in() != invalidated_range.out()) { @@ -297,8 +300,6 @@ void ViewerOutput::Retranslate() void ViewerOutput::VerifyLength() { - rational subtitle_length; - video_length_ = VerifyLengthInternal(Track::kVideo); if (video_cache_enabled_) { video_frame_cache_.SetLength(video_length_); @@ -309,7 +310,7 @@ void ViewerOutput::VerifyLength() audio_playback_cache_.SetLength(audio_length_); } - subtitle_length = VerifyLengthInternal(Track::kSubtitle); + rational subtitle_length = VerifyLengthInternal(Track::kSubtitle); rational real_length = qMax(subtitle_length, qMax(video_length_, audio_length_)); @@ -345,14 +346,19 @@ rational ViewerOutput::VerifyLengthInternal(Track::Type type) const case Track::kVideo: if (IsInputConnected(kTextureInput)) { NodeValueTable t = traverser.GenerateTable(GetConnectedOutput(kTextureInput), TimeRange(0, 0)); - qDebug() << "Got video length:" << t.Get(NodeValue::kRational, QStringLiteral("length")).value(); - return t.Get(NodeValue::kRational, QStringLiteral("length")).value(); + rational r = t.Get(NodeValue::kRational, QStringLiteral("length")).value(); + if (!r.isNaN()) { + return r; + } } break; case Track::kAudio: if (IsInputConnected(kSamplesInput)) { NodeValueTable t = traverser.GenerateTable(GetConnectedOutput(kSamplesInput), TimeRange(0, 0)); - return t.Get(NodeValue::kRational, QStringLiteral("length")).value(); + rational r = t.Get(NodeValue::kRational, QStringLiteral("length")).value();; + if (!r.isNaN()) { + return r; + } } break; case Track::kNone: @@ -361,7 +367,7 @@ rational ViewerOutput::VerifyLengthInternal(Track::Type type) const break; } - return rational(); + return 0; } NodeOutput ViewerOutput::GetConnectedTextureOutput() diff --git a/app/node/project/footage/footage.cpp b/app/node/project/footage/footage.cpp index dc7e112c8..9641aeb4a 100644 --- a/app/node/project/footage/footage.cpp +++ b/app/node/project/footage/footage.cpp @@ -196,7 +196,7 @@ rational Footage::VerifyLengthInternal(Track::Type type) const } } - return super::VerifyLengthInternal(type); + return 0; } QString Footage::GetColorspaceToUse(const VideoParams ¶ms) const diff --git a/app/node/project/sequence/sequence.cpp b/app/node/project/sequence/sequence.cpp index 3e7659dd7..4517c8011 100644 --- a/app/node/project/sequence/sequence.cpp +++ b/app/node/project/sequence/sequence.cpp @@ -123,7 +123,7 @@ rational Sequence::VerifyLengthInternal(Track::Type type) const } } - return rational(); + return 0; } void Sequence::InputConnectedEvent(const QString &input, int element, const NodeOutput &output) diff --git a/app/render/framehashcache.cpp b/app/render/framehashcache.cpp index ab2e65f3c..96e321e51 100644 --- a/app/render/framehashcache.cpp +++ b/app/render/framehashcache.cpp @@ -322,7 +322,7 @@ void FrameHashCache::ShiftEvent(const rational &from, const rational &to) // POSITIVE if moving forward -> // NEGATIVE if moving backward <- rational diff = to - from; - bool diff_is_negative = (diff < rational()); + bool diff_is_negative = (diff < 0); QList shifted_times; @@ -353,10 +353,12 @@ void FrameHashCache::ShiftEvent(const rational &from, const rational &to) void FrameHashCache::InvalidateEvent(const TimeRange &range) { - QVector invalid_frames = GetFrameListFromTimeRange({range}); + if (!timebase_.isNull()) { + QVector invalid_frames = GetFrameListFromTimeRange({range}); - foreach (const rational& r, invalid_frames) { - time_hash_map_.remove(r); + foreach (const rational& r, invalid_frames) { + time_hash_map_.remove(r); + } } } diff --git a/app/render/playbackcache.h b/app/render/playbackcache.h index 218955dff..391774ef5 100644 --- a/app/render/playbackcache.h +++ b/app/render/playbackcache.h @@ -35,7 +35,8 @@ class PlaybackCache : public QObject Q_OBJECT public: PlaybackCache(QObject* parent = nullptr) : - QObject(parent) + QObject(parent), + length_(0) { } diff --git a/app/render/previewautocacher.cpp b/app/render/previewautocacher.cpp index 373d3b278..375c700b5 100644 --- a/app/render/previewautocacher.cpp +++ b/app/render/previewautocacher.cpp @@ -18,8 +18,7 @@ PreviewAutoCacher::PreviewAutoCacher() : last_update_time_(0), ignore_next_mouse_button_(false) { - // Set default autocache range - SetPlayhead(rational()); + SetPlayhead(0); delayed_requeue_timer_.setInterval(Config::Current()[QStringLiteral("AutoCacheDelay")].toInt()); delayed_requeue_timer_.setSingleShot(true); diff --git a/app/widget/timelinewidget/timelinewidget.cpp b/app/widget/timelinewidget/timelinewidget.cpp index cacf96ccf..45ff05339 100644 --- a/app/widget/timelinewidget/timelinewidget.cpp +++ b/app/widget/timelinewidget/timelinewidget.cpp @@ -1224,7 +1224,7 @@ void TimelineWidget::RippleTo(Timeline::MovementMode mode) } // Find each track's nearest point and determine the overall timeline's nearest point - rational closest_point_to_playhead = (mode == Timeline::kTrimIn) ? rational() : RATIONAL_MAX; + rational closest_point_to_playhead = (mode == Timeline::kTrimIn) ? 0 : RATIONAL_MAX; foreach (const Timeline::EditToInfo& info, tracks) { if (info.nearest_block) { diff --git a/app/widget/viewer/viewer.cpp b/app/widget/viewer/viewer.cpp index 9e697bc88..29752c63e 100644 --- a/app/widget/viewer/viewer.cpp +++ b/app/widget/viewer/viewer.cpp @@ -203,7 +203,7 @@ void ViewerWidget::ConnectNodeEvent(ViewerOutput *n) SetViewerResolution(vp.width(), vp.height()); SetViewerPixelAspect(vp.pixel_aspect_ratio()); - last_length_ = rational(); + last_length_ = 0; LengthChangedSlot(n->GetLength()); ColorManager* color_manager = n->project()->color_manager(); @@ -358,7 +358,7 @@ void ViewerWidget::SetAutoCacheEnabled(bool e) void ViewerWidget::CacheEntireSequence() { - auto_cacher_.ForceCacheRange(TimeRange(rational(), GetConnectedNode()->video_frame_cache()->GetLength())); + auto_cacher_.ForceCacheRange(TimeRange(0, GetConnectedNode()->video_frame_cache()->GetLength())); } void ViewerWidget::CacheSequenceInOut()