Skip to content

Commit

Permalink
Merge branch 'saved-loops' of github.com:Holzhaus/mixxx into saved-loops
Browse files Browse the repository at this point in the history
  • Loading branch information
Holzhaus committed Jul 8, 2019
2 parents 2c431c5 + 68cd170 commit d46d07d
Show file tree
Hide file tree
Showing 19 changed files with 632 additions and 94 deletions.
408 changes: 394 additions & 14 deletions src/engine/controls/cuecontrol.cpp

Large diffs are not rendered by default.

77 changes: 75 additions & 2 deletions src/engine/controls/cuecontrol.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "track/track.h"

#define NUM_HOT_CUES 37
#define NUM_SAVED_LOOPS 37

class ControlObject;
class ControlPushButton;
Expand Down Expand Up @@ -111,6 +112,63 @@ class HotcueControl : public QObject {
double m_previewingPosition;
};

class SavedLoopControl : public QObject {
Q_OBJECT
public:
SavedLoopControl(QString group, int savedLoopNumber);
virtual ~SavedLoopControl();

inline int getSavedLoopNumber() { return m_iSavedLoopNumber; }
inline CuePointer getCue() { return m_pCue; }
double getPosition() const;
double getLength() const;
void setCue(CuePointer pCue);
void resetCue();
void setPosition(double position);
void setLength(double length);
void setColor(PredefinedColorPointer newColor);
PredefinedColorPointer getColor() const;

private slots:
void slotSavedLoopSet(double v);
void slotSavedLoopActivate(double v);
void slotSavedLoopReloop(double v);
void slotSavedLoopClear(double v);
void slotSavedLoopPositionChanged(double newPosition);
void slotSavedLoopLengthChanged(double newLength);
void slotSavedLoopColorChanged(double newColorId);

signals:
void savedLoopSet(SavedLoopControl* pSavedLoop, double v);
void savedLoopActivate(SavedLoopControl* pSavedLoop, double v);
void savedLoopReloop(SavedLoopControl* pSavedLoop, double v);
void savedLoopClear(SavedLoopControl* pSavedLoop, double v);
void savedLoopPositionChanged(SavedLoopControl* pSavedLoop, double newPosition);
void savedLoopLengthChanged(SavedLoopControl* pSavedLoop, double newLength);
void savedLoopColorChanged(SavedLoopControl* pSavedLoop, double newColorId);

private:
ConfigKey keyForControl(int savedLoop, const char* name);

QString m_group;
int m_iSavedLoopNumber;
CuePointer m_pCue;

// SavedLoop state controls
ControlObject* m_savedLoopPosition;
ControlObject* m_savedLoopLength;
ControlObject* m_savedLoopEnabled;
ControlObject* m_savedLoopColor;
// SavedLoop button controls
ControlObject* m_savedLoopSet;
ControlObject* m_savedLoopActivate;
ControlObject* m_savedLoopReloop;
ControlObject* m_savedLoopClear;

bool m_bPreviewing;
double m_previewingPosition;
};

class CueControl : public EngineControl {
Q_OBJECT
public:
Expand Down Expand Up @@ -145,6 +203,13 @@ class CueControl : public EngineControl {
void hotcueClear(HotcueControl* pControl, double v);
void hotcuePositionChanged(HotcueControl* pControl, double newPosition);

void savedLoopSet(SavedLoopControl* pControl, double v);
void savedLoopActivate(SavedLoopControl* pControl, double v);
void savedLoopReloop(SavedLoopControl* pControl, double v);
void savedLoopClear(SavedLoopControl* pControl, double v);
void savedLoopPositionChanged(SavedLoopControl* pControl, double newPosition);
void savedLoopLengthChanged(SavedLoopControl* pControl, double newPosition);

void cueSet(double v);
void cueClear(double v);
void cueGoto(double v);
Expand Down Expand Up @@ -186,8 +251,10 @@ class CueControl : public EngineControl {

// These methods are not thread safe, only call them when the lock is held.
void createControls();
void attachCue(CuePointer pCue, int hotcueNumber);
void detachCue(int hotcueNumber);
void attachHotcue(CuePointer pCue, int hotcueNumber);
void detachHotcue(int hotcueNumber);
void attachSavedLoop(CuePointer pCue, int savedLoopNumber);
void detachSavedLoop(int savedLoopNumber);
void loadCuesFromTrack();
void reloadCuesFromTrack();
double quantizeCuePoint(double position, Cue::CueSource source, QuantizeMode mode);
Expand All @@ -202,11 +269,17 @@ class CueControl : public EngineControl {
ControlObject* m_pPrevBeat;
ControlObject* m_pNextBeat;
ControlObject* m_pClosestBeat;
ControlProxy* m_pLoopStartPosition;
ControlProxy* m_pLoopEndPosition;
ControlProxy* m_pLoopEnabled;
bool m_bypassCueSetByPlay;

const int m_iNumHotCues;
QList<HotcueControl*> m_hotcueControls;

const int m_iNumSavedLoops;
QList<SavedLoopControl*> m_savedLoopControls;

ControlObject* m_pTrackSamples;
ControlObject* m_pCuePoint;
ControlObject* m_pCueMode;
Expand Down
6 changes: 6 additions & 0 deletions src/engine/controls/enginecontrol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@ EngineBuffer* EngineControl::getEngineBuffer() {
return m_pEngineBuffer;
}

void EngineControl::setLoop(double startPosition, double endPosition, bool reloop) {
if (m_pEngineBuffer) {
m_pEngineBuffer->setLoop(startPosition, endPosition, reloop);
}
}

void EngineControl::seekAbs(double samplePosition) {
if (m_pEngineBuffer) {
m_pEngineBuffer->slotControlSeekAbs(samplePosition);
Expand Down
2 changes: 2 additions & 0 deletions src/engine/controls/enginecontrol.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ class EngineControl : public QObject {
const double dTotalSamples, const double dTrackSampleRate);
QString getGroup() const;

void setLoop(double startPosition, double endPosition, bool reloop);

// Called to collect player features for effects processing.
virtual void collectFeatureState(GroupFeatureState* pGroupFeatures) const {
Q_UNUSED(pGroupFeatures);
Expand Down
95 changes: 64 additions & 31 deletions src/engine/controls/loopingcontrol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ LoopingControl::LoopingControl(QString group,
m_bAdjustingLoopInOld(false),
m_bAdjustingLoopOutOld(false),
m_bLoopOutPressedWhileLoopDisabled(false) {
m_oldLoopSamples = { kNoTrigger, kNoTrigger, false };
m_oldLoopSamples = { kNoTrigger, kNoTrigger, LoopSeekMode::MOVED_OUT };
m_loopSamples.setValue(m_oldLoopSamples);
m_currentSample.setValue(0.0);
m_pActiveBeatLoop = NULL;
Expand Down Expand Up @@ -279,7 +279,7 @@ void LoopingControl::slotLoopScale(double scaleFactor) {
}

// Reseek if the loop shrank out from under the playposition.
loopSamples.seek = (m_bLoopingEnabled && scaleFactor < 1.0);
loopSamples.seekMode = (m_bLoopingEnabled && scaleFactor < 1.0) ? LoopSeekMode::CHANGED : LoopSeekMode::MOVED_OUT;

m_loopSamples.setValue(loopSamples);

Expand Down Expand Up @@ -324,7 +324,7 @@ void LoopingControl::process(const double dRate,
if (loopSamples.start != m_oldLoopSamples.start ||
loopSamples.end != m_oldLoopSamples.end) {
// bool seek is only valid after the loop has changed
if (loopSamples.seek) {
if (loopSamples.seekMode == LoopSeekMode::CHANGED) {
// here the loop has changed and the play position
// should be moved with it
double target = seekInsideAdjustedLoop(currentSample,
Expand Down Expand Up @@ -379,27 +379,34 @@ double LoopingControl::nextTrigger(bool reverse,
if (loopSamples.start != m_oldLoopSamples.start ||
loopSamples.end != m_oldLoopSamples.end) {
// bool seek is only valid after the loop has changed
if (loopSamples.seek) {
// here the loop has changed and the play position
// should be moved with it
*pTarget = seekInsideAdjustedLoop(currentSample,
m_oldLoopSamples.start, loopSamples.start, loopSamples.end);
} else {
bool movedOut = false;
// Check if we have moved out of the loop, before we could enable it
if (reverse) {
if (loopSamples.start > currentSample) {
movedOut = true;
bool movedOut;
switch(loopSamples.seekMode) {
case LoopSeekMode::CHANGED:
// here the loop has changed and the play position
// should be moved with it
*pTarget = seekInsideAdjustedLoop(currentSample,
m_oldLoopSamples.start, loopSamples.start, loopSamples.end);
break;
case LoopSeekMode::MOVED_OUT:
movedOut = false;
// Check if we have moved out of the loop, before we could enable it
if (reverse) {
if (loopSamples.start > currentSample) {
movedOut = true;
}
} else {
if (loopSamples.end < currentSample) {
movedOut = true;
}
}
} else {
if (loopSamples.end < currentSample) {
movedOut = true;
if (movedOut) {
*pTarget = seekInsideAdjustedLoop(currentSample,
loopSamples.start, loopSamples.start, loopSamples.end);
}
}
if (movedOut) {
*pTarget = seekInsideAdjustedLoop(currentSample,
loopSamples.start, loopSamples.start, loopSamples.end);
}
break;
case LoopSeekMode::NONE:
// Nothing to do here
break;
}
m_oldLoopSamples = loopSamples;
if (*pTarget != kNoTrigger) {
Expand Down Expand Up @@ -452,6 +459,32 @@ void LoopingControl::hintReader(HintVector* pHintList) {
}
}

void LoopingControl::setLoop(double startPosition, double endPosition, bool reloop) {
qDebug() << "setLoop" << startPosition << endPosition << reloop;

LoopSamples loopSamples = m_loopSamples.getValue();
if (loopSamples.start != startPosition || loopSamples.end != endPosition || loopSamples.seekMode != LoopSeekMode::NONE) {
loopSamples.start = startPosition;
loopSamples.end = endPosition;
loopSamples.seekMode = LoopSeekMode::NONE;

clearActiveBeatLoop();

m_loopSamples.setValue(loopSamples);
m_pCOLoopStartPosition->set(loopSamples.start);
m_pCOLoopEndPosition->set(loopSamples.end);
setLoopingEnabled(true);
} else {
setLoopingEnabled(m_bLoopingEnabled ? reloop : true);
}

if (reloop) {
// seekExact is necessary here to prevent notifySeek() from disabling our loop
seekExact(static_cast<double>(
m_loopSamples.getValue().start));
}
}

void LoopingControl::setLoopInToCurrentPosition() {
// set loop-in position
BeatsPointer pBeats = m_pBeats;
Expand Down Expand Up @@ -505,9 +538,9 @@ void LoopingControl::setLoopInToCurrentPosition() {
if (loopSamples.start != kNoTrigger &&
loopSamples.end != kNoTrigger) {
setLoopingEnabled(true);
loopSamples.seek = true;
loopSamples.seekMode = LoopSeekMode::CHANGED;
} else {
loopSamples.seek = false;
loopSamples.seekMode = LoopSeekMode::MOVED_OUT;
}

if (m_pQuantizeEnabled->toBool()
Expand Down Expand Up @@ -605,9 +638,9 @@ void LoopingControl::setLoopOutToCurrentPosition() {
if (loopSamples.start != kNoTrigger &&
loopSamples.end != kNoTrigger) {
setLoopingEnabled(true);
loopSamples.seek = true;
loopSamples.seekMode = LoopSeekMode::CHANGED;
} else {
loopSamples.seek = false;
loopSamples.seekMode = LoopSeekMode::MOVED_OUT;
}

if (m_pQuantizeEnabled->toBool() && pBeats) {
Expand Down Expand Up @@ -726,7 +759,7 @@ void LoopingControl::slotLoopStartPos(double pos) {
setLoopingEnabled(false);
}

loopSamples.seek = false;
loopSamples.seekMode = LoopSeekMode::MOVED_OUT;
loopSamples.start = pos;
m_pCOLoopStartPosition->set(pos);

Expand Down Expand Up @@ -762,7 +795,7 @@ void LoopingControl::slotLoopEndPos(double pos) {
setLoopingEnabled(false);
}
loopSamples.end = pos;
loopSamples.seek = false;
loopSamples.seekMode = LoopSeekMode::MOVED_OUT;
m_pCOLoopEndPosition->set(pos);
m_loopSamples.setValue(loopSamples);
}
Expand Down Expand Up @@ -980,7 +1013,7 @@ void LoopingControl::slotBeatLoop(double beats, bool keepStartPoint, bool enable

// Calculate the new loop start and end samples
// give start and end defaults so we can detect problems
LoopSamples newloopSamples = {kNoTrigger, kNoTrigger, false};
LoopSamples newloopSamples = {kNoTrigger, kNoTrigger, LoopSeekMode::MOVED_OUT};
LoopSamples loopSamples = m_loopSamples.getValue();
double currentSample = m_currentSample.getValue();

Expand Down Expand Up @@ -1067,7 +1100,7 @@ void LoopingControl::slotBeatLoop(double beats, bool keepStartPoint, bool enable

// If resizing an inactive loop by changing beatloop_size,
// do not seek to the adjusted loop.
newloopSamples.seek = (keepStartPoint && (enable || m_bLoopingEnabled));
newloopSamples.seekMode = (keepStartPoint && (enable || m_bLoopingEnabled)) ? LoopSeekMode::CHANGED : LoopSeekMode::MOVED_OUT;

m_loopSamples.setValue(newloopSamples);
m_pCOLoopStartPosition->set(newloopSamples.start);
Expand Down Expand Up @@ -1169,7 +1202,7 @@ void LoopingControl::slotLoopMove(double beats) {

// If we are looping make sure that the play head does not leave the
// loop as a result of our adjustment.
loopSamples.seek = m_bLoopingEnabled;
loopSamples.seekMode = m_bLoopingEnabled ? LoopSeekMode::CHANGED : LoopSeekMode::MOVED_OUT;

loopSamples.start = new_loop_in;
loopSamples.end = new_loop_out;
Expand Down
9 changes: 8 additions & 1 deletion src/engine/controls/loopingcontrol.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ class LoopingControl : public EngineControl {

void notifySeek(double dNewPlaypos, bool adjustingPhase) override;

void setLoop(double startPosition, double endPosition, bool reloop);

bool isLoopingEnabled();

public slots:
Expand Down Expand Up @@ -89,11 +91,16 @@ class LoopingControl : public EngineControl {
void slotLoopHalve(double pressed);

private:
enum LoopSeekMode {
CHANGED, // force the playposition to be inside the loop after adjusting it.
MOVED_OUT,
NONE,
};

struct LoopSamples {
double start;
double end;
bool seek; // force the playposition to be inside the loop after adjusting it.
LoopSeekMode seekMode;
};

void setLoopingEnabled(bool enabled);
Expand Down
2 changes: 1 addition & 1 deletion src/engine/controls/vinylcontrolcontrol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ void VinylControlControl::slotControlVinylSeek(double fractionalPos) {
QListIterator<CuePointer> it(cuePoints);
while (it.hasNext()) {
CuePointer pCue(it.next());
if (pCue->getType() != Cue::CUE || pCue->getHotCue() == -1) {
if (pCue->getType() != Cue::CUE || pCue->getNumber() == -1) {
continue;
}

Expand Down
4 changes: 4 additions & 0 deletions src/engine/enginebuffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,10 @@ double EngineBuffer::getLocalBpm() {
return m_pBpmControl->getLocalBpm();
}

void EngineBuffer::setLoop(double startPosition, double endPosition, bool reloop) {
m_pLoopingControl->setLoop(startPosition, endPosition, reloop);
}

void EngineBuffer::setEngineMaster(EngineMaster* pEngineMaster) {
for (const auto& pControl: qAsConst(m_engineControls)) {
pControl->setEngineMaster(pEngineMaster);
Expand Down
2 changes: 2 additions & 0 deletions src/engine/enginebuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ class EngineBuffer : public EngineObject {
double getBpm();
// Returns the BPM of the loaded track around the current position (not thread-safe)
double getLocalBpm();
// Sets a loop for the loaded track (not thread safe)
void setLoop(double, double, bool);
// Sets pointer to other engine buffer/channel
void setEngineMaster(EngineMaster*);

Expand Down
4 changes: 2 additions & 2 deletions src/library/dao/cuedao.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ bool CueDAO::saveCue(Cue* cue) {
query.bindValue(":type", cue->getType());
query.bindValue(":position", cue->getPosition());
query.bindValue(":length", cue->getLength());
query.bindValue(":hotcue", cue->getHotCue());
query.bindValue(":hotcue", cue->getNumber());
query.bindValue(":label", cue->getLabel());
query.bindValue(":color", cue->getColor()->m_iId);

Expand Down Expand Up @@ -177,7 +177,7 @@ bool CueDAO::saveCue(Cue* cue) {
query.bindValue(":type", cue->getType());
query.bindValue(":position", cue->getPosition());
query.bindValue(":length", cue->getLength());
query.bindValue(":hotcue", cue->getHotCue());
query.bindValue(":hotcue", cue->getNumber());
query.bindValue(":label", cue->getLabel());
query.bindValue(":color", cue->getColor()->m_iId);

Expand Down

0 comments on commit d46d07d

Please sign in to comment.