-
-
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
Add FramePos type and use it for beatgrid creation #4043
Conversation
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 usage of std::isnan
is probably invalid and should be tested.
src/track/beatmap.cpp
Outdated
startBeat.set_frame_position( | ||
static_cast<google::protobuf::int32>(samplesToFrames(startSample))); | ||
stopBeat.set_frame_position(static_cast<google::protobuf::int32>(samplesToFrames(stopSample))); | ||
Beat startBeat = beatFromFramePos(mixxx::audio::FramePos::fromEngineSamplePos(startSample)); |
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.
beatFromFramePos(mixxx::audio::FramePos::fromEngineSamplePos(value)) is invoked multiple times. Extract as a small, inlined utility function?
I noticed that there is one special case where .toFullFrames()
is invoked before passing the value into beatFromFramePos()
. It might make sense to extract this as a separate function with a dedicated name to isolate the occurrences and have all possible variations close to each other.
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.
beatFromFramePos always strips away fractional frame positions. In fact, we can always invoke .toFullFrames()
. But I wasn't sure if that is intended.
The location where "toFullFrames" is called explicitly is the location where we previously called "std::floor" explicitly.
I wasn't sure if that is just coincidence or if it's indeed desired. But if I uncomment the debug assertion that checks that the frame position is not fractional, some tests fail. The qeustion is: Do we always want to discard frame positions silently, or should be make it explicit and re-add the assertion?
But that kind of investigation is out of scope for this PR IMHO.
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 name of the function is also not very intuitive. But let's just leave it as is for the moment.
If there are known issues that must be resolved before a release we might also use FIXME as a marker to distinguish mandatory from optional TODOs, e.g. to either enable the debug assertion or update the documentation. This could be handy to quickly find these spots when we decide to merge unfinished work.
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.
@daschuer might have a final look.
Thank you for taking up this tedious cleanup. |
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 very much for this PR. It is a good way forward. I have left some comments.
src/audio/frame.h
Outdated
return std::modf(value(), &integerPart) != 0; | ||
} | ||
|
||
[[nodiscard]] FramePos toFullFrames() const { |
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.
From the function mame it is not clear if it is foor or ceil or round.
Let's either remove this function or rename it.
Do we need the other candidates as well?
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.
Floor is not int cast.
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.
When I mentioned the naming is not ideal I had something like toLowerFrameBoundary()
in mind.
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 Done.
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 guess that for the future we need a more sophisticated solution:
- If the value is close (<
epsilon
) to a beat then usestd::round
- Otherwise use either the lower or upper boundary, depending on the use case.
The value of epsilon
may also vary.
EXPECT_FALSE(mixxx::audio::FramePos(99999).isFractional()); | ||
EXPECT_FALSE(mixxx::audio::FramePos(-99999).isFractional()); | ||
EXPECT_TRUE(mixxx::audio::FramePos(128.5).isFractional()); | ||
EXPECT_TRUE(mixxx::audio::FramePos(135.67).isFractional()); |
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 add her also a check with Nan values and denormals.
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.
What should it return? Is nan fractional?
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.
Let's accept that the result is undefined. It doesn't matter and passing NaN is a bug. Just add a debug assertion to detect this case.
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.
done.
src/track/beatmap.cpp
Outdated
beat.set_frame_position(static_cast<google::protobuf::int32>(samplesToFrames(dSamples))); | ||
Beat beat = beatFromFramePos( | ||
mixxx::audio::FramePos::fromEngineSamplePos(dSamples) | ||
.toFullFrames()); |
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 changes the code for negative values because of internal floor()
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 negative values are not allowed we should check for that. Otherwise rounding to frame boundaries should always be done in the same direction. Depending on the use case but independent of the sign. Rounding in different directions is probably wrong.
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 was wondering if std::trunc
is preferable because it always rounds towards zero. But no idea.
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.
On second thought: trunc may lead to tiny Bpm changes around zero because the distance between beats is smaller. So using floor is the right call IMHO.
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.
Yes right.
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.
Strictly speaking we have now a new beat map version.
But since we have now beats below zero it does not matter.
Anything else to do here? I'm already working on a PR that uses frame positions for the beats API. |
Thank you very much for jumping in here. |
Ripped out of #2961.
We already store frame positions in the protobuf object, so we can also use the new framepos type for that.
I only added the new type to the analyzer, beatutils and the beatgrid creation method to keep the size bearable.