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

Stems-upgrade of Cues & Loops #9

Open
wants to merge 19 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.
}

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;
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;
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 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