Skip to content
Open
2 changes: 1 addition & 1 deletion src/engraving/compat/writescorehook.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ void WriteScoreHook::onWriteExcerpts302(Score* score, XmlWriter& xml, WriteConte
if (isWriteExcerpts && score->isMaster() && !ctx.shouldWriteRange()) {
MasterScore* mScore = static_cast<MasterScore*>(score);
for (const Excerpt* excerpt : mScore->excerpts()) {
if (excerpt->excerptScore() != score) {
if (excerpt->excerptScore() && excerpt->excerptScore() != score) {
write::Writer::write(excerpt->excerptScore(), xml, ctx, *this); // recursion write
}
}
Expand Down
6 changes: 6 additions & 0 deletions src/engraving/dom/excerpt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -578,6 +578,12 @@ void MasterScore::initExcerpt(Excerpt* excerpt)

void MasterScore::initParts(Excerpt* excerpt)
{
// For lightweight excerpts (no excerptScore), parts and tracksMapping
// are already set from the file read, so we can skip this function
if (!excerpt->excerptScore()) {
return;
}

int nstaves { 1 }; // Initialise to 1 to force writing of the first part.
std::set<ID> assignedStavesIds;
for (Staff* excerptStaff : excerpt->excerptScore()->staves()) {
Expand Down
3 changes: 2 additions & 1 deletion src/engraving/dom/excerpt.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,12 +96,13 @@ class Excerpt
static void cloneSpanner(Spanner* s, Score* score, track_idx_t dstTrack, track_idx_t dstTrack2);
static void createLinkedTabs(MasterScore* score);

void setInited(bool inited);

private:
friend class MasterScore;

static void promoteGapRestsToRealRests(const Measure* measure, staff_idx_t staffIdx);

void setInited(bool inited);
void writeNameToMetaTags();

void updateTracksMapping();
Expand Down
10 changes: 10 additions & 0 deletions src/engraving/dom/masterscore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,16 @@ void MasterScore::addExcerpt(Excerpt* ex, size_t index)
setExcerptsChanged(true);
}

//---------------------------------------------------------
// addLightweightExcerpt
//---------------------------------------------------------

void MasterScore::addLightweightExcerpt(Excerpt* ex, size_t index)
{
excerpts().insert(excerpts().begin() + (index == muse::nidx ? excerpts().size() : index), ex);
setExcerptsChanged(true);
}

//---------------------------------------------------------
// removeExcerpt
//---------------------------------------------------------
Expand Down
1 change: 1 addition & 0 deletions src/engraving/dom/masterscore.h
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ class MasterScore : public Score
void setLoopBoundaryTick(LoopBoundaryType type, Fraction tick);

void addExcerpt(Excerpt*, size_t index = muse::nidx);
void addLightweightExcerpt(Excerpt*, size_t index = muse::nidx);
void removeExcerpt(Excerpt*);
void deleteExcerpt(Excerpt*);

Expand Down
3 changes: 3 additions & 0 deletions src/engraving/dom/midimapping.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,9 @@ int MasterScore::getNextFreeDrumMidiMapping(std::set<int>& occupiedMidiChannels)
void MasterScore::rebuildExcerptsMidiMapping()
{
for (Excerpt* ex : excerpts()) {
if (!ex->excerptScore()) {
continue;
}
for (Part* p : ex->excerptScore()->parts()) {
const Part* masterPart = p->masterPart();
if (!masterPart->score()->isMaster()) {
Expand Down
80 changes: 78 additions & 2 deletions src/engraving/rw/mscloader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include "../dom/audio.h"
#include "../dom/excerpt.h"
#include "../dom/imageStore.h"
#include "../dom/part.h"

#include "engraving/automation/iautomation.h"

Expand All @@ -52,6 +53,74 @@ using namespace muse::io;
using namespace mu::engraving;
using namespace mu::engraving::rw;

//---------------------------------------------------------
// readLightweightExcerpt
/// Read a lightweight excerpt (no full excerptScore).
/// Returns nullptr if not a lightweight excerpt.
//---------------------------------------------------------

static Excerpt* readLightweightExcerpt(MasterScore* masterScore, const ByteArray& excerptData, const String& fileName)
{
Excerpt* excerpt = nullptr;
TracksMap tracksMap;

XmlReader xml(excerptData);
while (xml.readNextStartElement()) {
if (xml.name() == "museScore") {
while (xml.readNextStartElement()) {
if (xml.name() == "Score") {
while (xml.readNextStartElement()) {
const AsciiStringView tag = xml.name();
if (tag == "lightweight") {
if (xml.readText().toInt() != 0) {
excerpt = new Excerpt(masterScore);
excerpt->setFileName(fileName);
} else {
return nullptr;
}
} else if (!excerpt) {
// Not lightweight - first element must be <lightweight>
return nullptr;
} else if (tag == "name") {
excerpt->setName(xml.readText(), false);
} else if (tag == "Tracklist") {
track_idx_t sTrack = static_cast<track_idx_t>(xml.intAttribute("sTrack", -1));
track_idx_t dstTrack = static_cast<track_idx_t>(xml.intAttribute("dstTrack", -1));
if (sTrack != muse::nidx && dstTrack != muse::nidx) {
tracksMap.insert({ sTrack, dstTrack });
}
xml.readNext();
} else if (tag == "initialPartId") {
excerpt->setInitialPartId(ID(xml.readText().toStdString()));
} else if (tag == "Part") {
ID partId(static_cast<uint64_t>(xml.intAttribute("id", 0)));
for (Part* part : masterScore->parts()) {
if (part->id() == partId) {
excerpt->parts().push_back(part);
break;
}
}
xml.readNext();
} else {
xml.unknown();
}
}
} else {
xml.skipCurrentElement();
}
}
} else {
xml.skipCurrentElement();
}
}

if (excerpt && !tracksMap.empty()) {
excerpt->setTracksMapping(tracksMap);
}

return excerpt;
}

static RetVal<IReaderPtr> makeReader(int version, bool ignoreVersionError)
{
if (!ignoreVersionError) {
Expand Down Expand Up @@ -159,6 +228,15 @@ Ret MscLoader::loadMscz(MasterScore* masterScore, const MscReader& mscReader, Se
if (ret && masterScore->mscVersion() >= 400 && mscReader.isContainer()) {
std::vector<String> excerptFileNames = mscReader.excerptFileNames();
for (const String& excerptFileName : excerptFileNames) {
ByteArray excerptData = mscReader.readExcerptFile(excerptFileName);

// Try to read as lightweight excerpt first
if (Excerpt* ex = readLightweightExcerpt(masterScore, excerptData, excerptFileName)) {
masterScore->addLightweightExcerpt(ex);
continue;
}

// Regular excerpt with full score
Score* partScore = masterScore->createScore();

compat::ReadStyleHook::setupDefaultStyle(partScore);
Expand All @@ -172,8 +250,6 @@ Ret MscLoader::loadMscz(MasterScore* masterScore, const MscReader& mscReader, Se
excerptStyleBuf.open(IODevice::ReadOnly);
partScore->style().read(&excerptStyleBuf);

ByteArray excerptData = mscReader.readExcerptFile(excerptFileName);

XmlReader xml(excerptData);
xml.setDocName(excerptFileName);

Expand Down
71 changes: 69 additions & 2 deletions src/engraving/rw/mscsaver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,17 @@
#include "mscsaver.h"

#include "global/io/buffer.h"
#include "global/serialization/xmlstreamwriter.h"

#include "dom/masterscore.h"
#include "dom/excerpt.h"
#include "dom/part.h"
#include "dom/imageStore.h"
#include "dom/audio.h"

#include "engraving/automation/iautomation.h"
#include "engraving/infrastructure/mscwriter.h"
#include "engraving/types/constants.h"

#include "rwregister.h"
#include "inoutdata.h"
Expand All @@ -41,6 +45,62 @@ using namespace muse::io;
using namespace mu::engraving;
using namespace mu::engraving::rw;

//---------------------------------------------------------
// writeLightweightExcerpt
/// Write an excerpt that has no excerptScore (potential excerpt)
/// in a lightweight XML format that preserves name, parts, and tracks
//---------------------------------------------------------

static void writeLightweightExcerpt(Excerpt* excerpt, MscWriter& mscWriter)
{
ByteArray excerptData;
Buffer excerptBuf(&excerptData);
excerptBuf.open(IODevice::WriteOnly);

{
XmlStreamWriter xml(&excerptBuf);
xml.startDocument();

xml.startElement("museScore", { { "version", Constants::MSC_VERSION_STR } });
xml.startElement("Score");

// Mark as lightweight excerpt
xml.element("lightweight", 1); // Use int, not bool

// Write name if not empty
if (!excerpt->name().empty()) {
xml.element("name", excerpt->name());
}

// Write tracks mapping
const TracksMap& tracks = excerpt->tracksMapping();
if (!tracks.empty()) {
for (auto it = tracks.cbegin(); it != tracks.cend(); ++it) {
xml.element("Tracklist", { { "sTrack", String::number(it->first) },
{ "dstTrack", String::number(it->second) } });
}
}

// Write initialPartId if set
if (excerpt->initialPartId().isValid()) {
xml.element("initialPartId", excerpt->initialPartId().toUint64());
}

// Write parts (as Part element with id attribute)
for (const Part* part : excerpt->parts()) {
xml.element("Part", { { "id", part->id().toUint64() } });
}

xml.endElement(); // Score
xml.endElement(); // museScore
xml.flush();
} // xml destructor ensures all data is flushed

excerptBuf.close();

mscWriter.addExcerptFile(excerpt->fileName(), excerptData);
}

bool MscSaver::writeMscz(MasterScore* score, MscWriter& mscWriter, bool createThumbnail,
const write::WriteContext* ctx)
{
Expand Down Expand Up @@ -86,12 +146,19 @@ bool MscSaver::writeMscz(MasterScore* score, MscWriter& mscWriter, bool createTh
for (size_t excerptIndex = 0; excerptIndex < excerpts.size(); ++excerptIndex) {
Excerpt* excerpt = excerpts.at(excerptIndex);

excerpt->updateFileName(excerptIndex);

Score* partScore = excerpt->excerptScore();
IF_ASSERT_FAILED(partScore && partScore != score) {

// Lightweight excerpts have no excerptScore - write minimal XML
if (!partScore) {
writeLightweightExcerpt(excerpt, mscWriter);
continue;
}

excerpt->updateFileName(excerptIndex);
IF_ASSERT_FAILED(partScore != score) {
continue;
}

// Write excerpt style
{
Expand Down
Loading
Loading