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

Cues & Loops upgrade for Stems (& Widget) #13759

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
144 changes: 131 additions & 13 deletions src/engine/controls/cuecontrol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,10 @@ CueControl::CueControl(const QString& group,
m_pTrackSamples = ControlObject::getControl(ConfigKey(group, "track_samples"));

m_pQuantizeEnabled = ControlObject::getControl(ConfigKey(group, "quantize"));
connect(m_pQuantizeEnabled, &ControlObject::valueChanged,
this, &CueControl::quantizeChanged,
connect(m_pQuantizeEnabled,
&ControlObject::valueChanged,
this,
&CueControl::quantizeChanged,
Qt::DirectConnection);

m_pClosestBeat = ControlObject::getControl(ConfigKey(group, "beat_closest"));
Expand Down Expand Up @@ -341,8 +343,10 @@ void CueControl::connectControls() {

// Hotcue controls
for (const auto& pControl : std::as_const(m_hotcueControls)) {
connect(pControl, &HotcueControl::hotcuePositionChanged,
this, &CueControl::hotcuePositionChanged,
connect(pControl,
&HotcueControl::hotcuePositionChanged,
this,
&CueControl::hotcuePositionChanged,
Qt::DirectConnection);
connect(pControl,
&HotcueControl::hotcueEndPositionChanged,
Expand Down Expand Up @@ -607,8 +611,8 @@ void CueControl::seekOnLoad(mixxx::audio::FramePos seekOnLoadPosition) {
}

void CueControl::cueUpdated() {
//auto lock = lockMutex(&m_mutex);
// We should get a trackCuesUpdated call anyway, so do nothing.
// auto lock = lockMutex(&m_mutex);
// We should get a trackCuesUpdated call anyway, so do nothing.
Copy link
Member

@daschuer daschuer Oct 13, 2024

Choose a reason for hiding this comment

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

Unrelated change? Befor clang format we had the rule to use "//" without space for commented code and "// " for information.

Make sure your editor doe not auto-format the whole file to avoid noise in your PRs like this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

this happens automatically with the pre-commit since the update about 10 days ago (was an update of some minutes)

}

void CueControl::loadCuesFromTrack() {
Expand Down Expand Up @@ -839,7 +843,7 @@ mixxx::RgbColor CueControl::colorFromConfig(const ConfigKey& configKey) {
};

void CueControl::hotcueSet(HotcueControl* pControl, double value, HotcueSetMode mode) {
//qDebug() << "CueControl::hotcueSet" << value;
// qDebug() << "CueControl::hotcueSet" << value;

if (value <= 0) {
return;
Expand All @@ -859,6 +863,10 @@ void CueControl::hotcueSet(HotcueControl* pControl, double value, HotcueSetMode
mixxx::audio::FramePos cueStartPosition;
mixxx::audio::FramePos cueEndPosition;
mixxx::CueType cueType = mixxx::CueType::Invalid;
int passStem1Vol;
Eve00000 marked this conversation as resolved.
Show resolved Hide resolved
int passStem2Vol;
int passStem3Vol;
int passStem4Vol;

bool loopEnabled = m_pLoopEnabled->toBool();
if (mode == HotcueSetMode::Auto) {
Expand All @@ -883,6 +891,54 @@ void CueControl::hotcueSet(HotcueControl* pControl, double value, HotcueSetMode
}
}

// EveCue-Loop
bool TrackStem = false;
Copy link
Member

Choose a reason for hiding this comment

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

bools should be named for building a "sentences" with if. Like if (hasStems) { or such. And start with a lower case.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

if (ControlObject::exists(ConfigKey(getGroup(), "stem_count"))) {
PollingControlProxy proxyStem(getGroup(), "stem_count");
Copy link
Member

Choose a reason for hiding this comment

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

we should avoid CO look up during Mixxx runtime, because this is a looking operation. Can you move this into the constructor or somewhere else? Checking for exist already creates the CO half way.
You can instead create the PollingProxy with ControlFlag::AllowMissingOrInvalid flags.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

TrackStem = proxyStem.get() > 1;
}

if (TrackStem) {
const QString groupBaseName = getGroup().remove("[").remove("]");
Copy link
Member

Choose a reason for hiding this comment

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

you remove the [ her and add it below.

Copy link
Contributor Author

@Eve00000 Eve00000 Oct 13, 2024

Choose a reason for hiding this comment

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

this was my best solution to make [ChannelXStemY] from [ChannelX]

const QString stemGroups[] = {
QString("[%1Stem1]").arg(groupBaseName),
QString("[%1Stem2]").arg(groupBaseName),
QString("[%1Stem3]").arg(groupBaseName),
QString("[%1Stem4]").arg(groupBaseName),
};

// get the mute multiplier
auto getMuteMultiplier = [](const QString& group) -> int {
if (ControlObject::exists(ConfigKey(group, "mute"))) {
auto proxyMute = std::make_unique<PollingControlProxy>(group, "mute");
return static_cast<bool>(proxyMute->get()) ? -1 : 1;
}
return 1; // Default multiplier when no mute
};

// get the volume value adjusted by the mute multiplier
auto getVolume = [](const QString& group, int muteMultiplier) -> int {
if (ControlObject::exists(ConfigKey(group, "volume"))) {
auto proxyVolume = std::make_unique<PollingControlProxy>(group, "volume");
return static_cast<int>(proxyVolume->get() * 100 * muteMultiplier);
}
return 100 * muteMultiplier; // Default value when volume not changed
};

// calc stem volume values
passStem1Vol = getVolume(stemGroups[0], getMuteMultiplier(stemGroups[0]));
passStem2Vol = getVolume(stemGroups[1], getMuteMultiplier(stemGroups[1]));
passStem3Vol = getVolume(stemGroups[2], getMuteMultiplier(stemGroups[2]));
passStem4Vol = getVolume(stemGroups[3], getMuteMultiplier(stemGroups[3]));

} else {
passStem1Vol = 100;
passStem2Vol = 100;
passStem3Vol = 100;
passStem4Vol = 100;
}
// EveCue-Loop

switch (mode) {
case HotcueSetMode::Cue: {
// If no loop is enabled, just store regular jump cue
Expand Down Expand Up @@ -954,7 +1010,11 @@ void CueControl::hotcueSet(HotcueControl* pControl, double value, HotcueSetMode
hotcueIndex,
cueStartPosition,
cueEndPosition,
color);
color,
passStem1Vol,
passStem2Vol,
passStem3Vol,
passStem4Vol);

// TODO(XXX) deal with spurious signals
attachCue(pCue, pControl);
Expand Down Expand Up @@ -1108,20 +1168,55 @@ void CueControl::hotcueCueLoop(HotcueControl* pControl, double value) {
}

void CueControl::hotcueActivate(HotcueControl* pControl, double value, HotcueSetMode mode) {
//qDebug() << "CueControl::hotcueActivate" << value;
// qDebug() << "CueControl::hotcueActivate" << value;

CuePointer pCue = pControl->getCue();
if (value > 0) {
// pressed
if (pCue && pCue->getPosition().isValid() &&
pCue->getType() != mixxx::CueType::Invalid) {
// bool TrackStem = m_pLoadedTrack->hasStem();
bool TrackStem = false;
if (ControlObject::exists(ConfigKey(getGroup(), "stem_count"))) {
PollingControlProxy proxyStem(getGroup(), "stem_count");
TrackStem = proxyStem.get() > 1;
}

if (TrackStem) {
const QString groupBaseName = getGroup().remove('[').remove(']');
const std::vector<QString> stemGroups = {
QString("[%1Stem1]").arg(groupBaseName),
QString("[%1Stem2]").arg(groupBaseName),
QString("[%1Stem3]").arg(groupBaseName),
QString("[%1Stem4]").arg(groupBaseName)};

auto setMuteAndVolume = [](const QString& group, int volume) {
if (ControlObject::exists(ConfigKey(group, "mute"))) {
auto proxyMute = std::make_unique<PollingControlProxy>(group, "mute");
proxyMute->set(volume < 1 ? 1 : 0);
}
if (ControlObject::exists(ConfigKey(group, "volume"))) {
auto proxyVol = std::make_unique<PollingControlProxy>(group, "volume");
proxyVol->set(std::abs(volume) / 100.0);
}
};

setMuteAndVolume(stemGroups[0], pCue->getStem1vol());
setMuteAndVolume(stemGroups[1], pCue->getStem2vol());
setMuteAndVolume(stemGroups[2], pCue->getStem3vol());
setMuteAndVolume(stemGroups[3], pCue->getStem4vol());
}

if (m_pPlay->toBool() && m_currentlyPreviewingIndex == Cue::kNoHotCue) {
// playing by Play button

switch (pCue->getType()) {
case mixxx::CueType::HotCue:

hotcueGoto(pControl, value);
break;
case mixxx::CueType::Loop:

if (m_pCurrentSavedLoopControl != pControl) {
setCurrentSavedLoopControlAndActivate(pControl);
} else {
Expand Down Expand Up @@ -1575,15 +1670,15 @@ void CueControl::cueDefault(double v) {

void CueControl::pause(double v) {
auto lock = lockMutex(&m_trackMutex);
//qDebug() << "CueControl::pause()" << v;
// qDebug() << "CueControl::pause()" << v;
if (v > 0.0) {
m_pPlay->set(0.0);
}
}

void CueControl::playStutter(double v) {
auto lock = lockMutex(&m_trackMutex);
//qDebug() << "playStutter" << v;
// qDebug() << "playStutter" << v;
if (v > 0.0) {
if (m_pPlay->toBool()) {
if (m_currentlyPreviewingIndex != Cue::kNoHotCue) {
Expand Down Expand Up @@ -2011,8 +2106,8 @@ void CueControl::outroEndActivate(double value) {
// This is also called from the engine thread. No locking allowed.
bool CueControl::updateIndicatorsAndModifyPlay(
bool newPlay, bool oldPlay, bool playPossible) {
//qDebug() << "updateIndicatorsAndModifyPlay" << newPlay << playPossible
// << m_iCurrentlyPreviewingHotcues << m_bPreviewing;
// qDebug() << "updateIndicatorsAndModifyPlay" << newPlay << playPossible
// << m_iCurrentlyPreviewingHotcues << m_bPreviewing;
CueMode cueMode = static_cast<CueMode>(static_cast<int>(m_pCueMode->get()));
if ((cueMode == CueMode::Denon || cueMode == CueMode::Numark) &&
newPlay && !oldPlay && playPossible &&
Expand Down Expand Up @@ -2475,6 +2570,15 @@ HotcueControl::HotcueControl(const QString& group, int hotcueIndex)
// Add an alias for the legacy hotcue_X_enabled CO
m_pHotcueStatus->addAlias(keyForControl(QStringLiteral("enabled")));

m_hotcueStem1vol = std::make_unique<ControlObject>(keyForControl(QStringLiteral("stem1vol")));
m_hotcueStem2vol = std::make_unique<ControlObject>(keyForControl(QStringLiteral("stem2vol")));
m_hotcueStem3vol = std::make_unique<ControlObject>(keyForControl(QStringLiteral("stem3vol")));
m_hotcueStem4vol = std::make_unique<ControlObject>(keyForControl(QStringLiteral("stem4vol")));
m_hotcueStem1vol->setReadOnly();
m_hotcueStem2vol->setReadOnly();
m_hotcueStem3vol->setReadOnly();
m_hotcueStem4vol->setReadOnly();

m_hotcueType = std::make_unique<ControlObject>(keyForControl(QStringLiteral("type")));
m_hotcueType->setReadOnly();

Expand Down Expand Up @@ -2685,6 +2789,7 @@ void HotcueControl::setCue(const CuePointer& pCue) {
setEndPosition(pos.endPosition);
// qDebug() << "HotcueControl::setCue";
setColor(pCue->getColor());
// setStemvol(int stemvol);
setStatus((pCue->getType() == mixxx::CueType::Invalid)
? HotcueControl::Status::Empty
: HotcueControl::Status::Set);
Expand Down Expand Up @@ -2726,6 +2831,19 @@ void HotcueControl::setType(mixxx::CueType type) {
m_hotcueType->forceSet(static_cast<double>(type));
}

void HotcueControl::setStem1vol(int stem1vol) {
m_hotcueStem1vol->set(static_cast<int>(stem1vol));
}
void HotcueControl::setStem2vol(int stem2vol) {
m_hotcueStem2vol->set(static_cast<int>(stem2vol));
}
void HotcueControl::setStem3vol(int stem3vol) {
m_hotcueStem3vol->set(static_cast<int>(stem3vol));
}
void HotcueControl::setStem4vol(int stem4vol) {
m_hotcueStem4vol->set(static_cast<int>(stem4vol));
}

void HotcueControl::setStatus(HotcueControl::Status status) {
m_pHotcueStatus->forceSet(static_cast<double>(status));
}
Expand Down
9 changes: 9 additions & 0 deletions src/engine/controls/cuecontrol.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,11 @@ class HotcueControl : public QObject {
void setColor(mixxx::RgbColor::optional_t newColor);
mixxx::RgbColor::optional_t getColor() const;

void setStem1vol(int stem1vol);
void setStem2vol(int stem2vol);
void setStem3vol(int stem3vol);
void setStem4vol(int stem4vol);

/// Used for caching the preview state of this hotcue control
/// for the case the cue is deleted during preview.
mixxx::CueType getPreviewingType() const {
Expand Down Expand Up @@ -167,6 +172,10 @@ class HotcueControl : public QObject {
std::unique_ptr<ControlObject> m_pHotcueStatus;
std::unique_ptr<ControlObject> m_hotcueType;
std::unique_ptr<ControlObject> m_hotcueColor;
std::unique_ptr<ControlObject> m_hotcueStem1vol;
std::unique_ptr<ControlObject> m_hotcueStem2vol;
std::unique_ptr<ControlObject> m_hotcueStem3vol;
std::unique_ptr<ControlObject> m_hotcueStem4vol;
// Hotcue button controls
std::unique_ptr<ControlPushButton> m_hotcueSet;
std::unique_ptr<ControlPushButton> m_hotcueSetCue;
Expand Down
51 changes: 35 additions & 16 deletions src/library/dao/cuedao.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ CuePointer cueFromRow(const QSqlRecord& row) {
int hotcue = row.value(row.indexOf("hotcue")).toInt();
QString label = labelFromQVariant(row.value(row.indexOf("label")));
mixxx::RgbColor::optional_t color = mixxx::RgbColor::fromQVariant(row.value(row.indexOf("color")));
int stem1vol = row.value(row.indexOf("stem1vol")).toInt();
int stem2vol = row.value(row.indexOf("stem2vol")).toInt();
int stem3vol = row.value(row.indexOf("stem3vol")).toInt();
int stem4vol = row.value(row.indexOf("stem4vol")).toInt();
VERIFY_OR_DEBUG_ASSERT(color) {
return CuePointer();
}
Expand All @@ -67,14 +71,19 @@ CuePointer cueFromRow(const QSqlRecord& row) {
lengthFrames,
hotcue,
label,
*color));
*color,
stem1vol,
stem2vol,
stem3vol,
stem4vol));
return pCue;
}

} // namespace

QList<CuePointer> CueDAO::getCuesForTrack(TrackId trackId) const {
//qDebug() << "CueDAO::getCuesForTrack" << QThread::currentThread() << m_database.connectionName();
// qDebug() << "CueDAO::getCuesForTrack" << QThread::currentThread() <<
// m_database.connectionName();
QList<CuePointer> cues;

FwdSqlQuery query(
Expand Down Expand Up @@ -132,13 +141,13 @@ bool CueDAO::deleteCuesForTracks(const QList<TrackId>& trackIds) const {
qDebug() << "CueDAO::deleteCuesForTracks" << QThread::currentThread() << m_database.connectionName();

QStringList idList;
for (const auto& trackId: trackIds) {
for (const auto& trackId : trackIds) {
idList << trackId.toString();
}

QSqlQuery query(m_database);
query.prepare(QStringLiteral("DELETE FROM " CUE_TABLE " WHERE track_id in (%1)")
.arg(idList.join(",")));
.arg(idList.join(",")));
if (query.exec()) {
return true;
} else {
Expand All @@ -148,7 +157,7 @@ bool CueDAO::deleteCuesForTracks(const QList<TrackId>& trackIds) const {
}

bool CueDAO::saveCue(TrackId trackId, Cue* cue) const {
//qDebug() << "CueDAO::saveCue" << QThread::currentThread() << m_database.connectionName();
// qDebug() << "CueDAO::saveCue" << QThread::currentThread() << m_database.connectionName();
VERIFY_OR_DEBUG_ASSERT(cue) {
return false;
}
Expand All @@ -158,22 +167,28 @@ bool CueDAO::saveCue(TrackId trackId, Cue* cue) const {
if (cue->getId().isValid()) {
// Update cue
query.prepare(QStringLiteral("UPDATE " CUE_TABLE " SET "
"track_id=:track_id,"
"type=:type,"
"position=:position,"
"length=:length,"
"hotcue=:hotcue,"
"label=:label,"
"color=:color"
" WHERE id=:id"));
"track_id=:track_id,"
"type=:type,"
"position=:position,"
"length=:length,"
"hotcue=:hotcue,"
"label=:label,"
"color=:color,"
"stem1vol=:stem1vol,"
"stem2vol=:stem2vol,"
"stem3vol=:stem3vol,"
"stem4vol=:stem4vol"
" WHERE id=:id"));
query.bindValue(":id", cue->getId().toVariant());
} else {
// New cue
query.prepare(
QStringLiteral("INSERT INTO " CUE_TABLE
" (track_id, type, position, length, hotcue, "
"label, color) VALUES (:track_id, :type, "
":position, :length, :hotcue, :label, :color)"));
"label, color, stem1vol, stem2vol, stem3vol, "
"stem4vol) VALUES (:track_id, :type, "
":position, :length, :hotcue, :label, :color, "
":stem1vol, :stem2vol, :stem3vol, :stem4vol)"));
}

// Bind values and execute query
Expand All @@ -183,7 +198,11 @@ bool CueDAO::saveCue(TrackId trackId, Cue* cue) const {
query.bindValue(":length", cue->getLengthFrames() * mixxx::kEngineChannelOutputCount);
query.bindValue(":hotcue", cue->getHotCue());
query.bindValue(":label", labelToQVariant(cue->getLabel()));
query.bindValue(":color", mixxx::RgbColor::toQVariant(cue->getColor()));
query.bindValue(":color", mixxx::RgbColor::toQVariant(cue->getColor())),
query.bindValue(":stem1vol", static_cast<int>(cue->getStem1vol())),
query.bindValue(":stem2vol", static_cast<int>(cue->getStem2vol())),
query.bindValue(":stem3vol", static_cast<int>(cue->getStem3vol())),
query.bindValue(":stem4vol", static_cast<int>(cue->getStem4vol()));
if (!query.exec()) {
LOG_FAILED_QUERY(query);
return false;
Expand Down
Loading
Loading