From 11ec4b52ace90d2cd1afd93b876021b3ddf7174e Mon Sep 17 00:00:00 2001 From: ronso0 Date: Mon, 22 Jan 2024 12:55:43 +0100 Subject: [PATCH 1/2] Hotcue: allow setting hotcue inside active, saved loop --- src/engine/controls/cuecontrol.cpp | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/engine/controls/cuecontrol.cpp b/src/engine/controls/cuecontrol.cpp index cf46758c6cd..a9d5cb81f07 100644 --- a/src/engine/controls/cuecontrol.cpp +++ b/src/engine/controls/cuecontrol.cpp @@ -852,7 +852,25 @@ void CueControl::hotcueSet(HotcueControl* pControl, double value, HotcueSetMode bool loopEnabled = m_pLoopEnabled->toBool(); if (mode == HotcueSetMode::Auto) { - mode = loopEnabled ? HotcueSetMode::Loop : HotcueSetMode::Cue; + if (loopEnabled) { + // Don't create a hotcue at loop start if there is one already. + // This allows to set a hotuce inside an active, saved loop with + // 'hotcue_X_activate'. + auto* pSavedLoopControl = m_pCurrentSavedLoopControl.loadAcquire(); + if (pSavedLoopControl && + pSavedLoopControl->getPosition() == + mixxx::audio::FramePos::fromEngineSamplePosMaybeInvalid( + m_pLoopStartPosition->get()) && + pSavedLoopControl->getEndPosition() == + mixxx::audio::FramePos::fromEngineSamplePosMaybeInvalid( + m_pLoopEndPosition->get())) { + mode = HotcueSetMode::Cue; + } else { + mode = HotcueSetMode::Loop; + } + } else { + mode = HotcueSetMode::Cue; + } } switch (mode) { From f76b233947030e5957b433563efd6e8f2e604a65 Mon Sep 17 00:00:00 2001 From: ronso0 Date: Fri, 26 Jan 2024 11:41:43 +0100 Subject: [PATCH 2/2] Test: add SetLoopAutoNoRedundantLoopCue --- src/test/hotcuecontrol_test.cpp | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/test/hotcuecontrol_test.cpp b/src/test/hotcuecontrol_test.cpp index 77826807452..ec29f4d11ad 100644 --- a/src/test/hotcuecontrol_test.cpp +++ b/src/test/hotcuecontrol_test.cpp @@ -36,6 +36,10 @@ class HotcueControlTest : public BaseSignalPathTest { m_pHotcue1EndPosition = std::make_unique(m_sGroup1, "hotcue_1_endposition"); m_pHotcue1Enabled = std::make_unique(m_sGroup1, "hotcue_1_enabled"); m_pHotcue1Clear = std::make_unique(m_sGroup1, "hotcue_1_clear"); + m_pHotcue2Activate = std::make_unique(m_sGroup1, "hotcue_2_activate"); + m_pHotcue2Enabled = std::make_unique(m_sGroup1, "hotcue_2_enabled"); + m_pHotcue2Position = std::make_unique(m_sGroup1, "hotcue_2_position"); + m_pHotcue2EndPosition = std::make_unique(m_sGroup1, "hotcue_2_endposition"); m_pQuantizeEnabled = std::make_unique(m_sGroup1, "quantize"); } @@ -110,6 +114,10 @@ class HotcueControlTest : public BaseSignalPathTest { std::unique_ptr m_pHotcue1EndPosition; std::unique_ptr m_pHotcue1Enabled; std::unique_ptr m_pHotcue1Clear; + std::unique_ptr m_pHotcue2Activate; + std::unique_ptr m_pHotcue2Enabled; + std::unique_ptr m_pHotcue2Position; + std::unique_ptr m_pHotcue2EndPosition; std::unique_ptr m_pQuantizeEnabled; }; @@ -250,7 +258,7 @@ TEST_F(HotcueControlTest, SetCueManual) { .isValid()); } -TEST_F(HotcueControlTest, SetLoopAuto) { +TEST_F(HotcueControlTest, SetLoopAutoNoRedundantLoopCue) { createAndLoadFakeTrack(); EXPECT_DOUBLE_EQ(static_cast(HotcueControl::Status::Empty), m_pHotcue1Enabled->get()); @@ -270,6 +278,19 @@ TEST_F(HotcueControlTest, SetLoopAuto) { EXPECT_DOUBLE_EQ(static_cast(HotcueControl::Status::Active), m_pHotcue1Enabled->get()); EXPECT_FRAMEPOS_EQ_CONTROL(loopStartPosition, m_pHotcue1Position); EXPECT_FRAMEPOS_EQ_CONTROL(loopEndPosition, m_pHotcue1EndPosition); + + // Setting another hotcue inside the loop should create a regular hotcue + // at the current position, not a redundant loop cue. + setCurrentFramePosition(mixxx::audio::FramePos(195)); + ProcessBuffer(); + + m_pHotcue2Activate->set(1); + m_pHotcue2Activate->set(0); + EXPECT_DOUBLE_EQ(static_cast(HotcueControl::Status::Set), m_pHotcue2Enabled->get()); + EXPECT_FRAMEPOS_EQ_CONTROL(currentFramePosition(), m_pHotcue2Position); + EXPECT_FALSE(mixxx::audio::FramePos::fromEngineSamplePosMaybeInvalid( + m_pHotcue2EndPosition->get()) + .isValid()); } TEST_F(HotcueControlTest, SetLoopManualWithLoop) {