Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
b0d68ec
Add drag-handle overlay to edit per-note velocities
tharos-devs Aug 14, 2026
2e1c2ad
Bump muse_framework submodule for note velocity override forwarding
tharos-devs Aug 14, 2026
9702adc
Merge branch 'feature/note-offset-drag-handles' into feature/note-vel…
tharos-devs Aug 14, 2026
ee2b643
Merge branch 'feature/note-offset-drag-handles' into feature/note-vel…
tharos-devs Aug 14, 2026
c4a8b0d
Re-pin muse submodule to velocity fix, fix codestyle violation
tharos-devs Aug 14, 2026
716b2e1
Merge branch 'feature/note-offset-drag-handles' into feature/note-vel…
tharos-devs Aug 14, 2026
8516f5d
Merge branch 'feature/note-offset-drag-handles' into feature/note-vel…
tharos-devs Aug 14, 2026
917b4f9
Show a live numeric readout next to the bar while dragging a velocity…
tharos-devs Aug 14, 2026
fb8b0e4
Merge branch 'feature/note-offset-drag-handles' into feature/note-vel…
tharos-devs Aug 14, 2026
e0d7ad5
Merge branch 'feature/note-offset-drag-handles' into feature/note-vel…
tharos-devs Aug 14, 2026
ddfdd63
Fix note velocity not propagating to tied-continuation notes
tharos-devs Aug 15, 2026
2f81c01
Write Pid::VELO_TYPE when saving notes
tharos-devs Aug 15, 2026
6116b58
Merge branch 'feature/note-offset-drag-handles' into feature/note-vel…
tharos-devs Aug 15, 2026
b2ba57a
Merge branch 'feature/note-offset-drag-handles' into feature/note-vel…
tharos-devs Aug 15, 2026
b84d071
Add "Reset note velocities" context-menu item
tharos-devs Aug 15, 2026
558094d
Keep a selected chord note's velocity bar on top and draggable
tharos-devs Aug 15, 2026
4e7e8be
Merge branch 'feature/note-offset-drag-handles' into feature/note-vel…
tharos-devs Aug 15, 2026
5a0959e
Merge branch 'feature/note-offset-drag-handles' into feature/note-vel…
tharos-devs Aug 18, 2026
0cda5e4
Show the actual dynamics-derived velocity in the Properties panel
tharos-devs Aug 18, 2026
70acf5b
Give velocity bars cursor priority over note-offset handles they cover
tharos-devs Aug 18, 2026
a147385
Make velocity bar dragging relative instead of jump-to-click
tharos-devs Aug 18, 2026
a07a3cc
Fix velocity-drag delta scale mismatch and redundant cursor updates
tharos-devs Aug 19, 2026
60d054f
Square off note-offset rectangle corners
tharos-devs Aug 19, 2026
bec6daa
Audition the note at its live velocity while dragging its bar
tharos-devs Aug 19, 2026
ee103eb
Add a Cmd/Ctrl-tap toggle to swap note-offset/velocity overlay priority
tharos-devs Aug 19, 2026
a607d10
Click a velocity bar to jump it directly to the clicked position
tharos-devs Aug 19, 2026
5dee821
Fix velocity-drag edge cases: phantom 0->1 bump, incomplete cancel re…
tharos-devs Aug 19, 2026
f378cb0
Force a cursor refresh right after the overlay-priority Cmd/Ctrl tap
tharos-devs Aug 19, 2026
cdba406
Make the Properties panel velocity field VeloType::OFFSET_VAL-aware
tharos-devs Aug 19, 2026
fe707db
Merge branch 'feature/note-offset-drag-handles' into feature/note-vel…
tharos-devs Aug 19, 2026
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
10 changes: 10 additions & 0 deletions src/engraving/playback/playbackmodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,16 @@ muse::async::Channel<InstrumentTrackId> PlaybackModel::trackRemoved() const
return m_trackRemoved;
}

dynamic_level_t PlaybackModel::appliableDynamicLevel(track_idx_t trackIdx, int tick) const
{
if (!m_playbackCtx) {
return dynamicLevelFromType(muse::mpe::DynamicType::Natural);
}

const int utick = repeatList().tick2utick(tick);
return m_playbackCtx->appliableDynamicLevel(trackIdx, utick);
}

void PlaybackModel::update(const int tickFrom, const int tickTo, const track_idx_t trackFrom, const track_idx_t trackTo,
ChangedTrackIdSet* trackChanges)
{
Expand Down
2 changes: 2 additions & 0 deletions src/engraving/playback/playbackmodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ class PlaybackModel : public muse::Contextable, public muse::async::Asyncable
muse::async::Channel<InstrumentTrackId> trackAdded() const;
muse::async::Channel<InstrumentTrackId> trackRemoved() const;

muse::mpe::dynamic_level_t appliableDynamicLevel(track_idx_t trackIdx, int tick) const;

private:
static const InstrumentTrackId METRONOME_TRACK_ID;
static const InstrumentTrackId CHORD_SYMBOLS_TRACK_ID;
Expand Down
4 changes: 2 additions & 2 deletions src/engraving/rw/write/twrite.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2505,8 +2505,8 @@ void TWrite::write(const Note* item, XmlWriter& xml, WriteContext& ctx)
xml.endElement();
}
for (Pid id : { Pid::PITCH, Pid::CENT_OFFSET, Pid::TPC1, Pid::TPC2, Pid::SMALL, Pid::MIRROR_HEAD, Pid::DOT_POSITION,
Pid::HEAD_SCHEME, Pid::HEAD_GROUP, Pid::USER_VELOCITY, Pid::PLAY, Pid::TUNING, Pid::FRET, Pid::STRING,
Pid::GHOST, Pid::DEAD, Pid::HEAD_TYPE, Pid::FIXED, Pid::FIXED_LINE,
Pid::HEAD_SCHEME, Pid::HEAD_GROUP, Pid::VELO_TYPE, Pid::USER_VELOCITY, Pid::PLAY, Pid::TUNING, Pid::FRET,
Pid::STRING, Pid::GHOST, Pid::DEAD, Pid::HEAD_TYPE, Pid::FIXED, Pid::FIXED_LINE,
Pid::PLAYBACK_START_OFFSET, Pid::PLAYBACK_DURATION_OFFSET }) {
writeProperty(item, xml, id);
}
Expand Down
3 changes: 3 additions & 0 deletions src/notation/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ target_sources(notation PRIVATE
inotationselectionrange.h
inotationautomation.h
inotationnoteoffsets.h
inotationnotevelocity.h
inotationinteraction.h
inotationstyle.h
inotationundostack.h
Expand Down Expand Up @@ -86,6 +87,8 @@ target_sources(notation PRIVATE
internal/notationautomation.h
internal/notationnoteoffsets.cpp
internal/notationnoteoffsets.h
internal/notationnotevelocity.cpp
internal/notationnotevelocity.h
internal/notationelements.cpp
internal/notationelements.h
internal/notationinteraction.cpp
Expand Down
1 change: 1 addition & 0 deletions src/notation/imasternotation.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ class IMasterNotation

virtual INotationAutomationPtr automation() const = 0;
virtual INotationNoteOffsetsPtr noteOffsets() const = 0;
virtual INotationNoteVelocityPtr noteVelocity() const = 0;
};

using IMasterNotationPtr = std::shared_ptr<IMasterNotation>;
Expand Down
3 changes: 3 additions & 0 deletions src/notation/inotation_fwd.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,7 @@ using INotationAutomationPtr = std::shared_ptr<INotationAutomation>;

class INotationNoteOffsets;
using INotationNoteOffsetsPtr = std::shared_ptr<INotationNoteOffsets>;

class INotationNoteVelocity;
using INotationNoteVelocityPtr = std::shared_ptr<INotationNoteVelocity>;
}
39 changes: 39 additions & 0 deletions src/notation/inotationnotevelocity.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* SPDX-License-Identifier: GPL-3.0-only
* MuseScore-Studio-CLA-applies
*
* MuseScore Studio
* Music Composition & Notation
*
* Copyright (C) 2026 MuseScore Limited
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

#pragma once

#include "async/notification.h"

namespace mu::notation {
class INotationNoteVelocity
{
public:
virtual ~INotationNoteVelocity() = default;

virtual bool isEditModeEnabled() const = 0;
virtual void setEditModeEnabled(bool enabled) = 0;
virtual muse::async::Notification editModeEnabledChanged() const = 0;
};

using INotationNoteVelocityPtr = std::shared_ptr<INotationNoteVelocity>;
}
4 changes: 4 additions & 0 deletions src/notation/inotationplayback.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ class INotationPlayback
virtual muse::async::Channel<engraving::InstrumentTrackId> trackAdded() const = 0;
virtual muse::async::Channel<engraving::InstrumentTrackId> trackRemoved() const = 0;

// Dynamic level (marking/hairpin only, no per-note override) that would apply at this tick,
// for use by UI that needs a musically-coherent baseline (e.g. a velocity editor).
virtual muse::mpe::dynamic_level_t appliableDynamicLevel(engraving::track_idx_t trackIdx, int tick) const = 0;

virtual muse::audio::secs_t totalPlayTime() const = 0;
virtual muse::async::Channel<muse::audio::secs_t> totalPlayTimeChanged() const = 0;

Expand Down
7 changes: 7 additions & 0 deletions src/notation/internal/masternotation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
#include "masternotationparts.h"
#include "notationautomation.h"
#include "notationnoteoffsets.h"
#include "notationnotevelocity.h"
#include "types/scorecreateoptions.h"

#ifdef MUE_BUILD_ENGRAVING_PLAYBACK
Expand Down Expand Up @@ -94,6 +95,7 @@ MasterNotation::MasterNotation(project::INotationProject* project, const muse::m

m_notationAutomation = std::make_shared<NotationAutomation>(undoStack());
m_notationNoteOffsets = std::make_shared<NotationNoteOffsets>();
m_notationNoteVelocity = std::make_shared<NotationNoteVelocity>();

m_parts->partsChanged().onNotify(this, [this]() {
notifyAboutNotationChanged();
Expand Down Expand Up @@ -773,6 +775,11 @@ INotationNoteOffsetsPtr MasterNotation::noteOffsets() const
return m_notationNoteOffsets;
}

INotationNoteVelocityPtr MasterNotation::noteVelocity() const
{
return m_notationNoteVelocity;
}

void MasterNotation::initNotationSoloMuteState(const INotationPtr notation)
{
IF_ASSERT_FAILED(notation) {
Expand Down
2 changes: 2 additions & 0 deletions src/notation/internal/masternotation.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ class MasterNotation : public IMasterNotation, public Notation, public std::enab

INotationAutomationPtr automation() const override;
INotationNoteOffsetsPtr noteOffsets() const override;
INotationNoteVelocityPtr noteVelocity() const override;

private:
friend class project::NotationProject;
Expand Down Expand Up @@ -104,6 +105,7 @@ class MasterNotation : public IMasterNotation, public Notation, public std::enab
INotationPlaybackPtr m_notationPlayback = nullptr;
INotationAutomationPtr m_notationAutomation = nullptr;
INotationNoteOffsetsPtr m_notationNoteOffsets = nullptr;
INotationNoteVelocityPtr m_notationNoteVelocity = nullptr;
muse::async::Notification m_hasPartsChanged;

mutable ExcerptNotationList m_potentialExcerpts;
Expand Down
44 changes: 44 additions & 0 deletions src/notation/internal/notationnotevelocity.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* SPDX-License-Identifier: GPL-3.0-only
* MuseScore-Studio-CLA-applies
*
* MuseScore Studio
* Music Composition & Notation
*
* Copyright (C) 2026 MuseScore Limited
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

#include "notationnotevelocity.h"

using namespace mu::notation;

bool NotationNoteVelocity::isEditModeEnabled() const
{
return m_isEditModeEnabled;
}

void NotationNoteVelocity::setEditModeEnabled(bool enabled)
{
if (m_isEditModeEnabled == enabled) {
return;
}
m_isEditModeEnabled = enabled;
m_editModeEnabledChanged.notify();
}

muse::async::Notification NotationNoteVelocity::editModeEnabledChanged() const
{
return m_editModeEnabledChanged;
}
40 changes: 40 additions & 0 deletions src/notation/internal/notationnotevelocity.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* SPDX-License-Identifier: GPL-3.0-only
* MuseScore-Studio-CLA-applies
*
* MuseScore Studio
* Music Composition & Notation
*
* Copyright (C) 2026 MuseScore Limited
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once

#include "../inotationnotevelocity.h"

#include "async/notification.h"

namespace mu::notation {
class NotationNoteVelocity : public INotationNoteVelocity
{
public:
bool isEditModeEnabled() const override;
void setEditModeEnabled(bool enabled) override;
muse::async::Notification editModeEnabledChanged() const override;

private:
bool m_isEditModeEnabled = false;
muse::async::Notification m_editModeEnabledChanged;
};
}
5 changes: 5 additions & 0 deletions src/notation/internal/notationplayback.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,11 @@ muse::async::Channel<InstrumentTrackId> NotationPlayback::trackRemoved() const
return m_playbackModel.trackRemoved();
}

muse::mpe::dynamic_level_t NotationPlayback::appliableDynamicLevel(track_idx_t trackIdx, int tick) const
{
return m_playbackModel.appliableDynamicLevel(trackIdx, tick);
}

void NotationPlayback::updateLoopBoundaries()
{
LoopBoundaries newBoundaries;
Expand Down
2 changes: 2 additions & 0 deletions src/notation/internal/notationplayback.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ class NotationPlayback : public INotationPlayback, public muse::async::Asyncable
muse::async::Channel<engraving::InstrumentTrackId> trackAdded() const override;
muse::async::Channel<engraving::InstrumentTrackId> trackRemoved() const override;

muse::mpe::dynamic_level_t appliableDynamicLevel(engraving::track_idx_t trackIdx, int tick) const override;

muse::audio::secs_t totalPlayTime() const override;
muse::async::Channel<muse::audio::secs_t> totalPlayTimeChanged() const override;

Expand Down
5 changes: 5 additions & 0 deletions src/notation/internal/notationplaybackstub.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,11 @@ muse::async::Channel<InstrumentTrackId> NotationPlaybackStub::trackRemoved() con
return muse::async::Channel<InstrumentTrackId>();
}

muse::mpe::dynamic_level_t NotationPlaybackStub::appliableDynamicLevel(track_idx_t, int) const
{
return muse::mpe::dynamicLevelFromType(muse::mpe::DynamicType::Natural);
}

muse::audio::secs_t NotationPlaybackStub::totalPlayTime() const
{
return muse::audio::secs_t();
Expand Down
2 changes: 2 additions & 0 deletions src/notation/internal/notationplaybackstub.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ class NotationPlaybackStub : public INotationPlayback
muse::async::Channel<engraving::InstrumentTrackId> trackAdded() const override;
muse::async::Channel<engraving::InstrumentTrackId> trackRemoved() const override;

muse::mpe::dynamic_level_t appliableDynamicLevel(engraving::track_idx_t trackIdx, int tick) const override;

muse::audio::secs_t totalPlayTime() const override;
muse::async::Channel<muse::audio::secs_t> totalPlayTimeChanged() const override;

Expand Down
3 changes: 3 additions & 0 deletions src/notationscene/inotationcommandscontroller.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ class INotationCommandsController : MODULE_CONTEXT_INTERFACE
virtual bool isNoteOffsetEditModeEnabled() const = 0;
virtual muse::async::Notification noteOffsetEditModeEnabledChanged() const = 0;

virtual bool isNoteVelocityEditModeEnabled() const = 0;
virtual muse::async::Notification noteVelocityEditModeEnabledChanged() const = 0;

virtual bool isDebuggingCommandEnabled(const muse::rcommand::Command& command) const = 0;
virtual muse::async::Notification debuggingOptionsChanged() const = 0;
};
Expand Down
Loading