Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
77adb22
Add native video sync proposal
ToranadoMusic May 7, 2026
2933505
Add project video attachment settings
ToranadoMusic May 7, 2026
8cbeb29
Add native video panel shell
ToranadoMusic May 7, 2026
1b67de0
Add embedded video playback to video panel
ToranadoMusic May 8, 2026
5719dfd
Sync video panel playback to score transport
ToranadoMusic May 8, 2026
6fe4690
Move video audio controls into mixer
ToranadoMusic May 8, 2026
8acf109
Align video controls as mixer channel
ToranadoMusic May 8, 2026
727cedc
Represent video as mixer channel
ToranadoMusic May 8, 2026
204f0c8
Make video panel responsive in bottom docks
ToranadoMusic May 8, 2026
d050feb
Let video preview resize with panel
ToranadoMusic May 8, 2026
055c98f
Fix video solo and negative offset sync
ToranadoMusic May 8, 2026
28d0ff3
Keep video panel controls below preview
ToranadoMusic May 8, 2026
04f26ae
Handle video solo in playback controller
ToranadoMusic May 8, 2026
01db9b1
Polish video mixer solo handling
ToranadoMusic May 26, 2026
8126933
Address video sync review comments
ToranadoMusic May 26, 2026
35a3e2b
Add video timecode and hit point timeline support
ToranadoMusic Jun 4, 2026
81481e8
Address video timeline review comments
ToranadoMusic Jun 4, 2026
8fa0769
Fix video panel CI configuration
ToranadoMusic Jun 4, 2026
2f48f97
Refine video panel timecode UI
ToranadoMusic Jun 4, 2026
4e32b81
Guard mixer delegates during model refresh
ToranadoMusic Jun 4, 2026
429190e
Revert mixer delegate guard
ToranadoMusic Jun 4, 2026
9388fec
Improve video timeline hit markers
ToranadoMusic Jun 4, 2026
5416a7c
Improve video scoring panel behavior
ToranadoMusic Jun 5, 2026
8a60618
Fix video hit point style CI regressions
ToranadoMusic Jun 5, 2026
3148bd5
Document video hit point text style handling
ToranadoMusic Jun 5, 2026
e3480dd
Lazy load video panel multimedia
ToranadoMusic Jun 25, 2026
b66cf83
Use playback state for video sync
ToranadoMusic Jun 25, 2026
582fa15
Load video panel when dock has size
ToranadoMusic Jun 26, 2026
8318b15
Merge branch 'main' into score-to-video
ToranadoMusic Jul 18, 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
4 changes: 4 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.

if (MUE_BUILD_PLAYBACK_MODULE)
find_package(Qt6 COMPONENTS Multimedia QUIET)
endif()

# Modules must be listed in dependency order
if (MUE_BUILD_BRAILLE_MODULE)
add_subdirectory(braille)
Expand Down
5 changes: 5 additions & 0 deletions src/app/configs/data/shortcuts.xml
Original file line number Diff line number Diff line change
Expand Up @@ -771,6 +771,11 @@
<seq>F12</seq>
<autorepeat>0</autorepeat>
</SC>
<SC>
<key>toggle-video-panel</key>
<seq>Ctrl+Alt+V</seq>
<autorepeat>0</autorepeat>
</SC>
<SC>
<key>quit</key>
<seq>Ctrl+Q</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 @@ -812,6 +812,11 @@
<seq>F12</seq>
<autorepeat>0</autorepeat>
</SC>
<SC>
<key>toggle-video-panel</key>
<seq>Ctrl+Alt+V</seq>
<autorepeat>0</autorepeat>
</SC>
<SC>
<key>quit</key>
<seq>Ctrl+Q</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 @@ -771,6 +771,11 @@
<seq>F12</seq>
<autorepeat>0</autorepeat>
</SC>
<SC>
<key>toggle-video-panel</key>
<seq>Ctrl+Alt+V</seq>
<autorepeat>0</autorepeat>
</SC>
<SC>
<key>quit</key>
<seq>Ctrl+Q</seq>
Expand Down
1 change: 1 addition & 0 deletions src/appshell/appshelltypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ static const DockName MIXER_PANEL_NAME("mixerPanel");
static const DockName PIANO_KEYBOARD_PANEL_NAME("pianoKeyboardPanel");
static const DockName TIMELINE_PANEL_NAME("timelinePanel");
static const DockName PERCUSSION_PANEL_NAME("percussionPanel");
static const DockName VIDEO_PANEL_NAME("videoPanel");

// Toolbars:
static const DockName NOTATION_TOOLBAR_NAME("notationToolBar");
Expand Down
80 changes: 80 additions & 0 deletions src/appshell/internal/applicationuiactions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "ui/view/iconcodes.h"
#include "context/uicontext.h"
#include "context/shortcutcontext.h"
#include "project/iprojectvideosettings.h"

#include "dockwindow/idockwindow.h"
#include "async/notification.h"
Expand All @@ -40,6 +41,18 @@ static const ActionCode FULL_SCREEN_CODE("fullscreen");
static const ActionCode TOGGLE_NAVIGATOR_ACTION_CODE("toggle-navigator");
static const ActionCode TOGGLE_BRAILLE_ACTION_CODE("toggle-braille-panel");
static const ActionCode TOGGLE_PERCUSSION_PANEL_ACTION_CODE("toggle-percussion-panel");
static const ActionCode VIDEO_TIMECODE_OFF_CODE("video-timecode-off");
static const ActionCode VIDEO_TIMECODE_ABOVE_CODE("video-timecode-above-bars");
static const ActionCode VIDEO_TIMECODE_BELOW_CODE("video-timecode-below-bars");

static ActionCodeList videoTimecodeActionCodes()
{
return {
VIDEO_TIMECODE_OFF_CODE,
VIDEO_TIMECODE_ABOVE_CODE,
VIDEO_TIMECODE_BELOW_CODE
};
}

const UiActionList ApplicationUiActions::m_actions = {
UiAction("quit",
Expand Down Expand Up @@ -200,6 +213,34 @@ const UiActionList ApplicationUiActions::m_actions = {
TranslatableString("action", "Show/hide piano keyboard"),
Checkable::Yes
),
UiAction("toggle-video-panel",
mu::context::UiCtxProjectOpened,
mu::context::CTX_ANY,
TranslatableString("action", "&Video"),
TranslatableString("action", "Show/hide video panel"),
Checkable::Yes
),
UiAction(VIDEO_TIMECODE_OFF_CODE,
mu::context::UiCtxProjectOpened,
mu::context::CTX_ANY,
TranslatableString("action", "&Off"),
TranslatableString("action", "Hide video timecode"),
Checkable::Yes
),
UiAction(VIDEO_TIMECODE_ABOVE_CODE,
mu::context::UiCtxProjectOpened,
mu::context::CTX_ANY,
TranslatableString("action", "&Above bars"),
TranslatableString("action", "Show video timecode above bars"),
Checkable::Yes
),
UiAction(VIDEO_TIMECODE_BELOW_CODE,
mu::context::UiCtxProjectOpened,
mu::context::CTX_ANY,
TranslatableString("action", "&Below bars"),
TranslatableString("action", "Show video timecode below bars"),
Checkable::Yes
),
UiAction(TOGGLE_PERCUSSION_PANEL_ACTION_CODE,
mu::context::UiCtxProjectOpened,
mu::context::CTX_NOTATION_OPENED,
Expand Down Expand Up @@ -253,6 +294,13 @@ void ApplicationUiActions::init()
dockWindowProvider()->windowChanged().onNotify(this, [this]() {
listenOpenedDocksChanged(dockWindowProvider()->window());
});

globalContext()->currentProjectChanged().onNotify(this, [this]() {
listenCurrentProjectVideoSettings();
m_actionCheckedChanged.send(videoTimecodeActionCodes());
});

listenCurrentProjectVideoSettings();
}

void ApplicationUiActions::listenOpenedDocksChanged(IDockWindow* window)
Expand Down Expand Up @@ -294,6 +342,24 @@ bool ApplicationUiActions::actionChecked(const UiAction& act) const
return mainWindow()->isFullScreen();
}

if (act.code == VIDEO_TIMECODE_OFF_CODE || act.code == VIDEO_TIMECODE_ABOVE_CODE || act.code == VIDEO_TIMECODE_BELOW_CODE) {
project::INotationProjectPtr project = globalContext()->currentProject();
project::IProjectVideoSettingsPtr settings = project ? project->videoSettings() : nullptr;
const project::VideoTimecodeDisplayMode mode = settings
? settings->attachment().timecodeDisplayMode
: project::VideoTimecodeDisplayMode::Off;

if (act.code == VIDEO_TIMECODE_OFF_CODE) {
return mode == project::VideoTimecodeDisplayMode::Off;
}

if (act.code == VIDEO_TIMECODE_ABOVE_CODE) {
return mode == project::VideoTimecodeDisplayMode::AboveBars;
}

return mode == project::VideoTimecodeDisplayMode::BelowBars;
}

QMap<ActionCode, DockName> toggleDockActions = ApplicationUiActions::toggleDockActions();
DockName dockName = toggleDockActions.value(act.code, DockName());

Expand Down Expand Up @@ -323,6 +389,19 @@ muse::async::Channel<ActionCodeList> ApplicationUiActions::actionCheckedChanged(
return m_actionCheckedChanged;
}

void ApplicationUiActions::listenCurrentProjectVideoSettings()
{
project::INotationProjectPtr project = globalContext()->currentProject();
project::IProjectVideoSettingsPtr settings = project ? project->videoSettings() : nullptr;
if (!settings) {
return;
}

settings->settingsChanged().onNotify(this, [this]() {
m_actionCheckedChanged.send(videoTimecodeActionCodes());
}, Asyncable::Mode::SetReplace);
}

const QMap<ActionCode, DockName>& ApplicationUiActions::toggleDockActions()
{
static const QMap<ActionCode, DockName> actionsMap {
Expand All @@ -341,6 +420,7 @@ const QMap<ActionCode, DockName>& ApplicationUiActions::toggleDockActions()
{ "toggle-timeline", TIMELINE_PANEL_NAME },
{ "toggle-mixer", MIXER_PANEL_NAME },
{ "toggle-piano-keyboard", PIANO_KEYBOARD_PANEL_NAME },
{ "toggle-video-panel", VIDEO_PANEL_NAME },
{ TOGGLE_PERCUSSION_PANEL_ACTION_CODE, PERCUSSION_PANEL_NAME },

{ "toggle-statusbar", NOTATION_STATUSBAR_NAME },
Expand Down
3 changes: 3 additions & 0 deletions src/appshell/internal/applicationuiactions.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "async/asyncable.h"
#include "ui/imainwindow.h"
#include "braille/ibrailleconfiguration.h"
#include "context/iglobalcontext.h"

#include "dockwindow/idockwindowprovider.h"

Expand All @@ -38,6 +39,7 @@ class ApplicationUiActions : public muse::ui::IUiActionsModule, public muse::Con
{
muse::GlobalInject<braille::IBrailleConfiguration> brailleConfiguration;
muse::ContextInject<IAppShellState> appShellState = { this };
muse::ContextInject<context::IGlobalContext> globalContext = { this };
muse::ContextInject<muse::ui::IMainWindow> mainWindow = { this };
muse::ContextInject<muse::dock::IDockWindowProvider> dockWindowProvider = { this };

Expand All @@ -58,6 +60,7 @@ class ApplicationUiActions : public muse::ui::IUiActionsModule, public muse::Con

private:
void listenOpenedDocksChanged(muse::dock::IDockWindow* window);
void listenCurrentProjectVideoSettings();

static const muse::ui::UiActionList m_actions;

Expand Down
31 changes: 31 additions & 0 deletions src/appshell/qml/MuseScore/AppShell/NotationPage/NotationPage.qml
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,37 @@ DockPage {
}
}
}
},

DockPanel {
id: videoPanel

objectName: root.pageModel.videoPanelName()
title: qsTrc("appshell", "Video")

height: 360
minimumHeight: root.horizontalPanelMinHeight
maximumHeight: root.horizontalPanelMaxHeight

minimumWidth: root.panelMinDimension
maximumWidth: root.panelMaxDimension

groupName: root.horizontalPanelsGroup

//! NOTE: hidden by default
visible: false

location: Location.Bottom

dropDestinations: root.horizontalPanelDropDestinations

navigationSection: root.navigationPanelSec(videoPanel.location)

VideoPanelLoader {
anchors.fill: parent
navigationSection: videoPanel.navigationSection
contentNavigationPanelOrderStart: videoPanel.contentNavigationPanelOrderStart
}
}
]

Expand Down
53 changes: 52 additions & 1 deletion src/appshell/qml/MuseScore/AppShell/appmenumodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ using namespace muse::actions;
using namespace muse::extensions;

static const ActionCode TOGGLE_UNDO_HISTORY_PANEL_CODE = "toggle-undo-history-panel";
static const ActionCode APP_MENU_VIDEO_TIMECODE_OFF_CODE = "video-timecode-off";
static const ActionCode APP_MENU_VIDEO_TIMECODE_ABOVE_CODE = "video-timecode-above-bars";
static const ActionCode APP_MENU_VIDEO_TIMECODE_BELOW_CODE = "video-timecode-below-bars";
static const QString VIEW_TOGGLE_UNDO_HISTORY_PANEL_ITEM_ID = "view/toggle-undo-history-panel";

static QString makeId(const ActionCode& actionCode, int itemIndex)
Expand Down Expand Up @@ -145,6 +148,22 @@ void AppMenuModel::setupConnections()

updateUndoRedoItems();
});

globalContext()->currentProjectChanged().onNotify(this, [this]() {
updateTimecodeItems();

project::INotationProjectPtr project = globalContext()->currentProject();
project::IProjectVideoSettingsPtr settings = project ? project->videoSettings() : nullptr;
if (!settings) {
return;
}

settings->settingsChanged().onNotify(this, [this]() {
updateTimecodeItems();
}, Asyncable::Mode::SetReplace);
});

updateTimecodeItems();
}

void AppMenuModel::onActionsStateChanges(const muse::actions::ActionCodeList& codes)
Expand Down Expand Up @@ -311,11 +330,13 @@ MenuItem* AppMenuModel::makeViewMenu()
makeMenuItem("toggle-timeline"),
makeMenuItem("toggle-mixer"),
makeMenuItem("toggle-piano-keyboard"),
makeMenuItem("toggle-video-panel"),
makeMenuItem("toggle-percussion-panel"),
makeMenuItem("command://playback/show-playback-setup"),
//makeMenuItem("toggle-scorecmp-tool"), // not implemented
makeSeparator(),
makeMenu(TranslatableString("appshell/menu/view", "&Toolbars"), makeToolbarsItems(), "menu-toolbars")
makeMenu(TranslatableString("appshell/menu/view", "&Toolbars"), makeToolbarsItems(), "menu-toolbars"),
makeMenu(TranslatableString("appshell/menu/view", "Video &timecode"), makeTimecodeItems(), "menu-video-timecode")
};

#ifdef MUSE_MODULE_WORKSPACE
Expand Down Expand Up @@ -794,6 +815,36 @@ MenuItemList AppMenuModel::makeToolbarsItems()
return items;
}

MenuItemList AppMenuModel::makeTimecodeItems()
{
project::INotationProjectPtr project = globalContext()->currentProject();
project::IProjectVideoSettingsPtr settings = project ? project->videoSettings() : nullptr;
const VideoTimecodeDisplayMode mode = settings
? settings->attachment().timecodeDisplayMode
: VideoTimecodeDisplayMode::Off;

auto makeTimecodeItem = [this, mode](const ActionCode& actionCode, VideoTimecodeDisplayMode itemMode) {
MenuItem* item = makeMenuItem(actionCode);
item->setSelectable(true);
item->setSelected(mode == itemMode);
return item;
};

return {
makeTimecodeItem(APP_MENU_VIDEO_TIMECODE_OFF_CODE, VideoTimecodeDisplayMode::Off),
makeTimecodeItem(APP_MENU_VIDEO_TIMECODE_ABOVE_CODE, VideoTimecodeDisplayMode::AboveBars),
makeTimecodeItem(APP_MENU_VIDEO_TIMECODE_BELOW_CODE, VideoTimecodeDisplayMode::BelowBars)
};
}

void AppMenuModel::updateTimecodeItems()
{
MenuItem& timecodeMenu = findMenu("menu-video-timecode");
if (timecodeMenu.isValid()) {
timecodeMenu.setSubitems(makeTimecodeItems());
}
}

MenuItemList AppMenuModel::makeShowItems()
{
MenuItemList items {
Expand Down
3 changes: 3 additions & 0 deletions src/appshell/qml/MuseScore/AppShell/appmenumodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include "context/iglobalcontext.h"
#include "extensions/iextensionsprovider.h"
#include "global/iglobalconfiguration.h"
#include "project/iprojectvideosettings.h"
#ifdef MUSE_MODULE_MUSESAMPLER
#include "musesampler/imusesamplerinfo.h"
#endif
Expand Down Expand Up @@ -87,6 +88,7 @@ class AppMenuModel : public muse::uicomponents::AbstractMenuModel
void setupConnections();

void onActionsStateChanges(const muse::actions::ActionCodeList& codes) override;
void updateTimecodeItems();

bool isMuseSamplerModuleAdded() const;

Expand Down Expand Up @@ -120,6 +122,7 @@ class AppMenuModel : public muse::uicomponents::AbstractMenuModel
muse::uicomponents::MenuItemList makeLinesItems();
muse::uicomponents::MenuItemList makeChordAndFretboardDiagramsItems();
muse::uicomponents::MenuItemList makeToolbarsItems();
muse::uicomponents::MenuItemList makeTimecodeItems();
muse::uicomponents::MenuItemList makeWorkspacesItems();
muse::uicomponents::MenuItemList makeShowItems();
muse::uicomponents::MenuItemList makePluginsItems();
Expand Down
Loading
Loading