Skip to content
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

Merged
merged 32 commits into from
Jul 2, 2021

Conversation

Holzhaus
Copy link
Member

@Holzhaus Holzhaus commented Jul 1, 2021

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.

Copy link
Contributor

@uklotzde uklotzde left a 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 Show resolved Hide resolved
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));
Copy link
Contributor

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.

Copy link
Member Author

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.

Copy link
Contributor

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.

src/audio/frame.h Outdated Show resolved Hide resolved
src/audio/frame.h Outdated Show resolved Hide resolved
src/audio/frame.h Outdated Show resolved Hide resolved
src/track/beatmap.cpp Outdated Show resolved Hide resolved
Copy link
Contributor

@uklotzde uklotzde left a 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.

@Be-ing
Copy link
Contributor

Be-ing commented Jul 1, 2021

Thank you for taking up this tedious cleanup.

Copy link
Member

@daschuer daschuer left a 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/analyzer/plugins/analyzerqueenmarybeats.cpp Outdated Show resolved Hide resolved
return std::modf(value(), &integerPart) != 0;
}

[[nodiscard]] FramePos toFullFrames() const {
Copy link
Member

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?

Copy link
Member

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.

Copy link
Contributor

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.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@uklotzde Done.

Copy link
Contributor

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 use std::round
  • Otherwise use either the lower or upper boundary, depending on the use case.

The value of epsilon may also vary.

src/audio/frame.h Show resolved Hide resolved
src/test/frametest.cpp Show resolved Hide resolved
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());
Copy link
Member

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.

Copy link
Member Author

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?

Copy link
Contributor

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.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

src/track/beatfactory.cpp Show resolved Hide resolved
beat.set_frame_position(static_cast<google::protobuf::int32>(samplesToFrames(dSamples)));
Beat beat = beatFromFramePos(
mixxx::audio::FramePos::fromEngineSamplePos(dSamples)
.toFullFrames());
Copy link
Member

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()

Copy link
Contributor

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.

Copy link
Member Author

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.

Copy link
Member Author

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.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes right.

Copy link
Member

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.

@Holzhaus Holzhaus requested a review from daschuer July 2, 2021 11:31
@Holzhaus
Copy link
Member Author

Holzhaus commented Jul 2, 2021

Anything else to do here? I'm already working on a PR that uses frame positions for the beats API.

@daschuer
Copy link
Member

daschuer commented Jul 2, 2021

Thank you very much for jumping in here.

@daschuer daschuer merged commit 5dc50d1 into mixxxdj:main Jul 2, 2021
@Holzhaus Holzhaus mentioned this pull request Jul 3, 2021
@Holzhaus Holzhaus added this to In Progress in Semanting Type Refactoring via automation Jul 6, 2021
@Holzhaus Holzhaus moved this from In Progress to Done in Semanting Type Refactoring Jul 6, 2021
@Holzhaus Holzhaus added this to Done in Beats Refactoring Jul 12, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

Successfully merging this pull request may close these issues.

None yet

5 participants