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

Bpm: Add a beatLength() method that returns a duration #4138

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

Holzhaus
Copy link
Member

I think semantically this makes sense and would look nicer in code. Right now, the Bpm::beatLength() method returns a mixxx::Duration. I also added a method Duration::toFrames(SampleRate) to make the code even easier to read.

The mixxx::Duration class uses a 64 bit integer for storing full nanoseconds. Does this precision suffice? I don't know. Some tests fail due to this loss of precision, because they hardcode a lot of decimal points. Please let me know if we should we change the tests, not use mixxx::Duration, or just not make this change at all?

Related Zulip Discussion: https://mixxx.zulipchat.com/#narrow/stream/109171-development/topic/Semantic.20type.20refactoring/near/246427762

@daschuer
Copy link
Member

Duration is a qint64 in nanoseconds It is not compatible to our play position in double and we are not able to lossless convert one to another. Even though the difference is probably marginal, it becomes notable if we iterate over a track. So I vote for using a double duration type for with a maximum precision.

Can we refactor the duration time to double? Unfortunately a qint64 dos not fit to a double, but I think we can't find a case where this is relevant in Mixxx.

@@ -62,6 +63,11 @@ class DurationBase {
return static_cast<double>(m_durationNanos);
}

// Returns the duration as an integer number of nanoseconds.
constexpr double toFrames(mixxx::audio::SampleRate sampleRate) const {
Copy link
Contributor

Choose a reason for hiding this comment

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

The duration should not know anything about frames and sample rates.

Copy link
Member Author

Choose a reason for hiding this comment

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

Writing duration.toDoubleSeconds() * sampleRate is tedious and not that expressive, so my initial idea was to "express a duration in frames". But I agree that a frame count is not a during without knowing the sample rate.

The alternative would be to add a method to the SampleRate class instead:

constexpr double SampleRate::frameCount(mixxx::Duration duration) {
    return duration.toDoubleSeconds() * value();
}

And using it would look like this:

constexpr auto bpm = mixxx::Bpm(120);
constexpr auto sampleRate = mixxx::audio::SampleRate(44100);

const double beatLengthFrames = sampleRate.frameCount(bpm.beatLength());

What do you think?

Copy link
Contributor

Choose a reason for hiding this comment

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

Conceptual Dependency Graph: Duration <- Sample Rate <- Frame

Sample rate knows about duration because of frequencies. But it doesn't need to know anything about frames.

Frames know about sampling because their purpose is to represent a continuous audio signal as discrete, sampled values.

Therefore any calculation that involves frames should not taint neither duration nor sample rate code.

Copy link
Contributor

Choose a reason for hiding this comment

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

Why not double operator*(Duration, SampleRate) (and its twin with switched arguments) in samplerate.h? The unit of the resulting value depends on the context.

@uklotzde
Copy link
Contributor

Duration is a qint64 in nanoseconds It is not compatible to our play position in double and we are not able to lossless convert one to another. Even though the difference is probably marginal, it becomes notable if we iterate over a track. So I vote for using a double duration type for with a maximum precision.

Can we refactor the duration time to double? Unfortunately a qint64 dos not fit to a double, but I think we can't find a case where this is relevant in Mixxx.

I would not recommend to do this. Then you will get rounding errors for integer durations measured in milliseconds or microseconds that currently can be represented exactly.

Better introduce a new type for this purpose to distinguish between system and audio duration.

@daschuer
Copy link
Member

Then you will get rounding errors for integer durations measured in milliseconds or microseconds that currently can be represented exactly.

Not if we normalize the double value to ns.
Both solutions work for me.

@uklotzde
Copy link
Contributor

Then you will get rounding errors for integer durations measured in milliseconds or microseconds that currently can be represented exactly.

Not if we normalize the double value to ns.
Both solutions work for me.

Ok, if we store nanoseconds internally then using a single representation would work.

@daschuer
Copy link
Member

A double can store integers exact up to 2^53 1,000,000,000,000,000

If we pick ns as base we are exact convertable for all times from
1 ns .. 1000000 s (11 days)

@Holzhaus
Copy link
Member Author

#4147

Holzhaus added a commit to Holzhaus/mixxx that referenced this pull request Jul 27, 2021
@Holzhaus Holzhaus marked this pull request as draft July 27, 2021 10:28
@github-actions
Copy link

This PR is marked as stale because it has been open 90 days with no activity.

@github-actions github-actions bot added the stale Stale issues that haven't been updated for a long time. label Oct 26, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
code quality library stale Stale issues that haven't been updated for a long time.
Projects
Status: In Progress
Development

Successfully merging this pull request may close these issues.

3 participants