Skip to content
Merged
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
5 changes: 5 additions & 0 deletions src/app/configs/data/shortcuts.xml
Original file line number Diff line number Diff line change
Expand Up @@ -768,6 +768,11 @@
<seq>Space</seq>
<autorepeat>0</autorepeat>
</SC>
<SC>
<key>pause-and-select</key>
<seq>Ctrl+Space</seq>
<autorepeat>0</autorepeat>
</SC>
<SC>
<key>play-prev-chord</key>
<seq>Left</seq>
Expand Down
5 changes: 5 additions & 0 deletions src/app/configs/data/shortcuts_azerty.xml
Original file line number Diff line number Diff line change
Expand Up @@ -794,6 +794,11 @@
<seq>Space</seq>
<autorepeat>0</autorepeat>
</SC>
<SC>
<key>pause-and-select</key>
<seq>Ctrl+Space</seq>
<autorepeat>0</autorepeat>
</SC>
<SC>
<key>play-prev-chord</key>
<seq>Left</seq>
Expand Down
5 changes: 5 additions & 0 deletions src/app/configs/data/shortcuts_mac.xml
Original file line number Diff line number Diff line change
Expand Up @@ -768,6 +768,11 @@
<seq>Space</seq>
<autorepeat>0</autorepeat>
</SC>
<SC>
<key>pause-and-select</key>
<seq>Alt+Space</seq>
<autorepeat>0</autorepeat>
</SC>
<SC>
<key>play-prev-chord</key>
<seq>Left</seq>
Expand Down
1 change: 1 addition & 0 deletions src/notation/inotationinteraction.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ class INotationInteraction
virtual void clearSelection() = 0;
virtual muse::async::Notification selectionChanged() const = 0;
virtual void selectTopOrBottomOfChord(MoveDirection d) = 0;
virtual void findAndSelectChordRest(const Fraction& tick) = 0;

virtual EngravingItem* contextItem() const = 0;

Expand Down
62 changes: 62 additions & 0 deletions src/notation/internal/notationinteraction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -871,6 +871,68 @@ void NotationInteraction::selectTopOrBottomOfChord(MoveDirection d)
showItem(target);
}

void NotationInteraction::findAndSelectChordRest(const Fraction& tick)
{
IF_ASSERT_FAILED(selection() && score()) {
return;
}

Segment* startSeg = score()->tick2leftSegment(tick, /*useMMrest*/ false, SegmentType::ChordRest);
IF_ASSERT_FAILED(startSeg && startSeg->isChordRestType()) {
return;
}

staff_idx_t lastSelectedStaffIdx = 0;
voice_idx_t lastSelectedVoiceIdx = 0;
if (selection()->isRange()) {
lastSelectedStaffIdx = selection()->range()->startStaffIndex();
} else if (!selection()->elements().empty()) {
const EngravingItem* lastSelectedItem = selection()->elements().back();
if (lastSelectedItem) {
lastSelectedStaffIdx = lastSelectedItem->staffIdx();
lastSelectedVoiceIdx = lastSelectedItem->voice();
}
}

// startSeg could be a CR segment in any staff. The idea of this method is to select the CR in the last selected
// staff (or the top staff if none was selected). The following outer loop iterates backwards through previous CR
// segments until a valid CR is found in the desired staff...
EngravingItem* toSelect = nullptr;
for (Segment* currSeg = startSeg; currSeg && !toSelect; currSeg = currSeg->prev(SegmentType::ChordRest)) {
toSelect = currSeg->element(staff2track(lastSelectedStaffIdx, lastSelectedVoiceIdx));
if (toSelect && toSelect->isChordRest()) {
break;
}
toSelect = nullptr;
// Inner loop: Couldn't find a valid CR in the last selected voice - try other voices in the same staff...
for (voice_idx_t currVoice = 0; currVoice < VOICES; ++currVoice) {
if (currVoice == lastSelectedVoiceIdx) {
// Already tried this...
continue;
}
toSelect = currSeg->element(staff2track(lastSelectedStaffIdx, currVoice));
if (toSelect && toSelect->isChordRest()) {
break;
}
toSelect = nullptr;
}
}

IF_ASSERT_FAILED(toSelect) {
return;
}

std::vector<EngravingItem*> itemsToSelect;
if (toSelect->isChord()) {
const std::vector<Note*>& notes = toChord(toSelect)->notes();
itemsToSelect.insert(itemsToSelect.end(), notes.begin(), notes.end());
} else {
itemsToSelect = { toSelect };
}

select(itemsToSelect, SelectType::REPLACE);
}

void NotationInteraction::select(const std::vector<EngravingItem*>& elements, SelectType type, staff_idx_t staffIndex)
{
TRACEFUNC;
Expand Down
1 change: 1 addition & 0 deletions src/notation/internal/notationinteraction.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ class NotationInteraction : public INotationInteraction, public muse::Injectable
void clearSelection() override;
muse::async::Notification selectionChanged() const override;
void selectTopOrBottomOfChord(MoveDirection d) override;
void findAndSelectChordRest(const Fraction& tick) override;
void moveSegmentSelection(MoveDirection d) override;

EngravingItem* contextItem() const override;
Expand Down
1 change: 1 addition & 0 deletions src/notation/tests/mocks/notationinteractionmock.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ class NotationInteractionMock : public INotationInteraction
MOCK_METHOD(void, clearSelection, (), (override));
MOCK_METHOD(muse::async::Notification, selectionChanged, (), (const, override));
MOCK_METHOD(void, selectTopOrBottomOfChord, (MoveDirection), (override));
MOCK_METHOD(void, findAndSelectChordRest, (const Fraction&), (override));

MOCK_METHOD(INotationSelectionFilterPtr, selectionFilter, (), (const, override));

Expand Down
31 changes: 28 additions & 3 deletions src/playback/internal/playbackcontroller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ using namespace mu::playback;

static const ActionCode PLAY_CODE("play");
static const ActionCode STOP_CODE("stop");
static const ActionCode PAUSE_AND_SELECT_CODE("pause-and-select");
static const ActionCode REWIND_CODE("rewind");
static const ActionCode LOOP_CODE("loop");
static const ActionCode LOOP_IN_CODE("loop-in");
Expand Down Expand Up @@ -92,7 +93,8 @@ static std::string resolveAuxTrackTitle(aux_channel_idx_t index, const AudioOutp
void PlaybackController::init()
{
dispatcher()->reg(this, PLAY_CODE, this, &PlaybackController::togglePlay);
dispatcher()->reg(this, STOP_CODE, this, &PlaybackController::pause);
dispatcher()->reg(this, STOP_CODE, [this]() { PlaybackController::pause(/*select*/ false); });
dispatcher()->reg(this, PAUSE_AND_SELECT_CODE, [this]() { PlaybackController::pause(/*select*/ true); });
dispatcher()->reg(this, REWIND_CODE, this, &PlaybackController::rewind);
dispatcher()->reg(this, LOOP_CODE, this, &PlaybackController::toggleLoopPlayback);
dispatcher()->reg(this, LOOP_IN_CODE, [this]() { addLoopBoundary(LoopBoundaryType::LoopIn); });
Expand Down Expand Up @@ -665,13 +667,21 @@ void PlaybackController::rewind(const ActionData& args)
seek(newPosition);
}

void PlaybackController::pause()
void PlaybackController::pause(bool select)
{
IF_ASSERT_FAILED(currentPlayer()) {
return;
}

if (isPaused()) {
return;
}

currentPlayer()->pause();

if (select) {
selectAtRawTick(m_currentTick);
}
}

void PlaybackController::stop()
Expand Down Expand Up @@ -701,6 +711,21 @@ void PlaybackController::resume()
currentPlayer()->resume(delay);
}

void PlaybackController::selectAtRawTick(const tick_t& rawTick)
{
if (!m_notation) {
return;
}

const RetVal<tick_t> playPositionTick = notationPlayback()->playPositionTickByRawTick(rawTick);
if (!playPositionTick.ret) {
return;
}

const Fraction playPositionFrac = Fraction::fromTicks(playPositionTick.val);
interaction()->findAndSelectChordRest(playPositionFrac);
}

secs_t PlaybackController::playbackStartSecs() const
{
if (!m_notation) {
Expand Down Expand Up @@ -750,7 +775,7 @@ InstrumentTrackIdSet PlaybackController::instrumentTrackIdSetForRangePlayback()

InstrumentTrackIdSet result;

for (const Part* part: selectedParts) {
for (const Part* part : selectedParts) {
if (const Instrument* startInstrument = part->instrument(startTick)) {
result.insert({ part->id(), startInstrument->id() });
}
Expand Down
4 changes: 3 additions & 1 deletion src/playback/internal/playbackcontroller.h
Original file line number Diff line number Diff line change
Expand Up @@ -155,10 +155,12 @@ class PlaybackController : public IPlaybackController, public muse::actions::Act
void togglePlay();
void rewind(const muse::actions::ActionData& args);
void play();
void pause();
void pause(bool select = false);
void stop();
void resume();

void selectAtRawTick(const muse::midi::tick_t& rawTick);

muse::audio::secs_t playbackStartSecs() const;
muse::audio::secs_t playbackEndSecs() const;

Expand Down
7 changes: 7 additions & 0 deletions src/playback/internal/playbackuiactions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@ const UiActionList PlaybackUiActions::m_mainActions = {
TranslatableString("action", "Stop playback"),
IconCode::Code::STOP
),
UiAction("pause-and-select",
mu::context::UiCtxProjectOpened,
mu::context::CTX_NOTATION_OPENED,
TranslatableString("action", "Pause and select"),
TranslatableString("action", "Pause and select playback position"),
IconCode::Code::PAUSE
),
UiAction("rewind",
mu::context::UiCtxProjectOpened,
mu::context::CTX_NOTATION_FOCUSED,
Expand Down
Loading