diff --git a/src/include/mx/api/OttavaData.h b/src/include/mx/api/OttavaData.h index bc1f1736f..f28e59629 100644 --- a/src/include/mx/api/OttavaData.h +++ b/src/include/mx/api/OttavaData.h @@ -28,7 +28,14 @@ class OttavaStart SpannerStart spannerStart; OttavaType ottavaType; - OttavaStart() : spannerStart{}, ottavaType{OttavaType::unspecified} + // Octave-shift size fidelity knob. The size value (8 or 15) follows from ottavaType, so a + // 15ma/15mb line always writes size="15" while an 8va/8vb line omits the redundant, spec- + // default size="8". When true, the writer also emits that redundant size="8"; the reader sets + // it when the source spelled the attribute out. Leave false (the default) when authoring. It + // has no effect on a 15ma/15mb line, whose size is always written. + bool writeDefaultSize; + + OttavaStart() : spannerStart{}, ottavaType{OttavaType::unspecified}, writeDefaultSize{false} { } }; @@ -52,6 +59,7 @@ class OttavaStop MXAPI_EQUALS_BEGIN(OttavaStart) MXAPI_EQUALS_MEMBER(spannerStart) MXAPI_EQUALS_MEMBER(ottavaType) +MXAPI_EQUALS_MEMBER(writeDefaultSize) MXAPI_EQUALS_END; MXAPI_NOT_EQUALS_AND_VECTORS(OttavaStart); diff --git a/src/private/mx/impl/DirectionReader.cpp b/src/private/mx/impl/DirectionReader.cpp index b62cc8753..528c112ab 100644 --- a/src/private/mx/impl/DirectionReader.cpp +++ b/src/private/mx/impl/DirectionReader.cpp @@ -840,6 +840,8 @@ void DirectionReader::parseOctaveShift(const core::DirectionType &directionType) api::OttavaStart start; start.spannerStart = impl::getSpannerStart(octaveShift); start.ottavaType = ottavaType; + const bool isEightLine = ottavaType == api::OttavaType::o8va || ottavaType == api::OttavaType::o8vb; + start.writeDefaultSize = isEightLine && octaveShift.size().has_value(); start.spannerStart.tickTimePosition = myCursor.tickTimePosition; myOutDirectionData.ottavaStarts.emplace_back(std::move(start)); appendOrderedComponent(api::DirectionComponentKind::ottavaStart, diff --git a/src/private/mx/impl/DirectionWriter.cpp b/src/private/mx/impl/DirectionWriter.cpp index d295121ee..38bb91838 100644 --- a/src/private/mx/impl/DirectionWriter.cpp +++ b/src/private/mx/impl/DirectionWriter.cpp @@ -399,32 +399,42 @@ void DirectionWriter::emitOttavaStart(const api::OttavaStart &ottavaStart, core: os.setNumber(core::NumberLevel{*number}); } + int sizeValue = 8; + switch (ottavaStart.ottavaType) { case api::OttavaType::o15ma: { os.setType(core::UpDownStopContinue::down()); - os.setSize(15); + sizeValue = 15; break; } case api::OttavaType::o15mb: { os.setType(core::UpDownStopContinue::up()); - os.setSize(15); + sizeValue = 15; break; } case api::OttavaType::o8va: { os.setType(core::UpDownStopContinue::down()); - os.setSize(8); + sizeValue = 8; break; } case api::OttavaType::o8vb: { os.setType(core::UpDownStopContinue::up()); - os.setSize(8); + sizeValue = 8; break; } default: break; } + // size follows from ottavaType; a 15ma/15mb line's size encodes the two-octave shift, so it is + // always written, while the redundant default size="8" is emitted only when writeDefaultSize is + // set (the source spelled it out). + if (sizeValue != 8 || ottavaStart.writeDefaultSize) + { + os.setSize(sizeValue); + } + core::DirectionType dt{}; dt.setChoice(core::DirectionTypeChoice::octaveShift(os)); addDirectionType(std::move(dt), direction);