rational: implement NaN mode

Also clarify code where 0 is used as a rational.
This commit is contained in:
itsmattkc
2021-04-17 15:04:03 +10:00
parent 88ebfb9d8e
commit 22b2d08baf
13 changed files with 132 additions and 108 deletions
+2 -2
View File
@@ -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);
+90 -72
View File
@@ -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<double>(numer_) / static_cast<double>(denom_);
} else {
return static_cast<double>(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();
*/
}
+7 -13
View File
@@ -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);
+4 -1
View File
@@ -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());
}
+1
View File
@@ -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)
{
}
+14 -8
View File
@@ -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<rational>();
return t.Get(NodeValue::kRational, QStringLiteral("length")).value<rational>();
rational r = t.Get(NodeValue::kRational, QStringLiteral("length")).value<rational>();
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>();
rational r = t.Get(NodeValue::kRational, QStringLiteral("length")).value<rational>();;
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()
+1 -1
View File
@@ -196,7 +196,7 @@ rational Footage::VerifyLengthInternal(Track::Type type) const
}
}
return super::VerifyLengthInternal(type);
return 0;
}
QString Footage::GetColorspaceToUse(const VideoParams &params) const
+1 -1
View File
@@ -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)
+6 -4
View File
@@ -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<HashTimePair> shifted_times;
@@ -353,10 +353,12 @@ void FrameHashCache::ShiftEvent(const rational &from, const rational &to)
void FrameHashCache::InvalidateEvent(const TimeRange &range)
{
QVector<rational> invalid_frames = GetFrameListFromTimeRange({range});
if (!timebase_.isNull()) {
QVector<rational> 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);
}
}
}
+2 -1
View File
@@ -35,7 +35,8 @@ class PlaybackCache : public QObject
Q_OBJECT
public:
PlaybackCache(QObject* parent = nullptr) :
QObject(parent)
QObject(parent),
length_(0)
{
}
+1 -2
View File
@@ -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);
+1 -1
View File
@@ -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) {
+2 -2
View File
@@ -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()