Skip to content
Merged
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
31 changes: 31 additions & 0 deletions src/include/mx/api/NoteData.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@

#pragma once

#include "mx/api/CurveData.h"
#include "mx/api/DurationData.h"
#include "mx/api/LyricData.h"
#include "mx/api/NoteAttachmentData.h"
#include "mx/api/PitchData.h"
#include "mx/api/PositionData.h"
#include "mx/api/PrintData.h"

#include <optional>
#include <vector>

namespace mx
Expand Down Expand Up @@ -75,6 +77,30 @@ enum class Stem
both
};

// A lone <tied type="let-ring">: no matching stop, and no sound-level <tie>
// counterpart (whose type is start/stop only). Modeled as its own struct
// rather than folding into NoteData's isTieStart / isTieStop.
struct TieLetRing
{
// Visual attributes carried by the <tied type="let-ring"> element.
PositionData positionData; // default-x/y, relative-x/y, placement
CurveOrientation curveOrientation;
bool isColorSpecified;
ColorData colorData;

TieLetRing() : positionData{}, curveOrientation{CurveOrientation::unspecified}, isColorSpecified{false}, colorData{}
{
}
};

MXAPI_EQUALS_BEGIN(TieLetRing)
MXAPI_EQUALS_MEMBER(positionData)
MXAPI_EQUALS_MEMBER(curveOrientation)
MXAPI_EQUALS_MEMBER(isColorSpecified)
MXAPI_EQUALS_MEMBER(colorData)
MXAPI_EQUALS_END;
MXAPI_NOT_EQUALS_AND_VECTORS(TieLetRing);

class NoteData
{
public:
Expand All @@ -98,6 +124,10 @@ class NoteData
bool isTieStart;
bool isTieStop;

// A laissez-vibrer / let-ring tie on this note (a lone <tied type="let-ring">
// with no matching stop). Independent of isTieStart / isTieStop. See TieLetRing.
std::optional<TieLetRing> tieLetRing;

NoteType noteType; // normal, cue, grace
Notehead notehead;
PitchData pitchData; // step, alter, octave, accidental, etc
Expand Down Expand Up @@ -140,6 +170,7 @@ MXAPI_EQUALS_MEMBER(isDisplayStepOctaveSpecified)
MXAPI_EQUALS_MEMBER(isChord)
MXAPI_EQUALS_MEMBER(isTieStart)
MXAPI_EQUALS_MEMBER(isTieStop)
MXAPI_EQUALS_MEMBER(tieLetRing)
MXAPI_EQUALS_MEMBER(noteType)
MXAPI_EQUALS_MEMBER(pitchData)
MXAPI_EQUALS_MEMBER(userRequestedVoiceNumber)
Expand Down
4 changes: 2 additions & 2 deletions src/private/mx/api/NoteData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ namespace api
{
NoteData::NoteData()
: isRest{false}, isMeasureRest{false}, isUnpitched{false}, isDisplayStepOctaveSpecified{false}, isChord{false},
isTieStart{false}, isTieStop{false}, noteType{NoteType::normal}, notehead{Notehead::normal}, pitchData{},
userRequestedVoiceNumber{-1}, stem{Stem::unspecified}, tickTimePosition{0}, durationData{}, beams{},
isTieStart{false}, isTieStop{false}, tieLetRing{}, noteType{NoteType::normal}, notehead{Notehead::normal},
pitchData{}, userRequestedVoiceNumber{-1}, stem{Stem::unspecified}, tickTimePosition{0}, durationData{}, beams{},
positionData{}, printData{}, noteAttachmentData{}, lyrics{}
{
}
Expand Down
60 changes: 57 additions & 3 deletions src/private/mx/impl/CurveFunctions.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#pragma once

#include "mx/api/CurveData.h"
#include "mx/api/NoteData.h"
#include "mx/core/generated/Slur.h"
#include "mx/core/generated/Tied.h"
#include "mx/impl/LineFunctions.h"
Expand Down Expand Up @@ -282,18 +283,71 @@ void writeAttributesFromCurveStop(const api::CurveStop inCurve, ATTRIBUTES_TYPE
}
}

// Emits a lone <tied type="let-ring"> from an api::TieLetRing, carrying its
// shared visual attributes (position, orientation, color).
inline void writeAttributesFromTieLetRing(const api::TieLetRing &inTie, core::Tied &outTied)
{
outTied.setType(core::TiedType::letRing());
impl::setAttributesFromPositionData(inTie.positionData, outTied);

if (inTie.isColorSpecified)
{
setAttributesFromColorData(inTie.colorData, outTied);
}

if (inTie.curveOrientation != api::CurveOrientation::unspecified)
{
outTied.setOrientation(inTie.curveOrientation == api::CurveOrientation::overhand ? core::OverUnder::over()
: core::OverUnder::under());
}
}

// Parses a lone <tied type="let-ring"> into an api::TieLetRing. A let-ring
// tie has no start/stop pairing, so it is captured on its own rather than in
// the curve vectors. The visual attributes it shares with any tied element
// (position, orientation, color) are carried across.
inline api::TieLetRing parseTieLetRing(const core::Tied &inTied)
{
api::TieLetRing c;
c.positionData = impl::getPositionData(inTied);
c.isColorSpecified = checkHasColor(&inTied);

if (c.isColorSpecified)
{
c.colorData = impl::getColor(inTied);
}

if (inTied.orientation().has_value())
{
c.curveOrientation = *inTied.orientation() == core::OverUnder::over() ? api::CurveOrientation::overhand
: api::CurveOrientation::underhand;
}
return c;
}

// takes either an mx::core::Tied or an mx::core::Slur
// populates the outNoteData.curveStart, cureContinuations
// or curveStop vector with the result
template <typename SLUR_OR_TIE_ELEMENT_TYPE>
void parseCurve(const SLUR_OR_TIE_ELEMENT_TYPE &slurOrTie, api::NoteData &outNoteData)
{
// Slur's type is StartStopContinue; Tied's is TiedType (whose let-ring
// alternative the old core could not represent; it falls through all
// branches and is ignored here).
// Slur's type is StartStopContinue; Tied's is TiedType, which also has a
// let-ring alternative. A let-ring value only occurs on <tied>, so the
// if constexpr below keeps this branch out of the slur instantiation (whose
// type has no letRing) and routes the lone <tied type="let-ring"> into
// NoteData::tieLetRing instead of the start/continue/stop curve vectors.
const auto outputType = slurOrTie.type();
using CurveTypeAttribute = std::decay_t<decltype(outputType)>;

if constexpr (std::is_same_v<std::decay_t<SLUR_OR_TIE_ELEMENT_TYPE>, core::Tied>)
{
if (core::TiedType::letRing() == outputType)
{
outNoteData.tieLetRing = parseTieLetRing(slurOrTie);
return;
}
}

if (CurveTypeAttribute::start() == outputType)
{
outNoteData.noteAttachmentData.curveStarts.emplace_back(parseCurveStart(slurOrTie));
Expand Down
10 changes: 10 additions & 0 deletions src/private/mx/impl/NotationsWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,16 @@ core::Notations NotationsWriter::getNotations() const
}
}

// A laissez-vibrer / let-ring tie is a lone <tied type="let-ring"> with no
// start/stop pairing, so it is emitted from its own field rather than the
// curve vectors above.
if (myNoteData.tieLetRing.has_value())
{
core::Tied tied;
writeAttributesFromTieLetRing(*myNoteData.tieLetRing, tied);
outNotations.addChoice(core::NotationsChoice::tied(tied));
}

for (const auto &tupletStop : myNoteData.noteAttachmentData.tupletStops)
{
core::Tuplet tuplet;
Expand Down
Loading
Loading