Skip to content

Commit

Permalink
Merge pull request #5534 from lvinken/use-direction-offset-tag
Browse files Browse the repository at this point in the history
fix #298552 - [MusicXML] use offset for non-spanning directions
  • Loading branch information
dmitrio95 committed Dec 24, 2019
2 parents bb78c39 + c65916e commit 5af1357
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 70 deletions.
67 changes: 46 additions & 21 deletions mscore/exportxml.cpp
Expand Up @@ -99,6 +99,7 @@
#include "libmscore/textline.h"
#include "libmscore/fermata.h"
#include "musicxmlfonthandler.h"
#include "libmscore/textframe.h"

namespace Ms {

Expand Down Expand Up @@ -334,6 +335,7 @@ class ExportMusicXml {
Score* score() const { return _score; };
double getTenthsFromInches(double) const;
double getTenthsFromDots(double) const;
Fraction tick() const { return _tick; }
};

//---------------------------------------------------------
Expand Down Expand Up @@ -1623,20 +1625,29 @@ void ExportMusicXml::barlineRight(Measure* m)
_xml.etag();
}

//---------------------------------------------------------
// calculateTimeDeltaInDivisions
//---------------------------------------------------------

static int calculateTimeDeltaInDivisions(const Fraction& t1, const Fraction& t2, const int divisions)
{
return (t1 - t2).ticks() / divisions;
}

//---------------------------------------------------------
// moveToTick
//---------------------------------------------------------

void ExportMusicXml::moveToTick(const Fraction& t)
{
// qDebug("ExportMusicXml::moveToTick(t=%d) tick=%d", t, tick);
//qDebug("ExportMusicXml::moveToTick(t=%s) _tick=%s", qPrintable(t.print()), qPrintable(_tick.print()));
if (t < _tick) {
#ifdef DEBUG_TICK
qDebug(" -> backup");
#endif
_attr.doAttr(_xml, false);
_xml.stag("backup");
_xml.tag("duration", (_tick - t).ticks() / div);
_xml.tag("duration", calculateTimeDeltaInDivisions(_tick, t, div));
_xml.etag();
}
else if (t > _tick) {
Expand All @@ -1645,7 +1656,7 @@ void ExportMusicXml::moveToTick(const Fraction& t)
#endif
_attr.doAttr(_xml, false);
_xml.stag("forward");
_xml.tag("duration", (t - _tick).ticks() / div);
_xml.tag("duration", calculateTimeDeltaInDivisions(t, _tick, div));
_xml.etag();
}
_tick = t;
Expand Down Expand Up @@ -3373,7 +3384,7 @@ static void beatUnit(XmlWriter& xml, const TDuration dur)
}
}

static void wordsMetrome(XmlWriter& xml, Score* s, TextBase const* const text)
static void wordsMetrome(XmlWriter& xml, Score* s, TextBase const* const text, const int offset)
{
//qDebug("wordsMetrome('%s')", qPrintable(text->xmlText()));
const QList<TextFragment> list = text->fragmentList();
Expand Down Expand Up @@ -3441,21 +3452,24 @@ static void wordsMetrome(XmlWriter& xml, Score* s, TextBase const* const text)
mttm.writeTextFragments(text->fragmentList(), xml);
xml.etag();
}

if (offset)
xml.tag("offset", offset);
}

void ExportMusicXml::tempoText(TempoText const* const text, int staff)
{
const auto offset = calculateTimeDeltaInDivisions(text->tick(), tick(), div);
/*
qDebug("ExportMusicXml::tempoText(TempoText='%s')", qPrintable(text->xmlText()));
qDebug("tick %s text->tick %s offset %d xmlText='%s')",
qPrintable(tick().print()),
qPrintable(text->tick().print()),
offset,
qPrintable(text->xmlText()));
*/
_attr.doAttr(_xml, false);
_xml.stag(QString("direction placement=\"%1\"").arg((text->placement() ==Placement::BELOW ) ? "below" : "above"));
wordsMetrome(_xml, _score, text);
/*
int offs = text->mxmlOff();
if (offs)
xml.tag("offset", offs);
*/
wordsMetrome(_xml, _score, text, offset);
if (staff)
_xml.tag("staff", staff);
_xml.tagE(QString("sound tempo=\"%1\"").arg(QString::number(text->tempo()*60.0)));
Expand All @@ -3468,8 +3482,12 @@ void ExportMusicXml::tempoText(TempoText const* const text, int staff)

void ExportMusicXml::words(TextBase const* const text, int staff)
{
const auto offset = calculateTimeDeltaInDivisions(text->tick(), tick(), div);
/*
qDebug("ExportMusicXml::words userOff.x=%f userOff.y=%f xmlText='%s' plainText='%s'",
qDebug("tick %s text->tick %s offset %d userOff.x=%f userOff.y=%f xmlText='%s' plainText='%s'",
qPrintable(tick().print()),
qPrintable(text->tick().print()),
offset,
text->offset().x(), text->offset().y(),
qPrintable(text->xmlText()),
qPrintable(text->plainText()));
Expand All @@ -3482,7 +3500,7 @@ void ExportMusicXml::words(TextBase const* const text, int staff)
}

directionTag(_xml, _attr, text);
wordsMetrome(_xml, _score, text);
wordsMetrome(_xml, _score, text, offset);
directionETag(_xml, staff);
}

Expand Down Expand Up @@ -3512,6 +3530,9 @@ void ExportMusicXml::rehearsal(RehearsalMark const* const rmk, int staff)
MScoreTextToMXML mttm("rehearsal", attr, defFmt, mtf);
mttm.writeTextFragments(rmk->fragmentList(), _xml);
_xml.etag();
const auto offset = calculateTimeDeltaInDivisions(rmk->tick(), tick(), div);
if (offset)
_xml.tag("offset", offset);
directionETag(_xml, staff);
}

Expand Down Expand Up @@ -3965,11 +3986,10 @@ void ExportMusicXml::dynamic(Dynamic const* const dyn, int staff)

_xml.etag();

/*
int offs = dyn->mxmlOff();
if (offs)
xml.tag("offset", offs);
*/
const auto offset = calculateTimeDeltaInDivisions(dyn->tick(), tick(), div);
if (offset)
_xml.tag("offset", offset);

if (staff)
_xml.tag("staff", staff);

Expand Down Expand Up @@ -4003,6 +4023,9 @@ void ExportMusicXml::symbol(Symbol const* const sym, int staff)
_xml.stag("direction-type");
_xml.tagE(mxmlName);
_xml.etag();
const auto offset = calculateTimeDeltaInDivisions(sym->tick(), tick(), div);
if (offset)
_xml.tag("offset", offset);
directionETag(_xml, staff);
}

Expand Down Expand Up @@ -4398,6 +4421,10 @@ static bool commonAnnotations(ExportMusicXml* exp, const Element* e, int sstaff)
// annotations
//---------------------------------------------------------

/*
* Write annotations that are attached to chords or rests
*/

// In MuseScore, Element::FRET_DIAGRAM and Element::HARMONY are separate annotations,
// in MusicXML they are combined in the harmony element. This means they have to be matched.
// TODO: replace/repair current algorithm (which can only handle one FRET_DIAGRAM and one HARMONY)
Expand Down Expand Up @@ -5438,10 +5465,8 @@ static void annotationsWithoutNote(ExportMusicXml* exp, const int strack, const
for (const auto element : segment->annotations()) {
if (!element->isFiguredBass() && !element->isHarmony()) { // handled elsewhere
const auto wtrack = findTrackForAnnotations(element->track(), segment); // track to write annotation
if (strack <= element->track() && element->track() < (strack + VOICES * staves) && wtrack < 0) {
exp->moveToTick(element->tick());
if (strack <= element->track() && element->track() < (strack + VOICES * staves) && wtrack < 0)
commonAnnotations(exp, element, staves > 1 ? 1 : 0);
}
}
}
}
Expand Down
73 changes: 37 additions & 36 deletions mscore/importmxmlpass2.cpp
Expand Up @@ -1925,7 +1925,7 @@ void MusicXMLParserPass2::measure(const QString& partId,
attributes(partId, measure, time + mTime);
else if (_e.name() == "direction") {
MusicXMLParserDirection dir(_e, _score, _pass1, *this, _logger);
dir.direction(partId, measure, time + mTime, _spanners);
dir.direction(partId, measure, time + mTime, _divs, _spanners);
}
else if (_e.name() == "figured-bass") {
FiguredBass* fb = figuredBass();
Expand Down Expand Up @@ -2300,6 +2300,25 @@ void MusicXMLParserPass2::print(Measure* measure)
}
}

//---------------------------------------------------------
// calcTicks
//---------------------------------------------------------

static Fraction calcTicks(const QString& text, int divs, MxmlLogger* logger, const QXmlStreamReader* const xmlreader)
{
Fraction dura(0, 0); // invalid unless set correctly

int intDura = text.toInt();
if (divs > 0) {
dura.set(intDura, 4 * divs);
dura.reduce();
}
else
logger->logError(QString("illegal or uninitialized divisions (%1)").arg(divs), xmlreader);

return dura;
}

//---------------------------------------------------------
// direction
//---------------------------------------------------------
Expand All @@ -2311,10 +2330,11 @@ void MusicXMLParserPass2::print(Measure* measure)
void MusicXMLParserDirection::direction(const QString& partId,
Measure* measure,
const Fraction& tick,
const int divisions,
MusicXmlSpannerMap& spanners)
{
Q_ASSERT(_e.isStartElement() && _e.name() == "direction");
//qDebug("direction tick %d", tick);
//qDebug("direction tick %s", qPrintable(tick.print()));

QString placement = _e.attributes().value("placement").toString();
int track = _pass1.trackForPart(partId);
Expand All @@ -2334,6 +2354,10 @@ void MusicXMLParserDirection::direction(const QString& partId,
while (_e.readNextStartElement()) {
if (_e.name() == "direction-type")
directionType(starts, stops);
else if (_e.name() == "offset")
_offset = calcTicks(_e.readElementText(), divisions, _logger, &_e);
else if (_e.name() == "sound")
sound();
else if (_e.name() == "staff") {
int nstaves = _pass1.getPart(partId)->nstaves();
QString strStaff = _e.readElementText();
Expand All @@ -2343,8 +2367,6 @@ void MusicXMLParserDirection::direction(const QString& partId,
else
_logger->logError(QString("invalid staff %1").arg(strStaff), &_e);
}
else if (_e.name() == "sound")
sound();
else
skipLogCurrElem();
}
Expand Down Expand Up @@ -2399,7 +2421,7 @@ void MusicXMLParserDirection::direction(const QString& partId,
}

//TODO:ws if (_hasDefaultY) t->textStyle().setYoff(_defaultY);
addElemOffset(t, track, placement, measure, tick);
addElemOffset(t, track, placement, measure, tick + _offset);
}
else if (_tpoSound > 0) {
double tpo = _tpoSound / 60;
Expand All @@ -2408,9 +2430,10 @@ void MusicXMLParserDirection::direction(const QString& partId,
t->setTempo(tpo);
t->setFollowText(true);

// TBD may want ro use tick + _offset if sound is affected
_score->setTempo(tick, tpo);

addElemOffset(t, track, placement, measure, tick);
addElemOffset(t, track, placement, measure, tick + _offset);
}

// do dynamics
Expand All @@ -2427,13 +2450,13 @@ void MusicXMLParserDirection::direction(const QString& partId,
dyn->setVelocity( dynaValue );
}
//TODO:ws if (_hasDefaultY) dyn->textStyle().setYoff(_defaultY);
addElemOffset(dyn, track, placement, measure, tick);
addElemOffset(dyn, track, placement, measure, tick + _offset);
}

// handle the elems
foreach( auto elem, _elems) {
// TODO (?) if (_hasDefaultY) elem->setYoff(_defaultY);
addElemOffset(elem, track, placement, measure, tick);
addElemOffset(elem, track, placement, measure, tick + _offset);
}

// handle the spanner stops first
Expand All @@ -2458,6 +2481,7 @@ void MusicXMLParserDirection::direction(const QString& partId,
}

// then handle the spanner starts
// TBD handle offset ?
foreach (auto desc, starts) {
auto& spdesc = _pass2.getSpanner({ desc._tp, desc._nr });
if (spdesc._isStarted) {
Expand Down Expand Up @@ -4631,23 +4655,6 @@ void MusicXMLParserPass2::notePrintSpacingNo(Fraction& dura)
Q_ASSERT(_e.isEndElement() && _e.name() == "note");
}

//---------------------------------------------------------
// calcTicks
//---------------------------------------------------------

static Fraction calcTicks(const QString& text, int divs)
{
Fraction dura(0, 0); // invalid unless set correctly

int intDura = text.toInt();
if (divs > 0)
dura.set(intDura, 4 * divs);
else
qDebug("illegal or uninitialized divisions (%d)", divs); // TODO

return dura;
}

//---------------------------------------------------------
// duration
//---------------------------------------------------------
Expand All @@ -4661,15 +4668,9 @@ void MusicXMLParserPass2::duration(Fraction& dura)
Q_ASSERT(_e.isStartElement() && _e.name() == "duration");

dura.set(0, 0); // invalid unless set correctly
int intDura = _e.readElementText().toInt();
if (intDura > 0) {
if (_divs > 0) {
dura.set(intDura, 4 * _divs);
dura.reduce(); // prevent overflow in later Fraction operations
}
else
_logger->logError(QString("illegal or uninitialized divisions (%1)").arg(_divs), &_e);
}
const auto elementText = _e.readElementText();
if (elementText.toInt() > 0)
dura = calcTicks(elementText, _divs, _logger, &_e);
else
_logger->logError(QString("illegal duration %1").arg(dura.print()), &_e);
//qDebug("duration %s valid %d", qPrintable(dura.print()), dura.isValid());
Expand Down Expand Up @@ -5052,7 +5053,7 @@ void MusicXMLParserPass2::harmony(const QString& partId, Measure* measure, const
else if (_e.name() == "level")
skipLogCurrElem();
else if (_e.name() == "offset")
offset = calcTicks(_e.readElementText(), _divs);
offset = calcTicks(_e.readElementText(), _divs, _logger, &_e);
else if (_e.name() == "staff") {
int nstaves = _pass1.getPart(partId)->nstaves();
QString strStaff = _e.readElementText();
Expand Down Expand Up @@ -6235,7 +6236,7 @@ MusicXMLParserDirection::MusicXMLParserDirection(QXmlStreamReader& e,
MxmlLogger* logger)
: _e(e), _score(score), _pass1(pass1), _pass2(pass2), _logger(logger),
_hasDefaultY(false), _defaultY(0.0), _coda(false), _segno(false),
_tpoMetro(0), _tpoSound(0)
_tpoMetro(0), _tpoSound(0), _offset(0, 1)
{
// nothing
}
Expand Down
3 changes: 2 additions & 1 deletion mscore/importmxmlpass2.h
Expand Up @@ -328,7 +328,7 @@ class MusicXMLParserPass2 {
class MusicXMLParserDirection {
public:
MusicXMLParserDirection(QXmlStreamReader& e, Score* score, const MusicXMLParserPass1& pass1, MusicXMLParserPass2& pass2, MxmlLogger* logger);
void direction(const QString& partId, Measure* measure, const Fraction& tick, MusicXmlSpannerMap& spanners);
void direction(const QString& partId, Measure* measure, const Fraction& tick, const int divisions, MusicXmlSpannerMap& spanners);

private:
QXmlStreamReader& _e;
Expand Down Expand Up @@ -357,6 +357,7 @@ class MusicXMLParserDirection {
double _tpoMetro; // tempo according to metronome
double _tpoSound; // tempo according to sound
QList<Element*> _elems;
Fraction _offset;

void directionType(QList<MusicXmlSpannerDesc>& starts, QList<MusicXmlSpannerDesc>& stops);
void bracket(const QString& type, const int number, QList<MusicXmlSpannerDesc>& starts, QList<MusicXmlSpannerDesc>& stops);
Expand Down

0 comments on commit 5af1357

Please sign in to comment.