-
-
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 hotcue_X_indicator
control, is 1.0 if playposition is at hotcue
#12951
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.
Good idea, the only issue we have that the code does not work in reverse. I think we have similar code for the beat indicator, you may re-use
Yes, for reverse I'll add a else branch. beat_active is different though and the code is more complex because it is supposed to light up LEDs or widgets for X milliseconds while the track is playing, i.e. be ON when the playhead is in a certain +- range at/around the actual beat. This is not required for hotcue_X_indicator which is only supposed to trigger while playing (don't miss it, duration doesn't matter) and when being stopped. |
7310d52
to
319f1cf
Compare
I adjusted it to
Scratching over a hotcue also activates the indicator. |
For testing I created a controller mapping with a callback connection and added a testing proposal to the PR description. |
🤷♂️
|
Set 1.0 if playposition is at hotcue or if we passed it since the last update. Ignore seeks over hotcues.
319f1cf
to
bb014d3
Compare
I simplified it and the tests all pass. Dunno what went wrong and why only these tests failed, couldn't spot a common call that could cause the failure. |
How should this CO behave in case of a saved loop? |
You describe the 3 tests in the description. Why not implementing them as test case? |
Exactly like with hotcues (saved loops are just hotcues with an end point).
Because I was lazy and noticed there are no tests for the cue indicator either ; p I don't think tests are important as these controls don't affect performance, but of course I can write some. |
I wonder if this is what the user expects? I could imagine that he would expect the indicator active, as long as the saved loop is running. Not sure about the use case here. |
What you describe is |
Doesn't |
What users may expect depends on the documentation.
If users want a loop indicator the can combine Let's merge this first step, then extend it if requested. |
src/engine/controls/cuecontrol.cpp
Outdated
if ((cuePos >= prevPos && cuePos <= currPos) || // forward | ||
(cuePos <= prevPos && cuePos >= currPos)) { // reverse | ||
pControl->setIndicator(1.0); | ||
} else { | ||
pControl->setIndicator(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.
In case of a saved loop and playing reverse, the duration of the loop needs to be 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.
Good catch, I did not test looping.
And admittedly that is a tricky one. There is a seek from [loop end - x] to [loop in + y] where x + y = buffer length, so we'd need to get/store loop start/end and check loop_enabled as well as whether the hotcue is inside the loop, and that for both play directions. Uff...
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.
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 think I managed to catch all hotcues correctly.
Manual tests passed (with the trace output and the test mapping).
I'll update the Mixxx tests accordingly.
4423e0f
to
5efd118
Compare
src/engine/controls/loopingcontrol.h
Outdated
@@ -98,8 +86,7 @@ class LoopingControl : public EngineControl { | |||
|
|||
signals: | |||
void loopReset(); | |||
void loopEnabledChanged(bool enabled); | |||
void loopUpdated(mixxx::audio::FramePos startPosition, mixxx::audio::FramePos endPosition); | |||
void loopUpdated(LoopInfo loopInfo); |
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.
Why does clazy insist this should be fully qualified when used in engine controls?
It has no issue with FrameInfo
.
Also, the reason clazy-fully-qualified-moc-types
warns is
not using fully-qualified type names [..] will break old-style connects and interaction with QML.
No old style here anymore, and is QML supposed to interact with engine controls??
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.
No idea. Does the proposed change 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.
Well, it builds (didn't run clazy locally), but I wonder if the reason is relevant here.
I'm not aware of any QML <--> EngineControl interaction, QML uses proxies for the library, players etc.
Control interaction is all done through EngineBuffer and the individual controls interacting with each other.
So I'd rather ignore it and go with a clazy exclude for now
// clazy:exclude=clazy-fully-qualified-moc-types
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.
Though it's annoying that it has to be added everywhere it is used.
a4c6a77
to
978c867
Compare
978c867
to
b3ffc4e
Compare
src/engine/controls/cuecontrol.cpp
Outdated
cuePos >= loopInfo.startPosition && | ||
cuePos < loopInfo.endPosition && |
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.
Can we put this into a helper isInsideLoop()
? to deduplicate this a bit?
// Hotcue inside loop? | ||
cuePos >= loopInfo.startPosition && | ||
cuePos < loopInfo.endPosition && | ||
(cuePos > prevPos || cuePos <= currPos)) { |
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.
cuePos > prevPos
is clear, because the loop has wrapped around, we passed the cue.
cuePos <= currPos
This one is not clear for me, doesn't it apply to all cues is is the loop?
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.
since we already detected that we wrapped around, cuePos <= currPos
covers all cues in between loop_in
and currPos
// Hotcue inside loop? | ||
cuePos >= loopInfo.startPosition && | ||
cuePos < loopInfo.endPosition && | ||
(cuePos < prevPos || cuePos >= currPos)) { |
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 same here:
cuePos >= currPos
What is this condition for?
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.
same as above: when playing reverse, this covers all cues that are after/at currPos
and before loop_out
src/engine/controls/cuecontrol.cpp
Outdated
prevPos < loopInfo.endPosition && | ||
currPos < loopInfo.endPosition) { | ||
// Check if we wrapped around | ||
if (reverse && prevPos < currPos) { |
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.
Doesn't the condition apply to any seek, independent of a loop?
I am just considering if it will work more universal we just check if cue position is inside the range of prevPos + the buffer length?
This will not work if the cue is just after the current loop though.
In case of seeks the buffer before the seek is cossfaded with the buffer after the seek. Is it inside a loop?
What is the use case for such a corner 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.
Doesn't the condition apply to any seek, independent of a loop?
Seeks are registered in notifySeek() which sets prevPos(), i.e. the loop detection doesn't kick in.
check if cue position is inside the range of prevPos + the buffer length?
Since the buffer is split into two parts if we wrap around, that's exactly what we do here. (besides, I also considered using LoopingControl's nextTrigger()
like ReadAheadManager does, or get the trigger already in EngineBuffer so it can pass it to CueControl and RAManager, but that is overkill IMO)
Actually, the original implementation worked just fine (incl. scratching and reverse, NOT for loops),
the current implementation works also for loops, but fails for scratching backwards inside a loop.
Back to square one...
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.
okay, "speed" from EngineBuffer works: it is "effective" reverse, i.e. reverse
or scratching backwards.
now everything works as expected.
b3ffc4e
to
313609c
Compare
I tested
and everything works as expected:
Now it's just the question if there need to be tests for every case. |
There is no pressing need for testing every single case. It depends more on you interest to contribute them.
There must be no debug output in the audio thread because it is locking. CueControl::updateIndicators() is inside the audio thread. |
I know, I was more referring to output that can be enabled on demand for troubleshooting. Re tests, I think the essence of the looping scenario can be covered rather easily. |
f9b888a
to
2989e99
Compare
2989e99
to
76ef49c
Compare
For now I have removed looping test commit c73a8f1 |
I've redone this based on the |
Yes, the new implementation looks more robust. |
So I push to this branch? |
Which do you like more? I prefer #13076. |
yes, me too, the question was just if you agree to overwrite this one. |
Or just close this, I don't mind. |
I'll close this one and refine #13076 sometime |
Just a little helper that allows having callbacks for when the playposition crosses a hotcue / loopcue.
For example:
Description
Basically, it's something like
beat_active
andcue_indicator
: it is activated (1.0) if(when stopped or when the buffer's new position is coincidentally at the hotcue position)
(while playing forward or backward and while scratching, not when seeking over a hotcue)
beat_active
is 1.0 if the playposition is in a certain range around a beat markercue_indicator
is 1.0 (maybe flashing) if the play position is exactly at the Cue (unlikely when playing)_Improvement?
Integrating it into
hotcue_X_status
would allow GUI feedback via WHotcueButton, it'd be just two more states that could be styled rather easily. #9760For now this is separate because integration/extension seems a bit cumbersome.
Nice to have
Proposal for testing