-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use mixxx::Bpm for some Beats functions #4044
Conversation
Converted to draft until #4043 is merged. |
This is ready now. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the future, we might also want to consider making (some parts of) the Bpm class constexpr.
I added some simple tests for the class that we can extend later. A later PR improves the |
src/test/bpmtest.cpp
Outdated
EXPECT_EQ(mixxx::Bpm(120), mixxx::Bpm(240) / 2); | ||
|
||
EXPECT_EQ(mixxx::Bpm(), mixxx::Bpm(mixxx::Bpm::kValueUndefined)); | ||
EXPECT_EQ(mixxx::Bpm(), mixxx::Bpm(0.0)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This last test is confusing. We could test that mixxx::Bpm::kValueUndefined
equals 0.0
but that doesn't really make any sense to me. Otherwise we would need to test the value of each and every constant.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The point is that all invalid values should be equal to another. But it was actually not working, fixed that.
src/track/bpm.h
Outdated
return bpm1.getValue() >= bpm2.getValue(); | ||
} | ||
|
||
inline bool operator==(Bpm bpm1, Bpm bpm2) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't notice this very special equality comparison! We should add a comment that all invalid values are considered equal.
We also should test the comparison of invalid and valid values.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will. But do you agree with this comparison? I think it makes sense to consider all invalid values equal.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not saying we should imitate this...
❯ node
Welcome to Node.js v14.17.0.
Type ".help" for more information.
> NaN === NaN
false
>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is actually the IEEE-754 standard. It's the same in C++. !(x == x)
is a way to implement isNaN
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It could be surprising as the other comparison operators like <=
behave differently! Do we really need it? Otherwise I would suggest to avoid these tweaks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@uklotzde test added.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It could be surprising as the other comparison operators like
<=
behave differently! Do we really need it? Otherwise I would suggest to avoid these tweaks.
I don't know. My rationale was that someValue == Bpm()
is true if someValue
is some invalid value.
Btw, I actually have a WIP PR that takes care of that and raises a debug assertion every time you try to do a calculation or comparison with an invalid value: 2217470#diff-6ee8fe6454f9e7e8637c1ba82b07d85ae618df4fbb651d66f8e5fff4b06f51e5
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It could be surprising as the other comparison operators like
<=
behave differently! Do we really need it? Otherwise I would suggest to avoid these tweaks.
I don't know. My rationale was that someValue == Bpm()
is true if someValue
is some invalid value.
I actually have a WIP PR that takes care of that and raises a debug assertion every time you try to do a calculation or comparison with an invalid value: 2217470#diff-6ee8fe6454f9e7e8637c1ba82b07d85ae618df4fbb651d66f8e5fff4b06f51e5
But I don't want to add all of that here. I can also revert these "all invalid values are equal" changes if you prefer.
return (!bpm1.hasValue() && !bpm2.hasValue()) || bpm1.getValue() <= bpm2.getValue(); | ||
} | ||
|
||
inline bool operator>(Bpm bpm1, Bpm bpm2) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The definitions of the comparison operators are inconsistent, i.e. the result of lhs < rhs
should equal !(lhs >= rhs)
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In which cases does it not?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that the <
operator uses getValue()
which throws a debug assert if at least one of the values is invalid.
50 < 100 = true
100 < 50 = false
100 < 100 = false
100 < invalid = debug assert
invalid < 100 = debug assert
invalid < invalid = debug assert
50 >= 100 = false
100 >= 100 = true
100 >= 100 = true
100 >= invalid = debug assert
invalid >= 100 = debug assert
invalid >= invalid = true (because they are equal)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Independent of debug assertions these operators must be interchangeable. Everything else is unexpected.
The condition <
must result in the same behavior as not >=
because as a developer I expect that I could use either of them depending on which is more suitable for structuring the code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I see an expression like if (!(a >= b))
I will refactor it to if (a < b)
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getValue() always returns 0.0 if the value is invalid, so it should still work fine:
50 < 100 = true
100 < 50 = false
100 < 100 = false
100 < invalid = false + debug assert
invalid < 100 = true + debug assert
invalid < invalid = false debug assert
50 >= 100 = false
100 >= 100 = true
100 >= 100 = true
100 >= invalid = true + debug assert
invalid >= 100 = false + debug assert
invalid >= invalid = true
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Usually you only implement <
and then the remaining operators in terms of this to prevent inconsistencies.
return bpm1.getValue() > bpm2.getValue(); | ||
} | ||
|
||
inline bool operator>=(Bpm bpm1, Bpm bpm2) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Both >
and >=
should delegate to <
and <=
with the arguments switched.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good point.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please fix the code style issues, then we can merge. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you! I guess the extra rounds during the review were worth the efforts ;) LGTM
Follow up to #4043. This uses the proper mixxx::Bpm type in some method signatures related to beats. I didn't change all methods to keep the diff reasonably small.