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

Mixxx Macros #2873

Closed
wants to merge 77 commits into from
Closed

Mixxx Macros #2873

wants to merge 77 commits into from

Conversation

xeruf
Copy link
Contributor

@xeruf xeruf commented Jun 15, 2020

For now only to get early feedback whether the file structure & initialization code is correct - no actual functionality.

Copy link
Member

@Holzhaus Holzhaus left a comment

Choose a reason for hiding this comment

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

I think it makes more sense to have a flip control per deck than a single one. This class boilerplate looks like it was inspired by RecordingManager, but IMHO the recording manager is unrelated and you should rather look at CueControl or LoopingControl for inspiration instead.

@uklotzde
Copy link
Contributor

Instead of introducing boilerplate classes that we might never need we should better focus on finalizing the basic concepts.

@Holzhaus
Copy link
Member

This is the format that Serato uses. It would be cool to use a format that can be made compatible for import/export: https://github.com/Holzhaus/serato-tags/blob/master/docs/serato_markers2.md#subentries-of-flip-entries

@Be-ing
Copy link
Contributor

Be-ing commented Jun 15, 2020

I can't tell if this is an appropriate structure because it doesn't do anything and I don't know what it's planned to do. I'm not sure if this class is needed, and if it would be needed, why it would need access to the UserSettings.

@xeruf
Copy link
Contributor Author

xeruf commented Jun 16, 2020

@xeruf
Copy link
Contributor Author

xeruf commented Jun 16, 2020

If we have central recording controls as serato does, we need some class like this, no?

image

@xeruf
Copy link
Contributor Author

xeruf commented Jun 17, 2020

I don't see a use case for Macro recording controls for each deck - you are not gonna record multiple macros in parallel anyways.

@Holzhaus
Copy link
Member

Holzhaus commented Jun 17, 2020

I think it makes sense because then it's all bound to a deck. It shouldn't be possible to record a macro and then save it to a slot of a different deck.

@ronso0
Copy link
Member

ronso0 commented Jun 17, 2020

I didn't wrap my head around the macro concept too much, but it make sense to have the respective controls as close as possible to the deck they're bound to.
For the mockups it's okay IMO to just add another row to decks, below transport controls.

@xeruf
Copy link
Contributor Author

xeruf commented Jun 17, 2020

I think it makes sense because then it's all bound to a deck. It shouldn't be possible to record a macro and then save it to a slot of a different deck.

This won't be possible - it remembers which deck it recorded on and will save it to that.

Please tell me a use-case where this way would improve it. How are we supposed to map the recording button if there is no single general Macro recording control? Also, it would clutter the UI.

@xeruf
Copy link
Contributor Author

xeruf commented Jun 17, 2020

I didn't wrap my head around the macro concept too much, but it make sense to have the respective controls as close as possible to the deck they're bound to.
For the mockups it's okay IMO to just add another row to decks, below transport controls.

The mockup above is almost exactly how Serato does it. Where this Row goes might change, but I'm pretty sure there is no benefit of having multiple such rows.

I don't want to discuss about what's technically architecturally "correct", I want to find a solution that makes sense. And as far as I have seen, an architecture similar to the RecordingManager does make sense for the recording part.

@xeruf
Copy link
Contributor Author

xeruf commented Jun 17, 2020

Let's continue this discussion in Zulip and don't forget that I have already written a specification for the controls.

@xeruf xeruf marked this pull request as ready for review June 22, 2020 18:30
@xeruf xeruf requested a review from Holzhaus June 22, 2020 18:30
@xeruf
Copy link
Contributor Author

xeruf commented Jun 22, 2020

It works!

Here's how to use it for now:

  • Click on the record button between the audio recording and broadcast buttons to arm recording
  • Jump to some hotcue on a deck to start recording. It will now only record this deck, and only cue jumps - feel free to try other kinds of jumps, there is a debug message printed when something was recorded
  • Click the recording button again to stop
  • The recorded jumps are printed as debug statements

@xeruf xeruf changed the title Add MacroManager Implement Macro Recording Jun 22, 2020
Copy link
Member

@Holzhaus Holzhaus left a comment

Choose a reason for hiding this comment

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

👍 Didn't have time to test it yet, but here are some first comments.

src/recording/macromanager.h Outdated Show resolved Hide resolved
src/engine/enginebuffer.cpp Outdated Show resolved Hide resolved
#define MAX_MACRO_SIZE 1000
#define NO_DECK nullptr

struct MacroAction {
Copy link
Member

Choose a reason for hiding this comment

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

Not that important for now, but please use classes for stuff that isn't in the anonymous name space.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Could you add that to the coding guidelines in the wiki? I wanted to know your policy for structs but didn't find any.

Isn't this the exact use case struts where meant for?

Copy link
Member

Choose a reason for hiding this comment

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

In C, yes. In C++, structs are just regular classes with default member visibility public instead of private.

Copy link
Contributor Author

@xeruf xeruf Jun 30, 2020

Choose a reason for hiding this comment

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

Yeah, but default visibility public is appropriate here, or not? They are almost plain data structures.

Also, please document in https://github.com/mixxxdj/mixxx/wiki/coding_guidelines

src/recording/macromanager.h Outdated Show resolved Hide resolved
src/recording/macromanager.h Outdated Show resolved Hide resolved
src/engine/enginebuffer.cpp Outdated Show resolved Hide resolved
@xeruf
Copy link
Contributor Author

xeruf commented Jun 22, 2020

Theoretically you are right about how everything should be atomic, but I can't envision a use case where it would actually make a difference, so can't we simply avoid that overhead?

@uklotzde
Copy link
Contributor

Theoretically you are right about how everything should be atomic, but I can't envision a use case where it would actually make a difference, so can't we simply avoid that overhead?

No.

@Be-ing
Copy link
Contributor

Be-ing commented Jun 23, 2020

can't we simply avoid that overhead?

Code as if the worst case scenario is happening all the time, because it will happen sometimes.

@Holzhaus
Copy link
Member

I'm unable to build this:

/home/jan/Projects/mixxx/src/engine/enginebuffer.cpp: In member function 'void EngineBuffer::slotControlSeekAbs(double)':
/home/jan/Projects/mixxx/src/engine/enginebuffer.cpp:606:40: error: qualified-id in declaration before '(' token
  606 | void EngineBuffer::slotControlSeekExact(double playPosition) {
      |                                        ^
/home/jan/Projects/mixxx/src/engine/enginebuffer.cpp:610:36: error: qualified-id in declaration before '(' token
  610 | void EngineBuffer::doSeekFractional(double fractionalPos, enum SeekRequest seekType) {
      |                                    ^
/home/jan/Projects/mixxx/src/engine/enginebuffer.cpp:619:33: error: qualified-id in declaration before '(' token
  619 | void EngineBuffer::doSeekPlayPos(double new_playpos, enum SeekRequest seekType) {
      |                                 ^
/home/jan/Projects/mixxx/src/engine/enginebuffer.cpp:631:49: error: qualified-id in declaration before '(' token
  631 | bool EngineBuffer::updateIndicatorsAndModifyPlay(bool newPlay) {
      |                                                 ^
/home/jan/Projects/mixxx/src/engine/enginebuffer.cpp:647:30: error: qualified-id in declaration before '(' token
  647 | void EngineBuffer::verifyPlay() {
      |                              ^
/home/jan/Projects/mixxx/src/engine/enginebuffer.cpp:655:42: error: qualified-id in declaration before '(' token
  655 | void EngineBuffer::slotControlPlayRequest(double v) {
      |                                          ^
/home/jan/Projects/mixxx/src/engine/enginebuffer.cpp:673:36: error: qualified-id in declaration before '(' token
  673 | void EngineBuffer::slotControlStart(double v)
      |                                    ^
/home/jan/Projects/mixxx/src/engine/enginebuffer.cpp:680:34: error: qualified-id in declaration before '(' token
  680 | void EngineBuffer::slotControlEnd(double v)
      |                                  ^
/home/jan/Projects/mixxx/src/engine/enginebuffer.cpp:687:44: error: qualified-id in declaration before '(' token
  687 | void EngineBuffer::slotControlPlayFromStart(double v)
      |                                            ^
/home/jan/Projects/mixxx/src/engine/enginebuffer.cpp:695:49: error: qualified-id in declaration before '(' token
  695 | void EngineBuffer::slotControlJumpToStartAndStop(double v)
      |                                                 ^
/home/jan/Projects/mixxx/src/engine/enginebuffer.cpp:703:35: error: qualified-id in declaration before '(' token
  703 | void EngineBuffer::slotControlStop(double v)
      |                                   ^
/home/jan/Projects/mixxx/src/engine/enginebuffer.cpp:710:44: error: qualified-id in declaration before '(' token
  710 | void EngineBuffer::slotKeylockEngineChanged(double dIndex) {
      |                                            ^
/home/jan/Projects/mixxx/src/engine/enginebuffer.cpp:724:38: error: qualified-id in declaration before '(' token
  724 | void EngineBuffer::processTrackLocked(
      |                                      ^
/home/jan/Projects/mixxx/src/engine/enginebuffer.cpp:1031:27: error: qualified-id in declaration before '(' token
 1031 | void EngineBuffer::process(CSAMPLE* pOutput, const int iBufferSize) {
      |                           ^
/home/jan/Projects/mixxx/src/engine/enginebuffer.cpp:1102:31: error: qualified-id in declaration before '(' token
 1102 | void EngineBuffer::processSlip(int iBufferSize) {
      |                               ^
/home/jan/Projects/mixxx/src/engine/enginebuffer.cpp:1125:39: error: qualified-id in declaration before '(' token
 1125 | void EngineBuffer::processSyncRequests() {
      |                                       ^
/home/jan/Projects/mixxx/src/engine/enginebuffer.cpp:1151:31: error: qualified-id in declaration before '(' token
 1151 | void EngineBuffer::processSeek(bool paused) {
      |                               ^
/home/jan/Projects/mixxx/src/engine/enginebuffer.cpp:1226:31: error: qualified-id in declaration before '(' token
 1226 | void EngineBuffer::postProcess(const int iBufferSize) {
      |                               ^
/home/jan/Projects/mixxx/src/engine/enginebuffer.cpp:1250:36: error: qualified-id in declaration before '(' token
 1250 | void EngineBuffer::updateIndicators(double speed, int iBufferSize) {
      |                                    ^
/home/jan/Projects/mixxx/src/engine/enginebuffer.cpp:1297:30: error: qualified-id in declaration before '(' token
 1297 | void EngineBuffer::hintReader(const double dRate) {
      |                              ^
/home/jan/Projects/mixxx/src/engine/enginebuffer.cpp:1321:29: error: qualified-id in declaration before '(' token
 1321 | void EngineBuffer::loadTrack(TrackPointer pTrack, bool play) {
      |                             ^
/home/jan/Projects/mixxx/src/engine/enginebuffer.cpp:1333:30: error: qualified-id in declaration before '(' token
 1333 | void EngineBuffer::addControl(EngineControl* pControl) {
      |                              ^
/home/jan/Projects/mixxx/src/engine/enginebuffer.cpp:1339:31: error: qualified-id in declaration before '(' token
 1339 | void EngineBuffer::bindWorkers(EngineWorkerScheduler* pWorkerScheduler) {
      |                               ^
/home/jan/Projects/mixxx/src/engine/enginebuffer.cpp:1343:33: error: qualified-id in declaration before '(' token
 1343 | bool EngineBuffer::isTrackLoaded() {
      |                                 ^
/home/jan/Projects/mixxx/src/engine/enginebuffer.cpp:1350:41: error: qualified-id in declaration before '(' token
 1350 | bool EngineBuffer::getQueuedSeekPosition(double* pSeekPosition) {
      |                                         ^
/home/jan/Projects/mixxx/src/engine/enginebuffer.cpp:1360:34: error: qualified-id in declaration before '(' token
 1360 | void EngineBuffer::slotEjectTrack(double v) {
      |                                  ^
/home/jan/Projects/mixxx/src/engine/enginebuffer.cpp:1371:37: error: qualified-id in declaration before '(' token
 1371 | double EngineBuffer::getExactPlayPos() {
      |                                     ^
/home/jan/Projects/mixxx/src/engine/enginebuffer.cpp:1382:38: error: qualified-id in declaration before '(' token
 1382 | double EngineBuffer::getVisualPlayPos() {
      |                                      ^
/home/jan/Projects/mixxx/src/engine/enginebuffer.cpp:1386:37: error: qualified-id in declaration before '(' token
 1386 | double EngineBuffer::getTrackSamples() {
      |                                     ^
/home/jan/Projects/mixxx/src/engine/enginebuffer.cpp:1390:36: error: qualified-id in declaration before '(' token
 1390 | void EngineBuffer::setScalerForTest(EngineBufferScale* pScaleVinyl,
      |                                    ^
/home/jan/Projects/mixxx/src/engine/enginebuffer.cpp:1401:35: error: qualified-id in declaration before '(' token
 1401 | void EngineBuffer::collectFeatures(GroupFeatureState* pGroupFeatures) const {
      |                                   ^
/home/jan/Projects/mixxx/src/engine/enginebuffer.cpp:1407:34: error: qualified-id in declaration before '(' token
 1407 | double EngineBuffer::getRateRatio() const {
      |                                  ^
/home/jan/Projects/mixxx/src/engine/enginebuffer.cpp:1414:37: error: qualified-id in declaration before '(' token
 1414 | void EngineBuffer::notifyTrackLoaded(
      |                                     ^
/home/jan/Projects/mixxx/src/engine/enginebuffer.cpp:1443:41: error: qualified-id in declaration before '(' token
 1443 | void EngineBuffer::slotUpdatedTrackBeats() {
      |                                         ^
/home/jan/Projects/mixxx/src/engine/enginebuffer.cpp:1450:1: error: expected '}' at end of input
 1450 | }
      | ^
/home/jan/Projects/mixxx/src/engine/enginebuffer.cpp:593:60: note: to match this '{'
  593 | void EngineBuffer::slotControlSeekAbs(double playPosition) {
      |                                                            ^

@Holzhaus
Copy link
Member

After adding a closing brace manually, compilation works but I'm getting a bunch of linker errors:

/usr/bin/ld: /home/jan/Projects/mixxx/src/mixxx.cpp:1178: undefined reference to `RecordingManagerBase::isRecordingActive() const'

@Holzhaus
Copy link
Member

Holzhaus commented Jun 24, 2020

@xerus2000 We don't want broken commits in our history, so please fix them. My workflow for this is committing the fixes as small atomic commits, then rebase and copy each line with a fixing commit directly behind it's corresponding broken commit and replace pick with fixup in the fixing commit line. Then force-push.

@xeruf
Copy link
Contributor Author

xeruf commented Jun 24, 2020

Yeah, I usually watch out for this as well. It built fine for me, but I'll check again.

@xeruf
Copy link
Contributor Author

xeruf commented Jun 24, 2020

Yeah, messed up the CMakeList again and didn't notice it due to cache. I fixed it in an extra commit - because of the merge rebasing didn't work properly.

src/macros/macrorecorder.h Show resolved Hide resolved
src/macros/macrorecorder.h Outdated Show resolved Hide resolved
src/macros/macrorecorder.cpp Outdated Show resolved Hide resolved
auto armed = MacroState::Armed;
// TODO(xerus) add concurrency test
while (!m_macroRecordingState.compare_exchange_weak(armed, MacroState::Disabled))
QThread::yieldCurrentThread();
Copy link
Member

Choose a reason for hiding this comment

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

If we need to reset the recording state to armed to ensure that the macro has been fully written, you should just use SPSCQueue instead.

@xerus2000 ping.

For Library & MacroRecorder the public definition of their
config/control group key was removed and replaced by an entry in the
anonymous namespace in the cpp file.
src/macros/macro.h Outdated Show resolved Hide resolved
Copy link
Member

@Holzhaus Holzhaus left a comment

Choose a reason for hiding this comment

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

Looks good so far, thanks!

res/schema.xml Show resolved Hide resolved
src/database/mixxxdb.cpp Outdated Show resolved Hide resolved
Remove all mutability functionality which was previously needed for
recording, instead the Macro class is now used for playback.
@xeruf
Copy link
Contributor Author

xeruf commented Aug 4, 2020

Woops. Well, we can start with a blank slate then.

@Holzhaus
Copy link
Member

Holzhaus commented Aug 4, 2020

Huh? Why did you close this?

@xeruf
Copy link
Contributor Author

xeruf commented Aug 4, 2020

Accidentally deleted the branch ^^
But it's not that bad, we have basically resolved all comments here and it was getting really tedious to scroll through all the old stuff.

@xeruf xeruf mentioned this pull request Aug 4, 2020
13 tasks
@ronso0
Copy link
Member

ronso0 commented Aug 4, 2020

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants