Skip to content
Closed
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
1 change: 1 addition & 0 deletions src/macos_integration/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ install(FILES ${QT_INSTALL_PLUGINS}/platforms/libqcocoa.dylib
install(CODE "
execute_process(COMMAND rm -f \"${the_appex_path}/Contents/MacOS/MuseScoreQuickLookPreviewExtension.emit-module.d\")
execute_process(COMMAND rm -f \"${the_appex_path}/Contents/MacOS/MuseScoreQuickLookPreviewExtension.d\")
execute_process(COMMAND codesign --force --sign - --timestamp=none \"${the_appex_path}/Contents/MacOS/platforms/libqcocoa.dylib\")
execute_process(COMMAND codesign --force --sign - --timestamp=none --options runtime --entitlements \"${CMAKE_CURRENT_SOURCE_DIR}/entitlements.plist\" \"${the_appex_path}\")
Comment on lines +119 to 120
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Look at how the .appex / nested plugins are signed during packaging.
fd -t f 'package.sh' buildscripts | xargs -r rg -nC3 'codesign|--deep|libqcocoa|appex|--entitlements'
# Any other place that signs platform plugins?
rg -nC3 'libqcocoa|platforms' --glob '!**/*.dylib'

Repository: musescore/MuseScore

Length of output: 35656


Clarify packaging re-signing: ad-hoc libqcocoa.dylib may not be overwritten

CMake does ad-hoc signing of the bundled nested dylib before signing the .appex:

execute_process(COMMAND codesign --force --sign - --timestamp=none \"${the_appex_path}/Contents/MacOS/platforms/libqcocoa.dylib\")
execute_process(COMMAND codesign --force --sign - --timestamp=none --options runtime --entitlements \"${CMAKE_CURRENT_SOURCE_DIR}/entitlements.plist\" \"${the_appex_path}\")

In buildscripts/packaging/macOS/package.sh, the packaging step re-signs only the .appex with codesign ... -s "Developer ID Application: MuseScore" without --deep (and there’s no explicit re-sign of libqcocoa.dylib there); --deep is only used when re-signing the main app, followed by codesign --verify --deep --strict. That means the ad-hoc signature on libqcocoa.dylib could persist into the shipped bundle.

Ensure packaging either (a) re-signs the nested libqcocoa.dylib explicitly with the real Developer ID identity, or (b) re-signs the .appex recursively (via --deep) so the ad-hoc signature is overwritten; otherwise spctl/notarization may fail.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/macos_integration/CMakeLists.txt` around lines 119 - 120, The CMake step
ad-hoc-signs the nested library (execute_process calling codesign on
"${the_appex_path}/Contents/MacOS/platforms/libqcocoa.dylib") but the packaging
script (buildscripts/packaging/macOS/package.sh) only re-signs the .appex
without --deep, so the ad-hoc signature can persist; fix by updating packaging
to either explicitly re-sign the nested libqcocoa.dylib with the real Developer
ID identity (the same style as the .appex sign, using the Developer ID
Application: MuseScore identity and entitlements.plist) or when signing the
.appex in package.sh add the --deep flag so the recursive signing overwrites the
ad-hoc signature; ensure the codesign invocation in package.sh mirrors the
runtime/options/entitlements usage used elsewhere so spctl/notarization sees the
proper Developer ID signature.

")

Expand Down
108 changes: 106 additions & 2 deletions src/playback/internal/playbackcontroller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include "engraving/dom/factory.h"

#include "audio/common/audioutils.h"
#include "audio/common/soundfonttypes.h"
#include "audio/devtools/inputlag.h"

#include "containers.h"
Expand Down Expand Up @@ -84,6 +85,44 @@ static AudioOutputParams makeReverbOutputParams()
return result;
}

static AudioInputParams defaultBasicInputParams()
{
static const AudioResourceId DEFAULT_SOUND_FONT_NAME = "MS Basic";
static const AudioResourceMeta DEFAULT_AUDIO_RESOURCE_META = {
DEFAULT_SOUND_FONT_NAME,
"Fluid",
{
{ PLAYBACK_SETUP_DATA_ATTRIBUTE, muse::mpe::GENERIC_SETUP_DATA_STRING },
{ synth::SOUNDFONT_NAME_ATTRIBUTE, muse::String::fromStdString(DEFAULT_SOUND_FONT_NAME) }
},
AudioResourceType::FluidSoundfont,
false /*hasNativeEditor*/
};

return { DEFAULT_AUDIO_RESOURCE_META, {} };
}

static bool needsProfileInputFallback(const AudioInputParams& params)
{
if (!params.isValid()) {
return true;
}

if (params.resourceMeta.type == AudioResourceType::MuseSamplerSoundPack
&& params.resourceMeta.attributeVal(PLAYBACK_SETUP_DATA_ATTRIBUTE).isEmpty()) {
return true;
}

return false;
}

static bool shouldAvoidMuseSamplerInput(const AudioInputParams& params, const SoundProfileName& activeProfileName,
const SoundProfileName& museSoundsProfileName)
{
return params.resourceMeta.type == AudioResourceType::MuseSamplerSoundPack
|| activeProfileName == museSoundsProfileName;
}

static std::string resolveAuxTrackTitle(aux_channel_idx_t index, const AudioOutputParams& params, bool considerFx = true)
{
if (considerFx && params.fxChain.size() == 1) {
Expand Down Expand Up @@ -1128,8 +1167,12 @@ void PlaybackController::doAddTrack(const InstrumentTrackId& instrumentTrackId,

bool isMetronome = notationPlayback()->metronomeTrackId() == instrumentTrackId;

if (!inParams.isValid()) {
if (isMetronome) {
const bool useBasicProfile = isMetronome
|| shouldAvoidMuseSamplerInput(inParams, audioSettings()->activeSoundProfile(),
configuration()->museSoundsProfileName());

if (needsProfileInputFallback(inParams) || useBasicProfile) {
if (useBasicProfile) {
const SoundProfile& profile = profilesRepo()->profile(configuration()->basicSoundProfileName());
inParams = { profile.findResource(playbackData.setupData), {} };
} else {
Expand All @@ -1138,6 +1181,10 @@ void PlaybackController::doAddTrack(const InstrumentTrackId& instrumentTrackId,
}
}

if (!inParams.isValid()) {
inParams = defaultBasicInputParams();
}

if (!isMetronome && originParams.auxSends.empty()) {
const muse::String& instrumentSoundId = inParams.resourceMeta.attributeVal(PLAYBACK_SETUP_DATA_ATTRIBUTE);
AudioSourceType sourceType = inParams.isValid() ? inParams.type() : AudioSourceType::Fluid;
Expand Down Expand Up @@ -1700,6 +1747,63 @@ void PlaybackController::applyProfile(const SoundProfileName& profileName)
audioSettingsPtr->setActiveSoundProfile(profileName);
}

bool PlaybackController::hasAvailableMuseSoundsReassignments() const
{
return museSoundsReassignmentCandidateCount() > 0;
}

void PlaybackController::reassignInstrumentsToAvailableMuseSounds()
{
if (!hasAvailableMuseSoundsReassignments()) {
return;
}

applyProfile(configuration()->museSoundsProfileName());
}

size_t PlaybackController::museSoundsReassignmentCandidateCount() const
{
INotationPlaybackPtr nPlayback = notationPlayback();
project::IProjectAudioSettingsPtr audioSettingsPtr = audioSettings();

if (!nPlayback || !audioSettingsPtr) {
return 0;
}

const SoundProfile& museSoundsProfile = profilesRepo()->profile(configuration()->museSoundsProfileName());
if (!museSoundsProfile.isValid()) {
return 0;
}

const InstrumentTrackId& metronomeTrackId = nPlayback->metronomeTrackId();
size_t result = 0;

for (const auto& pair : m_instrumentTrackIdMap) {
const InstrumentTrackId& instrumentTrackId = pair.first;
if (instrumentTrackId == metronomeTrackId) {
continue;
}

const mpe::PlaybackData& playbackData = nPlayback->trackPlaybackData(instrumentTrackId);
const AudioResourceMeta& museSoundsMatch = museSoundsProfile.findResource(playbackData.setupData);
if (!museSoundsMatch.isValid()) {
continue;
}

const AudioInputParams& currentParams = audioSettingsPtr->trackInputParams(instrumentTrackId);
if (currentParams.resourceMeta.type == AudioResourceType::MuseSamplerSoundPack
&& currentParams.resourceMeta.id == museSoundsMatch.id) {
continue;
}

if (!currentParams.isValid() || currentParams.resourceMeta.type == AudioResourceType::FluidSoundfont) {
++result;
}
}

return result;
}

void PlaybackController::setNotation(notation::INotationPtr notation)
{
if (m_notation == notation) {
Expand Down
3 changes: 3 additions & 0 deletions src/playback/internal/playbackcontroller.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,8 @@ class PlaybackController : public IPlaybackController, public muse::actions::Act
muse::Progress loadingProgress() const override;

void applyProfile(const SoundProfileName& profileName) override;
bool hasAvailableMuseSoundsReassignments() const override;
void reassignInstrumentsToAvailableMuseSounds() override;

void setNotation(notation::INotationPtr notation) override;
void setMasterNotation(notation::IMasterNotationPtr masterNotation);
Expand Down Expand Up @@ -194,6 +196,7 @@ class PlaybackController : public IPlaybackController, public muse::actions::Act
void reloadPlaybackCache();

void openPlaybackSetupDialog();
size_t museSoundsReassignmentCandidateCount() const;

void addLoopBoundary(notation::LoopBoundaryType type);
void addLoopBoundaryToTick(notation::LoopBoundaryType type, int tick);
Expand Down
2 changes: 2 additions & 0 deletions src/playback/iplaybackcontroller.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ class IPlaybackController : MODULE_CONTEXT_INTERFACE
virtual muse::Progress loadingProgress() const = 0;

virtual void applyProfile(const SoundProfileName& profileName) = 0;
virtual bool hasAvailableMuseSoundsReassignments() const = 0;
virtual void reassignInstrumentsToAvailableMuseSounds() = 0;

virtual void setNotation(notation::INotationPtr notation) = 0;
virtual void setIsExportingAudio(bool exporting) = 0;
Expand Down
1 change: 1 addition & 0 deletions src/playback/playbackmodule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ void PlaybackModule::resolveImports()
auto ir = globalIoc()->resolve<muse::interactive::IInteractiveUriRegister>(mname);
if (ir) {
ir->registerQmlUri(Uri("musescore://playback/soundprofilesdialog"), "MuseScore.Playback", "SoundProfilesDialog");
ir->registerQmlUri(Uri("musescore://playback/musesoundsreassigndialog"), "MuseScore.Playback", "MuseSoundsReassignDialog");
}
}

Expand Down
3 changes: 3 additions & 0 deletions src/playback/qml/MuseScore/Playback/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ qt_add_qml_module(playback_qml
mixerpanelcontextmenumodel.h
mixerpanelmodel.cpp
mixerpanelmodel.h
musesoundsreassignmodel.cpp
musesoundsreassignmodel.h
msbasicpresetscategories.h
notationregionsbeingprocessedmodel.cpp
notationregionsbeingprocessedmodel.h
Expand Down Expand Up @@ -75,6 +77,7 @@ qt_add_qml_module(playback_qml
internal/VolumePressureMeter.qml
internal/VolumeSlider.qml
MixerPanel.qml
MuseSoundsReassignDialog.qml
NotationRegionsBeingProcessedView.qml
OnlineSoundsStatusView.qml
PlaybackLoadingInfo.qml
Expand Down
24 changes: 19 additions & 5 deletions src/playback/qml/MuseScore/Playback/MixerPanel.qml
Original file line number Diff line number Diff line change
Expand Up @@ -140,25 +140,39 @@ ColumnLayout {

ScrollBar.horizontal: horizontalScrollBar

ScrollBar.vertical: StyledScrollBar { policy: ScrollBar.AlwaysOn }
ScrollBar.vertical: null

property bool completed: false
property bool pendingPositionViewAtEnd: false
property bool resourcePickingActive: soundSection.resourcePickingActive || fxSection.resourcePickingActive

function positionViewAtEnd() {
if (!flickable.completed) {
return
}

if (flickable.contentY == flickable.contentHeight) {
let endY = Math.max(0, flickable.contentHeight - flickable.height)
if (Math.abs(flickable.contentY - endY) < 1) {
return
}

flickable.contentY = flickable.contentHeight - flickable.height
flickable.contentY = endY
}

function schedulePositionViewAtEnd() {
if (!flickable.completed || flickable.pendingPositionViewAtEnd) {
return
}

flickable.pendingPositionViewAtEnd = true
Qt.callLater(function() {
flickable.pendingPositionViewAtEnd = false
flickable.positionViewAtEnd()
})
}

onContentHeightChanged: {
flickable.positionViewAtEnd()
flickable.schedulePositionViewAtEnd()
}

Component.onCompleted: {
Expand Down Expand Up @@ -360,6 +374,6 @@ ColumnLayout {
}

onHeightChanged: {
flickable.positionViewAtEnd()
flickable.schedulePositionViewAtEnd()
}
}
Loading