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
166 changes: 166 additions & 0 deletions src/include/mx/api/NoteRelationData.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
// MusicXML Class Library
// Copyright (c) by Matthew James Briggs
// Distributed under the MIT License

#pragma once

#include "mx/api/ApiCommon.h"
#include "mx/api/DurationData.h"
#include "mx/api/NoteData.h"

#include <optional>
#include <string>
#include <vector>

namespace mx
{
namespace api
{
// Whether a metronome-tied marks the start or the stop of a tie between two note figures in a
// metric-relationship metronome mark. As with ordinary ties, both ends should be present, each on
// its own note figure.
enum class MetronomeTieType
{
start,
stop
};

// Whether a metronome-tuplet marks the start or the stop of the tuplet bracket.
enum class MetronomeTupletType
{
start,
stop
};

// Which numbers a metronome-tuplet displays: the actual count, both the actual and normal counts,
// or neither. unspecified means the source omitted show-number and it is not written back.
enum class MetronomeShowNumber
{
unspecified,
actual,
both,
none
};

// A beam drawn on a note figure inside a metric-relationship metronome mark. value is the beam
// action (begin, continue = extend, end, or a forward/backward hook). number is the beam level,
// counting from 1 for the eighth-note beam upward; an absent number means MusicXML's default of
// level 1 and is written without the attribute.
struct MetronomeBeam
{
Beam value;
std::optional<int> number;

MetronomeBeam() : value{Beam::unspecified}, number{}
{
}
};

// A tuplet drawn on a note figure inside a metric-relationship metronome mark: the
// time-modification ratio -- actualNotes in the time of normalNotes, where a normal note is a
// normalType note carrying normalDots dots -- plus how the bracket and numbers are shown.
// normalType unspecified means the ratio uses the figure's own note type.
struct MetronomeTuplet
{
int actualNotes;
int normalNotes;
DurationName normalType;
int normalDots;
MetronomeTupletType type;
Bool bracket;
MetronomeShowNumber showNumber;

MetronomeTuplet()
: actualNotes{0}, normalNotes{0}, normalType{DurationName::unspecified}, normalDots{0},
type{MetronomeTupletType::start}, bracket{Bool::unspecified}, showNumber{MetronomeShowNumber::unspecified}
{
}
};

// One note figure inside a metric-relationship metronome mark. metronomeType is the note value
// drawn (quarter, eighth, ...) and dots its augmentation dots; beams, tie, and tuplet decorate the
// figure the way beams, ties, and tuplets decorate a real note, but using the reduced
// metronome-specific vocabulary.
struct MetronomeNoteData
{
DurationName metronomeType;
int dots;
std::vector<MetronomeBeam> beams;
std::optional<MetronomeTieType> tie;
std::optional<MetronomeTuplet> tuplet;

MetronomeNoteData() : metronomeType{DurationName::unspecified}, dots{0}, beams{}, tie{}, tuplet{}
{
}
};

// The right-hand side of a metric relationship: the relation symbol drawn between the two note
// groups (MusicXML currently allows only "equals"; an empty symbol also means "equals") followed
// by the note figures to its right (at least one).
struct MetronomeRelation
{
std::string symbol;
std::vector<MetronomeNoteData> notes;

MetronomeRelation() : symbol{}, notes{}
{
}
};

// A metric-relationship metronome mark -- the general form of a metric modulation, drawn with note
// pictures rather than the bare beat-units of the simpler MetricModulation form. A typical mark
// equates two note groups, e.g. "two beamed eighths = a quarter-note triplet". notes is the
// left-hand group (at least one figure). relation, when present, holds the relation symbol and the
// right-hand group; when absent the mark shows a single group with no equation. arrows draws
// metric-modulation arrows on both sides of the mark.
struct NoteRelation
{
bool arrows;
std::vector<MetronomeNoteData> notes;
std::optional<MetronomeRelation> relation;

NoteRelation() : arrows{false}, notes{}, relation{}
{
}
};

MXAPI_EQUALS_BEGIN(MetronomeBeam)
MXAPI_EQUALS_MEMBER(value)
MXAPI_EQUALS_MEMBER(number)
MXAPI_EQUALS_END;
MXAPI_NOT_EQUALS_AND_VECTORS(MetronomeBeam);

MXAPI_EQUALS_BEGIN(MetronomeTuplet)
MXAPI_EQUALS_MEMBER(actualNotes)
MXAPI_EQUALS_MEMBER(normalNotes)
MXAPI_EQUALS_MEMBER(normalType)
MXAPI_EQUALS_MEMBER(normalDots)
MXAPI_EQUALS_MEMBER(type)
MXAPI_EQUALS_MEMBER(bracket)
MXAPI_EQUALS_MEMBER(showNumber)
MXAPI_EQUALS_END;
MXAPI_NOT_EQUALS_AND_VECTORS(MetronomeTuplet);

MXAPI_EQUALS_BEGIN(MetronomeNoteData)
MXAPI_EQUALS_MEMBER(metronomeType)
MXAPI_EQUALS_MEMBER(dots)
MXAPI_EQUALS_MEMBER(beams)
MXAPI_EQUALS_MEMBER(tie)
MXAPI_EQUALS_MEMBER(tuplet)
MXAPI_EQUALS_END;
MXAPI_NOT_EQUALS_AND_VECTORS(MetronomeNoteData);

MXAPI_EQUALS_BEGIN(MetronomeRelation)
MXAPI_EQUALS_MEMBER(symbol)
MXAPI_EQUALS_MEMBER(notes)
MXAPI_EQUALS_END;
MXAPI_NOT_EQUALS_AND_VECTORS(MetronomeRelation);

MXAPI_EQUALS_BEGIN(NoteRelation)
MXAPI_EQUALS_MEMBER(arrows)
MXAPI_EQUALS_MEMBER(notes)
MXAPI_EQUALS_MEMBER(relation)
MXAPI_EQUALS_END;
MXAPI_NOT_EQUALS_AND_VECTORS(NoteRelation);
} // namespace api
} // namespace mx
114 changes: 83 additions & 31 deletions src/include/mx/api/TempoData.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,64 +7,116 @@
#include "mx/api/ColorData.h"
#include "mx/api/DurationData.h"
#include "mx/api/FontData.h"
#include "mx/api/NoteRelationData.h"
#include "mx/api/PositionData.h"
#include "mx/api/PrintData.h"

#include <optional>
#include <string>
#include <variant>
#include <vector>

namespace mx
{
namespace api
{
// Which kind of metronome mark a TempoData carries.
enum class TempoType
// A single note picture in a beat-unit metronome mark: a note value (type) with augmentation
// dots. Used for the tied continuations of a beat-unit, e.g. the eighth in "quarter + eighth".
struct BeatUnit
{
unspecified,
beatsPerMinute,
metricModulation
DurationName type;
int dots;

BeatUnit() : type{DurationName::unspecified}, dots{0}
{
}
};

// A metronome mark of the form "note = number", e.g. a quarter note at 120. durationName and
// dots give the beat-unit -- the note picture to the left of the equals sign. beatsPerMinute is
// the value to the right of it. MusicXML allows free text there ("120", "ca. 76", a range like
// "126-138"), so it is a string; an empty string is a valid "unset" value. A player that needs a
// numeric playback tempo should read SoundData::tempo, which is always numeric and expressed in
// quarter notes per minute.
// A metronome mark of the form "note = number", e.g. a quarter note at 120. durationName and dots
// give the beat-unit -- the note picture to the left of the equals sign. tiedBeatUnits holds any
// further note pictures tied to it (a mark like "quarter + eighth = 120" ties an eighth to the
// quarter). beatsPerMinute is the value to the right of the equals sign; MusicXML allows free text
// there ("120", "ca. 76", a range like "126-138"), so it is a string, and an empty string is a
// valid "unset" value. A player that needs a numeric playback tempo should read SoundData::tempo,
// which is always numeric and expressed in quarter notes per minute.
struct BeatsPerMinute
{
DurationName durationName;
int dots;
std::vector<BeatUnit> tiedBeatUnits;
std::string beatsPerMinute;

BeatsPerMinute() : durationName{DurationName::unspecified}, dots{VALUE_UNSPECIFIED}, beatsPerMinute{}
BeatsPerMinute()
: durationName{DurationName::unspecified}, dots{VALUE_UNSPECIFIED}, tiedBeatUnits{}, beatsPerMinute{}
{
}
};

// A metronome mark of the form "note = note", e.g. a dotted quarter equal to a half note -- the
// two-beat-unit spelling of a metric modulation. The left and right beat-units are each a note
// picture (durationName + dots). playbackBeatsPerMinute is unused for this form.
// picture (durationName + dots), and each may carry tied continuations. playbackBeatsPerMinute is
// unused for this form.
struct MetricModulation
{
DurationName leftDurationName;
int leftDots;
std::vector<BeatUnit> leftTiedBeatUnits;
DurationName rightDurationName;
int rightDots;
std::vector<BeatUnit> rightTiedBeatUnits;
BeatsPerMinute playbackBeatsPerMinute;

MetricModulation()
: leftDurationName{DurationName::unspecified}, leftDots{VALUE_UNSPECIFIED},
rightDurationName{DurationName::unspecified}, rightDots{VALUE_UNSPECIFIED}, playbackBeatsPerMinute{}
: leftDurationName{DurationName::unspecified}, leftDots{VALUE_UNSPECIFIED}, leftTiedBeatUnits{},
rightDurationName{DurationName::unspecified}, rightDots{VALUE_UNSPECIFIED}, rightTiedBeatUnits{},
playbackBeatsPerMinute{}
{
}
};

// A metronome (tempo) mark carried by a <direction>. tempoType selects the form: a
// note-equals-number mark (beatsPerMinute) or a note-equals-note metric modulation
// (metricModulation). positionData, fontData, color, and id give the mark's placement and
// The body of a metronome mark: exactly one of the three forms a <metronome> can take. Construct
// it from whichever form you mean; query kind() (or the is...() helpers) and read the matching
// accessor. Reading the wrong accessor returns a default-constructed value rather than throwing.
class TempoChoice
{
public:
enum class Kind
{
beatsPerMinute,
metricModulation,
noteRelation
};

// Defaults to an (empty) beats-per-minute mark, the first alternative.
TempoChoice();

TempoChoice(BeatsPerMinute value);
TempoChoice(MetricModulation value);
TempoChoice(NoteRelation value);

Kind kind() const;
bool isBeatsPerMinute() const;
bool isMetricModulation() const;
bool isNoteRelation() const;

// Each accessor returns a copy of the held alternative. Check the matching is...() first; a
// wrong-kind access returns a default-constructed value.
BeatsPerMinute beatsPerMinute() const;
MetricModulation metricModulation() const;
NoteRelation noteRelation() const;

bool operator==(const TempoChoice &other) const;

private:
std::variant<BeatsPerMinute, MetricModulation, NoteRelation> myValue;
};

// A metronome (tempo) mark carried by a <direction>. choice holds the mark's body -- a
// note-equals-number mark, a note-equals-note metric modulation, or a note-relationship mark
// drawn with note figures. positionData, fontData, color, and id give the mark's placement and
// appearance; justify aligns the mark's content within its box; printObject set to 'no' keeps the
// mark in the file without drawing it; isParenthetical draws the mark in parentheses.
// mark in the file without drawing it (common for relationship marks); isParenthetical draws the
// mark in parentheses.
class TempoData
{
public:
Expand All @@ -77,34 +129,36 @@ class TempoData
std::optional<std::string> id;
HorizontalAlignment justify;
Bool printObject;
TempoType tempoType;

// only used when tempoType is 'beatsPerMinute'
BeatsPerMinute beatsPerMinute;

// only used when tempoType is 'metricModulation'
MetricModulation metricModulation;
TempoChoice choice;

TempoData()
: tickTime{0}, isParenthetical{Bool::unspecified}, printData{}, positionData{}, fontData{}, color{}, id{},
justify{HorizontalAlignment::unspecified}, printObject{Bool::unspecified}, tempoType{TempoType::unspecified},
beatsPerMinute{}, metricModulation{}
justify{HorizontalAlignment::unspecified}, printObject{Bool::unspecified}, choice{}
{
}
};

MXAPI_EQUALS_BEGIN(BeatUnit)
MXAPI_EQUALS_MEMBER(type)
MXAPI_EQUALS_MEMBER(dots)
MXAPI_EQUALS_END;
MXAPI_NOT_EQUALS_AND_VECTORS(BeatUnit);

MXAPI_EQUALS_BEGIN(BeatsPerMinute)
MXAPI_EQUALS_MEMBER(durationName)
MXAPI_EQUALS_MEMBER(dots)
MXAPI_EQUALS_MEMBER(tiedBeatUnits)
MXAPI_EQUALS_MEMBER(beatsPerMinute)
MXAPI_EQUALS_END;
MXAPI_NOT_EQUALS_AND_VECTORS(BeatsPerMinute);

MXAPI_EQUALS_BEGIN(MetricModulation)
MXAPI_EQUALS_MEMBER(leftDurationName)
MXAPI_EQUALS_MEMBER(leftDots)
MXAPI_EQUALS_MEMBER(leftTiedBeatUnits)
MXAPI_EQUALS_MEMBER(rightDurationName)
MXAPI_EQUALS_MEMBER(rightDots)
MXAPI_EQUALS_MEMBER(rightTiedBeatUnits)
MXAPI_EQUALS_MEMBER(playbackBeatsPerMinute)
MXAPI_EQUALS_END;
MXAPI_NOT_EQUALS_AND_VECTORS(MetricModulation);
Expand All @@ -119,9 +173,7 @@ MXAPI_EQUALS_MEMBER(color)
MXAPI_EQUALS_MEMBER(id)
MXAPI_EQUALS_MEMBER(justify)
MXAPI_EQUALS_MEMBER(printObject)
MXAPI_EQUALS_MEMBER(tempoType)
MXAPI_EQUALS_MEMBER(beatsPerMinute)
MXAPI_EQUALS_MEMBER(metricModulation)
MXAPI_EQUALS_MEMBER(choice)
MXAPI_EQUALS_END;
MXAPI_NOT_EQUALS_AND_VECTORS(TempoData);
} // namespace api
Expand Down
Loading
Loading