-
-
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
implement instant doubles functionality (clone deck) #1892
Conversation
af01be3
to
f42d5e8
Compare
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.
Some of the elements aren't copied. I'm not sure where to go from here. Any ideas?
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 for the work so far. I assume your problems are mainly cases by follow up code reverting your changes. So I think the code need to be moved arround a bit and special case these places losing the initial or the copied values.
I think in general, you shools load a paused track, adjust the COs and then play and seek it with special engine code that achieved playing sample exact like the source track. If this is a case we will get +6 dB volume. If not we get an unwanted phaser effect.
With the latest revision, this works, but the play position is slightly behind. |
slotControlSeekExact() excepts double, there must be another problem. |
Yes, there is another problem. |
If you move thelogic to EngineBuffer:: process, you can access all data. You just need a static atomic varianle that is used to communicate between the instances if EmgineBuffer. Did you have a look at control valve atomic? IMHO that suites well as a container class for all shared data. |
I'll take a look. |
To be specific, please move just the "sync to other deck's playposition atomically" logic, not the whole instant doubles functionality! Let's try to keep as much "business logic" as possible out of the engine, as it's already a rats nest. |
It may be moot if you move this into the engine, but just for reference you can get the "track_samples" CO to get the number of samples in a deck's loaded track -- you can multiply that by playposition to get a sample value for slotControlSeekExact. |
Ahh, I wondering if you could get the non fractional position. Thanks. |
I have looked into the engine code when working on #1896 |
I'll look at that. I have been looking at adding code to EngineMaster that tells one channel to seek to the position of another. I need to figure out the best way to trigger it though. |
I pushed a new rev that performs the copy operation in the engine thread. This works perfectly, the new track is started in the exact right position. I am going to work on cleaning this up. I think I am going to use |
I cleaned this up some. Now it uses There are still some oddities around looping. It looks like the loop in and out points aren't copied to duplicate track. I'll need to set them manually. |
updated the loop copying 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.
Nice progress. Thanks
I wonder if this should be the default if you drag a track from one deck to an other.
src/engine/enginebuffer.cpp
Outdated
@@ -1137,6 +1147,12 @@ void EngineBuffer::processSyncRequests() { | |||
m_pEngineSync->requestEnableSync(m_pSyncControl, true); | |||
m_pEngineSync->requestEnableSync(m_pSyncControl, false); | |||
break; | |||
case SYNC_REQUEST_POSITION: | |||
pChannel = m_pEngineSync->pickNonSyncSyncTarget(m_pSyncControl->getChannel()); |
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 picks a kind of random other channel, but we want the source deck.
I would use a QAtomicPointer and store the source pChannel this can be checked in processSyncRequests() and if set, bypass the other flags and setNewPlaypos with the pOtherEngineBuffer->getVisualPlayPos().
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.
A pickNonSyncSyncTarget()
call is still in there but, everything has been reworked to copy from a channel passed in. I'll need some guidance on how to determine which channel to copy from though.
Added a way to trigger the copy on double tap of the load buttons. I'll address the feedback shortly. I tried writing some tests for this, but didn't get very far. I ran into issues when the clone code tries to load the track playing on the other deck. |
Ok, I think I have addressed the comments thus far. There are still some tweaks that need to be made though. |
mixxx::Duration elapsed = m_cloneTimer.restart(); | ||
if (elapsed < mixxx::Duration::fromSeconds(1)) { | ||
// load pressed twice quickly, clone instead of loading | ||
EngineChannel* pChannel = m_pEngineMaster->getEngineSync()->pickNonSyncSyncTarget(m_pChannel); |
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 needs to be replaced with something more specific and predictable, but I don't know what that is. I'll need some guidance here.
src/mixer/basetrackplayer.cpp
Outdated
|
||
// copy play state | ||
ControlObject::set(ConfigKey(getGroup(), "play"), | ||
ControlObject::get(ConfigKey(m_cloneFromChannel->getGroup(), "play"))); |
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.
previously I was triggering a play on load, but this caused the track to play before it was seeked and this was audible sometimes. Triggering the play here reduces the gap between play and seek, but it might make sense to move the play operation as part of the requestSyncPosition()
operation though.
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 attempted to play from the engine thread as part of the sync operation, but it didn't work quite right.
Yes, I think that would be the most straightforward way to expose the functionality in the GUI. |
I'll look into that. |
Updated with drag and drop functionality. Dragging a track from one deck to another clones the deck. |
"[Channel1]" , "CloneFromDeck" is just an ControlObject and expects the 1 based source deck number as value. I think you cannot map it via keyboard. |
So right now that's the idea behind the double load functionality. It clones the first playing deck. That doesn't work if you want to have two separate groups of doubling going on though or if you have three or 4 decks playing, but for most uses, just cloning the other playing deck will be sufficient. We could also just hard code cloning between decks 1+2 and 3+4, such that if the user activates clone on one of those decks, the target is always the other deck in the group. Not sure if that's better or not. Personally I like it the way it is now, I think that's more flexible. |
One of my Load buttons is faulty (sends multiple 'pressed' signals sometimes). The connected long-press function (set star rating) fails and I'd try to press again, but currently the hard-coded clone functionality gives me a deck clone and I'd have to pick and load the previous track again manually. Can I somehow opt out of that 'double-press to Load' behaviour? |
Maybe it doesn't need an option but just an improvement of the double-press detection. as described, by accident this press pattern happens with my broken button: Only trigger cloning when a simple double-tap is detected. This would be an improvement for some, while noone would suffer. |
I don't think it makes sense to compensate for broken hardware in the code like that. This could have unforseen consequences on non-broken hardware. IMO the best solution would be to detect this in your mapping script and supress the second press. That or patch your local copy. |
Okay, I'll find a way around, probably with complex timer filters in my script (as I have a long-press function already). After some quick investigation I think the issue is that the time between button releases is measured, rather in between button presses, or better: duration of both presses. Out of curiosity (and to not just cut off the disccussion):
I suggest to filter out [long press] [short press] which is a gesture not supposed to do anything, [short press] [short press] would work as before. |
The control object doesn't know anything about the duration of button presses or the difference between press and release. It just knows a load operation was requested. It's checking the timing between load operations. Thinking about this further, the mapping script is the only place you could implement your solution. Regarding potential unforseen issues. I don't know, but that's the point. If we are making assumptions about what the user or hardware is telling us (beyond the known expected behavior), there is the potential for our assumptions to be wrong and lead to unexpected behavior. Think of when auto correct fails on your smartphone keyboard. I think other users with broken hardware will just replace or repair their broken hardware. If every one of these devices was faulty in the same exact way, then it might make sense to distribute this work around in the mapping script. |
In general, I think the mapping is a actually the right place to fix it. I consider a missing denouncing in the controller firmware more likely than a hardware issue. However, something else might be wrong. Or do we have actually [looong press] [short release] [short press] [short release] [short press]? |
After further testing I think this is the case unfortuantely. It happened quite often earlier when I tried [looong press] [short press], now I can't reproduce anymore. Maybe because of this extensive button massage :) Anyway, the double-tap happens accidentially too often, and filtering this in the script is a lot of work, so I'd prefer this to be opt-out. |
Ok, this makes sense in this case. |
I won't forget as I already work on the peferences option in https://github.com/ronso0/mixxx/tree/clone-deck-opt-out but it'not working yet. |
anyone's currently working on the direct-clone COs like |
No, we decided to go the CloneFromDeck route. |
I don't mind |
The CloneFromDeck CO was implemented as part of this PR. You specify the deck number you want to clone from as the parameter. |
could you give me a working example for a script? I have a hard time scrolling through the code and expected to find something in controllers/controllerpickermenu.cpp |
The CloneFromDeck CO is documented on the wiki. I don't have a working example to share. Basically in a controller mapping, it would work like this. The function names and argument order may be wrong, but this is the idea.
That would clone from deck 2 to deck 1. |
this works: used in a script: |
Double pressing Issues
If the shortcuts for loading and cloning could be decoupled, it would make this feature even more useful. And it makes #2042 obsolete, introducing another preference option just to disable this feature is not necessary. |
The problem with only having a separate way to trigger this is that you then have to duplicate that everywhere you might trigger a load or clone. We need a way to say "load button was pressed, then pressed again, cancel the first load". That would solve your second issue and result in the dialog briefly appearing then disappearing. For first issue, I don't know Mixxx or Qt well enough to really address that. In theory it should be possible to capture any shift+left/right for the whole window and then determine the correct action. |
an idea I had while working on #2042 :
Wouldn't that solve both issues? |
#2042 would still be very helpful for controller use case IMO |
cloning comes in very handy when I play with 3 decks and want to get the running tracks back to decks 1 & 2. |
I considered this approach. This would work, but we would need to take special care to skip the delay in the "LoadAndPlay" case. Also, a delay would make loading tracks feel laggy. |
what about this: |
This is a start on implementing instant doubles functionality. As is, copying rate, and loop state works. Copying play state works sometimes. Copying pitch_adjust and playposition don't seem to work at all. I'm open to suggestions.
https://bugs.launchpad.net/mixxx/+bug/408111