From 45aa54a0d4a2acb6182af37a6709338357b1690f Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Mon, 16 Sep 2024 17:13:50 +0200
Subject: [PATCH 001/105] Add iocmme.h/cpp

---
 Verovio.xcodeproj/project.pbxproj | 16 +++++++++
 include/vrv/iocmme.h              | 44 +++++++++++++++++++++++++
 src/iocmme.cpp                    | 55 +++++++++++++++++++++++++++++++
 3 files changed, 115 insertions(+)
 create mode 100644 include/vrv/iocmme.h
 create mode 100644 src/iocmme.cpp

diff --git a/Verovio.xcodeproj/project.pbxproj b/Verovio.xcodeproj/project.pbxproj
index 0f0b7a4035c..4ad7bd2ea3c 100644
--- a/Verovio.xcodeproj/project.pbxproj
+++ b/Verovio.xcodeproj/project.pbxproj
@@ -326,6 +326,12 @@
 		4D64137E2035F68600BB630E /* mdiv.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4D64137D2035F68600BB630E /* mdiv.cpp */; };
 		4D64137F2035F68600BB630E /* mdiv.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4D64137D2035F68600BB630E /* mdiv.cpp */; };
 		4D6413802035F68600BB630E /* mdiv.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4D64137D2035F68600BB630E /* mdiv.cpp */; };
+		4D6479372C9881B800CD9539 /* iocmme.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4D6479362C9881B800CD9539 /* iocmme.cpp */; };
+		4D6479392C98825900CD9539 /* iocmme.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4D6479362C9881B800CD9539 /* iocmme.cpp */; };
+		4D64793A2C98825A00CD9539 /* iocmme.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4D6479362C9881B800CD9539 /* iocmme.cpp */; };
+		4D64793B2C98825A00CD9539 /* iocmme.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4D6479362C9881B800CD9539 /* iocmme.cpp */; };
+		4D64793C2C98826600CD9539 /* iocmme.h in Headers */ = {isa = PBXBuildFile; fileRef = 4D6479382C9881DB00CD9539 /* iocmme.h */; };
+		4D64793D2C98826600CD9539 /* iocmme.h in Headers */ = {isa = PBXBuildFile; fileRef = 4D6479382C9881DB00CD9539 /* iocmme.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		4D674B3F255F40AC008AEF4C /* plica.h in Headers */ = {isa = PBXBuildFile; fileRef = 4D674B3E255F40AC008AEF4C /* plica.h */; };
 		4D674B40255F40AC008AEF4C /* plica.h in Headers */ = {isa = PBXBuildFile; fileRef = 4D674B3E255F40AC008AEF4C /* plica.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		4D674B46255F40B7008AEF4C /* plica.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4D674B45255F40B7008AEF4C /* plica.cpp */; };
@@ -1871,6 +1877,8 @@
 		4D6413772035F58200BB630E /* pages.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = pages.cpp; path = src/pages.cpp; sourceTree = "<group>"; };
 		4D64137B2035F67C00BB630E /* mdiv.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = mdiv.h; path = include/vrv/mdiv.h; sourceTree = "<group>"; };
 		4D64137D2035F68600BB630E /* mdiv.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = mdiv.cpp; path = src/mdiv.cpp; sourceTree = "<group>"; };
+		4D6479362C9881B800CD9539 /* iocmme.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = iocmme.cpp; path = src/iocmme.cpp; sourceTree = "<group>"; };
+		4D6479382C9881DB00CD9539 /* iocmme.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = iocmme.h; path = include/vrv/iocmme.h; sourceTree = "<group>"; };
 		4D674B3E255F40AC008AEF4C /* plica.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = plica.h; path = include/vrv/plica.h; sourceTree = "<group>"; };
 		4D674B45255F40B7008AEF4C /* plica.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = plica.cpp; path = src/plica.cpp; sourceTree = "<group>"; };
 		4D6DF9F420E1071E00C12BBD /* subst.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = subst.cpp; path = src/subst.cpp; sourceTree = "<group>"; };
@@ -2877,6 +2885,8 @@
 				8F59291718854BF800FE51AD /* iobase.h */,
 				402197931F2E09DA00182DF1 /* ioabc.cpp */,
 				402197921F2E09CB00182DF1 /* ioabc.h */,
+				4D6479382C9881DB00CD9539 /* iocmme.h */,
+				4D6479362C9881B800CD9539 /* iocmme.cpp */,
 				8F086EC1188539540037FD8E /* iodarms.cpp */,
 				8F59291818854BF800FE51AD /* iodarms.h */,
 				4DA20ED01D0F151900706C4A /* iohumdrum.cpp */,
@@ -3440,6 +3450,7 @@
 				E7901659298BCA97008FDB4E /* calcalignmentxposfunctor.h in Headers */,
 				BDEF9ECC26725248008A3A47 /* caesura.h in Headers */,
 				E7ADB3AB29D1921300825D5D /* adjustarticfunctor.h in Headers */,
+				4D64793C2C98826600CD9539 /* iocmme.h in Headers */,
 				E73E86252A069C640089DF74 /* transposefunctor.h in Headers */,
 				4DEC4DD921C8295700D1D273 /* rdg.h in Headers */,
 				4DB3D8D01F83D11800B5FC2B /* mordent.h in Headers */,
@@ -3561,6 +3572,7 @@
 				4D4CDEA82C079026005621E9 /* adjustneumexfunctor.h in Headers */,
 				BB4C4B5222A932D7001F6AF0 /* ftrem.h in Headers */,
 				E7E9C11329B0A1E200CFCE2F /* adjustaccidxfunctor.h in Headers */,
+				4D64793D2C98826600CD9539 /* iocmme.h in Headers */,
 				4D88AD0B289673F50006D7DA /* symbol.h in Headers */,
 				4D1EB6A72A2A40CB00AF2F98 /* textlayoutelement.h in Headers */,
 				4D2E759022BC2B71004C51F0 /* course.h in Headers */,
@@ -4084,6 +4096,7 @@
 				40C2E4222052A6F60003625F /* pb.cpp in Sources */,
 				4DA0EAF322BB77C300A7EBEB /* facsimileinterface.cpp in Sources */,
 				4D1694241E3A44F300569BF4 /* positioninterface.cpp in Sources */,
+				4D6479392C98825900CD9539 /* iocmme.cpp in Sources */,
 				4D1694251E3A44F300569BF4 /* timestamp.cpp in Sources */,
 				4D1694261E3A44F300569BF4 /* MidiEventList.cpp in Sources */,
 				4DA0EAC822BB779400A7EBEB /* facsimile.cpp in Sources */,
@@ -4422,6 +4435,7 @@
 				4D79642126C167100026288B /* pagemilestone.cpp in Sources */,
 				4D2073FB22A3BCE000E0765F /* tabgrp.cpp in Sources */,
 				4DEC4DBA21C8288900D1D273 /* choice.cpp in Sources */,
+				4D6479372C9881B800CD9539 /* iocmme.cpp in Sources */,
 				4DACC9882990F29A00B55913 /* atts_mei.cpp in Sources */,
 				4D2073F522A3BCE000E0765F /* tuning.cpp in Sources */,
 				8F086F06188539540037FD8E /* view_beam.cpp in Sources */,
@@ -4661,6 +4675,7 @@
 				8F3DD35418854B2E0051330C /* slur.cpp in Sources */,
 				40C2E4232052A6F70003625F /* pb.cpp in Sources */,
 				4D64137A2035F58200BB630E /* pages.cpp in Sources */,
+				4D64793A2C98825A00CD9539 /* iocmme.cpp in Sources */,
 				4D1694741E3A455200569BF4 /* humlib.cpp in Sources */,
 				4DB3D8C31F83D0EF00B5FC2B /* anchoredtext.cpp in Sources */,
 				4DB3D8E71F83D16A00B5FC2B /* ftrem.cpp in Sources */,
@@ -4950,6 +4965,7 @@
 				BB4C4B6122A932D7001F6AF0 /* mrpt.cpp in Sources */,
 				BB4C4AE722A932BC001F6AF0 /* damage.cpp in Sources */,
 				4DA0EACA22BB779400A7EBEB /* facsimile.cpp in Sources */,
+				4D64793B2C98825A00CD9539 /* iocmme.cpp in Sources */,
 				BB4C4B4B22A932D7001F6AF0 /* custos.cpp in Sources */,
 				BB4C4BB822A932FC001F6AF0 /* Binasc.cpp in Sources */,
 				BB4C4B7B22A932D7001F6AF0 /* tuplet.cpp in Sources */,
diff --git a/include/vrv/iocmme.h b/include/vrv/iocmme.h
new file mode 100644
index 00000000000..43aa6e2a4e6
--- /dev/null
+++ b/include/vrv/iocmme.h
@@ -0,0 +1,44 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        iocmme.h
+// Author:      Laurent Pugin
+// Created:     2024
+// Copyright (c) Authors and others. All rights reserved.
+/////////////////////////////////////////////////////////////////////////////
+
+#ifndef __VRV_IOCMME_H__
+#define __VRV_IOCMME_H__
+
+#include <string>
+#include <vector>
+
+//----------------------------------------------------------------------------
+
+#include "iobase.h"
+#include "pugixml.hpp"
+#include "vrvdef.h"
+
+namespace vrv {
+
+//----------------------------------------------------------------------------
+// CmmeInput
+//----------------------------------------------------------------------------
+
+class CmmeInput : public Input {
+public:
+    // constructors and destructors
+    CmmeInput(Doc *doc);
+    virtual ~CmmeInput();
+
+    bool Import(const std::string &cmme) override;
+
+private:
+    //
+public:
+    //
+private:
+    //
+};
+
+} // namespace vrv
+
+#endif
diff --git a/src/iocmme.cpp b/src/iocmme.cpp
new file mode 100644
index 00000000000..c097a318b16
--- /dev/null
+++ b/src/iocmme.cpp
@@ -0,0 +1,55 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        iocmme.cpp
+// Author:      Laurent Pugin
+// Created:     2024
+// Copyright (c) Authors and others. All rights reserved.
+/////////////////////////////////////////////////////////////////////////////
+
+#include "iocmme.h"
+
+//----------------------------------------------------------------------------
+
+#include <cassert>
+#include <fstream>
+#include <sstream>
+#include <string>
+
+//----------------------------------------------------------------------------
+
+#include "barline.h"
+#include "clef.h"
+#include "doc.h"
+#include "layer.h"
+#include "mdiv.h"
+#include "measure.h"
+#include "note.h"
+#include "score.h"
+#include "section.h"
+#include "staff.h"
+#include "staffdef.h"
+#include "staffgrp.h"
+#include "vrv.h"
+
+//----------------------------------------------------------------------------
+
+namespace vrv {
+
+//----------------------------------------------------------------------------
+// CmmeInput
+//----------------------------------------------------------------------------
+
+CmmeInput::CmmeInput(Doc *doc) : Input(doc) {}
+
+CmmeInput::~CmmeInput() {}
+
+//////////////////////////////////////////////////////////////////////////
+
+bool CmmeInput::Import(const std::string &cmme)
+{
+    m_doc->Reset();
+    m_doc->SetType(Raw);
+
+    return true;
+}
+
+} // namespace vrv

From ce7c7406f775657b002c160eb1e432ce69bb1168 Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Tue, 17 Sep 2024 15:25:20 +0200
Subject: [PATCH 002/105] Enable cmme inputFrom

---
 include/vrv/toolkitdef.h | 1 +
 src/options.cpp          | 3 ++-
 src/toolkit.cpp          | 7 +++++++
 3 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/include/vrv/toolkitdef.h b/include/vrv/toolkitdef.h
index 246a4c1bc74..254c8449809 100644
--- a/include/vrv/toolkitdef.h
+++ b/include/vrv/toolkitdef.h
@@ -19,6 +19,7 @@ enum FileFormat {
     HUMMIDI,
     PAE,
     ABC,
+    CMME,
     DARMS,
     VOLPIANO,
     MUSICXML,
diff --git a/src/options.cpp b/src/options.cpp
index ba0265ef74a..d4ec5dc44f4 100644
--- a/src/options.cpp
+++ b/src/options.cpp
@@ -914,7 +914,8 @@ Options::Options()
     m_baseOptions.AddOption(&m_allPages);
 
     m_inputFrom.SetInfo("Input from",
-        "Select input format from: \"abc\", \"darms\", \"esac\", \"humdrum\", \"mei\", \"pae\", \"volpiano\", \"xml\" "
+        "Select input format from: \"abc\", \"cmme.xml\", \"darms\", \"esac\", \"humdrum\", \"mei\", \"pae\", "
+        "\"volpiano\", \"xml\" "
         "(musicxml), \"musicxml-hum\" (musicxml via humdrum)");
     m_inputFrom.Init("mei");
     m_inputFrom.SetKey("inputFrom");
diff --git a/src/toolkit.cpp b/src/toolkit.cpp
index a81a8c61034..086562628d1 100644
--- a/src/toolkit.cpp
+++ b/src/toolkit.cpp
@@ -24,6 +24,7 @@
 #include "filereader.h"
 #include "findfunctor.h"
 #include "ioabc.h"
+#include "iocmme.h"
 #include "iodarms.h"
 #include "iohumdrum.h"
 #include "iomei.h"
@@ -206,6 +207,9 @@ bool Toolkit::SetInputFrom(std::string const &inputFrom)
     else if (inputFrom == "volpiano") {
         m_inputFrom = VOLPIANO;
     }
+    else if (inputFrom == "cmme.xml") {
+        m_inputFrom = CMME;
+    }
     else if ((inputFrom == "humdrum") || (inputFrom == "hum")) {
         m_inputFrom = HUMDRUM;
     }
@@ -548,6 +552,9 @@ bool Toolkit::LoadData(const std::string &data, bool resetLogBuffer)
     else if (inputFormat == VOLPIANO) {
         input = new VolpianoInput(&m_doc);
     }
+    else if (inputFormat == CMME) {
+        input = new CmmeInput(&m_doc);
+    }
 #ifndef NO_HUMDRUM_SUPPORT
     else if (inputFormat == HUMDRUM) {
         // LogInfo("Importing Humdrum data");

From ebcdc1de9068f9f04e42f46dcb93d78fe7011615 Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Tue, 17 Sep 2024 15:25:53 +0200
Subject: [PATCH 003/105] Basic implementation (WIP)

---
 include/vrv/iocmme.h |  35 ++++-
 src/iocmme.cpp       | 321 ++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 352 insertions(+), 4 deletions(-)

diff --git a/include/vrv/iocmme.h b/include/vrv/iocmme.h
index 43aa6e2a4e6..4ae04a4cb3f 100644
--- a/include/vrv/iocmme.h
+++ b/include/vrv/iocmme.h
@@ -19,6 +19,15 @@
 
 namespace vrv {
 
+class Clef;
+class Layer;
+class Measure;
+class Score;
+
+namespace cmme {
+
+}
+
 //----------------------------------------------------------------------------
 // CmmeInput
 //----------------------------------------------------------------------------
@@ -32,11 +41,35 @@ class CmmeInput : public Input {
     bool Import(const std::string &cmme) override;
 
 private:
-    //
+    void MakeSection(pugi::xml_node musicSectionNode);
+
+    void MakeStaff(pugi::xml_node voiceNode);
+
+    void MakeClef(pugi::xml_node clefNode);
+    void MakeDot(pugi::xml_node dotNode);
+    void MakeMensuration(pugi::xml_node mensurationNode);
+    void MakeOriginalText(pugi::xml_node originalTextNode);
+    void MakeNote(pugi::xml_node noteNode);
+    void MakeRest(pugi::xml_node restNode);
+
+    data_DURATION ReadDuration(pugi::xml_node durationNode, int &num, int &numbase);
+    // std::pair<int, int> ReadNumNumbase(pugi::xml_node durationNode);
+
+    std::string AsString(const pugi::xml_node node) const;
+    std::string ChildAsString(const pugi::xml_node node, const std::string &child) const;
+    int AsInt(const pugi::xml_node node) const;
+    int ChildAsInt(const pugi::xml_node node, const std::string &child) const;
+
 public:
     //
 private:
     //
+    Score *m_score;
+    Measure *m_currentSection;
+    Layer *m_currentLayer;
+
+    int m_numVoices;
+    std::vector<std::string> m_voices;
 };
 
 } // namespace vrv
diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index c097a318b16..63116ac4e4f 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -19,10 +19,12 @@
 #include "barline.h"
 #include "clef.h"
 #include "doc.h"
+#include "dot.h"
 #include "layer.h"
 #include "mdiv.h"
 #include "measure.h"
 #include "note.h"
+#include "rest.h"
 #include "score.h"
 #include "section.h"
 #include "staff.h"
@@ -38,7 +40,12 @@ namespace vrv {
 // CmmeInput
 //----------------------------------------------------------------------------
 
-CmmeInput::CmmeInput(Doc *doc) : Input(doc) {}
+CmmeInput::CmmeInput(Doc *doc) : Input(doc)
+{
+    m_score = NULL;
+    m_currentSection = NULL;
+    m_currentLayer = NULL;
+}
 
 CmmeInput::~CmmeInput() {}
 
@@ -46,10 +53,318 @@ CmmeInput::~CmmeInput() {}
 
 bool CmmeInput::Import(const std::string &cmme)
 {
-    m_doc->Reset();
-    m_doc->SetType(Raw);
+    try {
+        m_doc->Reset();
+        m_doc->SetType(Raw);
+        pugi::xml_document doc;
+        doc.load_string(cmme.c_str(), (pugi::parse_comments | pugi::parse_default) & ~pugi::parse_eol);
+        pugi::xml_node root = doc.first_child();
+
+        // The mDiv
+        Mdiv *mdiv = new Mdiv();
+        mdiv->m_visibility = Visible;
+        m_doc->AddChild(mdiv);
+        // The score
+        m_score = new Score();
+        mdiv->AddChild(m_score);
+
+        pugi::xpath_node_set voices = root.select_nodes("/Piece/VoiceData/Voice");
+
+        for (pugi::xpath_node voiceNode : voices) {
+            m_numVoices++;
+            std::string name = ChildAsString(voiceNode.node(), "Name");
+            m_voices.push_back(name);
+        }
+
+        pugi::xpath_node_set musicSections = root.select_nodes("/Piece/MusicSection/*");
+
+        for (pugi::xpath_node musicSectionNode : musicSections) {
+            MakeSection(musicSectionNode.node());
+        }
+
+        // add minimal scoreDef
+        StaffGrp *staffGrp = new StaffGrp();
+        for (int i = 0; i < m_numVoices; ++i) {
+            StaffDef *staffDef = new StaffDef();
+            staffDef->SetN(i + 1);
+            staffDef->SetLines(5);
+            staffGrp->AddChild(staffDef);
+        }
+
+        m_score->GetScoreDef()->AddChild(staffGrp);
+
+        m_doc->ConvertToPageBasedDoc();
+        // return this->ReadDoc(root);
+    }
+    catch (char *str) {
+        LogError("%s", str);
+        return false;
+    }
 
     return true;
 }
 
+void CmmeInput::MakeSection(pugi::xml_node musicSectionNode)
+{
+    assert(m_score);
+
+    std::string sectionType = musicSectionNode.name();
+
+    // the section
+    Section *section = new Section();
+    section->SetType(sectionType);
+    m_score->AddChild(section);
+
+    m_currentSection = new Measure(UNMEASURED, 1);
+    section->AddChild(m_currentSection);
+
+    pugi::xpath_node_set voices = musicSectionNode.select_nodes("./Voice");
+
+    for (int i = 0; i < m_numVoices; ++i) {
+        std::string xpath = StringFormat("./Voice[VoiceNum[text()='%d']]", i + 1);
+        pugi::xpath_node voice = musicSectionNode.select_node(xpath.c_str());
+        if (voice) {
+            MakeStaff(voice.node());
+        }
+        else {
+            Staff *staff = new Staff(i + 1);
+            staff->SetVisible(BOOLEAN_false);
+            m_currentSection->AddChild(staff);
+        }
+    }
+}
+
+void CmmeInput::MakeStaff(pugi::xml_node voiceNode)
+{
+    assert(m_currentSection);
+
+    int numVoice = this->ChildAsInt(voiceNode, "VoiceNum");
+
+    Staff *staff = new Staff(numVoice);
+    m_currentLayer = new Layer();
+    m_currentLayer->SetN(1);
+
+    pugi::xpath_node_set events = voiceNode.select_nodes("./EventList/*");
+
+    for (pugi::xpath_node event : events) {
+        pugi::xml_node eventNode = event.node();
+        std::string name = eventNode.name();
+        if (name == "Clef") {
+            if (eventNode.select_node("./Signature")) {
+            }
+            else {
+                MakeClef(eventNode);
+            }
+        }
+        else if (name == "Dot") {
+            MakeDot(eventNode);
+        }
+        else if (name == "Mensuration") {
+            MakeMensuration(eventNode);
+        }
+        else if (name == "Note") {
+            MakeNote(eventNode);
+        }
+        else if (name == "OriginalText") {
+            MakeOriginalText(eventNode);
+        }
+        else if (name == "Rest") {
+            MakeRest(eventNode);
+        }
+        else {
+            LogWarning("Unsupported event '%s'", name.c_str());
+        }
+    }
+
+    staff->AddChild(m_currentLayer);
+    m_currentSection->AddChild(staff);
+}
+
+void CmmeInput::MakeClef(pugi::xml_node clefNode)
+{
+    static const std::map<std::string, data_CLEFSHAPE> shapeMap{
+        { "C", CLEFSHAPE_C }, //
+        { "F", CLEFSHAPE_F }, //
+        { "G", CLEFSHAPE_G }, //
+        { "Frnd", CLEFSHAPE_F }, //
+    };
+
+    std::string appearance = this->ChildAsString(clefNode, "Appearance");
+    if (!shapeMap.contains(appearance)) {
+        LogWarning("Unknown clef '%s", appearance.c_str());
+        return;
+    }
+
+    Clef *clef = new Clef();
+    int staffLoc = this->ChildAsInt(clefNode, "StaffLoc");
+    staffLoc = (staffLoc + 1) / 2;
+    clef->SetLine(staffLoc);
+
+    data_CLEFSHAPE shape = shapeMap.at(appearance);
+    clef->SetShape(shape);
+
+    m_currentLayer->AddChild(clef);
+
+    return;
+}
+
+void CmmeInput::MakeDot(pugi::xml_node dotNode)
+{
+    Dot *dot = new Dot();
+    m_currentLayer->AddChild(dot);
+
+    return;
+}
+
+void CmmeInput::MakeMensuration(pugi::xml_node mensurationNode)
+{
+    return;
+}
+
+void CmmeInput::MakeNote(pugi::xml_node noteNode)
+{
+    static const std::map<std::string, data_PITCHNAME> Step2PitchName{
+        { "C", PITCHNAME_c }, //
+        { "D", PITCHNAME_d }, //
+        { "E", PITCHNAME_e }, //
+        { "F", PITCHNAME_f }, //
+        { "G", PITCHNAME_g }, //
+        { "A", PITCHNAME_a }, //
+        { "B", PITCHNAME_b } //
+    };
+
+    Note *note = new Note();
+    std::string step = this->ChildAsString(noteNode, "LetterName");
+    data_PITCHNAME pname = Step2PitchName.contains(step) ? Step2PitchName.at(step) : PITCHNAME_c;
+    note->SetPname(pname);
+
+    int num;
+    int numbase;
+    data_DURATION duration = this->ReadDuration(noteNode, num, numbase);
+    note->SetDur(duration);
+    if (num != VRV_UNSET && numbase != VRV_UNSET) {
+        note->SetNumbase(num);
+        note->SetNum(numbase);
+    }
+
+    int oct = this->ChildAsInt(noteNode, "OctaveNum");
+    if ((pname != PITCHNAME_a) && (pname != PITCHNAME_b)) oct += 1;
+    note->SetOct(oct);
+
+    if (noteNode.child("Colored")) {
+        note->SetColored(BOOLEAN_true);
+    }
+
+    m_currentLayer->AddChild(note);
+
+    return;
+}
+
+void CmmeInput::MakeOriginalText(pugi::xml_node originalTextNode)
+{
+    return;
+}
+
+void CmmeInput::MakeRest(pugi::xml_node restNode)
+{
+    Rest *rest = new Rest();
+    int num;
+    int numbase;
+    rest->SetDur(this->ReadDuration(restNode, num, numbase));
+    if (num != VRV_UNSET && numbase != VRV_UNSET) {
+        rest->SetNumbase(num);
+        rest->SetNum(numbase);
+    }
+
+    m_currentLayer->AddChild(rest);
+
+    return;
+}
+
+data_DURATION CmmeInput::ReadDuration(pugi::xml_node durationNode, int &num, int &numbase)
+{
+    static const std::map<std::string, data_DURATION> durationMap{
+        { "Maxima", DURATION_maxima }, //
+        { "Longa", DURATION_longa }, //
+        { "Brevis", DURATION_brevis }, //
+        { "Semibrevis", DURATION_semibrevis }, //
+        { "Minima", DURATION_minima }, //
+        { "Semiminima", DURATION_semiminima }, //
+        { "Fusa", DURATION_fusa }, //
+        { "Semifusa", DURATION_semifusa } //
+    };
+
+    std::string type = this->ChildAsString(durationNode, "Type");
+    data_DURATION duration = durationMap.contains(type) ? durationMap.at(type) : DURATION_brevis;
+
+    num = VRV_UNSET;
+    numbase = VRV_UNSET;
+
+    static const std::map<std::string, std::pair<int, int>> fractionMap{
+        { "Maxima", { 16, 1 } }, //
+        { "Longa", { 8, 1 } }, //
+        { "Brevis", { 4, 1 } }, //
+        { "Semibrevis", { 2, 1 } }, //
+        { "Minima", { 1, 1 } }, //
+        { "Semiminima", { 1, 2 } }, //
+        { "Fusa", { 1, 4 } }, //
+        { "Semifusa", { 1, 8 } } //
+    };
+
+    if (durationNode.child("Length")) {
+        num = this->ChildAsInt(durationNode.child("Length"), "Num");
+        numbase = this->ChildAsInt(durationNode.child("Length"), "Den");
+
+        auto ratio = fractionMap.at(type);
+        if (ratio.first != num || ratio.second != numbase) {
+            num *= ratio.second;
+            numbase *= ratio.first;
+        }
+        else {
+            num = VRV_UNSET;
+            numbase = VRV_UNSET;
+        }
+    }
+
+    return duration;
+}
+
+std::string CmmeInput::AsString(const pugi::xml_node node) const
+{
+    assert(node);
+
+    if (node.text()) {
+        return std::string(node.text().as_string());
+    }
+    return "";
+}
+
+std::string CmmeInput::ChildAsString(const pugi::xml_node node, const std::string &child) const
+{
+    pugi::xpath_node childNode = node.select_node(child.c_str());
+    if (childNode.node()) {
+        return this->AsString(childNode.node());
+    }
+    return "";
+}
+
+int CmmeInput::AsInt(const pugi::xml_node node) const
+{
+    assert(node);
+
+    if (node.text()) {
+        return (node.text().as_int());
+    }
+    return VRV_UNSET;
+}
+
+int CmmeInput::ChildAsInt(const pugi::xml_node node, const std::string &child) const
+{
+    pugi::xpath_node childNode = node.select_node(child.c_str());
+    if (childNode.node()) {
+        return this->AsInt(childNode.node());
+    }
+    return VRV_UNSET;
+}
+
 } // namespace vrv

From 73527eb5f9d58c04b952d4d2b5ec696f8a39aacc Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Tue, 17 Sep 2024 17:54:35 +0200
Subject: [PATCH 004/105] Proper handling of durations

---
 include/vrv/iocmme.h | 11 ++++++-
 src/iocmme.cpp       | 78 ++++++++++++++++++++++++++++++++++++--------
 2 files changed, 75 insertions(+), 14 deletions(-)

diff --git a/include/vrv/iocmme.h b/include/vrv/iocmme.h
index 4ae04a4cb3f..794d10df798 100644
--- a/include/vrv/iocmme.h
+++ b/include/vrv/iocmme.h
@@ -26,7 +26,14 @@ class Score;
 
 namespace cmme {
 
-}
+    struct mensuration {
+        int prolatio = 2;
+        int tempus = 2;
+        int modusminor = 2;
+        int modusmaior = 2;
+    };
+
+} // namespace cmme
 
 //----------------------------------------------------------------------------
 // CmmeInput
@@ -67,6 +74,8 @@ class CmmeInput : public Input {
     Score *m_score;
     Measure *m_currentSection;
     Layer *m_currentLayer;
+    std::vector<cmme::mensuration> m_mensurations;
+    cmme::mensuration *m_currentMensuration;
 
     int m_numVoices;
     std::vector<std::string> m_voices;
diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index 63116ac4e4f..ba233ab666f 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -23,6 +23,7 @@
 #include "layer.h"
 #include "mdiv.h"
 #include "measure.h"
+#include "mensur.h"
 #include "note.h"
 #include "rest.h"
 #include "score.h"
@@ -45,6 +46,7 @@ CmmeInput::CmmeInput(Doc *doc) : Input(doc)
     m_score = NULL;
     m_currentSection = NULL;
     m_currentLayer = NULL;
+    m_currentMensuration = NULL;
 }
 
 CmmeInput::~CmmeInput() {}
@@ -75,6 +77,7 @@ bool CmmeInput::Import(const std::string &cmme)
             std::string name = ChildAsString(voiceNode.node(), "Name");
             m_voices.push_back(name);
         }
+        m_mensurations.resize(m_numVoices);
 
         pugi::xpath_node_set musicSections = root.select_nodes("/Piece/MusicSection/*");
 
@@ -88,7 +91,15 @@ bool CmmeInput::Import(const std::string &cmme)
             StaffDef *staffDef = new StaffDef();
             staffDef->SetN(i + 1);
             staffDef->SetLines(5);
+            staffDef->SetNotationtype(NOTATIONTYPE_mensural);
             staffGrp->AddChild(staffDef);
+            // Default mensur with everything binary in CMME
+            Mensur *mensur = new Mensur();
+            mensur->SetProlatio(PROLATIO_2);
+            mensur->SetTempus(TEMPUS_2);
+            mensur->SetModusminor(MODUSMINOR_2);
+            mensur->SetModusmaior(MODUSMAIOR_2);
+            staffDef->AddChild(mensur);
         }
 
         m_score->GetScoreDef()->AddChild(staffGrp);
@@ -144,6 +155,8 @@ void CmmeInput::MakeStaff(pugi::xml_node voiceNode)
     m_currentLayer = new Layer();
     m_currentLayer->SetN(1);
 
+    m_currentMensuration = &m_mensurations.at(numVoice - 1);
+
     pugi::xpath_node_set events = voiceNode.select_nodes("./EventList/*");
 
     for (pugi::xpath_node event : events) {
@@ -218,6 +231,30 @@ void CmmeInput::MakeDot(pugi::xml_node dotNode)
 
 void CmmeInput::MakeMensuration(pugi::xml_node mensurationNode)
 {
+    pugi::xml_node mensInfo = mensurationNode.child("MensInfo");
+    if (mensInfo) {
+        m_currentMensuration->prolatio = this->ChildAsInt(mensInfo, "Prolatio");
+        m_currentMensuration->tempus = this->ChildAsInt(mensInfo, "Tempus");
+        m_currentMensuration->modusminor = this->ChildAsInt(mensInfo, "ModusMinor");
+        m_currentMensuration->modusmaior = this->ChildAsInt(mensInfo, "ModusMaior");
+    }
+
+    Mensur *mensur = new Mensur();
+    data_PROLATIO prolatio = (m_currentMensuration->prolatio == 3) ? PROLATIO_3 : PROLATIO_2;
+    mensur->SetProlatio(prolatio);
+    data_TEMPUS tempus = (m_currentMensuration->tempus == 3) ? TEMPUS_3 : TEMPUS_2;
+    mensur->SetTempus(tempus);
+    data_MODUSMINOR modusminor = (m_currentMensuration->modusminor == 3) ? MODUSMINOR_3 : MODUSMINOR_2;
+    mensur->SetModusminor(modusminor);
+    data_MODUSMAIOR modusmaior = (m_currentMensuration->modusmaior == 3) ? MODUSMAIOR_3 : MODUSMAIOR_2;
+    mensur->SetModusmaior(modusmaior);
+    data_MENSURATIONSIGN sign = (m_currentMensuration->tempus == 3) ? MENSURATIONSIGN_O : MENSURATIONSIGN_C;
+    mensur->SetSign(sign);
+    data_BOOLEAN dot = (m_currentMensuration->prolatio == 3) ? BOOLEAN_true : BOOLEAN_false;
+    mensur->SetDot(dot);
+
+    m_currentLayer->AddChild(mensur);
+
     return;
 }
 
@@ -270,7 +307,8 @@ void CmmeInput::MakeRest(pugi::xml_node restNode)
     Rest *rest = new Rest();
     int num;
     int numbase;
-    rest->SetDur(this->ReadDuration(restNode, num, numbase));
+    data_DURATION duration = this->ReadDuration(restNode, num, numbase);
+    rest->SetDur(duration);
     if (num != VRV_UNSET && numbase != VRV_UNSET) {
         rest->SetNumbase(num);
         rest->SetNum(numbase);
@@ -300,22 +338,36 @@ data_DURATION CmmeInput::ReadDuration(pugi::xml_node durationNode, int &num, int
     num = VRV_UNSET;
     numbase = VRV_UNSET;
 
-    static const std::map<std::string, std::pair<int, int>> fractionMap{
-        { "Maxima", { 16, 1 } }, //
-        { "Longa", { 8, 1 } }, //
-        { "Brevis", { 4, 1 } }, //
-        { "Semibrevis", { 2, 1 } }, //
-        { "Minima", { 1, 1 } }, //
-        { "Semiminima", { 1, 2 } }, //
-        { "Fusa", { 1, 4 } }, //
-        { "Semifusa", { 1, 8 } } //
-    };
-
     if (durationNode.child("Length")) {
         num = this->ChildAsInt(durationNode.child("Length"), "Num");
         numbase = this->ChildAsInt(durationNode.child("Length"), "Den");
 
-        auto ratio = fractionMap.at(type);
+        std::pair<int, int> ratio = { 1, 1 };
+
+        if (type == "Maxima") {
+            ratio.first *= m_currentMensuration->modusmaior * m_currentMensuration->modusminor
+                * m_currentMensuration->tempus * m_currentMensuration->prolatio;
+        }
+        else if (type == "Longa") {
+            ratio.first
+                *= m_currentMensuration->modusminor * m_currentMensuration->tempus * m_currentMensuration->prolatio;
+        }
+        else if (type == "Brevis") {
+            ratio.first *= m_currentMensuration->tempus * m_currentMensuration->prolatio;
+        }
+        else if (type == "Semibrevis") {
+            ratio.first *= m_currentMensuration->prolatio;
+        }
+        else if (type == "Semiminima") {
+            ratio.second = 2;
+        }
+        else if (type == "Fusa") {
+            ratio.second = 4;
+        }
+        else if (type == "Semifusa") {
+            ratio.second = 8;
+        }
+
         if (ratio.first != num || ratio.second != numbase) {
             num *= ratio.second;
             numbase *= ratio.first;

From ec1072fd53c0ea546ab57ab8c08d25f2b4b72095 Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Tue, 17 Sep 2024 21:37:53 +0200
Subject: [PATCH 005/105] Add label

---
 include/vrv/iocmme.h |  5 +++--
 src/iocmme.cpp       | 14 ++++++++++++++
 2 files changed, 17 insertions(+), 2 deletions(-)

diff --git a/include/vrv/iocmme.h b/include/vrv/iocmme.h
index 794d10df798..b83ffb282ed 100644
--- a/include/vrv/iocmme.h
+++ b/include/vrv/iocmme.h
@@ -49,7 +49,6 @@ class CmmeInput : public Input {
 
 private:
     void MakeSection(pugi::xml_node musicSectionNode);
-
     void MakeStaff(pugi::xml_node voiceNode);
 
     void MakeClef(pugi::xml_node clefNode);
@@ -60,8 +59,10 @@ class CmmeInput : public Input {
     void MakeRest(pugi::xml_node restNode);
 
     data_DURATION ReadDuration(pugi::xml_node durationNode, int &num, int &numbase);
-    // std::pair<int, int> ReadNumNumbase(pugi::xml_node durationNode);
 
+    /**
+     * Helper methods for accessing and converting text in elements
+     */
     std::string AsString(const pugi::xml_node node) const;
     std::string ChildAsString(const pugi::xml_node node, const std::string &child) const;
     int AsInt(const pugi::xml_node node) const;
diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index ba233ab666f..7fe22ff3241 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -20,6 +20,7 @@
 #include "clef.h"
 #include "doc.h"
 #include "dot.h"
+#include "label.h"
 #include "layer.h"
 #include "mdiv.h"
 #include "measure.h"
@@ -31,6 +32,7 @@
 #include "staff.h"
 #include "staffdef.h"
 #include "staffgrp.h"
+#include "text.h"
 #include "vrv.h"
 
 //----------------------------------------------------------------------------
@@ -74,6 +76,7 @@ bool CmmeInput::Import(const std::string &cmme)
 
         for (pugi::xpath_node voiceNode : voices) {
             m_numVoices++;
+            // Get the voice name if any
             std::string name = ChildAsString(voiceNode.node(), "Name");
             m_voices.push_back(name);
         }
@@ -87,12 +90,23 @@ bool CmmeInput::Import(const std::string &cmme)
 
         // add minimal scoreDef
         StaffGrp *staffGrp = new StaffGrp();
+        GrpSym *grpSym = new GrpSym();
+        grpSym->SetSymbol(staffGroupingSym_SYMBOL_bracket);
+        staffGrp->AddChild(grpSym);
         for (int i = 0; i < m_numVoices; ++i) {
             StaffDef *staffDef = new StaffDef();
             staffDef->SetN(i + 1);
             staffDef->SetLines(5);
             staffDef->SetNotationtype(NOTATIONTYPE_mensural);
             staffGrp->AddChild(staffDef);
+            // Label
+            if (!m_voices.at(i).empty()) {
+                Label *label = new Label();
+                Text *text = new Text();
+                text->SetText(UTF8to32(m_voices.at(i)));
+                label->AddChild(text);
+                staffDef->AddChild(label);
+            }
             // Default mensur with everything binary in CMME
             Mensur *mensur = new Mensur();
             mensur->SetProlatio(PROLATIO_2);

From 2facfd049dda8cc5e17847c0aea5fee6454021cf Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Tue, 17 Sep 2024 21:42:17 +0200
Subject: [PATCH 006/105] Asserts and checks

---
 src/iocmme.cpp | 21 +++++++++++++++++++--
 1 file changed, 19 insertions(+), 2 deletions(-)

diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index 7fe22ff3241..2fdb97e5926 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -216,6 +216,8 @@ void CmmeInput::MakeClef(pugi::xml_node clefNode)
         { "Frnd", CLEFSHAPE_F }, //
     };
 
+    assert(m_currentLayer);
+
     std::string appearance = this->ChildAsString(clefNode, "Appearance");
     if (!shapeMap.contains(appearance)) {
         LogWarning("Unknown clef '%s", appearance.c_str());
@@ -237,6 +239,8 @@ void CmmeInput::MakeClef(pugi::xml_node clefNode)
 
 void CmmeInput::MakeDot(pugi::xml_node dotNode)
 {
+    assert(m_currentLayer);
+
     Dot *dot = new Dot();
     m_currentLayer->AddChild(dot);
 
@@ -245,6 +249,9 @@ void CmmeInput::MakeDot(pugi::xml_node dotNode)
 
 void CmmeInput::MakeMensuration(pugi::xml_node mensurationNode)
 {
+    assert(m_currentLayer);
+    assert(m_currentMensuration);
+
     pugi::xml_node mensInfo = mensurationNode.child("MensInfo");
     if (mensInfo) {
         m_currentMensuration->prolatio = this->ChildAsInt(mensInfo, "Prolatio");
@@ -284,6 +291,8 @@ void CmmeInput::MakeNote(pugi::xml_node noteNode)
         { "B", PITCHNAME_b } //
     };
 
+    assert(m_currentLayer);
+
     Note *note = new Note();
     std::string step = this->ChildAsString(noteNode, "LetterName");
     data_PITCHNAME pname = Step2PitchName.contains(step) ? Step2PitchName.at(step) : PITCHNAME_c;
@@ -318,6 +327,8 @@ void CmmeInput::MakeOriginalText(pugi::xml_node originalTextNode)
 
 void CmmeInput::MakeRest(pugi::xml_node restNode)
 {
+    assert(m_currentLayer);
+
     Rest *rest = new Rest();
     int num;
     int numbase;
@@ -346,6 +357,8 @@ data_DURATION CmmeInput::ReadDuration(pugi::xml_node durationNode, int &num, int
         { "Semifusa", DURATION_semifusa } //
     };
 
+    assert(m_currentMensuration);
+
     std::string type = this->ChildAsString(durationNode, "Type");
     data_DURATION duration = durationMap.contains(type) ? durationMap.at(type) : DURATION_brevis;
 
@@ -397,7 +410,7 @@ data_DURATION CmmeInput::ReadDuration(pugi::xml_node durationNode, int &num, int
 
 std::string CmmeInput::AsString(const pugi::xml_node node) const
 {
-    assert(node);
+    if (!node) return "";
 
     if (node.text()) {
         return std::string(node.text().as_string());
@@ -407,6 +420,8 @@ std::string CmmeInput::AsString(const pugi::xml_node node) const
 
 std::string CmmeInput::ChildAsString(const pugi::xml_node node, const std::string &child) const
 {
+    if (!node) return "";
+
     pugi::xpath_node childNode = node.select_node(child.c_str());
     if (childNode.node()) {
         return this->AsString(childNode.node());
@@ -416,7 +431,7 @@ std::string CmmeInput::ChildAsString(const pugi::xml_node node, const std::strin
 
 int CmmeInput::AsInt(const pugi::xml_node node) const
 {
-    assert(node);
+    if (!node) return VRV_UNSET;
 
     if (node.text()) {
         return (node.text().as_int());
@@ -426,6 +441,8 @@ int CmmeInput::AsInt(const pugi::xml_node node) const
 
 int CmmeInput::ChildAsInt(const pugi::xml_node node, const std::string &child) const
 {
+    if (!node) return VRV_UNSET;
+
     pugi::xpath_node childNode = node.select_node(child.c_str());
     if (childNode.node()) {
         return this->AsInt(childNode.node());

From c7022b8a460c232435ad337b8dd36e7236f047cd Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Tue, 17 Sep 2024 21:43:37 +0200
Subject: [PATCH 007/105] Rename variable

---
 src/iocmme.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index 2fdb97e5926..3c6b2b21ac4 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -281,7 +281,7 @@ void CmmeInput::MakeMensuration(pugi::xml_node mensurationNode)
 
 void CmmeInput::MakeNote(pugi::xml_node noteNode)
 {
-    static const std::map<std::string, data_PITCHNAME> Step2PitchName{
+    static const std::map<std::string, data_PITCHNAME> pitchMap{
         { "C", PITCHNAME_c }, //
         { "D", PITCHNAME_d }, //
         { "E", PITCHNAME_e }, //
@@ -295,7 +295,7 @@ void CmmeInput::MakeNote(pugi::xml_node noteNode)
 
     Note *note = new Note();
     std::string step = this->ChildAsString(noteNode, "LetterName");
-    data_PITCHNAME pname = Step2PitchName.contains(step) ? Step2PitchName.at(step) : PITCHNAME_c;
+    data_PITCHNAME pname = pitchMap.contains(step) ? pitchMap.at(step) : PITCHNAME_c;
     note->SetPname(pname);
 
     int num;

From 784ee5d3ba550b1efaafdbc16d56052a0a9ff98e Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Tue, 17 Sep 2024 22:05:55 +0200
Subject: [PATCH 008/105] Add function to check if node is clef

---
 include/vrv/iocmme.h |  9 +++---
 src/iocmme.cpp       | 67 ++++++++++++++++++++++++--------------------
 2 files changed, 41 insertions(+), 35 deletions(-)

diff --git a/include/vrv/iocmme.h b/include/vrv/iocmme.h
index b83ffb282ed..a4f88ecefca 100644
--- a/include/vrv/iocmme.h
+++ b/include/vrv/iocmme.h
@@ -26,7 +26,7 @@ class Score;
 
 namespace cmme {
 
-    struct mensuration {
+    struct mensInfo {
         int prolatio = 2;
         int tempus = 2;
         int modusminor = 2;
@@ -58,7 +58,8 @@ class CmmeInput : public Input {
     void MakeNote(pugi::xml_node noteNode);
     void MakeRest(pugi::xml_node restNode);
 
-    data_DURATION ReadDuration(pugi::xml_node durationNode, int &num, int &numbase);
+    data_DURATION ReadDuration(pugi::xml_node durationNode, int &num, int &numbase) const;
+    bool IsClef(pugi::xml_node clefNode) const;
 
     /**
      * Helper methods for accessing and converting text in elements
@@ -75,8 +76,8 @@ class CmmeInput : public Input {
     Score *m_score;
     Measure *m_currentSection;
     Layer *m_currentLayer;
-    std::vector<cmme::mensuration> m_mensurations;
-    cmme::mensuration *m_currentMensuration;
+    std::vector<cmme::mensInfo> m_mensInfos;
+    cmme::mensInfo *m_mensInfo;
 
     int m_numVoices;
     std::vector<std::string> m_voices;
diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index 3c6b2b21ac4..320b8bda8f0 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -9,6 +9,7 @@
 
 //----------------------------------------------------------------------------
 
+#include <algorithm>
 #include <cassert>
 #include <fstream>
 #include <sstream>
@@ -48,7 +49,7 @@ CmmeInput::CmmeInput(Doc *doc) : Input(doc)
     m_score = NULL;
     m_currentSection = NULL;
     m_currentLayer = NULL;
-    m_currentMensuration = NULL;
+    m_mensInfo = NULL;
 }
 
 CmmeInput::~CmmeInput() {}
@@ -80,7 +81,7 @@ bool CmmeInput::Import(const std::string &cmme)
             std::string name = ChildAsString(voiceNode.node(), "Name");
             m_voices.push_back(name);
         }
-        m_mensurations.resize(m_numVoices);
+        m_mensInfos.resize(m_numVoices);
 
         pugi::xpath_node_set musicSections = root.select_nodes("/Piece/MusicSection/*");
 
@@ -169,7 +170,7 @@ void CmmeInput::MakeStaff(pugi::xml_node voiceNode)
     m_currentLayer = new Layer();
     m_currentLayer->SetN(1);
 
-    m_currentMensuration = &m_mensurations.at(numVoice - 1);
+    m_mensInfo = &m_mensInfos.at(numVoice - 1);
 
     pugi::xpath_node_set events = voiceNode.select_nodes("./EventList/*");
 
@@ -177,10 +178,10 @@ void CmmeInput::MakeStaff(pugi::xml_node voiceNode)
         pugi::xml_node eventNode = event.node();
         std::string name = eventNode.name();
         if (name == "Clef") {
-            if (eventNode.select_node("./Signature")) {
+            if (this->IsClef(eventNode)) {
+                MakeClef(eventNode);
             }
             else {
-                MakeClef(eventNode);
             }
         }
         else if (name == "Dot") {
@@ -214,22 +215,18 @@ void CmmeInput::MakeClef(pugi::xml_node clefNode)
         { "F", CLEFSHAPE_F }, //
         { "G", CLEFSHAPE_G }, //
         { "Frnd", CLEFSHAPE_F }, //
+        { "Fsqr", CLEFSHAPE_F }, //
     };
 
     assert(m_currentLayer);
 
-    std::string appearance = this->ChildAsString(clefNode, "Appearance");
-    if (!shapeMap.contains(appearance)) {
-        LogWarning("Unknown clef '%s", appearance.c_str());
-        return;
-    }
-
     Clef *clef = new Clef();
     int staffLoc = this->ChildAsInt(clefNode, "StaffLoc");
     staffLoc = (staffLoc + 1) / 2;
     clef->SetLine(staffLoc);
 
-    data_CLEFSHAPE shape = shapeMap.at(appearance);
+    std::string appearance = this->ChildAsString(clefNode, "Appearance");
+    data_CLEFSHAPE shape = shapeMap.contains(appearance) ? shapeMap.at(appearance) : CLEFSHAPE_C;
     clef->SetShape(shape);
 
     m_currentLayer->AddChild(clef);
@@ -250,28 +247,28 @@ void CmmeInput::MakeDot(pugi::xml_node dotNode)
 void CmmeInput::MakeMensuration(pugi::xml_node mensurationNode)
 {
     assert(m_currentLayer);
-    assert(m_currentMensuration);
+    assert(m_mensInfo);
 
     pugi::xml_node mensInfo = mensurationNode.child("MensInfo");
     if (mensInfo) {
-        m_currentMensuration->prolatio = this->ChildAsInt(mensInfo, "Prolatio");
-        m_currentMensuration->tempus = this->ChildAsInt(mensInfo, "Tempus");
-        m_currentMensuration->modusminor = this->ChildAsInt(mensInfo, "ModusMinor");
-        m_currentMensuration->modusmaior = this->ChildAsInt(mensInfo, "ModusMaior");
+        m_mensInfo->prolatio = this->ChildAsInt(mensInfo, "Prolatio");
+        m_mensInfo->tempus = this->ChildAsInt(mensInfo, "Tempus");
+        m_mensInfo->modusminor = this->ChildAsInt(mensInfo, "ModusMinor");
+        m_mensInfo->modusmaior = this->ChildAsInt(mensInfo, "ModusMaior");
     }
 
     Mensur *mensur = new Mensur();
-    data_PROLATIO prolatio = (m_currentMensuration->prolatio == 3) ? PROLATIO_3 : PROLATIO_2;
+    data_PROLATIO prolatio = (m_mensInfo->prolatio == 3) ? PROLATIO_3 : PROLATIO_2;
     mensur->SetProlatio(prolatio);
-    data_TEMPUS tempus = (m_currentMensuration->tempus == 3) ? TEMPUS_3 : TEMPUS_2;
+    data_TEMPUS tempus = (m_mensInfo->tempus == 3) ? TEMPUS_3 : TEMPUS_2;
     mensur->SetTempus(tempus);
-    data_MODUSMINOR modusminor = (m_currentMensuration->modusminor == 3) ? MODUSMINOR_3 : MODUSMINOR_2;
+    data_MODUSMINOR modusminor = (m_mensInfo->modusminor == 3) ? MODUSMINOR_3 : MODUSMINOR_2;
     mensur->SetModusminor(modusminor);
-    data_MODUSMAIOR modusmaior = (m_currentMensuration->modusmaior == 3) ? MODUSMAIOR_3 : MODUSMAIOR_2;
+    data_MODUSMAIOR modusmaior = (m_mensInfo->modusmaior == 3) ? MODUSMAIOR_3 : MODUSMAIOR_2;
     mensur->SetModusmaior(modusmaior);
-    data_MENSURATIONSIGN sign = (m_currentMensuration->tempus == 3) ? MENSURATIONSIGN_O : MENSURATIONSIGN_C;
+    data_MENSURATIONSIGN sign = (m_mensInfo->tempus == 3) ? MENSURATIONSIGN_O : MENSURATIONSIGN_C;
     mensur->SetSign(sign);
-    data_BOOLEAN dot = (m_currentMensuration->prolatio == 3) ? BOOLEAN_true : BOOLEAN_false;
+    data_BOOLEAN dot = (m_mensInfo->prolatio == 3) ? BOOLEAN_true : BOOLEAN_false;
     mensur->SetDot(dot);
 
     m_currentLayer->AddChild(mensur);
@@ -344,7 +341,7 @@ void CmmeInput::MakeRest(pugi::xml_node restNode)
     return;
 }
 
-data_DURATION CmmeInput::ReadDuration(pugi::xml_node durationNode, int &num, int &numbase)
+data_DURATION CmmeInput::ReadDuration(pugi::xml_node durationNode, int &num, int &numbase) const
 {
     static const std::map<std::string, data_DURATION> durationMap{
         { "Maxima", DURATION_maxima }, //
@@ -357,7 +354,7 @@ data_DURATION CmmeInput::ReadDuration(pugi::xml_node durationNode, int &num, int
         { "Semifusa", DURATION_semifusa } //
     };
 
-    assert(m_currentMensuration);
+    assert(m_mensInfo);
 
     std::string type = this->ChildAsString(durationNode, "Type");
     data_DURATION duration = durationMap.contains(type) ? durationMap.at(type) : DURATION_brevis;
@@ -372,18 +369,16 @@ data_DURATION CmmeInput::ReadDuration(pugi::xml_node durationNode, int &num, int
         std::pair<int, int> ratio = { 1, 1 };
 
         if (type == "Maxima") {
-            ratio.first *= m_currentMensuration->modusmaior * m_currentMensuration->modusminor
-                * m_currentMensuration->tempus * m_currentMensuration->prolatio;
+            ratio.first *= m_mensInfo->modusmaior * m_mensInfo->modusminor * m_mensInfo->tempus * m_mensInfo->prolatio;
         }
         else if (type == "Longa") {
-            ratio.first
-                *= m_currentMensuration->modusminor * m_currentMensuration->tempus * m_currentMensuration->prolatio;
+            ratio.first *= m_mensInfo->modusminor * m_mensInfo->tempus * m_mensInfo->prolatio;
         }
         else if (type == "Brevis") {
-            ratio.first *= m_currentMensuration->tempus * m_currentMensuration->prolatio;
+            ratio.first *= m_mensInfo->tempus * m_mensInfo->prolatio;
         }
         else if (type == "Semibrevis") {
-            ratio.first *= m_currentMensuration->prolatio;
+            ratio.first *= m_mensInfo->prolatio;
         }
         else if (type == "Semiminima") {
             ratio.second = 2;
@@ -408,6 +403,16 @@ data_DURATION CmmeInput::ReadDuration(pugi::xml_node durationNode, int &num, int
     return duration;
 }
 
+bool CmmeInput::IsClef(const pugi::xml_node clefNode) const
+{
+    static std::vector<std::string> clefs = { "C", "F", "Fsqr", "Frnd", "G" };
+
+    if (clefNode.select_node("./Signature")) return false;
+
+    std::string appearance = this->ChildAsString(clefNode, "Appearance");
+    return (std::find(clefs.begin(), clefs.end(), appearance) != clefs.end());
+}
+
 std::string CmmeInput::AsString(const pugi::xml_node node) const
 {
     if (!node) return "";

From 26e997c999273df2d2ae606c382674be15ab031b Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Tue, 17 Sep 2024 22:09:39 +0200
Subject: [PATCH 009/105] Rename methods

---
 include/vrv/iocmme.h | 18 +++++++++---------
 src/iocmme.cpp       | 32 ++++++++++++++++----------------
 2 files changed, 25 insertions(+), 25 deletions(-)

diff --git a/include/vrv/iocmme.h b/include/vrv/iocmme.h
index a4f88ecefca..7cc33ff071e 100644
--- a/include/vrv/iocmme.h
+++ b/include/vrv/iocmme.h
@@ -48,15 +48,15 @@ class CmmeInput : public Input {
     bool Import(const std::string &cmme) override;
 
 private:
-    void MakeSection(pugi::xml_node musicSectionNode);
-    void MakeStaff(pugi::xml_node voiceNode);
-
-    void MakeClef(pugi::xml_node clefNode);
-    void MakeDot(pugi::xml_node dotNode);
-    void MakeMensuration(pugi::xml_node mensurationNode);
-    void MakeOriginalText(pugi::xml_node originalTextNode);
-    void MakeNote(pugi::xml_node noteNode);
-    void MakeRest(pugi::xml_node restNode);
+    void CreateSection(pugi::xml_node musicSectionNode);
+    void CreateStaff(pugi::xml_node voiceNode);
+
+    void CreateClef(pugi::xml_node clefNode);
+    void CreateDot(pugi::xml_node dotNode);
+    void CreateMensuration(pugi::xml_node mensurationNode);
+    void CreateOriginalText(pugi::xml_node originalTextNode);
+    void CreateNote(pugi::xml_node noteNode);
+    void CreateRest(pugi::xml_node restNode);
 
     data_DURATION ReadDuration(pugi::xml_node durationNode, int &num, int &numbase) const;
     bool IsClef(pugi::xml_node clefNode) const;
diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index 320b8bda8f0..75fa6fa06c3 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -86,7 +86,7 @@ bool CmmeInput::Import(const std::string &cmme)
         pugi::xpath_node_set musicSections = root.select_nodes("/Piece/MusicSection/*");
 
         for (pugi::xpath_node musicSectionNode : musicSections) {
-            MakeSection(musicSectionNode.node());
+            CreateSection(musicSectionNode.node());
         }
 
         // add minimal scoreDef
@@ -130,7 +130,7 @@ bool CmmeInput::Import(const std::string &cmme)
     return true;
 }
 
-void CmmeInput::MakeSection(pugi::xml_node musicSectionNode)
+void CmmeInput::CreateSection(pugi::xml_node musicSectionNode)
 {
     assert(m_score);
 
@@ -150,7 +150,7 @@ void CmmeInput::MakeSection(pugi::xml_node musicSectionNode)
         std::string xpath = StringFormat("./Voice[VoiceNum[text()='%d']]", i + 1);
         pugi::xpath_node voice = musicSectionNode.select_node(xpath.c_str());
         if (voice) {
-            MakeStaff(voice.node());
+            CreateStaff(voice.node());
         }
         else {
             Staff *staff = new Staff(i + 1);
@@ -160,7 +160,7 @@ void CmmeInput::MakeSection(pugi::xml_node musicSectionNode)
     }
 }
 
-void CmmeInput::MakeStaff(pugi::xml_node voiceNode)
+void CmmeInput::CreateStaff(pugi::xml_node voiceNode)
 {
     assert(m_currentSection);
 
@@ -179,25 +179,25 @@ void CmmeInput::MakeStaff(pugi::xml_node voiceNode)
         std::string name = eventNode.name();
         if (name == "Clef") {
             if (this->IsClef(eventNode)) {
-                MakeClef(eventNode);
+                CreateClef(eventNode);
             }
             else {
             }
         }
         else if (name == "Dot") {
-            MakeDot(eventNode);
+            CreateDot(eventNode);
         }
         else if (name == "Mensuration") {
-            MakeMensuration(eventNode);
+            CreateMensuration(eventNode);
         }
         else if (name == "Note") {
-            MakeNote(eventNode);
+            CreateNote(eventNode);
         }
         else if (name == "OriginalText") {
-            MakeOriginalText(eventNode);
+            CreateOriginalText(eventNode);
         }
         else if (name == "Rest") {
-            MakeRest(eventNode);
+            CreateRest(eventNode);
         }
         else {
             LogWarning("Unsupported event '%s'", name.c_str());
@@ -208,7 +208,7 @@ void CmmeInput::MakeStaff(pugi::xml_node voiceNode)
     m_currentSection->AddChild(staff);
 }
 
-void CmmeInput::MakeClef(pugi::xml_node clefNode)
+void CmmeInput::CreateClef(pugi::xml_node clefNode)
 {
     static const std::map<std::string, data_CLEFSHAPE> shapeMap{
         { "C", CLEFSHAPE_C }, //
@@ -234,7 +234,7 @@ void CmmeInput::MakeClef(pugi::xml_node clefNode)
     return;
 }
 
-void CmmeInput::MakeDot(pugi::xml_node dotNode)
+void CmmeInput::CreateDot(pugi::xml_node dotNode)
 {
     assert(m_currentLayer);
 
@@ -244,7 +244,7 @@ void CmmeInput::MakeDot(pugi::xml_node dotNode)
     return;
 }
 
-void CmmeInput::MakeMensuration(pugi::xml_node mensurationNode)
+void CmmeInput::CreateMensuration(pugi::xml_node mensurationNode)
 {
     assert(m_currentLayer);
     assert(m_mensInfo);
@@ -276,7 +276,7 @@ void CmmeInput::MakeMensuration(pugi::xml_node mensurationNode)
     return;
 }
 
-void CmmeInput::MakeNote(pugi::xml_node noteNode)
+void CmmeInput::CreateNote(pugi::xml_node noteNode)
 {
     static const std::map<std::string, data_PITCHNAME> pitchMap{
         { "C", PITCHNAME_c }, //
@@ -317,12 +317,12 @@ void CmmeInput::MakeNote(pugi::xml_node noteNode)
     return;
 }
 
-void CmmeInput::MakeOriginalText(pugi::xml_node originalTextNode)
+void CmmeInput::CreateOriginalText(pugi::xml_node originalTextNode)
 {
     return;
 }
 
-void CmmeInput::MakeRest(pugi::xml_node restNode)
+void CmmeInput::CreateRest(pugi::xml_node restNode)
 {
     assert(m_currentLayer);
 

From 69e64c794377bfafe01611b799b59dbe04b84bb6 Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Tue, 17 Sep 2024 22:15:53 +0200
Subject: [PATCH 010/105] Add comment

---
 src/iocmme.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index 75fa6fa06c3..808fde0291a 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -141,6 +141,7 @@ void CmmeInput::CreateSection(pugi::xml_node musicSectionNode)
     section->SetType(sectionType);
     m_score->AddChild(section);
 
+    // the current section is a invisible measure
     m_currentSection = new Measure(UNMEASURED, 1);
     section->AddChild(m_currentSection);
 

From 7420f7e7943ccd6e01562e0006a647b771a2625b Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Tue, 17 Sep 2024 22:32:31 +0200
Subject: [PATCH 011/105] Add syllables

---
 include/vrv/iocmme.h |  4 ++++
 src/iocmme.cpp       | 24 ++++++++++++++++++++++++
 2 files changed, 28 insertions(+)

diff --git a/include/vrv/iocmme.h b/include/vrv/iocmme.h
index 7cc33ff071e..aa86d4a44f9 100644
--- a/include/vrv/iocmme.h
+++ b/include/vrv/iocmme.h
@@ -22,6 +22,7 @@ namespace vrv {
 class Clef;
 class Layer;
 class Measure;
+class Note;
 class Score;
 
 namespace cmme {
@@ -58,6 +59,8 @@ class CmmeInput : public Input {
     void CreateNote(pugi::xml_node noteNode);
     void CreateRest(pugi::xml_node restNode);
 
+    void CreateVerse(pugi::xml_node verseNode);
+
     data_DURATION ReadDuration(pugi::xml_node durationNode, int &num, int &numbase) const;
     bool IsClef(pugi::xml_node clefNode) const;
 
@@ -76,6 +79,7 @@ class CmmeInput : public Input {
     Score *m_score;
     Measure *m_currentSection;
     Layer *m_currentLayer;
+    Note *m_currentNote;
     std::vector<cmme::mensInfo> m_mensInfos;
     cmme::mensInfo *m_mensInfo;
 
diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index 808fde0291a..3dbebe2894a 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -33,7 +33,9 @@
 #include "staff.h"
 #include "staffdef.h"
 #include "staffgrp.h"
+#include "syl.h"
 #include "text.h"
+#include "verse.h"
 #include "vrv.h"
 
 //----------------------------------------------------------------------------
@@ -49,6 +51,7 @@ CmmeInput::CmmeInput(Doc *doc) : Input(doc)
     m_score = NULL;
     m_currentSection = NULL;
     m_currentLayer = NULL;
+    m_currentNote = NULL;
     m_mensInfo = NULL;
 }
 
@@ -313,6 +316,12 @@ void CmmeInput::CreateNote(pugi::xml_node noteNode)
         note->SetColored(BOOLEAN_true);
     }
 
+    if (noteNode.child("ModernText")) {
+        m_currentNote = note;
+        CreateVerse(noteNode.child("ModernText"));
+        m_currentNote = NULL;
+    }
+
     m_currentLayer->AddChild(note);
 
     return;
@@ -342,6 +351,21 @@ void CmmeInput::CreateRest(pugi::xml_node restNode)
     return;
 }
 
+void CmmeInput::CreateVerse(pugi::xml_node verseNode)
+{
+    assert(m_currentNote);
+
+    Verse *verse = new Verse();
+    verse->SetN(1);
+    Syl *syl = new Syl();
+    Text *text = new Text();
+    std::string sylText = this->ChildAsString(verseNode, "Syllable");
+    text->SetText(UTF8to32(sylText));
+    syl->AddChild(text);
+    verse->AddChild(syl);
+    m_currentNote->AddChild(verse);
+}
+
 data_DURATION CmmeInput::ReadDuration(pugi::xml_node durationNode, int &num, int &numbase) const
 {
     static const std::map<std::string, data_DURATION> durationMap{

From bb5fe592b21bd8a5c40c37a0e0606c0a5e47feae Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Wed, 18 Sep 2024 08:10:51 +0200
Subject: [PATCH 012/105] Document the code

---
 include/vrv/iocmme.h | 14 ++++++++++++--
 src/iocmme.cpp       | 20 +++++++++++++-------
 2 files changed, 25 insertions(+), 9 deletions(-)

diff --git a/include/vrv/iocmme.h b/include/vrv/iocmme.h
index aa86d4a44f9..258f81b28c4 100644
--- a/include/vrv/iocmme.h
+++ b/include/vrv/iocmme.h
@@ -65,25 +65,35 @@ class CmmeInput : public Input {
     bool IsClef(pugi::xml_node clefNode) const;
 
     /**
-     * Helper methods for accessing and converting text in elements
+     * Helper methods for accessing and converting text in elements.
+     * Return "" or VRV_UNSET if the node is NULL or the node or the text not found
      */
+    ///@{
     std::string AsString(const pugi::xml_node node) const;
     std::string ChildAsString(const pugi::xml_node node, const std::string &child) const;
     int AsInt(const pugi::xml_node node) const;
     int ChildAsInt(const pugi::xml_node node, const std::string &child) const;
+    ///@}
 
 public:
     //
 private:
-    //
+    /** The current score (only one) */
     Score *m_score;
+    /** The current un-measured measure acting a a MEI section */
     Measure *m_currentSection;
+    /** The current layer (or container) to which the layer elements have to be added */
     Layer *m_currentLayer;
+    /** The current note */
     Note *m_currentNote;
+    /** The mensural infos for all voices */
     std::vector<cmme::mensInfo> m_mensInfos;
+    /** The mensural info for the current voice */
     cmme::mensInfo *m_mensInfo;
 
+    /** The number of voices as given in the general data */
     int m_numVoices;
+    /** The name of the voices - if any */
     std::vector<std::string> m_voices;
 };
 
diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index 3dbebe2894a..8476f64188a 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -76,14 +76,15 @@ bool CmmeInput::Import(const std::string &cmme)
         m_score = new Score();
         mdiv->AddChild(m_score);
 
+        // We asssume that there is always as many Voice elements than given in NumVoices
         pugi::xpath_node_set voices = root.select_nodes("/Piece/VoiceData/Voice");
-
         for (pugi::xpath_node voiceNode : voices) {
             m_numVoices++;
             // Get the voice name if any
             std::string name = ChildAsString(voiceNode.node(), "Name");
             m_voices.push_back(name);
         }
+        // Allocatate the mensural infos initialized with everything binary
         m_mensInfos.resize(m_numVoices);
 
         pugi::xpath_node_set musicSections = root.select_nodes("/Piece/MusicSection/*");
@@ -123,7 +124,6 @@ bool CmmeInput::Import(const std::string &cmme)
         m_score->GetScoreDef()->AddChild(staffGrp);
 
         m_doc->ConvertToPageBasedDoc();
-        // return this->ReadDoc(root);
     }
     catch (char *str) {
         LogError("%s", str);
@@ -139,17 +139,17 @@ void CmmeInput::CreateSection(pugi::xml_node musicSectionNode)
 
     std::string sectionType = musicSectionNode.name();
 
-    // the section
+    // Create a new section
     Section *section = new Section();
+    // Add the section type (MensuralMusic, Plainchant) to `@type`
     section->SetType(sectionType);
     m_score->AddChild(section);
 
-    // the current section is a invisible measure
+    // Set the current section to an invisible unmeasured measure
     m_currentSection = new Measure(UNMEASURED, 1);
     section->AddChild(m_currentSection);
 
-    pugi::xpath_node_set voices = musicSectionNode.select_nodes("./Voice");
-
+    // Loop through the number of voices and parse Voice or create an empty staff if not given
     for (int i = 0; i < m_numVoices; ++i) {
         std::string xpath = StringFormat("./Voice[VoiceNum[text()='%d']]", i + 1);
         pugi::xpath_node voice = musicSectionNode.select_node(xpath.c_str());
@@ -174,10 +174,11 @@ void CmmeInput::CreateStaff(pugi::xml_node voiceNode)
     m_currentLayer = new Layer();
     m_currentLayer->SetN(1);
 
+    // (Re)-set the current mens info to the corresponding voice
     m_mensInfo = &m_mensInfos.at(numVoice - 1);
 
+    // Loop through the event lists
     pugi::xpath_node_set events = voiceNode.select_nodes("./EventList/*");
-
     for (pugi::xpath_node event : events) {
         pugi::xml_node eventNode = event.node();
         std::string name = eventNode.name();
@@ -230,6 +231,7 @@ void CmmeInput::CreateClef(pugi::xml_node clefNode)
     clef->SetLine(staffLoc);
 
     std::string appearance = this->ChildAsString(clefNode, "Appearance");
+    // Default clef to C
     data_CLEFSHAPE shape = shapeMap.contains(appearance) ? shapeMap.at(appearance) : CLEFSHAPE_C;
     clef->SetShape(shape);
 
@@ -296,6 +298,7 @@ void CmmeInput::CreateNote(pugi::xml_node noteNode)
 
     Note *note = new Note();
     std::string step = this->ChildAsString(noteNode, "LetterName");
+    // Default pitch to C
     data_PITCHNAME pname = pitchMap.contains(step) ? pitchMap.at(step) : PITCHNAME_c;
     note->SetPname(pname);
 
@@ -382,6 +385,7 @@ data_DURATION CmmeInput::ReadDuration(pugi::xml_node durationNode, int &num, int
     assert(m_mensInfo);
 
     std::string type = this->ChildAsString(durationNode, "Type");
+    // Default duration to brevis
     data_DURATION duration = durationMap.contains(type) ? durationMap.at(type) : DURATION_brevis;
 
     num = VRV_UNSET;
@@ -432,8 +436,10 @@ bool CmmeInput::IsClef(const pugi::xml_node clefNode) const
 {
     static std::vector<std::string> clefs = { "C", "F", "Fsqr", "Frnd", "G" };
 
+    // Checking this is not enough since it is somethimes missing in CMME files
     if (clefNode.select_node("./Signature")) return false;
 
+    // Also check the clef appearance
     std::string appearance = this->ChildAsString(clefNode, "Appearance");
     return (std::find(clefs.begin(), clefs.end(), appearance) != clefs.end());
 }

From df88b743cdf5331e888d6b3c85ec58bf08bcdfb9 Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Wed, 18 Sep 2024 09:50:07 +0200
Subject: [PATCH 013/105] Add syllable connectors

---
 include/vrv/iocmme.h |  2 ++
 src/iocmme.cpp       | 19 +++++++++++++++++++
 2 files changed, 21 insertions(+)

diff --git a/include/vrv/iocmme.h b/include/vrv/iocmme.h
index 258f81b28c4..7cefb1377f1 100644
--- a/include/vrv/iocmme.h
+++ b/include/vrv/iocmme.h
@@ -86,6 +86,8 @@ class CmmeInput : public Input {
     Layer *m_currentLayer;
     /** The current note */
     Note *m_currentNote;
+    /** The syllable is not the first */
+    bool m_isInSyllable;
     /** The mensural infos for all voices */
     std::vector<cmme::mensInfo> m_mensInfos;
     /** The mensural info for the current voice */
diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index 8476f64188a..e24fb6ca4ab 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -52,6 +52,7 @@ CmmeInput::CmmeInput(Doc *doc) : Input(doc)
     m_currentSection = NULL;
     m_currentLayer = NULL;
     m_currentNote = NULL;
+    m_isInSyllable = false;
     m_mensInfo = NULL;
 }
 
@@ -176,6 +177,8 @@ void CmmeInput::CreateStaff(pugi::xml_node voiceNode)
 
     // (Re)-set the current mens info to the corresponding voice
     m_mensInfo = &m_mensInfos.at(numVoice - 1);
+    // Reset the syllable position
+    m_isInSyllable = false;
 
     // Loop through the event lists
     pugi::xpath_node_set events = voiceNode.select_nodes("./EventList/*");
@@ -364,6 +367,22 @@ void CmmeInput::CreateVerse(pugi::xml_node verseNode)
     Text *text = new Text();
     std::string sylText = this->ChildAsString(verseNode, "Syllable");
     text->SetText(UTF8to32(sylText));
+
+    if (verseNode.child("WordEnd")) {
+        syl->SetWordpos(sylLog_WORDPOS_t);
+        m_isInSyllable = false;
+    }
+    else {
+        if (m_isInSyllable) {
+            syl->SetWordpos(sylLog_WORDPOS_m);
+        }
+        else {
+            syl->SetWordpos(sylLog_WORDPOS_i);
+        }
+        m_isInSyllable = true;
+        syl->SetCon(sylLog_CON_d);
+    }
+
     syl->AddChild(text);
     verse->AddChild(syl);
     m_currentNote->AddChild(verse);

From 88cbc16dca50d2bf78cd6ba24b7a5e2ee9ddfc8e Mon Sep 17 00:00:00 2001
From: martha-thomae <thomaemartha@gmail.com>
Date: Wed, 18 Sep 2024 11:03:24 +0200
Subject: [PATCH 014/105] Add key signatures

---
 include/vrv/iocmme.h |  1 +
 src/iocmme.cpp       | 46 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 47 insertions(+)

diff --git a/include/vrv/iocmme.h b/include/vrv/iocmme.h
index 7cefb1377f1..132be0b66ae 100644
--- a/include/vrv/iocmme.h
+++ b/include/vrv/iocmme.h
@@ -54,6 +54,7 @@ class CmmeInput : public Input {
 
     void CreateClef(pugi::xml_node clefNode);
     void CreateDot(pugi::xml_node dotNode);
+    void CreateKeySig(pugi::xml_node keyNode);
     void CreateMensuration(pugi::xml_node mensurationNode);
     void CreateOriginalText(pugi::xml_node originalTextNode);
     void CreateNote(pugi::xml_node noteNode);
diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index e24fb6ca4ab..eeb161712c3 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -21,6 +21,8 @@
 #include "clef.h"
 #include "doc.h"
 #include "dot.h"
+#include "keyaccid.h"
+#include "keysig.h"
 #include "label.h"
 #include "layer.h"
 #include "mdiv.h"
@@ -190,6 +192,7 @@ void CmmeInput::CreateStaff(pugi::xml_node voiceNode)
                 CreateClef(eventNode);
             }
             else {
+                CreateKeySig(eventNode);
             }
         }
         else if (name == "Dot") {
@@ -253,6 +256,49 @@ void CmmeInput::CreateDot(pugi::xml_node dotNode)
     return;
 }
 
+void CmmeInput::CreateKeySig(pugi::xml_node keyNode)
+{
+    assert(m_currentLayer);
+
+    static const std::map<std::string, data_ACCIDENTAL_WRITTEN> shapeMap{
+        { "Bmol", ACCIDENTAL_WRITTEN_f }, //
+        { "BmolDouble", ACCIDENTAL_WRITTEN_f }, //
+        { "Bqua", ACCIDENTAL_WRITTEN_n }, //
+        { "Diesis", ACCIDENTAL_WRITTEN_s }, //
+    };
+
+    KeySig *keysig = new KeySig();
+    KeyAccid *keyaccid = new KeyAccid();
+    std::string appearance = this->ChildAsString(keyNode, "Appearance");
+    data_ACCIDENTAL_WRITTEN accid = shapeMap.contains(appearance) ? shapeMap.at(appearance) : ACCIDENTAL_WRITTEN_f;
+    keyaccid->SetAccid(accid);
+
+    static const std::map<std::string, data_PITCHNAME> pitchMap{
+        { "C", PITCHNAME_c }, //
+        { "D", PITCHNAME_d }, //
+        { "E", PITCHNAME_e }, //
+        { "F", PITCHNAME_f }, //
+        { "G", PITCHNAME_g }, //
+        { "A", PITCHNAME_a }, //
+        { "B", PITCHNAME_b } //
+    };
+
+    std::string step = this->ChildAsString(keyNode, "Pitch/LetterName");
+    // Default pitch to C
+    data_PITCHNAME pname = pitchMap.contains(step) ? pitchMap.at(step) : PITCHNAME_c;
+    keyaccid->SetPname(pname);
+
+    int oct = this->ChildAsInt(keyNode, "Pitch/OctaveNum");
+    if ((pname != PITCHNAME_a) && (pname != PITCHNAME_b)) oct += 1;
+    keyaccid->SetOct(oct);
+
+    int staffLoc = this->ChildAsInt(keyNode, "StaffLoc");
+    keyaccid->SetLoc(staffLoc - 1);
+
+    m_currentLayer->AddChild(keysig);
+    keysig->AddChild(keyaccid);
+}
+
 void CmmeInput::CreateMensuration(pugi::xml_node mensurationNode)
 {
     assert(m_currentLayer);

From 2f9eead00d13ac61029ff4c73232c38a4a8cff7d Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Wed, 18 Sep 2024 11:27:43 +0200
Subject: [PATCH 015/105] Add fermata support for CMME

---
 src/iocmme.cpp | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index e24fb6ca4ab..a909af03fc3 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -327,6 +327,10 @@ void CmmeInput::CreateNote(pugi::xml_node noteNode)
         CreateVerse(noteNode.child("ModernText"));
         m_currentNote = NULL;
     }
+    
+    if (noteNode.child("Corona")) {
+        note->SetFermata(STAFFREL_basic_above);
+    }
 
     m_currentLayer->AddChild(note);
 

From 49553d8a137a011c947e9bc7ae3d919b9a553063 Mon Sep 17 00:00:00 2001
From: martha-thomae <thomaemartha@gmail.com>
Date: Wed, 18 Sep 2024 11:37:17 +0200
Subject: [PATCH 016/105] Add support for accids

---
 include/vrv/iocmme.h |  1 +
 src/iocmme.cpp       | 62 +++++++++++++++++++++++++++++++++++++-------
 2 files changed, 54 insertions(+), 9 deletions(-)

diff --git a/include/vrv/iocmme.h b/include/vrv/iocmme.h
index 132be0b66ae..a0def5be56a 100644
--- a/include/vrv/iocmme.h
+++ b/include/vrv/iocmme.h
@@ -52,6 +52,7 @@ class CmmeInput : public Input {
     void CreateSection(pugi::xml_node musicSectionNode);
     void CreateStaff(pugi::xml_node voiceNode);
 
+    void CreateAccid(pugi::xml_node accidNode);
     void CreateClef(pugi::xml_node clefNode);
     void CreateDot(pugi::xml_node dotNode);
     void CreateKeySig(pugi::xml_node keyNode);
diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index eeb161712c3..ec5ee25b68d 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -191,9 +191,12 @@ void CmmeInput::CreateStaff(pugi::xml_node voiceNode)
             if (this->IsClef(eventNode)) {
                 CreateClef(eventNode);
             }
-            else {
+            else if (eventNode.select_node("./Signature")) {
                 CreateKeySig(eventNode);
             }
+            else {
+                CreateAccid(eventNode);
+            }
         }
         else if (name == "Dot") {
             CreateDot(eventNode);
@@ -219,6 +222,47 @@ void CmmeInput::CreateStaff(pugi::xml_node voiceNode)
     m_currentSection->AddChild(staff);
 }
 
+void CmmeInput::CreateAccid(pugi::xml_node accidNode)
+{
+    static const std::map<std::string, data_ACCIDENTAL_WRITTEN> shapeMap{
+        { "Bmol", ACCIDENTAL_WRITTEN_f }, //
+        { "BmolDouble", ACCIDENTAL_WRITTEN_f }, //
+        { "Bqua", ACCIDENTAL_WRITTEN_n }, //
+        { "Diesis", ACCIDENTAL_WRITTEN_s }, //
+    };
+
+    static const std::map<std::string, data_PITCHNAME> pitchMap{
+        { "C", PITCHNAME_c }, //
+        { "D", PITCHNAME_d }, //
+        { "E", PITCHNAME_e }, //
+        { "F", PITCHNAME_f }, //
+        { "G", PITCHNAME_g }, //
+        { "A", PITCHNAME_a }, //
+        { "B", PITCHNAME_b } //
+    };
+
+    assert(m_currentLayer);
+
+    Accid *accidElement = new Accid();
+    std::string appearance = this->ChildAsString(accidNode, "Appearance");
+    data_ACCIDENTAL_WRITTEN accid = shapeMap.contains(appearance) ? shapeMap.at(appearance) : ACCIDENTAL_WRITTEN_f;
+    accidElement->SetAccid(accid);
+
+    std::string step = this->ChildAsString(accidNode, "Pitch/LetterName");
+    // Default pitch to C
+    data_PITCHNAME ploc = pitchMap.contains(step) ? pitchMap.at(step) : PITCHNAME_c;
+    accidElement->SetPloc(ploc);
+
+    int oct = this->ChildAsInt(accidNode, "Pitch/OctaveNum");
+    if ((ploc != PITCHNAME_a) && (ploc != PITCHNAME_b)) oct += 1;
+    accidElement->SetOloc(oct);
+
+    int staffLoc = this->ChildAsInt(accidNode, "StaffLoc");
+    accidElement->SetLoc(staffLoc - 1);
+
+    m_currentLayer->AddChild(accidElement);
+}
+
 void CmmeInput::CreateClef(pugi::xml_node clefNode)
 {
     static const std::map<std::string, data_CLEFSHAPE> shapeMap{
@@ -258,8 +302,6 @@ void CmmeInput::CreateDot(pugi::xml_node dotNode)
 
 void CmmeInput::CreateKeySig(pugi::xml_node keyNode)
 {
-    assert(m_currentLayer);
-
     static const std::map<std::string, data_ACCIDENTAL_WRITTEN> shapeMap{
         { "Bmol", ACCIDENTAL_WRITTEN_f }, //
         { "BmolDouble", ACCIDENTAL_WRITTEN_f }, //
@@ -267,12 +309,6 @@ void CmmeInput::CreateKeySig(pugi::xml_node keyNode)
         { "Diesis", ACCIDENTAL_WRITTEN_s }, //
     };
 
-    KeySig *keysig = new KeySig();
-    KeyAccid *keyaccid = new KeyAccid();
-    std::string appearance = this->ChildAsString(keyNode, "Appearance");
-    data_ACCIDENTAL_WRITTEN accid = shapeMap.contains(appearance) ? shapeMap.at(appearance) : ACCIDENTAL_WRITTEN_f;
-    keyaccid->SetAccid(accid);
-
     static const std::map<std::string, data_PITCHNAME> pitchMap{
         { "C", PITCHNAME_c }, //
         { "D", PITCHNAME_d }, //
@@ -283,6 +319,14 @@ void CmmeInput::CreateKeySig(pugi::xml_node keyNode)
         { "B", PITCHNAME_b } //
     };
 
+    assert(m_currentLayer);
+
+    KeySig *keysig = new KeySig();
+    KeyAccid *keyaccid = new KeyAccid();
+    std::string appearance = this->ChildAsString(keyNode, "Appearance");
+    data_ACCIDENTAL_WRITTEN accid = shapeMap.contains(appearance) ? shapeMap.at(appearance) : ACCIDENTAL_WRITTEN_f;
+    keyaccid->SetAccid(accid);
+
     std::string step = this->ChildAsString(keyNode, "Pitch/LetterName");
     // Default pitch to C
     data_PITCHNAME pname = pitchMap.contains(step) ? pitchMap.at(step) : PITCHNAME_c;

From b138848eb21e8dd601109966017deb8a1d631713 Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Wed, 18 Sep 2024 11:40:39 +0200
Subject: [PATCH 017/105] Skip accidental adjustment when not descendant of a
 note

---
 src/adjustaccidxfunctor.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/adjustaccidxfunctor.cpp b/src/adjustaccidxfunctor.cpp
index 7bc8b01512d..d33a6038e56 100644
--- a/src/adjustaccidxfunctor.cpp
+++ b/src/adjustaccidxfunctor.cpp
@@ -60,6 +60,9 @@ FunctorCode AdjustAccidXFunctor::VisitAlignmentReference(AlignmentReference *ali
     for (Accid *accid : accids) {
         // Skip any accid that was already adjusted
         if (m_adjustedAccids.count(accid) > 0) continue;
+        // Skip accid not descendant of a note (e.g., mensural)
+        if (!accid->GetFirstAncestor(NOTE)) continue;
+        
         auto range = octaveEquivalence.equal_range(accid);
         // Handle at least two octave accids without unisons
         int octaveAccidCount = 0;

From 155c81b5a9c197c4952acb934229496bb057f46e Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Wed, 18 Sep 2024 11:53:45 +0200
Subject: [PATCH 018/105] Add support for CMME modern accidentals

---
 src/iocmme.cpp | 25 ++++++++++++++++++++++++-
 1 file changed, 24 insertions(+), 1 deletion(-)

diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index a909af03fc3..ddfd17c9327 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -17,6 +17,7 @@
 
 //----------------------------------------------------------------------------
 
+#include "accid.h"
 #include "barline.h"
 #include "clef.h"
 #include "doc.h"
@@ -297,6 +298,16 @@ void CmmeInput::CreateNote(pugi::xml_node noteNode)
         { "B", PITCHNAME_b } //
     };
 
+    static const std::map<int, data_ACCIDENTAL_WRITTEN> accidMap{
+        { -3, ACCIDENTAL_WRITTEN_tf }, //
+        { -2, ACCIDENTAL_WRITTEN_ff }, //
+        { -1, ACCIDENTAL_WRITTEN_f }, //
+        { 0, ACCIDENTAL_WRITTEN_n }, //
+        { 1, ACCIDENTAL_WRITTEN_s }, //
+        { 2, ACCIDENTAL_WRITTEN_ss }, //
+        { 3, ACCIDENTAL_WRITTEN_sx }, //
+    };
+
     assert(m_currentLayer);
 
     Note *note = new Note();
@@ -327,11 +338,23 @@ void CmmeInput::CreateNote(pugi::xml_node noteNode)
         CreateVerse(noteNode.child("ModernText"));
         m_currentNote = NULL;
     }
-    
+
     if (noteNode.child("Corona")) {
         note->SetFermata(STAFFREL_basic_above);
     }
 
+    if (noteNode.child("ModernAccidental")) {
+        Accid *accid = new Accid();
+        int offset = this->ChildAsInt(noteNode.child("ModernAccidental"), "PitchOffset");
+        offset = std::min(3, offset);
+        offset = std::max(-3, offset);
+        // Default pitch to C
+        data_ACCIDENTAL_WRITTEN accidWritten = accidMap.contains(offset) ? accidMap.at(offset) : ACCIDENTAL_WRITTEN_n;
+        accid->SetAccid(accidWritten);
+        accid->SetFunc(accidLog_FUNC_edit);
+        note->AddChild(accid);
+    }
+
     m_currentLayer->AddChild(note);
 
     return;

From b723eabcc90844e4d0d89af0d7dbf611c4841e0f Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Wed, 18 Sep 2024 12:03:23 +0200
Subject: [PATCH 019/105] Add support for stem direction and position in CMME

---
 src/iocmme.cpp | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index ddfd17c9327..9fed096b9c6 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -308,6 +308,13 @@ void CmmeInput::CreateNote(pugi::xml_node noteNode)
         { 3, ACCIDENTAL_WRITTEN_sx }, //
     };
 
+    static const std::map<std::string, data_STEMDIRECTION> stemDirMap{
+        { "Up", STEMDIRECTION_up }, //
+        { "Down", STEMDIRECTION_down }, //
+        { "Left", STEMDIRECTION_left }, //
+        { "Right", STEMDIRECTION_right }, //
+    };
+
     assert(m_currentLayer);
 
     Note *note = new Note();
@@ -355,6 +362,23 @@ void CmmeInput::CreateNote(pugi::xml_node noteNode)
         note->AddChild(accid);
     }
 
+    if (noteNode.child("Stem")) {
+        std::string dir = this->ChildAsString(noteNode.child("Stem"), "Dir");
+        if (dir == "Barline") {
+            LogWarning("Unsupported 'Barline' stem direction");
+        }
+        data_STEMDIRECTION stemDir = stemDirMap.contains(dir) ? stemDirMap.at(dir) : STEMDIRECTION_NONE;
+        note->SetStemDir(STEMDIRECTION_down);
+
+        std::string side = this->ChildAsString(noteNode.child("Stem"), "Side");
+        if (side == "Left") {
+            note->SetStemPos(STEMPOSITION_left);
+        }
+        else if (side == "Right") {
+            note->SetStemPos(STEMPOSITION_right);
+        }
+    }
+
     m_currentLayer->AddChild(note);
 
     return;

From cdad08508b61f296cb07534bfadd53a29e802428 Mon Sep 17 00:00:00 2001
From: martha-thomae <thomaemartha@gmail.com>
Date: Wed, 18 Sep 2024 12:04:11 +0200
Subject: [PATCH 020/105] Add support for signatures with multiple components
 in cmme

---
 include/vrv/iocmme.h |  3 +++
 src/iocmme.cpp       | 19 +++++++++++++++----
 2 files changed, 18 insertions(+), 4 deletions(-)

diff --git a/include/vrv/iocmme.h b/include/vrv/iocmme.h
index a0def5be56a..1e5ce08bad7 100644
--- a/include/vrv/iocmme.h
+++ b/include/vrv/iocmme.h
@@ -20,6 +20,7 @@
 namespace vrv {
 
 class Clef;
+class KeySig;
 class Layer;
 class Measure;
 class Note;
@@ -86,6 +87,8 @@ class CmmeInput : public Input {
     Measure *m_currentSection;
     /** The current layer (or container) to which the layer elements have to be added */
     Layer *m_currentLayer;
+    /** The current key signature to which extra flats might be added */
+    KeySig *m_currentSignature;
     /** The current note */
     Note *m_currentNote;
     /** The syllable is not the first */
diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index 21731046d05..dca15cafde1 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -53,6 +53,7 @@ CmmeInput::CmmeInput(Doc *doc) : Input(doc)
     m_score = NULL;
     m_currentSection = NULL;
     m_currentLayer = NULL;
+    m_currentSignature = NULL;
     m_currentNote = NULL;
     m_isInSyllable = false;
     m_mensInfo = NULL;
@@ -181,6 +182,8 @@ void CmmeInput::CreateStaff(pugi::xml_node voiceNode)
     m_mensInfo = &m_mensInfos.at(numVoice - 1);
     // Reset the syllable position
     m_isInSyllable = false;
+    bool keySigFound = false;
+    m_currentSignature = NULL;
 
     // Loop through the event lists
     pugi::xpath_node_set events = voiceNode.select_nodes("./EventList/*");
@@ -192,6 +195,7 @@ void CmmeInput::CreateStaff(pugi::xml_node voiceNode)
                 CreateClef(eventNode);
             }
             else if (eventNode.select_node("./Signature")) {
+                keySigFound = true;
                 CreateKeySig(eventNode);
             }
             else {
@@ -216,6 +220,10 @@ void CmmeInput::CreateStaff(pugi::xml_node voiceNode)
         else {
             LogWarning("Unsupported event '%s'", name.c_str());
         }
+        if (!keySigFound) {
+            m_currentSignature = NULL;
+        }
+        keySigFound = false;
     }
 
     staff->AddChild(m_currentLayer);
@@ -321,7 +329,11 @@ void CmmeInput::CreateKeySig(pugi::xml_node keyNode)
 
     assert(m_currentLayer);
 
-    KeySig *keysig = new KeySig();
+    if (!m_currentSignature) {
+        m_currentSignature = new KeySig();
+        m_currentLayer->AddChild(m_currentSignature);
+    }
+
     KeyAccid *keyaccid = new KeyAccid();
     std::string appearance = this->ChildAsString(keyNode, "Appearance");
     data_ACCIDENTAL_WRITTEN accid = shapeMap.contains(appearance) ? shapeMap.at(appearance) : ACCIDENTAL_WRITTEN_f;
@@ -339,8 +351,7 @@ void CmmeInput::CreateKeySig(pugi::xml_node keyNode)
     int staffLoc = this->ChildAsInt(keyNode, "StaffLoc");
     keyaccid->SetLoc(staffLoc - 1);
 
-    m_currentLayer->AddChild(keysig);
-    keysig->AddChild(keyaccid);
+    m_currentSignature->AddChild(keyaccid);
 }
 
 void CmmeInput::CreateMensuration(pugi::xml_node mensurationNode)
@@ -417,7 +428,7 @@ void CmmeInput::CreateNote(pugi::xml_node noteNode)
         CreateVerse(noteNode.child("ModernText"));
         m_currentNote = NULL;
     }
-    
+
     if (noteNode.child("Corona")) {
         note->SetFermata(STAFFREL_basic_above);
     }

From 27ee36f529da9b4dad559bf50d175708d23d6f29 Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Wed, 18 Sep 2024 12:04:43 +0200
Subject: [PATCH 021/105] Fix formatting in src/adjustaccidxfunctor.cpp

---
 src/adjustaccidxfunctor.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/adjustaccidxfunctor.cpp b/src/adjustaccidxfunctor.cpp
index d33a6038e56..cd57103adee 100644
--- a/src/adjustaccidxfunctor.cpp
+++ b/src/adjustaccidxfunctor.cpp
@@ -62,7 +62,7 @@ FunctorCode AdjustAccidXFunctor::VisitAlignmentReference(AlignmentReference *ali
         if (m_adjustedAccids.count(accid) > 0) continue;
         // Skip accid not descendant of a note (e.g., mensural)
         if (!accid->GetFirstAncestor(NOTE)) continue;
-        
+
         auto range = octaveEquivalence.equal_range(accid);
         // Handle at least two octave accids without unisons
         int octaveAccidCount = 0;

From d97dae2acc4979701a8c696d18bf13bb5c3b781e Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Wed, 18 Sep 2024 12:17:35 +0200
Subject: [PATCH 022/105] Add support for fermata below in CMME

---
 src/iocmme.cpp | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index 07bfc2a6360..36556e004fc 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -448,7 +448,11 @@ void CmmeInput::CreateNote(pugi::xml_node noteNode)
     }
 
     if (noteNode.child("Corona")) {
-        note->SetFermata(STAFFREL_basic_above);
+        data_STAFFREL_basic position = STAFFREL_basic_above;
+        if (noteNode.select_node("Corona/Orientation[text()='Down']")) {
+            position = STAFFREL_basic_below;
+        }
+        note->SetFermata(position);
     }
 
     if (noteNode.child("ModernAccidental")) {

From 9f769f7b3bfce740b533b5a75f75a132c15dad18 Mon Sep 17 00:00:00 2001
From: martha-thomae <thomaemartha@gmail.com>
Date: Wed, 18 Sep 2024 12:18:19 +0200
Subject: [PATCH 023/105] Add support for multi-event signatures in cmme

---
 src/iocmme.cpp | 15 ++++++++++++++-
 1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index dca15cafde1..fdadf760432 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -208,6 +208,20 @@ void CmmeInput::CreateStaff(pugi::xml_node voiceNode)
         else if (name == "Mensuration") {
             CreateMensuration(eventNode);
         }
+        else if (name == "MultiEvent") {
+            /// Assuming that a multievent contains a key signature, all events are key signatures
+            if (eventNode.select_node("./Clef/Signature")) {
+                m_currentSignature = NULL;
+                pugi::xpath_node_set clefs = eventNode.select_nodes("./Clef");
+                for (pugi::xpath_node clef : clefs) {
+                    pugi::xml_node clefNode = clef.node();
+                    CreateKeySig(clefNode);
+                }
+            }
+            else {
+                LogWarning("Unsupported event '%s'", name.c_str());
+            }
+        }
         else if (name == "Note") {
             CreateNote(eventNode);
         }
@@ -225,7 +239,6 @@ void CmmeInput::CreateStaff(pugi::xml_node voiceNode)
         }
         keySigFound = false;
     }
-
     staff->AddChild(m_currentLayer);
     m_currentSection->AddChild(staff);
 }

From be293d079287cc76f6ee2207ad5d735793bc4bbd Mon Sep 17 00:00:00 2001
From: martha-thomae <thomaemartha@gmail.com>
Date: Wed, 18 Sep 2024 12:31:09 +0200
Subject: [PATCH 024/105] Add custos from cmme file

---
 include/vrv/iocmme.h |  1 +
 src/iocmme.cpp       | 33 +++++++++++++++++++++++++++++++++
 2 files changed, 34 insertions(+)

diff --git a/include/vrv/iocmme.h b/include/vrv/iocmme.h
index 1e5ce08bad7..1ea88672c95 100644
--- a/include/vrv/iocmme.h
+++ b/include/vrv/iocmme.h
@@ -55,6 +55,7 @@ class CmmeInput : public Input {
 
     void CreateAccid(pugi::xml_node accidNode);
     void CreateClef(pugi::xml_node clefNode);
+    void CreateCustos(pugi::xml_node custosNode);
     void CreateDot(pugi::xml_node dotNode);
     void CreateKeySig(pugi::xml_node keyNode);
     void CreateMensuration(pugi::xml_node mensurationNode);
diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index fdadf760432..ef238552168 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -19,6 +19,7 @@
 
 #include "barline.h"
 #include "clef.h"
+#include "custos.h"
 #include "doc.h"
 #include "dot.h"
 #include "keyaccid.h"
@@ -202,6 +203,9 @@ void CmmeInput::CreateStaff(pugi::xml_node voiceNode)
                 CreateAccid(eventNode);
             }
         }
+        else if (name == "Custos") {
+            CreateCustos(eventNode);
+        }
         else if (name == "Dot") {
             CreateDot(eventNode);
         }
@@ -311,6 +315,35 @@ void CmmeInput::CreateClef(pugi::xml_node clefNode)
     return;
 }
 
+void CmmeInput::CreateCustos(pugi::xml_node custosNode)
+{
+    static const std::map<std::string, data_PITCHNAME> pitchMap{
+        { "C", PITCHNAME_c }, //
+        { "D", PITCHNAME_d }, //
+        { "E", PITCHNAME_e }, //
+        { "F", PITCHNAME_f }, //
+        { "G", PITCHNAME_g }, //
+        { "A", PITCHNAME_a }, //
+        { "B", PITCHNAME_b } //
+    };
+
+    assert(m_currentLayer);
+
+    Custos *custos = new Custos();
+    std::string step = this->ChildAsString(custosNode, "LetterName");
+    // Default pitch to C
+    data_PITCHNAME pname = pitchMap.contains(step) ? pitchMap.at(step) : PITCHNAME_c;
+    custos->SetPname(pname);
+
+    int oct = this->ChildAsInt(custosNode, "OctaveNum");
+    if ((pname != PITCHNAME_a) && (pname != PITCHNAME_b)) oct += 1;
+    custos->SetOct(oct);
+
+    m_currentLayer->AddChild(custos);
+
+    return;
+}
+
 void CmmeInput::CreateDot(pugi::xml_node dotNode)
 {
     assert(m_currentLayer);

From a66f8b7eadbc05c328224ec6fbc2d51af652ce6b Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Wed, 18 Sep 2024 13:29:21 +0200
Subject: [PATCH 025/105] Add ligature support for CMME

---
 include/vrv/iocmme.h |  2 +-
 src/iocmme.cpp       | 64 +++++++++++++++++++++++++++++++-------------
 2 files changed, 46 insertions(+), 20 deletions(-)

diff --git a/include/vrv/iocmme.h b/include/vrv/iocmme.h
index 1e5ce08bad7..2cca4ab2e7e 100644
--- a/include/vrv/iocmme.h
+++ b/include/vrv/iocmme.h
@@ -86,7 +86,7 @@ class CmmeInput : public Input {
     /** The current un-measured measure acting a a MEI section */
     Measure *m_currentSection;
     /** The current layer (or container) to which the layer elements have to be added */
-    Layer *m_currentLayer;
+    Object *m_currentContainer;
     /** The current key signature to which extra flats might be added */
     KeySig *m_currentSignature;
     /** The current note */
diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index 36556e004fc..163b2a5eaf6 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -26,6 +26,7 @@
 #include "keysig.h"
 #include "label.h"
 #include "layer.h"
+#include "ligature.h"
 #include "mdiv.h"
 #include "measure.h"
 #include "mensur.h"
@@ -53,7 +54,7 @@ CmmeInput::CmmeInput(Doc *doc) : Input(doc)
 {
     m_score = NULL;
     m_currentSection = NULL;
-    m_currentLayer = NULL;
+    m_currentContainer = NULL;
     m_currentSignature = NULL;
     m_currentNote = NULL;
     m_isInSyllable = false;
@@ -176,8 +177,9 @@ void CmmeInput::CreateStaff(pugi::xml_node voiceNode)
     int numVoice = this->ChildAsInt(voiceNode, "VoiceNum");
 
     Staff *staff = new Staff(numVoice);
-    m_currentLayer = new Layer();
-    m_currentLayer->SetN(1);
+    Layer *layer = new Layer();
+    layer->SetN(1);
+    m_currentContainer = layer;
 
     // (Re)-set the current mens info to the corresponding voice
     m_mensInfo = &m_mensInfos.at(numVoice - 1);
@@ -227,7 +229,7 @@ void CmmeInput::CreateStaff(pugi::xml_node voiceNode)
         keySigFound = false;
     }
 
-    staff->AddChild(m_currentLayer);
+    staff->AddChild(m_currentContainer);
     m_currentSection->AddChild(staff);
 }
 
@@ -250,7 +252,7 @@ void CmmeInput::CreateAccid(pugi::xml_node accidNode)
         { "B", PITCHNAME_b } //
     };
 
-    assert(m_currentLayer);
+    assert(m_currentContainer);
 
     Accid *accidElement = new Accid();
     std::string appearance = this->ChildAsString(accidNode, "Appearance");
@@ -269,7 +271,7 @@ void CmmeInput::CreateAccid(pugi::xml_node accidNode)
     int staffLoc = this->ChildAsInt(accidNode, "StaffLoc");
     accidElement->SetLoc(staffLoc - 1);
 
-    m_currentLayer->AddChild(accidElement);
+    m_currentContainer->AddChild(accidElement);
 }
 
 void CmmeInput::CreateClef(pugi::xml_node clefNode)
@@ -282,7 +284,7 @@ void CmmeInput::CreateClef(pugi::xml_node clefNode)
         { "Fsqr", CLEFSHAPE_F }, //
     };
 
-    assert(m_currentLayer);
+    assert(m_currentContainer);
 
     Clef *clef = new Clef();
     int staffLoc = this->ChildAsInt(clefNode, "StaffLoc");
@@ -294,17 +296,17 @@ void CmmeInput::CreateClef(pugi::xml_node clefNode)
     data_CLEFSHAPE shape = shapeMap.contains(appearance) ? shapeMap.at(appearance) : CLEFSHAPE_C;
     clef->SetShape(shape);
 
-    m_currentLayer->AddChild(clef);
+    m_currentContainer->AddChild(clef);
 
     return;
 }
 
 void CmmeInput::CreateDot(pugi::xml_node dotNode)
 {
-    assert(m_currentLayer);
+    assert(m_currentContainer);
 
     Dot *dot = new Dot();
-    m_currentLayer->AddChild(dot);
+    m_currentContainer->AddChild(dot);
 
     return;
 }
@@ -328,11 +330,11 @@ void CmmeInput::CreateKeySig(pugi::xml_node keyNode)
         { "B", PITCHNAME_b } //
     };
 
-    assert(m_currentLayer);
+    assert(m_currentContainer);
 
     if (!m_currentSignature) {
         m_currentSignature = new KeySig();
-        m_currentLayer->AddChild(m_currentSignature);
+        m_currentContainer->AddChild(m_currentSignature);
     }
 
     KeyAccid *keyaccid = new KeyAccid();
@@ -357,7 +359,7 @@ void CmmeInput::CreateKeySig(pugi::xml_node keyNode)
 
 void CmmeInput::CreateMensuration(pugi::xml_node mensurationNode)
 {
-    assert(m_currentLayer);
+    assert(m_currentContainer);
     assert(m_mensInfo);
 
     pugi::xml_node mensInfo = mensurationNode.child("MensInfo");
@@ -382,7 +384,7 @@ void CmmeInput::CreateMensuration(pugi::xml_node mensurationNode)
     data_BOOLEAN dot = (m_mensInfo->prolatio == 3) ? BOOLEAN_true : BOOLEAN_false;
     mensur->SetDot(dot);
 
-    m_currentLayer->AddChild(mensur);
+    m_currentContainer->AddChild(mensur);
 
     return;
 }
@@ -416,7 +418,7 @@ void CmmeInput::CreateNote(pugi::xml_node noteNode)
         { "Right", STEMDIRECTION_right }, //
     };
 
-    assert(m_currentLayer);
+    assert(m_currentContainer);
 
     Note *note = new Note();
     std::string step = this->ChildAsString(noteNode, "LetterName");
@@ -473,7 +475,7 @@ void CmmeInput::CreateNote(pugi::xml_node noteNode)
             LogWarning("Unsupported 'Barline' stem direction");
         }
         data_STEMDIRECTION stemDir = stemDirMap.contains(dir) ? stemDirMap.at(dir) : STEMDIRECTION_NONE;
-        note->SetStemDir(STEMDIRECTION_down);
+        note->SetStemDir(stemDir);
 
         std::string side = this->ChildAsString(noteNode.child("Stem"), "Side");
         if (side == "Left") {
@@ -484,7 +486,31 @@ void CmmeInput::CreateNote(pugi::xml_node noteNode)
         }
     }
 
-    m_currentLayer->AddChild(note);
+    if (noteNode.child("Lig")) {
+        std::string lig = this->ChildAsString(noteNode, "Lig");
+        if (lig == "Retrorsum") {
+            LogWarning("Unsupported 'Retrorsum' ligature");
+        }
+        data_LIGATUREFORM form = (lig == "Obliqua") ? LIGATUREFORM_obliqua : LIGATUREFORM_recta;
+        // First note of the ligature, create the ligature element
+        if (!m_currentContainer->Is(LIGATURE)) {
+            Ligature *ligature = new Ligature();
+            ligature->SetForm(form);
+            m_currentContainer->AddChild(ligature);
+            m_currentContainer = ligature;
+        }
+        // Otherwise simply add the `@lig` to the note
+        else {
+            note->SetLig(form);
+        }
+    }
+
+    m_currentContainer->AddChild(note);
+
+    // We have processed the last note of a ligature
+    if (m_currentContainer->Is(LIGATURE) && !noteNode.child("Lig")) {
+        m_currentContainer = m_currentContainer->GetParent();
+    }
 
     return;
 }
@@ -496,7 +522,7 @@ void CmmeInput::CreateOriginalText(pugi::xml_node originalTextNode)
 
 void CmmeInput::CreateRest(pugi::xml_node restNode)
 {
-    assert(m_currentLayer);
+    assert(m_currentContainer);
 
     Rest *rest = new Rest();
     int num;
@@ -508,7 +534,7 @@ void CmmeInput::CreateRest(pugi::xml_node restNode)
         rest->SetNum(numbase);
     }
 
-    m_currentLayer->AddChild(rest);
+    m_currentContainer->AddChild(rest);
 
     return;
 }

From e0f6e981b77c66f2cc3223d734cff04dd4b62cc7 Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Wed, 18 Sep 2024 14:36:09 +0200
Subject: [PATCH 026/105] Pull develop-cmme

---
 include/vrv/iocmme.h |  4 +++
 src/iocmme.cpp       | 78 ++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 76 insertions(+), 6 deletions(-)

diff --git a/include/vrv/iocmme.h b/include/vrv/iocmme.h
index 08fddf9573f..a6660198c59 100644
--- a/include/vrv/iocmme.h
+++ b/include/vrv/iocmme.h
@@ -52,6 +52,10 @@ class CmmeInput : public Input {
 private:
     void CreateSection(pugi::xml_node musicSectionNode);
     void CreateStaff(pugi::xml_node voiceNode);
+    void CreateApp(pugi::xml_node appNode);
+    void CreateLemOrRdg(pugi::xml_node lemOrRdgNode, bool isFirst);
+
+    void ReadEvents(pugi::xml_node eventsNode);
 
     void CreateAccid(pugi::xml_node accidNode);
     void CreateClef(pugi::xml_node clefNode);
diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index 9f17a8ca1b2..afb6c2cf0dd 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -18,6 +18,7 @@
 //----------------------------------------------------------------------------
 
 #include "accid.h"
+#include "app.h"
 #include "barline.h"
 #include "clef.h"
 #include "custos.h"
@@ -27,11 +28,13 @@
 #include "keysig.h"
 #include "label.h"
 #include "layer.h"
+#include "lem.h"
 #include "ligature.h"
 #include "mdiv.h"
 #include "measure.h"
 #include "mensur.h"
 #include "note.h"
+#include "rdg.h"
 #include "rest.h"
 #include "score.h"
 #include "section.h"
@@ -186,11 +189,75 @@ void CmmeInput::CreateStaff(pugi::xml_node voiceNode)
     m_mensInfo = &m_mensInfos.at(numVoice - 1);
     // Reset the syllable position
     m_isInSyllable = false;
-    bool keySigFound = false;
     m_currentSignature = NULL;
 
     // Loop through the event lists
-    pugi::xpath_node_set events = voiceNode.select_nodes("./EventList/*");
+    ReadEvents(voiceNode.child("EventList"));
+
+    staff->AddChild(m_currentContainer);
+    m_currentSection->AddChild(staff);
+}
+
+void CmmeInput::CreateApp(pugi::xml_node appNode)
+{
+    assert(m_currentContainer);
+
+    App *app = new App(EDITORIAL_LAYER);
+    m_currentContainer->AddChild(app);
+    m_currentContainer = app;
+
+    // Loop through the event lists
+    pugi::xpath_node_set lemOrRdgs = appNode.select_nodes("./Reading");
+    bool isFirst = true;
+    for (pugi::xpath_node lemOrRdg : lemOrRdgs) {
+        pugi::xml_node lemOrRdgNode = lemOrRdg.node();
+        this->CreateLemOrRdg(lemOrRdgNode, isFirst);
+        isFirst = false;
+    }
+
+    m_currentContainer = m_currentContainer->GetParent();
+}
+
+void CmmeInput::CreateLemOrRdg(pugi::xml_node lemOrRdgNode, bool isFirst)
+{
+    assert(m_currentContainer);
+    std::string versionId = this->ChildAsString(lemOrRdgNode, "VariantVersionID");
+
+    EditorialElement *lemOrRdg = NULL;
+    if (isFirst && (lemOrRdgNode.child("PreferredReading") || (versionId == "DEFAULT"))) {
+        lemOrRdg = new Lem();
+    }
+    else {
+        lemOrRdg = new Rdg();
+    }
+    lemOrRdg->m_visibility = (isFirst) ? Visible : Hidden;
+
+    if (lemOrRdg->Is(RDG)) lemOrRdg->SetLabel(versionId);
+
+    if (lemOrRdgNode.child("Error")) {
+        lemOrRdg->SetType("Error");
+    }
+    else if (lemOrRdgNode.child("Lacuna")) {
+        lemOrRdg->SetType("Lacuna");
+    }
+
+    m_currentContainer->AddChild(lemOrRdg);
+
+    m_currentContainer = lemOrRdg;
+
+    ReadEvents(lemOrRdgNode.child("Music"));
+
+    m_currentContainer = m_currentContainer->GetParent();
+}
+
+void CmmeInput::ReadEvents(pugi::xml_node eventsNode)
+{
+    assert(m_currentContainer);
+
+    bool keySigFound = false;
+
+    // Loop through the event lists
+    pugi::xpath_node_set events = eventsNode.select_nodes("./*");
     for (pugi::xpath_node event : events) {
         pugi::xml_node eventNode = event.node();
         std::string name = eventNode.name();
@@ -238,6 +305,9 @@ void CmmeInput::CreateStaff(pugi::xml_node voiceNode)
         else if (name == "Rest") {
             CreateRest(eventNode);
         }
+        else if (name == "VariantReadings") {
+            CreateApp(eventNode);
+        }
         else {
             LogWarning("Unsupported event '%s'", name.c_str());
         }
@@ -246,10 +316,6 @@ void CmmeInput::CreateStaff(pugi::xml_node voiceNode)
         }
         keySigFound = false;
     }
-
-    staff->AddChild(m_currentContainer);
-  
-    m_currentSection->AddChild(staff);
 }
 
 void CmmeInput::CreateAccid(pugi::xml_node accidNode)

From 8ac88cddc1079dd6ea2f3829356f2d47ad2f7579 Mon Sep 17 00:00:00 2001
From: martha-thomae <thomaemartha@gmail.com>
Date: Wed, 18 Sep 2024 15:05:13 +0200
Subject: [PATCH 027/105] Add support for cmme barlines

---
 include/vrv/iocmme.h |  1 +
 src/iocmme.cpp       | 41 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 42 insertions(+)

diff --git a/include/vrv/iocmme.h b/include/vrv/iocmme.h
index 1ea88672c95..19f5bd03a67 100644
--- a/include/vrv/iocmme.h
+++ b/include/vrv/iocmme.h
@@ -54,6 +54,7 @@ class CmmeInput : public Input {
     void CreateStaff(pugi::xml_node voiceNode);
 
     void CreateAccid(pugi::xml_node accidNode);
+    void CreateBarline(pugi::xml_node barlineNode);
     void CreateClef(pugi::xml_node clefNode);
     void CreateCustos(pugi::xml_node custosNode);
     void CreateDot(pugi::xml_node dotNode);
diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index ef238552168..14a03519e67 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -212,6 +212,13 @@ void CmmeInput::CreateStaff(pugi::xml_node voiceNode)
         else if (name == "Mensuration") {
             CreateMensuration(eventNode);
         }
+        else if (name == "MiscItem") {
+            /// Assuming that a MiscItem contains only one child
+            if (eventNode.select_node("./Barline")) {
+                pugi::xml_node barlineNode = eventNode.select_node("./Barline").node();
+                CreateBarline(barlineNode);
+            }
+        }
         else if (name == "MultiEvent") {
             /// Assuming that a multievent contains a key signature, all events are key signatures
             if (eventNode.select_node("./Clef/Signature")) {
@@ -288,6 +295,40 @@ void CmmeInput::CreateAccid(pugi::xml_node accidNode)
     m_currentLayer->AddChild(accidElement);
 }
 
+void CmmeInput::CreateBarline(pugi::xml_node barlineNode)
+{
+    assert(m_currentLayer);
+
+    BarLine *barline = new BarLine();
+
+    /// Determine the barLine/@form based on the CMME <Barline>'s <NumLines> and <RepeatSign>
+    int formNumLines = this->ChildAsInt(barlineNode, "NumLines");
+    if (formNumLines == 1) {
+        barline->SetForm(BARRENDITION_single);
+    }
+    else if (formNumLines == 2) {
+        barline->SetForm(BARRENDITION_dbl);
+    }
+    else if (formNumLines != VRV_UNSET) {
+        LogWarning("Unsupported barline (with more than 2 lines)");
+    } ///@form is overwritten to 'rptboth' when <RepeatSign> is used
+    if (barlineNode.select_node("./RepeatSign")) {
+        barline->SetForm(BARRENDITION_rptboth);
+    }
+    /// Determine the barLine/@place
+    int bottomLine = this->ChildAsInt(barlineNode, "BottomStaffLine");
+    if (bottomLine != VRV_UNSET) {
+        int place = bottomLine * 2;
+        barline->SetPlace(place);
+    }
+    /// Determine the barLine/@len
+    int numSpaces = this->ChildAsInt(barlineNode, "NumSpaces");
+    if (numSpaces != VRV_UNSET) {
+        barline->SetLen(numSpaces * 2);
+    }
+    m_currentLayer->AddChild(barline);
+}
+
 void CmmeInput::CreateClef(pugi::xml_node clefNode)
 {
     static const std::map<std::string, data_CLEFSHAPE> shapeMap{

From 43bff2048a7fcda3df8dbf1d7f97e513da112547 Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Wed, 18 Sep 2024 15:12:39 +0200
Subject: [PATCH 028/105] Add support for annot (editorial commentary) in CMME

---
 include/vrv/iocmme.h |  1 +
 src/iocmme.cpp       | 70 ++++++++++++++++++++++++++++++++------------
 2 files changed, 53 insertions(+), 18 deletions(-)

diff --git a/include/vrv/iocmme.h b/include/vrv/iocmme.h
index a6660198c59..96aa928b09c 100644
--- a/include/vrv/iocmme.h
+++ b/include/vrv/iocmme.h
@@ -56,6 +56,7 @@ class CmmeInput : public Input {
     void CreateLemOrRdg(pugi::xml_node lemOrRdgNode, bool isFirst);
 
     void ReadEvents(pugi::xml_node eventsNode);
+    void ReadEditorialCommentary(pugi::xml_node evenNode, Object *object);
 
     void CreateAccid(pugi::xml_node accidNode);
     void CreateClef(pugi::xml_node clefNode);
diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index afb6c2cf0dd..dc7dc953fc6 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -18,6 +18,7 @@
 //----------------------------------------------------------------------------
 
 #include "accid.h"
+#include "annot.h"
 #include "app.h"
 #include "barline.h"
 #include "clef.h"
@@ -191,11 +192,11 @@ void CmmeInput::CreateStaff(pugi::xml_node voiceNode)
     m_isInSyllable = false;
     m_currentSignature = NULL;
 
-    // Loop through the event lists
-    ReadEvents(voiceNode.child("EventList"));
-
     staff->AddChild(m_currentContainer);
     m_currentSection->AddChild(staff);
+
+    // Loop through the event lists
+    ReadEvents(voiceNode.child("EventList"));
 }
 
 void CmmeInput::CreateApp(pugi::xml_node appNode)
@@ -318,6 +319,22 @@ void CmmeInput::ReadEvents(pugi::xml_node eventsNode)
     }
 }
 
+void CmmeInput::ReadEditorialCommentary(pugi::xml_node eventNode, Object *object)
+{
+    std::string commentary = this->ChildAsString(eventNode, "EditorialCommentary");
+
+    if (!commentary.empty()) {
+        Annot *annot = new Annot();
+        Text *text = new Text();
+        text->SetText(UTF8to32(commentary));
+        annot->AddChild(text);
+        xsdAnyURI_List list;
+        list.push_back("#" + object->GetID());
+        annot->SetPlist(list);
+        m_currentSection->AddChild(annot);
+    }
+}
+
 void CmmeInput::CreateAccid(pugi::xml_node accidNode)
 {
     static const std::map<std::string, data_ACCIDENTAL_WRITTEN> shapeMap{
@@ -339,24 +356,27 @@ void CmmeInput::CreateAccid(pugi::xml_node accidNode)
 
     assert(m_currentContainer);
 
-    Accid *accidElement = new Accid();
+    Accid *accid = new Accid();
     std::string appearance = this->ChildAsString(accidNode, "Appearance");
-    data_ACCIDENTAL_WRITTEN accid = shapeMap.contains(appearance) ? shapeMap.at(appearance) : ACCIDENTAL_WRITTEN_f;
-    accidElement->SetAccid(accid);
+    data_ACCIDENTAL_WRITTEN accidWritten
+        = shapeMap.contains(appearance) ? shapeMap.at(appearance) : ACCIDENTAL_WRITTEN_f;
+    accid->SetAccid(accidWritten);
 
     std::string step = this->ChildAsString(accidNode, "Pitch/LetterName");
     // Default pitch to C
     data_PITCHNAME ploc = pitchMap.contains(step) ? pitchMap.at(step) : PITCHNAME_c;
-    accidElement->SetPloc(ploc);
+    accid->SetPloc(ploc);
 
     int oct = this->ChildAsInt(accidNode, "Pitch/OctaveNum");
     if ((ploc != PITCHNAME_a) && (ploc != PITCHNAME_b)) oct += 1;
-    accidElement->SetOloc(oct);
+    accid->SetOloc(oct);
 
     int staffLoc = this->ChildAsInt(accidNode, "StaffLoc");
-    accidElement->SetLoc(staffLoc - 1);
+    accid->SetLoc(staffLoc - 1);
+
+    this->ReadEditorialCommentary(accidNode, accid);
 
-    m_currentContainer->AddChild(accidElement);
+    m_currentContainer->AddChild(accid);
 }
 
 void CmmeInput::CreateClef(pugi::xml_node clefNode)
@@ -381,6 +401,8 @@ void CmmeInput::CreateClef(pugi::xml_node clefNode)
     data_CLEFSHAPE shape = shapeMap.contains(appearance) ? shapeMap.at(appearance) : CLEFSHAPE_C;
     clef->SetShape(shape);
 
+    this->ReadEditorialCommentary(clefNode, clef);
+
     m_currentContainer->AddChild(clef);
 
     return;
@@ -398,7 +420,7 @@ void CmmeInput::CreateCustos(pugi::xml_node custosNode)
         { "B", PITCHNAME_b } //
     };
 
-    assert(m_currentLayer);
+    assert(m_currentContainer);
 
     Custos *custos = new Custos();
     std::string step = this->ChildAsString(custosNode, "LetterName");
@@ -410,7 +432,9 @@ void CmmeInput::CreateCustos(pugi::xml_node custosNode)
     if ((pname != PITCHNAME_a) && (pname != PITCHNAME_b)) oct += 1;
     custos->SetOct(oct);
 
-    m_currentLayer->AddChild(custos);
+    this->ReadEditorialCommentary(custosNode, custos);
+
+    m_currentContainer->AddChild(custos);
 
     return;
 }
@@ -422,6 +446,8 @@ void CmmeInput::CreateDot(pugi::xml_node dotNode)
     Dot *dot = new Dot();
     m_currentContainer->AddChild(dot);
 
+    this->ReadEditorialCommentary(dotNode, dot);
+
     return;
 }
 
@@ -451,24 +477,26 @@ void CmmeInput::CreateKeySig(pugi::xml_node keyNode)
         m_currentContainer->AddChild(m_currentSignature);
     }
 
-    KeyAccid *keyaccid = new KeyAccid();
+    KeyAccid *keyAccid = new KeyAccid();
     std::string appearance = this->ChildAsString(keyNode, "Appearance");
     data_ACCIDENTAL_WRITTEN accid = shapeMap.contains(appearance) ? shapeMap.at(appearance) : ACCIDENTAL_WRITTEN_f;
-    keyaccid->SetAccid(accid);
+    keyAccid->SetAccid(accid);
 
     std::string step = this->ChildAsString(keyNode, "Pitch/LetterName");
     // Default pitch to C
     data_PITCHNAME pname = pitchMap.contains(step) ? pitchMap.at(step) : PITCHNAME_c;
-    keyaccid->SetPname(pname);
+    keyAccid->SetPname(pname);
 
     int oct = this->ChildAsInt(keyNode, "Pitch/OctaveNum");
     if ((pname != PITCHNAME_a) && (pname != PITCHNAME_b)) oct += 1;
-    keyaccid->SetOct(oct);
+    keyAccid->SetOct(oct);
 
     int staffLoc = this->ChildAsInt(keyNode, "StaffLoc");
-    keyaccid->SetLoc(staffLoc - 1);
+    keyAccid->SetLoc(staffLoc - 1);
+
+    this->ReadEditorialCommentary(keyNode, keyAccid);
 
-    m_currentSignature->AddChild(keyaccid);
+    m_currentSignature->AddChild(keyAccid);
 }
 
 void CmmeInput::CreateMensuration(pugi::xml_node mensurationNode)
@@ -498,6 +526,8 @@ void CmmeInput::CreateMensuration(pugi::xml_node mensurationNode)
     data_BOOLEAN dot = (m_mensInfo->prolatio == 3) ? BOOLEAN_true : BOOLEAN_false;
     mensur->SetDot(dot);
 
+    this->ReadEditorialCommentary(mensurationNode, mensur);
+
     m_currentContainer->AddChild(mensur);
 
     return;
@@ -619,6 +649,8 @@ void CmmeInput::CreateNote(pugi::xml_node noteNode)
         }
     }
 
+    this->ReadEditorialCommentary(noteNode, note);
+
     m_currentContainer->AddChild(note);
 
     // We have processed the last note of a ligature
@@ -648,6 +680,8 @@ void CmmeInput::CreateRest(pugi::xml_node restNode)
         rest->SetNum(numbase);
     }
 
+    this->ReadEditorialCommentary(restNode, rest);
+
     m_currentContainer->AddChild(rest);
 
     return;

From eb3bef151ad4d301508668afb915c2ec5a5d883e Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Wed, 18 Sep 2024 15:40:15 +0200
Subject: [PATCH 029/105] Fixing m_currentLayer name change and cosmetic
 variable renaming

---
 src/iocmme.cpp | 27 +++++++++++++++------------
 1 file changed, 15 insertions(+), 12 deletions(-)

diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index 8a9e8abe24b..2c8799291b3 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -388,36 +388,39 @@ void CmmeInput::CreateAccid(pugi::xml_node accidNode)
 
 void CmmeInput::CreateBarline(pugi::xml_node barlineNode)
 {
-    assert(m_currentLayer);
+    assert(m_currentContainer);
 
-    BarLine *barline = new BarLine();
+    BarLine *barLine = new BarLine();
 
-    /// Determine the barLine/@form based on the CMME <Barline>'s <NumLines> and <RepeatSign>
+    // Determine the barLine/@form based on the CMME <Barline>'s <NumLines> and <RepeatSign>
     int formNumLines = this->ChildAsInt(barlineNode, "NumLines");
     if (formNumLines == 1) {
-        barline->SetForm(BARRENDITION_single);
+        barLine->SetForm(BARRENDITION_single);
     }
     else if (formNumLines == 2) {
-        barline->SetForm(BARRENDITION_dbl);
+        barLine->SetForm(BARRENDITION_dbl);
     }
     else if (formNumLines != VRV_UNSET) {
         LogWarning("Unsupported barline (with more than 2 lines)");
-    } ///@form is overwritten to 'rptboth' when <RepeatSign> is used
+    }
+
+    // `@form` is overwritten to 'rptboth' when <RepeatSign> is used
     if (barlineNode.select_node("./RepeatSign")) {
-        barline->SetForm(BARRENDITION_rptboth);
+        barLine->SetForm(BARRENDITION_rptboth);
     }
-    /// Determine the barLine/@place
+    // Determine the barLine/@place
     int bottomLine = this->ChildAsInt(barlineNode, "BottomStaffLine");
     if (bottomLine != VRV_UNSET) {
         int place = bottomLine * 2;
-        barline->SetPlace(place);
+        barLine->SetPlace(place);
     }
-    /// Determine the barLine/@len
+    // Determine the barLine/@len
     int numSpaces = this->ChildAsInt(barlineNode, "NumSpaces");
     if (numSpaces != VRV_UNSET) {
-        barline->SetLen(numSpaces * 2);
+        barLine->SetLen(numSpaces * 2);
     }
-    m_currentLayer->AddChild(barline);
+
+    m_currentContainer->AddChild(barLine);
 }
 
 void CmmeInput::CreateClef(pugi::xml_node clefNode)

From 750a3e82cf2c45685b6d0c0e34ffaf07433c0580 Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Wed, 18 Sep 2024 16:26:44 +0200
Subject: [PATCH 030/105] Formatting with clang-format 18.1.8

---
 include/vrv/beam.h              | 2 +-
 include/vrv/boundingbox.h       | 4 ++--
 include/vrv/devicecontext.h     | 2 +-
 include/vrv/devicecontextbase.h | 2 +-
 include/vrv/docselection.h      | 2 +-
 include/vrv/floatingobject.h    | 4 ++--
 include/vrv/functorinterface.h  | 4 ++--
 include/vrv/interface.h         | 4 ++--
 include/vrv/iobase.h            | 2 +-
 include/vrv/options.h           | 4 ++--
 include/vrv/resources.h         | 4 ++--
 include/vrv/textelement.h       | 2 +-
 include/vrv/transposition.h     | 2 +-
 13 files changed, 19 insertions(+), 19 deletions(-)

diff --git a/include/vrv/beam.h b/include/vrv/beam.h
index 6f6b56482c0..fc10fce8b19 100644
--- a/include/vrv/beam.h
+++ b/include/vrv/beam.h
@@ -225,7 +225,7 @@ class BeamSegment {
 class BeamSpanSegment : public BeamSegment {
 public:
     BeamSpanSegment();
-    virtual ~BeamSpanSegment(){};
+    virtual ~BeamSpanSegment() {};
 
     /**
      * Set/get methods for member variables
diff --git a/include/vrv/boundingbox.h b/include/vrv/boundingbox.h
index 20a437657c5..9b04e512297 100644
--- a/include/vrv/boundingbox.h
+++ b/include/vrv/boundingbox.h
@@ -37,7 +37,7 @@ class BoundingBox {
      */
     ///@{
     BoundingBox();
-    virtual ~BoundingBox(){};
+    virtual ~BoundingBox() {};
     virtual ClassId GetClassId() const = 0;
     bool Is(ClassId classId) const { return (this->GetClassId() == classId); }
     bool Is(const std::vector<ClassId> &classIds) const;
@@ -342,7 +342,7 @@ class SegmentedLine {
      */
     ///@{
     SegmentedLine(int start, int end);
-    virtual ~SegmentedLine(){};
+    virtual ~SegmentedLine() {};
     ///@}
 
     /**
diff --git a/include/vrv/devicecontext.h b/include/vrv/devicecontext.h
index 95c62a81208..05b1ee7a94a 100644
--- a/include/vrv/devicecontext.h
+++ b/include/vrv/devicecontext.h
@@ -92,7 +92,7 @@ class DeviceContext {
         m_pushBack = false;
         m_viewBoxFactor = (double)DEFINITION_FACTOR;
     }
-    virtual ~DeviceContext(){};
+    virtual ~DeviceContext() {};
     ClassId GetClassId() const { return m_classId; }
     bool Is(ClassId classId) const { return (m_classId == classId); }
     ///@}
diff --git a/include/vrv/devicecontextbase.h b/include/vrv/devicecontextbase.h
index faa51c6d05d..2a2cf7e1054 100644
--- a/include/vrv/devicecontextbase.h
+++ b/include/vrv/devicecontextbase.h
@@ -144,7 +144,7 @@ class FontInfo {
         m_widthToHeightRatio = 1.0;
         m_smuflFont = SMUFL_NONE;
     }
-    virtual ~FontInfo(){};
+    virtual ~FontInfo() {};
 
     // accessors and modifiers for the font elements
     int GetPointSize() const { return m_pointSize; }
diff --git a/include/vrv/docselection.h b/include/vrv/docselection.h
index 5bd30339161..9949347ff8e 100644
--- a/include/vrv/docselection.h
+++ b/include/vrv/docselection.h
@@ -29,7 +29,7 @@ class DocSelection {
      */
     ///@{
     DocSelection();
-    virtual ~DocSelection(){};
+    virtual ~DocSelection() {};
     ///@}
 
     /**
diff --git a/include/vrv/floatingobject.h b/include/vrv/floatingobject.h
index f6cd1f450a3..5b0321c3edc 100644
--- a/include/vrv/floatingobject.h
+++ b/include/vrv/floatingobject.h
@@ -167,7 +167,7 @@ class FloatingPositioner : public BoundingBox {
 public:
     // constructors and destructors
     FloatingPositioner(FloatingObject *object, StaffAlignment *alignment, char spanningType);
-    virtual ~FloatingPositioner(){};
+    virtual ~FloatingPositioner() {};
     ClassId GetClassId() const override { return FLOATING_POSITIONER; }
 
     virtual void ResetPositioner();
@@ -485,7 +485,7 @@ class CurveSpannedElement {
         m_discarded = false;
         m_isBelow = true;
     }
-    virtual ~CurveSpannedElement(){};
+    virtual ~CurveSpannedElement() {};
 
     Point m_rotatedPoints[4];
     const BoundingBox *m_boundingBox;
diff --git a/include/vrv/functorinterface.h b/include/vrv/functorinterface.h
index 38424282880..452df9a0fc7 100644
--- a/include/vrv/functorinterface.h
+++ b/include/vrv/functorinterface.h
@@ -156,7 +156,7 @@ class FunctorInterface {
      * @name Constructors, destructors
      */
     ///@{
-    FunctorInterface(){};
+    FunctorInterface() {};
     virtual ~FunctorInterface() = default;
     ///@}
 
@@ -513,7 +513,7 @@ class ConstFunctorInterface {
      * @name Constructors, destructors
      */
     ///@{
-    ConstFunctorInterface(){};
+    ConstFunctorInterface() {};
     virtual ~ConstFunctorInterface() = default;
     ///@}
 
diff --git a/include/vrv/interface.h b/include/vrv/interface.h
index 7b389b85424..a957e63abe2 100644
--- a/include/vrv/interface.h
+++ b/include/vrv/interface.h
@@ -37,8 +37,8 @@ class Interface {
      * Reset method reset all attribute classes
      */
     ///@{
-    Interface(){};
-    virtual ~Interface(){};
+    Interface() {};
+    virtual ~Interface() {};
     ///@}
 
     /**
diff --git a/include/vrv/iobase.h b/include/vrv/iobase.h
index e5cfc903acc..8dcab892fe3 100644
--- a/include/vrv/iobase.h
+++ b/include/vrv/iobase.h
@@ -33,7 +33,7 @@ class Output {
     // constructors and destructors
     Output(Doc *doc, std::string filename);
     Output(Doc *doc);
-    Output(){};
+    Output() {};
     virtual ~Output();
 
     /**
diff --git a/include/vrv/options.h b/include/vrv/options.h
index 0e9160240a7..a525e216fbd 100644
--- a/include/vrv/options.h
+++ b/include/vrv/options.h
@@ -417,8 +417,8 @@ class OptionIntMap : public Option {
 class OptionStaffrel : public Option {
 public:
     // constructors and destructors
-    OptionStaffrel(){};
-    virtual ~OptionStaffrel(){};
+    OptionStaffrel() {};
+    virtual ~OptionStaffrel() {};
     void CopyTo(Option *option) override;
     // Alternate type style cannot have a restricted list of possible values
     void Init(data_STAFFREL defaultValue);
diff --git a/include/vrv/resources.h b/include/vrv/resources.h
index 7988fb1faf4..94224baa8ee 100644
--- a/include/vrv/resources.h
+++ b/include/vrv/resources.h
@@ -136,8 +136,8 @@ class Resources {
     class LoadedFont {
 
     public:
-        LoadedFont(const std::string &name, bool isFallback) : m_name(name), m_isFallback(isFallback){};
-        ~LoadedFont(){};
+        LoadedFont(const std::string &name, bool isFallback) : m_name(name), m_isFallback(isFallback) {};
+        ~LoadedFont() {};
         const std::string GetName() const { return m_name; };
         const GlyphTable &GetGlyphTable() const { return m_glyphTable; };
         GlyphTable &GetGlyphTableForModification() { return m_glyphTable; };
diff --git a/include/vrv/textelement.h b/include/vrv/textelement.h
index 2c334275484..c11bc8472ef 100644
--- a/include/vrv/textelement.h
+++ b/include/vrv/textelement.h
@@ -105,7 +105,7 @@ class TextDrawingParams {
         m_enclose = TEXTRENDITION_NONE;
         m_textEnclose = ENCLOSURE_NONE;
     }
-    virtual ~TextDrawingParams(){};
+    virtual ~TextDrawingParams() {};
 
     int m_x;
     int m_y;
diff --git a/include/vrv/transposition.h b/include/vrv/transposition.h
index 9359ab0e5f0..505ab7b1d48 100644
--- a/include/vrv/transposition.h
+++ b/include/vrv/transposition.h
@@ -43,7 +43,7 @@ class TransPitch {
     // octave number of pitch: 4 = middle-C octave
     int m_oct;
 
-    TransPitch(){};
+    TransPitch() {};
     TransPitch(int aPname, int anAccid, int anOct);
     TransPitch(data_PITCHNAME pname, data_ACCIDENTAL_GESTURAL accidG, data_ACCIDENTAL_WRITTEN accidW, int oct);
     TransPitch(const TransPitch &pitch);

From f55b6ede44b51750a55a68fbc4c8130c72e3d302 Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Wed, 18 Sep 2024 16:46:38 +0200
Subject: [PATCH 031/105] Revert formatting

---
 include/vrv/beam.h              |  2 +-
 include/vrv/boundingbox.h       |  4 ++--
 include/vrv/devicecontext.h     | 12 ++++++------
 include/vrv/devicecontextbase.h |  2 +-
 include/vrv/docselection.h      |  2 +-
 include/vrv/floatingobject.h    |  4 ++--
 include/vrv/functorinterface.h  |  4 ++--
 include/vrv/interface.h         |  4 ++--
 include/vrv/iobase.h            |  2 +-
 include/vrv/layerelement.h      |  2 +-
 include/vrv/options.h           |  4 ++--
 include/vrv/resources.h         |  4 ++--
 include/vrv/textelement.h       |  2 +-
 include/vrv/transposition.h     |  2 +-
 include/vrv/view.h              |  2 +-
 15 files changed, 26 insertions(+), 26 deletions(-)

diff --git a/include/vrv/beam.h b/include/vrv/beam.h
index fc10fce8b19..6f6b56482c0 100644
--- a/include/vrv/beam.h
+++ b/include/vrv/beam.h
@@ -225,7 +225,7 @@ class BeamSegment {
 class BeamSpanSegment : public BeamSegment {
 public:
     BeamSpanSegment();
-    virtual ~BeamSpanSegment() {};
+    virtual ~BeamSpanSegment(){};
 
     /**
      * Set/get methods for member variables
diff --git a/include/vrv/boundingbox.h b/include/vrv/boundingbox.h
index 9b04e512297..20a437657c5 100644
--- a/include/vrv/boundingbox.h
+++ b/include/vrv/boundingbox.h
@@ -37,7 +37,7 @@ class BoundingBox {
      */
     ///@{
     BoundingBox();
-    virtual ~BoundingBox() {};
+    virtual ~BoundingBox(){};
     virtual ClassId GetClassId() const = 0;
     bool Is(ClassId classId) const { return (this->GetClassId() == classId); }
     bool Is(const std::vector<ClassId> &classIds) const;
@@ -342,7 +342,7 @@ class SegmentedLine {
      */
     ///@{
     SegmentedLine(int start, int end);
-    virtual ~SegmentedLine() {};
+    virtual ~SegmentedLine(){};
     ///@}
 
     /**
diff --git a/include/vrv/devicecontext.h b/include/vrv/devicecontext.h
index 05b1ee7a94a..fde5c18875d 100644
--- a/include/vrv/devicecontext.h
+++ b/include/vrv/devicecontext.h
@@ -92,7 +92,7 @@ class DeviceContext {
         m_pushBack = false;
         m_viewBoxFactor = (double)DEFINITION_FACTOR;
     }
-    virtual ~DeviceContext() {};
+    virtual ~DeviceContext(){};
     ClassId GetClassId() const { return m_classId; }
     bool Is(ClassId classId) const { return (m_classId == classId); }
     ///@}
@@ -211,7 +211,7 @@ class DeviceContext {
      * Special method for forcing bounding boxes to be updated
      * Used for invisible elements (e.g., <space>) that needs to be take into account in spacing
      */
-    virtual void DrawPlaceholder(int x, int y) {};
+    virtual void DrawPlaceholder(int x, int y){};
 
     /**
      * @name Method for starting and ending a text
@@ -262,14 +262,14 @@ class DeviceContext {
      * For example, the method can be used for grouping shapes in <g></g> in SVG
      */
     ///@{
-    virtual void StartCustomGraphic(const std::string &name, std::string gClass = "", std::string gId = "") {};
-    virtual void EndCustomGraphic() {};
+    virtual void StartCustomGraphic(const std::string &name, std::string gClass = "", std::string gId = ""){};
+    virtual void EndCustomGraphic(){};
     ///@}
 
     /**
      * Method for changing the color of a custom graphic
      */
-    virtual void SetCustomGraphicColor(const std::string &color) {};
+    virtual void SetCustomGraphicColor(const std::string &color){};
 
     /**
      * @name Methods for re-starting and ending a graphic for objects drawn in separate steps
@@ -312,7 +312,7 @@ class DeviceContext {
      * @name Method for adding description element
      */
     ///@{
-    virtual void AddDescription(const std::string &text) {};
+    virtual void AddDescription(const std::string &text){};
     ///@}
 
     /**
diff --git a/include/vrv/devicecontextbase.h b/include/vrv/devicecontextbase.h
index 2a2cf7e1054..faa51c6d05d 100644
--- a/include/vrv/devicecontextbase.h
+++ b/include/vrv/devicecontextbase.h
@@ -144,7 +144,7 @@ class FontInfo {
         m_widthToHeightRatio = 1.0;
         m_smuflFont = SMUFL_NONE;
     }
-    virtual ~FontInfo() {};
+    virtual ~FontInfo(){};
 
     // accessors and modifiers for the font elements
     int GetPointSize() const { return m_pointSize; }
diff --git a/include/vrv/docselection.h b/include/vrv/docselection.h
index 9949347ff8e..5bd30339161 100644
--- a/include/vrv/docselection.h
+++ b/include/vrv/docselection.h
@@ -29,7 +29,7 @@ class DocSelection {
      */
     ///@{
     DocSelection();
-    virtual ~DocSelection() {};
+    virtual ~DocSelection(){};
     ///@}
 
     /**
diff --git a/include/vrv/floatingobject.h b/include/vrv/floatingobject.h
index 5b0321c3edc..f6cd1f450a3 100644
--- a/include/vrv/floatingobject.h
+++ b/include/vrv/floatingobject.h
@@ -167,7 +167,7 @@ class FloatingPositioner : public BoundingBox {
 public:
     // constructors and destructors
     FloatingPositioner(FloatingObject *object, StaffAlignment *alignment, char spanningType);
-    virtual ~FloatingPositioner() {};
+    virtual ~FloatingPositioner(){};
     ClassId GetClassId() const override { return FLOATING_POSITIONER; }
 
     virtual void ResetPositioner();
@@ -485,7 +485,7 @@ class CurveSpannedElement {
         m_discarded = false;
         m_isBelow = true;
     }
-    virtual ~CurveSpannedElement() {};
+    virtual ~CurveSpannedElement(){};
 
     Point m_rotatedPoints[4];
     const BoundingBox *m_boundingBox;
diff --git a/include/vrv/functorinterface.h b/include/vrv/functorinterface.h
index 452df9a0fc7..38424282880 100644
--- a/include/vrv/functorinterface.h
+++ b/include/vrv/functorinterface.h
@@ -156,7 +156,7 @@ class FunctorInterface {
      * @name Constructors, destructors
      */
     ///@{
-    FunctorInterface() {};
+    FunctorInterface(){};
     virtual ~FunctorInterface() = default;
     ///@}
 
@@ -513,7 +513,7 @@ class ConstFunctorInterface {
      * @name Constructors, destructors
      */
     ///@{
-    ConstFunctorInterface() {};
+    ConstFunctorInterface(){};
     virtual ~ConstFunctorInterface() = default;
     ///@}
 
diff --git a/include/vrv/interface.h b/include/vrv/interface.h
index a957e63abe2..7b389b85424 100644
--- a/include/vrv/interface.h
+++ b/include/vrv/interface.h
@@ -37,8 +37,8 @@ class Interface {
      * Reset method reset all attribute classes
      */
     ///@{
-    Interface() {};
-    virtual ~Interface() {};
+    Interface(){};
+    virtual ~Interface(){};
     ///@}
 
     /**
diff --git a/include/vrv/iobase.h b/include/vrv/iobase.h
index 8dcab892fe3..e5cfc903acc 100644
--- a/include/vrv/iobase.h
+++ b/include/vrv/iobase.h
@@ -33,7 +33,7 @@ class Output {
     // constructors and destructors
     Output(Doc *doc, std::string filename);
     Output(Doc *doc);
-    Output() {};
+    Output(){};
     virtual ~Output();
 
     /**
diff --git a/include/vrv/layerelement.h b/include/vrv/layerelement.h
index 4740e64373b..6b1491e5e74 100644
--- a/include/vrv/layerelement.h
+++ b/include/vrv/layerelement.h
@@ -296,7 +296,7 @@ class LayerElement : public Object,
     /**
      * Helper function to set shortening for elements with beam interface
      */
-    virtual void SetElementShortening(int shortening) {};
+    virtual void SetElementShortening(int shortening){};
 
     /**
      * Get the stem mod for the element (if any)
diff --git a/include/vrv/options.h b/include/vrv/options.h
index a525e216fbd..0e9160240a7 100644
--- a/include/vrv/options.h
+++ b/include/vrv/options.h
@@ -417,8 +417,8 @@ class OptionIntMap : public Option {
 class OptionStaffrel : public Option {
 public:
     // constructors and destructors
-    OptionStaffrel() {};
-    virtual ~OptionStaffrel() {};
+    OptionStaffrel(){};
+    virtual ~OptionStaffrel(){};
     void CopyTo(Option *option) override;
     // Alternate type style cannot have a restricted list of possible values
     void Init(data_STAFFREL defaultValue);
diff --git a/include/vrv/resources.h b/include/vrv/resources.h
index 94224baa8ee..7988fb1faf4 100644
--- a/include/vrv/resources.h
+++ b/include/vrv/resources.h
@@ -136,8 +136,8 @@ class Resources {
     class LoadedFont {
 
     public:
-        LoadedFont(const std::string &name, bool isFallback) : m_name(name), m_isFallback(isFallback) {};
-        ~LoadedFont() {};
+        LoadedFont(const std::string &name, bool isFallback) : m_name(name), m_isFallback(isFallback){};
+        ~LoadedFont(){};
         const std::string GetName() const { return m_name; };
         const GlyphTable &GetGlyphTable() const { return m_glyphTable; };
         GlyphTable &GetGlyphTableForModification() { return m_glyphTable; };
diff --git a/include/vrv/textelement.h b/include/vrv/textelement.h
index c11bc8472ef..2c334275484 100644
--- a/include/vrv/textelement.h
+++ b/include/vrv/textelement.h
@@ -105,7 +105,7 @@ class TextDrawingParams {
         m_enclose = TEXTRENDITION_NONE;
         m_textEnclose = ENCLOSURE_NONE;
     }
-    virtual ~TextDrawingParams() {};
+    virtual ~TextDrawingParams(){};
 
     int m_x;
     int m_y;
diff --git a/include/vrv/transposition.h b/include/vrv/transposition.h
index 505ab7b1d48..9359ab0e5f0 100644
--- a/include/vrv/transposition.h
+++ b/include/vrv/transposition.h
@@ -43,7 +43,7 @@ class TransPitch {
     // octave number of pitch: 4 = middle-C octave
     int m_oct;
 
-    TransPitch() {};
+    TransPitch(){};
     TransPitch(int aPname, int anAccid, int anOct);
     TransPitch(data_PITCHNAME pname, data_ACCIDENTAL_GESTURAL accidG, data_ACCIDENTAL_WRITTEN accidW, int oct);
     TransPitch(const TransPitch &pitch);
diff --git a/include/vrv/view.h b/include/vrv/view.h
index e7faedd7ad1..82719ece3c2 100644
--- a/include/vrv/view.h
+++ b/include/vrv/view.h
@@ -122,7 +122,7 @@ class View {
     virtual void DoRefresh() {}
     virtual void DoResize() {}
     virtual void DoReset() {}
-    virtual void OnPageChange() {};
+    virtual void OnPageChange() {}
     ///@}
 
     /**

From f68b557c3316395caf357cc1fb1bdaf1d005f3b3 Mon Sep 17 00:00:00 2001
From: martha-thomae <thomaemartha@gmail.com>
Date: Wed, 18 Sep 2024 17:07:51 +0200
Subject: [PATCH 032/105] Add mensuration signs and dots for cmme

---
 src/iocmme.cpp | 20 ++++++++++++++++----
 1 file changed, 16 insertions(+), 4 deletions(-)

diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index 2c8799291b3..61ec05e527f 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -565,10 +565,22 @@ void CmmeInput::CreateMensuration(pugi::xml_node mensurationNode)
     mensur->SetModusminor(modusminor);
     data_MODUSMAIOR modusmaior = (m_mensInfo->modusmaior == 3) ? MODUSMAIOR_3 : MODUSMAIOR_2;
     mensur->SetModusmaior(modusmaior);
-    data_MENSURATIONSIGN sign = (m_mensInfo->tempus == 3) ? MENSURATIONSIGN_O : MENSURATIONSIGN_C;
-    mensur->SetSign(sign);
-    data_BOOLEAN dot = (m_mensInfo->prolatio == 3) ? BOOLEAN_true : BOOLEAN_false;
-    mensur->SetDot(dot);
+    
+    pugi::xml_node signNode = mensurationNode.child("Sign");
+    std::string signValue = this->ChildAsString(signNode, "MainSymbol");
+    if (signValue == "O") {
+        mensur->SetSign(MENSURATIONSIGN_O);
+    } else if (signValue == "C") {
+        mensur->SetSign(MENSURATIONSIGN_C);
+    } else {
+        LogWarning("Unsupported mesuration sign in CMME (not 'O' or 'C')");
+    }
+    pugi::xml_node dotNode = signNode.child("Dot");
+    if (dotNode) {
+        mensur->SetDot(BOOLEAN_true);
+    } else {
+        mensur->SetDot(BOOLEAN_false);
+    }
 
     this->ReadEditorialCommentary(mensurationNode, mensur);
 

From 12431023e8cd4120e2e43cf3a9f09ab933df23a4 Mon Sep 17 00:00:00 2001
From: martha-thomae <thomaemartha@gmail.com>
Date: Wed, 18 Sep 2024 17:10:05 +0200
Subject: [PATCH 033/105] Fix formatting

---
 src/iocmme.cpp | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index 61ec05e527f..3efcad197b9 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -565,20 +565,23 @@ void CmmeInput::CreateMensuration(pugi::xml_node mensurationNode)
     mensur->SetModusminor(modusminor);
     data_MODUSMAIOR modusmaior = (m_mensInfo->modusmaior == 3) ? MODUSMAIOR_3 : MODUSMAIOR_2;
     mensur->SetModusmaior(modusmaior);
-    
+
     pugi::xml_node signNode = mensurationNode.child("Sign");
     std::string signValue = this->ChildAsString(signNode, "MainSymbol");
     if (signValue == "O") {
         mensur->SetSign(MENSURATIONSIGN_O);
-    } else if (signValue == "C") {
+    }
+    else if (signValue == "C") {
         mensur->SetSign(MENSURATIONSIGN_C);
-    } else {
+    }
+    else {
         LogWarning("Unsupported mesuration sign in CMME (not 'O' or 'C')");
     }
     pugi::xml_node dotNode = signNode.child("Dot");
     if (dotNode) {
         mensur->SetDot(BOOLEAN_true);
-    } else {
+    }
+    else {
         mensur->SetDot(BOOLEAN_false);
     }
 

From 63a463df08ad5dc2bb02acb04328b758057587b5 Mon Sep 17 00:00:00 2001
From: "Martha E. Thomae" <thomaemartha@gmail.com>
Date: Wed, 18 Sep 2024 17:39:58 +0200
Subject: [PATCH 034/105] Update src/iocmme.cpp

Co-authored-by: Laurent Pugin <lxpugin@gmail.com>
---
 src/iocmme.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index 3efcad197b9..40af5ae2114 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -577,7 +577,7 @@ void CmmeInput::CreateMensuration(pugi::xml_node mensurationNode)
     else {
         LogWarning("Unsupported mesuration sign in CMME (not 'O' or 'C')");
     }
-    pugi::xml_node dotNode = signNode.child("Dot");
+    pugi::xml_node dotNode = (signNode) ? signNode.child("Dot") : pugi::xml_node(NULL);
     if (dotNode) {
         mensur->SetDot(BOOLEAN_true);
     }

From 17e82fd405b90db3e356eaec6c8af684679276ca Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Wed, 18 Sep 2024 18:37:07 +0200
Subject: [PATCH 035/105] Reduce if / else to conditional ?

---
 src/iocmme.cpp | 8 ++------
 1 file changed, 2 insertions(+), 6 deletions(-)

diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index 40af5ae2114..4ef16b40aa8 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -577,13 +577,9 @@ void CmmeInput::CreateMensuration(pugi::xml_node mensurationNode)
     else {
         LogWarning("Unsupported mesuration sign in CMME (not 'O' or 'C')");
     }
+
     pugi::xml_node dotNode = (signNode) ? signNode.child("Dot") : pugi::xml_node(NULL);
-    if (dotNode) {
-        mensur->SetDot(BOOLEAN_true);
-    }
-    else {
-        mensur->SetDot(BOOLEAN_false);
-    }
+    mensur->SetDot(((dotNode) ? BOOLEAN_true : BOOLEAN_false));
 
     this->ReadEditorialCommentary(mensurationNode, mensur);
 

From b0a5dd5753c9c5ab9e84a4903868015a5fd149c5 Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Thu, 19 Sep 2024 08:17:27 +0200
Subject: [PATCH 036/105] Auto-detect cmme xml

---
 src/toolkit.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/toolkit.cpp b/src/toolkit.cpp
index 086562628d1..fccf857f48e 100644
--- a/src/toolkit.cpp
+++ b/src/toolkit.cpp
@@ -300,6 +300,9 @@ FileFormat Toolkit::IdentifyInputFrom(const std::string &data)
         if (std::regex_search(initial, std::regex("<(!DOCTYPE )?(score-partwise|opus|score-timewise)[\\s\\n>]"))) {
             return musicxmlDefault;
         }
+        if (std::regex_search(initial, std::regex("<(Piece xmlns=\"http://www.cmme.org\")[\\s\\n>]"))) {
+            return CMME;
+        }
         LogWarning("Warning: Trying to load unknown XML data which cannot be identified.");
         return UNKNOWN;
     }

From 022d1a5b09795c827d9659da29de551bc0cc358a Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Thu, 19 Sep 2024 09:00:21 +0200
Subject: [PATCH 037/105] Add distinct alignment position for custos

---
 include/vrv/horizontalaligner.h | 1 +
 src/alignfunctor.cpp            | 3 +++
 2 files changed, 4 insertions(+)

diff --git a/include/vrv/horizontalaligner.h b/include/vrv/horizontalaligner.h
index 73f8e09c03d..0ace9f9cbfe 100644
--- a/include/vrv/horizontalaligner.h
+++ b/include/vrv/horizontalaligner.h
@@ -43,6 +43,7 @@ enum AlignmentType {
     ALIGNMENT_MENSUR,
     ALIGNMENT_METERSIG,
     ALIGNMENT_DOT,
+    ALIGNMENT_CUSTOS,
     ALIGNMENT_ACCID,
     ALIGNMENT_GRACENOTE,
     ALIGNMENT_BARLINE,
diff --git a/src/alignfunctor.cpp b/src/alignfunctor.cpp
index c3877c156ee..e709222563c 100644
--- a/src/alignfunctor.cpp
+++ b/src/alignfunctor.cpp
@@ -253,6 +253,9 @@ FunctorCode AlignHorizontallyFunctor::VisitLayerElement(LayerElement *layerEleme
             type = ALIGNMENT_DOT;
         }
     }
+    else if (layerElement->Is(CUSTOS)) {
+        type = ALIGNMENT_CUSTOS;
+    }
     else if (layerElement->Is(ACCID)) {
         // accid within note was already taken into account by noteParent
         type = ALIGNMENT_ACCID;

From b8694181d2971865ada38c298cd30476c8c1bac4 Mon Sep 17 00:00:00 2001
From: martha-thomae <thomaemartha@gmail.com>
Date: Thu, 19 Sep 2024 09:41:42 +0200
Subject: [PATCH 038/105] Add orientation for mensuration signs in cmme

---
 src/iocmme.cpp | 24 +++++++++++++++++++++++-
 1 file changed, 23 insertions(+), 1 deletion(-)

diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index 4ef16b40aa8..8090c2d17b6 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -556,6 +556,7 @@ void CmmeInput::CreateMensuration(pugi::xml_node mensurationNode)
         m_mensInfo->modusmaior = this->ChildAsInt(mensInfo, "ModusMaior");
     }
 
+    /// Mensuration: logical domain
     Mensur *mensur = new Mensur();
     data_PROLATIO prolatio = (m_mensInfo->prolatio == 3) ? PROLATIO_3 : PROLATIO_2;
     mensur->SetProlatio(prolatio);
@@ -566,6 +567,8 @@ void CmmeInput::CreateMensuration(pugi::xml_node mensurationNode)
     data_MODUSMAIOR modusmaior = (m_mensInfo->modusmaior == 3) ? MODUSMAIOR_3 : MODUSMAIOR_2;
     mensur->SetModusmaior(modusmaior);
 
+    /// Mensuration: visual domain
+    /// Sign/MainSymbol to @sign
     pugi::xml_node signNode = mensurationNode.child("Sign");
     std::string signValue = this->ChildAsString(signNode, "MainSymbol");
     if (signValue == "O") {
@@ -577,9 +580,28 @@ void CmmeInput::CreateMensuration(pugi::xml_node mensurationNode)
     else {
         LogWarning("Unsupported mesuration sign in CMME (not 'O' or 'C')");
     }
-
+    /// Sign/Dot to @dot
     pugi::xml_node dotNode = (signNode) ? signNode.child("Dot") : pugi::xml_node(NULL);
     mensur->SetDot(((dotNode) ? BOOLEAN_true : BOOLEAN_false));
+    /// Sign/Strokes to @slash
+    int strokes = this->ChildAsInt(signNode, "Strokes");
+    if (strokes != VRV_UNSET) {
+        mensur->SetSlash(strokes);
+    }
+    /// Sign/Orientation to @orient
+    std::string orientation = this->ChildAsString(signNode, "Orientation");
+    if (orientation == "Reversed") {
+        mensur->SetOrient(ORIENTATION_reversed);
+    }
+    else if (orientation == "90CW") {
+        mensur->SetOrient(ORIENTATION_90CW);
+    }
+    else if (orientation == "90CCW") {
+        mensur->SetOrient(ORIENTATION_90CCW);
+    }
+    else {
+        LogWarning("Unsupported mesuration orientation in CMME (not 'Reversed' or '90CW' or '90CCW')");
+    }
 
     this->ReadEditorialCommentary(mensurationNode, mensur);
 

From 7bc738a209b0e8c1996fb58c1d4397c600896bda Mon Sep 17 00:00:00 2001
From: martha-thomae <thomaemartha@gmail.com>
Date: Thu, 19 Sep 2024 10:10:22 +0200
Subject: [PATCH 039/105] Fix for cases where there is no mensuration sign or
 orientation

---
 src/iocmme.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index 8090c2d17b6..8e5c073995f 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -577,7 +577,7 @@ void CmmeInput::CreateMensuration(pugi::xml_node mensurationNode)
     else if (signValue == "C") {
         mensur->SetSign(MENSURATIONSIGN_C);
     }
-    else {
+    else if (signValue != "") {
         LogWarning("Unsupported mesuration sign in CMME (not 'O' or 'C')");
     }
     /// Sign/Dot to @dot
@@ -599,7 +599,7 @@ void CmmeInput::CreateMensuration(pugi::xml_node mensurationNode)
     else if (orientation == "90CCW") {
         mensur->SetOrient(ORIENTATION_90CCW);
     }
-    else {
+    else if (orientation != "") {
         LogWarning("Unsupported mesuration orientation in CMME (not 'Reversed' or '90CW' or '90CCW')");
     }
 

From 45b80ecc25605d2dbe2fcb93cfa8dd83edcc466b Mon Sep 17 00:00:00 2001
From: martha-thomae <thomaemartha@gmail.com>
Date: Thu, 19 Sep 2024 10:10:46 +0200
Subject: [PATCH 040/105] Add support for cmme chords

---
 include/vrv/iocmme.h |  2 ++
 src/iocmme.cpp       | 26 ++++++++++++++++++++++++++
 2 files changed, 28 insertions(+)

diff --git a/include/vrv/iocmme.h b/include/vrv/iocmme.h
index 88d8bb78001..14292361100 100644
--- a/include/vrv/iocmme.h
+++ b/include/vrv/iocmme.h
@@ -19,6 +19,7 @@
 
 namespace vrv {
 
+class Chord;
 class Clef;
 class KeySig;
 class Layer;
@@ -60,6 +61,7 @@ class CmmeInput : public Input {
 
     void CreateAccid(pugi::xml_node accidNode);
     void CreateBarline(pugi::xml_node barlineNode);
+    void CreateChord(pugi::xml_node chordNode);
     void CreateClef(pugi::xml_node clefNode);
     void CreateCustos(pugi::xml_node custosNode);
     void CreateDot(pugi::xml_node dotNode);
diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index 8e5c073995f..584f244f501 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -21,6 +21,7 @@
 #include "annot.h"
 #include "app.h"
 #include "barline.h"
+#include "chord.h"
 #include "clef.h"
 #include "custos.h"
 #include "doc.h"
@@ -300,6 +301,10 @@ void CmmeInput::ReadEvents(pugi::xml_node eventsNode)
                     CreateKeySig(clefNode);
                 }
             }
+            else if (eventNode.select_node("./Note")) {
+                // Assuming that this only contains notes (and is a chord)
+                CreateChord(eventNode);
+            }
             else {
                 LogWarning("Unsupported event '%s'", name.c_str());
             }
@@ -423,6 +428,27 @@ void CmmeInput::CreateBarline(pugi::xml_node barlineNode)
     m_currentContainer->AddChild(barLine);
 }
 
+void CmmeInput::CreateChord(pugi::xml_node chordNode)
+{
+    assert(m_currentContainer);
+
+    Chord *chord = new Chord();
+    m_currentContainer->AddChild(chord);
+    m_currentContainer = chord;
+    pugi::xpath_node_set events = chordNode.select_nodes("./*");
+    for (pugi::xpath_node event : events) {
+        pugi::xml_node eventNode = event.node();
+        std::string name = eventNode.name();
+        if (name == "Note") {
+            CreateNote(eventNode);
+        }
+        else {
+            LogWarning("Unsupported chord component: '%s'", name.c_str());
+        }
+    }
+    m_currentContainer = m_currentContainer->GetParent();
+}
+
 void CmmeInput::CreateClef(pugi::xml_node clefNode)
 {
     static const std::map<std::string, data_CLEFSHAPE> shapeMap{

From f851aefc0526dec67293aa04bcd821ae012edf66 Mon Sep 17 00:00:00 2001
From: martha-thomae <thomaemartha@gmail.com>
Date: Thu, 19 Sep 2024 10:46:55 +0200
Subject: [PATCH 041/105] Add num and numbase in mensur

---
 src/iocmme.cpp | 18 +++++++++++++++---
 1 file changed, 15 insertions(+), 3 deletions(-)

diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index 584f244f501..c6f7d28cb34 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -594,7 +594,7 @@ void CmmeInput::CreateMensuration(pugi::xml_node mensurationNode)
     mensur->SetModusmaior(modusmaior);
 
     /// Mensuration: visual domain
-    /// Sign/MainSymbol to @sign
+    /// Mensuration/Sign/MainSymbol to @sign
     pugi::xml_node signNode = mensurationNode.child("Sign");
     std::string signValue = this->ChildAsString(signNode, "MainSymbol");
     if (signValue == "O") {
@@ -606,7 +606,7 @@ void CmmeInput::CreateMensuration(pugi::xml_node mensurationNode)
     else if (signValue != "") {
         LogWarning("Unsupported mesuration sign in CMME (not 'O' or 'C')");
     }
-    /// Sign/Dot to @dot
+    /// Mensuration/Sign/Dot to @dot
     pugi::xml_node dotNode = (signNode) ? signNode.child("Dot") : pugi::xml_node(NULL);
     mensur->SetDot(((dotNode) ? BOOLEAN_true : BOOLEAN_false));
     /// Sign/Strokes to @slash
@@ -614,7 +614,7 @@ void CmmeInput::CreateMensuration(pugi::xml_node mensurationNode)
     if (strokes != VRV_UNSET) {
         mensur->SetSlash(strokes);
     }
-    /// Sign/Orientation to @orient
+    /// Mensuration/Sign/Orientation to @orient
     std::string orientation = this->ChildAsString(signNode, "Orientation");
     if (orientation == "Reversed") {
         mensur->SetOrient(ORIENTATION_reversed);
@@ -628,6 +628,18 @@ void CmmeInput::CreateMensuration(pugi::xml_node mensurationNode)
     else if (orientation != "") {
         LogWarning("Unsupported mesuration orientation in CMME (not 'Reversed' or '90CW' or '90CCW')");
     }
+    /// Mensuration/Number/Num to @num and Number/Den to @numbase
+    pugi::xml_node numberNode = mensurationNode.child("Number");
+    if (numberNode != NULL) {
+        int numValue = this->ChildAsInt(numberNode, "Num");
+        int denValue = this->ChildAsInt(numberNode, "Den");
+        if (numValue != VRV_UNSET and numValue != 0) {
+            mensur->SetNum(numValue);
+        }
+        if (denValue != VRV_UNSET and denValue != 0) {
+            mensur->SetNumbase(denValue);
+        }
+    }
 
     this->ReadEditorialCommentary(mensurationNode, mensur);
 

From 76c0a2118a66a7cbbe9300ed35bdb39cf0d375b6 Mon Sep 17 00:00:00 2001
From: martha-thomae <thomaemartha@gmail.com>
Date: Thu, 19 Sep 2024 10:59:47 +0200
Subject: [PATCH 042/105] Add support for StaffLoc in mensur

---
 src/iocmme.cpp | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index c6f7d28cb34..e6a85fb7bb9 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -640,6 +640,11 @@ void CmmeInput::CreateMensuration(pugi::xml_node mensurationNode)
             mensur->SetNumbase(denValue);
         }
     }
+    /// Menusration/StaffLoc to @loc
+    int staffLoc = this->ChildAsInt(mensurationNode, "StaffLoc");
+    if (staffLoc != VRV_UNSET) {
+        mensur->SetLoc(staffLoc);
+    }
 
     this->ReadEditorialCommentary(mensurationNode, mensur);
 

From f4884ca5a7dc3ecf67638534276be51659271e3f Mon Sep 17 00:00:00 2001
From: martha-thomae <thomaemartha@gmail.com>
Date: Thu, 19 Sep 2024 11:25:49 +0200
Subject: [PATCH 043/105] Add lines for readibility

---
 src/iocmme.cpp | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index e6a85fb7bb9..756f54ff245 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -606,14 +606,17 @@ void CmmeInput::CreateMensuration(pugi::xml_node mensurationNode)
     else if (signValue != "") {
         LogWarning("Unsupported mesuration sign in CMME (not 'O' or 'C')");
     }
+
     /// Mensuration/Sign/Dot to @dot
     pugi::xml_node dotNode = (signNode) ? signNode.child("Dot") : pugi::xml_node(NULL);
     mensur->SetDot(((dotNode) ? BOOLEAN_true : BOOLEAN_false));
+
     /// Sign/Strokes to @slash
     int strokes = this->ChildAsInt(signNode, "Strokes");
     if (strokes != VRV_UNSET) {
         mensur->SetSlash(strokes);
     }
+
     /// Mensuration/Sign/Orientation to @orient
     std::string orientation = this->ChildAsString(signNode, "Orientation");
     if (orientation == "Reversed") {
@@ -628,6 +631,7 @@ void CmmeInput::CreateMensuration(pugi::xml_node mensurationNode)
     else if (orientation != "") {
         LogWarning("Unsupported mesuration orientation in CMME (not 'Reversed' or '90CW' or '90CCW')");
     }
+
     /// Mensuration/Number/Num to @num and Number/Den to @numbase
     pugi::xml_node numberNode = mensurationNode.child("Number");
     if (numberNode != NULL) {
@@ -640,6 +644,7 @@ void CmmeInput::CreateMensuration(pugi::xml_node mensurationNode)
             mensur->SetNumbase(denValue);
         }
     }
+
     /// Menusration/StaffLoc to @loc
     int staffLoc = this->ChildAsInt(mensurationNode, "StaffLoc");
     if (staffLoc != VRV_UNSET) {

From 6105ae750b1b6e41f2e08c9fbf3fbe281ef8d6db Mon Sep 17 00:00:00 2001
From: martha-thomae <thomaemartha@gmail.com>
Date: Thu, 19 Sep 2024 11:38:01 +0200
Subject: [PATCH 044/105] Make a map for orientation

---
 src/iocmme.cpp | 19 +++++++------------
 1 file changed, 7 insertions(+), 12 deletions(-)

diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index 756f54ff245..17829d2a5be 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -618,19 +618,14 @@ void CmmeInput::CreateMensuration(pugi::xml_node mensurationNode)
     }
 
     /// Mensuration/Sign/Orientation to @orient
+    static const std::map<std::string, data_ORIENTATION> orientationMap{
+        { "Reversed", ORIENTATION_reversed }, //
+        { "90CW", ORIENTATION_90CW }, //
+        { "90CCW", ORIENTATION_90CCW } //
+    };
     std::string orientation = this->ChildAsString(signNode, "Orientation");
-    if (orientation == "Reversed") {
-        mensur->SetOrient(ORIENTATION_reversed);
-    }
-    else if (orientation == "90CW") {
-        mensur->SetOrient(ORIENTATION_90CW);
-    }
-    else if (orientation == "90CCW") {
-        mensur->SetOrient(ORIENTATION_90CCW);
-    }
-    else if (orientation != "") {
-        LogWarning("Unsupported mesuration orientation in CMME (not 'Reversed' or '90CW' or '90CCW')");
-    }
+    data_ORIENTATION orient = orientationMap.contains(orientation) ? orientationMap.at(orientation) : ORIENTATION_NONE;
+    mensur->SetOrient(orient);
 
     /// Mensuration/Number/Num to @num and Number/Den to @numbase
     pugi::xml_node numberNode = mensurationNode.child("Number");

From 3931d221ab770eef490d2d1a93ded2b1eec01066 Mon Sep 17 00:00:00 2001
From: David Lewis <d.lewis@gold.ac.uk>
Date: Thu, 19 Sep 2024 12:20:48 +0200
Subject: [PATCH 045/105] Add dur info to chords on CMME import

---
 include/vrv/iocmme.h |  3 +++
 src/iocmme.cpp       | 12 ++++++++++++
 2 files changed, 15 insertions(+)

diff --git a/include/vrv/iocmme.h b/include/vrv/iocmme.h
index 14292361100..17b439fd740 100644
--- a/include/vrv/iocmme.h
+++ b/include/vrv/iocmme.h
@@ -9,6 +9,7 @@
 #define __VRV_IOCMME_H__
 
 #include <string>
+#include <utility>
 #include <vector>
 
 //----------------------------------------------------------------------------
@@ -106,6 +107,8 @@ class CmmeInput : public Input {
     std::vector<cmme::mensInfo> m_mensInfos;
     /** The mensural info for the current voice */
     cmme::mensInfo *m_mensInfo;
+    /** Latest note and its absolute duration (in minims) */
+    std::pair<Note *, double> m_lastNoteDuration;
 
     /** The number of voices as given in the general data */
     int m_numVoices;
diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index 17829d2a5be..d038eb77320 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -65,6 +65,7 @@ CmmeInput::CmmeInput(Doc *doc) : Input(doc)
     m_currentNote = NULL;
     m_isInSyllable = false;
     m_mensInfo = NULL;
+    m_lastNoteDuration = std::make_pair(nullptr, 0.0);
 }
 
 CmmeInput::~CmmeInput() {}
@@ -436,11 +437,21 @@ void CmmeInput::CreateChord(pugi::xml_node chordNode)
     m_currentContainer->AddChild(chord);
     m_currentContainer = chord;
     pugi::xpath_node_set events = chordNode.select_nodes("./*");
+    double longestDuration = 0;
     for (pugi::xpath_node event : events) {
         pugi::xml_node eventNode = event.node();
         std::string name = eventNode.name();
         if (name == "Note") {
             CreateNote(eventNode);
+            // If this is the longest note, we will need it to add duration
+            // info to chord
+            if ((m_lastNoteDuration.second > longestDuration)) {
+                longestDuration = m_lastNoteDuration.second;
+                Note *note = m_lastNoteDuration.first;
+                chord->SetDur(note->GetDur());
+                chord->SetNum(note->GetNum());
+                chord->SetNumbase(note->GetNumbase());
+            }
         }
         else {
             LogWarning("Unsupported chord component: '%s'", name.c_str());
@@ -697,6 +708,7 @@ void CmmeInput::CreateNote(pugi::xml_node noteNode)
     if (num != VRV_UNSET && numbase != VRV_UNSET) {
         note->SetNumbase(num);
         note->SetNum(numbase);
+        m_lastNoteDuration = std::make_pair(note, num / numbase);
     }
 
     int oct = this->ChildAsInt(noteNode, "OctaveNum");

From 983132c22da25278c061e2ff545582f9576972dc Mon Sep 17 00:00:00 2001
From: martha-thomae <thomaemartha@gmail.com>
Date: Thu, 19 Sep 2024 12:25:28 +0200
Subject: [PATCH 046/105] Add comment about how to get Num and Den into cmme

---
 src/iocmme.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index 17829d2a5be..e83e5db8e00 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -628,6 +628,9 @@ void CmmeInput::CreateMensuration(pugi::xml_node mensurationNode)
     mensur->SetOrient(orient);
 
     /// Mensuration/Number/Num to @num and Number/Den to @numbase
+    /// However, Number/Den cannot be entered in the CMME Editor.
+    /// It can only be added in the XML manually and imported into the CMME Editor,
+    /// where it won't render, but one can see it in the "Event Inspector."
     pugi::xml_node numberNode = mensurationNode.child("Number");
     if (numberNode != NULL) {
         int numValue = this->ChildAsInt(numberNode, "Num");

From 898e30c6e78d8d9f58a4beb10296fdb2489d82e2 Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Thu, 19 Sep 2024 14:15:15 +0200
Subject: [PATCH 047/105] Add CMME mention in `projectDesc`

---
 src/iocmme.cpp | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index 2b6f9316c1d..9e7974faaf0 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -77,6 +77,15 @@ bool CmmeInput::Import(const std::string &cmme)
     try {
         m_doc->Reset();
         m_doc->SetType(Raw);
+
+        // Genereate the header and add a comment to the project description
+        m_doc->GenerateMEIHeader(false);
+        pugi::xml_node projectDesc = m_doc->m_header.first_child().select_node("//projectDesc").node();
+        if (projectDesc) {
+            pugi::xml_node p1 = projectDesc.append_child("p");
+            p1.text().set("Converted from CMME XML");
+        }
+
         pugi::xml_document doc;
         doc.load_string(cmme.c_str(), (pugi::parse_comments | pugi::parse_default) & ~pugi::parse_eol);
         pugi::xml_node root = doc.first_child();

From 6ef0c962724a9fc60525a13229bc336b02dd2e09 Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Thu, 19 Sep 2024 14:19:11 +0200
Subject: [PATCH 048/105] Revert adding duration to chords

---
 include/vrv/iocmme.h |  2 --
 src/iocmme.cpp       | 24 +++++++++---------------
 2 files changed, 9 insertions(+), 17 deletions(-)

diff --git a/include/vrv/iocmme.h b/include/vrv/iocmme.h
index 17b439fd740..296d0179153 100644
--- a/include/vrv/iocmme.h
+++ b/include/vrv/iocmme.h
@@ -107,8 +107,6 @@ class CmmeInput : public Input {
     std::vector<cmme::mensInfo> m_mensInfos;
     /** The mensural info for the current voice */
     cmme::mensInfo *m_mensInfo;
-    /** Latest note and its absolute duration (in minims) */
-    std::pair<Note *, double> m_lastNoteDuration;
 
     /** The number of voices as given in the general data */
     int m_numVoices;
diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index 9e7974faaf0..32b00a71b66 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -65,7 +65,6 @@ CmmeInput::CmmeInput(Doc *doc) : Input(doc)
     m_currentNote = NULL;
     m_isInSyllable = false;
     m_mensInfo = NULL;
-    m_lastNoteDuration = std::make_pair(nullptr, 0.0);
 }
 
 CmmeInput::~CmmeInput() {}
@@ -452,15 +451,6 @@ void CmmeInput::CreateChord(pugi::xml_node chordNode)
         std::string name = eventNode.name();
         if (name == "Note") {
             CreateNote(eventNode);
-            // If this is the longest note, we will need it to add duration
-            // info to chord
-            if ((m_lastNoteDuration.second > longestDuration)) {
-                longestDuration = m_lastNoteDuration.second;
-                Note *note = m_lastNoteDuration.first;
-                chord->SetDur(note->GetDur());
-                chord->SetNum(note->GetNum());
-                chord->SetNumbase(note->GetNumbase());
-            }
         }
         else {
             LogWarning("Unsupported chord component: '%s'", name.c_str());
@@ -720,7 +710,6 @@ void CmmeInput::CreateNote(pugi::xml_node noteNode)
     if (num != VRV_UNSET && numbase != VRV_UNSET) {
         note->SetNumbase(num);
         note->SetNum(numbase);
-        m_lastNoteDuration = std::make_pair(note, num / numbase);
     }
 
     int oct = this->ChildAsInt(noteNode, "OctaveNum");
@@ -782,10 +771,15 @@ void CmmeInput::CreateNote(pugi::xml_node noteNode)
         data_LIGATUREFORM form = (lig == "Obliqua") ? LIGATUREFORM_obliqua : LIGATUREFORM_recta;
         // First note of the ligature, create the ligature element
         if (!m_currentContainer->Is(LIGATURE)) {
-            Ligature *ligature = new Ligature();
-            ligature->SetForm(form);
-            m_currentContainer->AddChild(ligature);
-            m_currentContainer = ligature;
+            if (m_currentContainer->Is(CHORD)) {
+                LogWarning("Ligature within chord is not supported");
+            }
+            else {
+                Ligature *ligature = new Ligature();
+                ligature->SetForm(form);
+                m_currentContainer->AddChild(ligature);
+                m_currentContainer = ligature;
+            }
         }
         // Otherwise simply add the `@lig` to the note
         else {

From 0720247422370d71792a8229132b516a5443879b Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Thu, 19 Sep 2024 14:45:18 +0200
Subject: [PATCH 049/105] Align mensural chords looking at the duration of the
 notes

---
 src/layerelement.cpp | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/src/layerelement.cpp b/src/layerelement.cpp
index 78cae4a13e8..92d8abc1dd3 100644
--- a/src/layerelement.cpp
+++ b/src/layerelement.cpp
@@ -677,6 +677,18 @@ double LayerElement::GetAlignmentDuration(
         return 0.0;
     }
 
+    // Mensural chords are aligned looking at the duration of the notes
+    if (this->Is(CHORD) && IsMensuralType(notationType)) {
+        double duration = 0.0;
+        ListOfConstObjects notes = this->FindAllDescendantsByType(NOTE);
+        for (const Object *object : notes) {
+            const Note *note = vrv_cast<const Note *>(object);
+            double noteDuration = note->GetAlignmentDuration(mensur, meterSig, notGraceOnly, notationType);
+            duration = std::max(duration, noteDuration);
+        }
+        return duration;
+    }
+
     // Only resolve simple sameas links to avoid infinite recursion
     const LayerElement *sameas = dynamic_cast<const LayerElement *>(this->GetSameasLink());
     if (sameas && !sameas->HasSameasLink()) {

From 7f1eef2b37e3e339ba98d72a5d66c712b8d5b35e Mon Sep 17 00:00:00 2001
From: martha-thomae <thomaemartha@gmail.com>
Date: Thu, 19 Sep 2024 15:37:12 +0200
Subject: [PATCH 050/105] Add support for proportions Missing getNum and
 getNumbase methods for proport object in Verovio

---
 include/vrv/iocmme.h |  1 +
 src/iocmme.cpp       | 46 ++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 45 insertions(+), 2 deletions(-)

diff --git a/include/vrv/iocmme.h b/include/vrv/iocmme.h
index 17b439fd740..f9fdc6d674c 100644
--- a/include/vrv/iocmme.h
+++ b/include/vrv/iocmme.h
@@ -69,6 +69,7 @@ class CmmeInput : public Input {
     void CreateKeySig(pugi::xml_node keyNode);
     void CreateMensuration(pugi::xml_node mensurationNode);
     void CreateOriginalText(pugi::xml_node originalTextNode);
+    void CreateProport(pugi::xml_node proportNode);
     void CreateNote(pugi::xml_node noteNode);
     void CreateRest(pugi::xml_node restNode);
 
diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index 2b6f9316c1d..704b23116c2 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -36,6 +36,7 @@
 #include "measure.h"
 #include "mensur.h"
 #include "note.h"
+#include "proport.h"
 #include "rdg.h"
 #include "rest.h"
 #include "score.h"
@@ -316,6 +317,9 @@ void CmmeInput::ReadEvents(pugi::xml_node eventsNode)
         else if (name == "OriginalText") {
             CreateOriginalText(eventNode);
         }
+        else if (name == "Proportion") {
+            CreateProport(eventNode);
+        }
         else if (name == "Rest") {
             CreateRest(eventNode);
         }
@@ -622,7 +626,7 @@ void CmmeInput::CreateMensuration(pugi::xml_node mensurationNode)
     pugi::xml_node dotNode = (signNode) ? signNode.child("Dot") : pugi::xml_node(NULL);
     mensur->SetDot(((dotNode) ? BOOLEAN_true : BOOLEAN_false));
 
-    /// Sign/Strokes to @slash
+    /// Mensuration/Sign/Strokes to @slash
     int strokes = this->ChildAsInt(signNode, "Strokes");
     if (strokes != VRV_UNSET) {
         mensur->SetSlash(strokes);
@@ -654,7 +658,7 @@ void CmmeInput::CreateMensuration(pugi::xml_node mensurationNode)
         }
     }
 
-    /// Menusration/StaffLoc to @loc
+    /// Mensuration/StaffLoc to @loc
     int staffLoc = this->ChildAsInt(mensurationNode, "StaffLoc");
     if (staffLoc != VRV_UNSET) {
         mensur->SetLoc(staffLoc);
@@ -664,6 +668,24 @@ void CmmeInput::CreateMensuration(pugi::xml_node mensurationNode)
 
     m_currentContainer->AddChild(mensur);
 
+    /// Proportion part coming from CMME <TempoChange> in <MensInfo> in <Mensuration>. In this case, create an MEI
+    /// <proport> element that follows the MEI <mensuration> element and that contains the proport/@num and
+    /// proport/@numbase values of 'num' and 'den'
+    pugi::xml_node tempoChangeNode = mensInfo.child("TempoChange");
+    if (tempoChangeNode != NULL) {
+        Proport *proport = new Proport();
+        int numVal = this->ChildAsInt(tempoChangeNode, "Num");
+        int denVal = this->ChildAsInt(tempoChangeNode, "Den");
+        if (numVal != VRV_UNSET) {
+            proport->SetNum(numVal);
+        }
+        if (denVal != VRV_UNSET) {
+            proport->SetNumbase(denVal);
+        }
+        proport->SetType("cmme_tempo_change");
+        m_currentContainer->AddChild(proport);
+    }
+
     return;
 }
 
@@ -801,6 +823,26 @@ void CmmeInput::CreateOriginalText(pugi::xml_node originalTextNode)
     return;
 }
 
+void CmmeInput::CreateProport(pugi::xml_node proportNode)
+{
+    assert(m_currentContainer);
+
+    /// Proportion part coming from CMME <Proportion>. In this case, create an MEI <proport> element is created alone
+    /// (not following an MEI <mensuration> element)
+    Proport *proport = new Proport();
+    int numVal = this->ChildAsInt(proportNode, "Num");
+    int denVal = this->ChildAsInt(proportNode, "Den");
+    if (numVal != VRV_UNSET) {
+        proport->SetNum(numVal);
+    }
+    if (denVal != VRV_UNSET) {
+        proport->SetNumbase(denVal);
+    }
+    proport->SetType("cmme_proportion");
+    m_currentContainer->AddChild(proport);
+    return;
+}
+
 void CmmeInput::CreateRest(pugi::xml_node restNode)
 {
     assert(m_currentContainer);

From 85a1ba485aceaaf884f8f027395dd3080278f1d7 Mon Sep 17 00:00:00 2001
From: David Lewis <d.lewis@gold.ac.uk>
Date: Thu, 19 Sep 2024 15:51:58 +0200
Subject: [PATCH 051/105] Line and page ends from CMME

---
 include/vrv/iocmme.h |  1 +
 src/iocmme.cpp       | 19 +++++++++++++++++++
 2 files changed, 20 insertions(+)

diff --git a/include/vrv/iocmme.h b/include/vrv/iocmme.h
index 296d0179153..5390697f1e5 100644
--- a/include/vrv/iocmme.h
+++ b/include/vrv/iocmme.h
@@ -62,6 +62,7 @@ class CmmeInput : public Input {
 
     void CreateAccid(pugi::xml_node accidNode);
     void CreateBarline(pugi::xml_node barlineNode);
+    void CreateBreak(pugi::xml_node breakNode);
     void CreateChord(pugi::xml_node chordNode);
     void CreateClef(pugi::xml_node clefNode);
     void CreateCustos(pugi::xml_node custosNode);
diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index 32b00a71b66..17df6749bfa 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -26,6 +26,7 @@
 #include "custos.h"
 #include "doc.h"
 #include "dot.h"
+#include "genericlayerelement.h"
 #include "keyaccid.h"
 #include "keysig.h"
 #include "label.h"
@@ -290,6 +291,9 @@ void CmmeInput::ReadEvents(pugi::xml_node eventsNode)
         else if (name == "Dot") {
             CreateDot(eventNode);
         }
+	else if (name == "LineEnd") {
+	  CreateBreak(eventNode);
+	}
         else if (name == "Mensuration") {
             CreateMensuration(eventNode);
         }
@@ -437,6 +441,21 @@ void CmmeInput::CreateBarline(pugi::xml_node barlineNode)
     m_currentContainer->AddChild(barLine);
 }
 
+void CmmeInput::CreateBreak(pugi::xml_node breakNode)
+{
+    assert(m_currentContainer);
+
+    // This is either a system or page break (usually only 
+    // in one part, so not easy to visualise in score)
+    if (breakNode.select_node("./PageEnd")){
+        GenericLayerElement *pb = new GenericLayerElement("pb");
+        m_currentContainer->AddChild(pb);
+    } else {
+        GenericLayerElement *sb = new GenericLayerElement("sb");
+        m_currentContainer->AddChild(sb);
+    }
+}
+
 void CmmeInput::CreateChord(pugi::xml_node chordNode)
 {
     assert(m_currentContainer);

From 87ff9719f404dbf4b4fa1452fff43f54a0c4e846 Mon Sep 17 00:00:00 2001
From: martha-thomae <thomaemartha@gmail.com>
Date: Thu, 19 Sep 2024 16:00:43 +0200
Subject: [PATCH 052/105] Add support for 'Small' CMME element Co-authored-by:
 annplaksin <anna.plaksin@uni-paderborn.de>

---
 src/iocmme.cpp | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index b1a80a57757..fc511a61ceb 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -641,6 +641,14 @@ void CmmeInput::CreateMensuration(pugi::xml_node mensurationNode)
     data_ORIENTATION orient = orientationMap.contains(orientation) ? orientationMap.at(orientation) : ORIENTATION_NONE;
     mensur->SetOrient(orient);
 
+    /// Mensuration/Small to @fontsize=small (not yet rendered in Verovio).
+    /// In the long run, we should add @size to att.mensur.vis because we have @mensur.size for <staffDef>, see class
+    /// att.mensural.vis
+    pugi::xml_node smallNode = mensurationNode.child("Small");
+    if (smallNode != NULL) {
+        mensur->m_unsupported.push_back(std::make_pair("fontsize", "small"));
+    }
+
     /// Mensuration/Number/Num to @num and Number/Den to @numbase
     /// However, Number/Den cannot be entered in the CMME Editor.
     /// It can only be added in the XML manually and imported into the CMME Editor,

From ae226ac5c435091023654026966ed28e4a26d7b5 Mon Sep 17 00:00:00 2001
From: David Lewis <d.lewis@gold.ac.uk>
Date: Thu, 19 Sep 2024 16:06:09 +0200
Subject: [PATCH 053/105] Minor cleanup of CreateChord for CMME import

---
 src/iocmme.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index 17df6749bfa..c945645fd4f 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -464,7 +464,6 @@ void CmmeInput::CreateChord(pugi::xml_node chordNode)
     m_currentContainer->AddChild(chord);
     m_currentContainer = chord;
     pugi::xpath_node_set events = chordNode.select_nodes("./*");
-    double longestDuration = 0;
     for (pugi::xpath_node event : events) {
         pugi::xml_node eventNode = event.node();
         std::string name = eventNode.name();

From c81e251f7b835ed18ac30f72a73d6f4bd89325fa Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Thu, 19 Sep 2024 16:19:55 +0200
Subject: [PATCH 054/105] Enable content for generic layer elements

---
 include/vrv/genericlayerelement.h |  8 ++++++++
 src/iomei.cpp                     | 14 ++++++++++++++
 2 files changed, 22 insertions(+)

diff --git a/include/vrv/genericlayerelement.h b/include/vrv/genericlayerelement.h
index 2a422527511..6b93f50a142 100644
--- a/include/vrv/genericlayerelement.h
+++ b/include/vrv/genericlayerelement.h
@@ -39,6 +39,12 @@ class GenericLayerElement : public LayerElement {
      */
     std::string GetMEIName() const { return m_meiName; }
 
+    /**
+     * Return the MEI element original name
+     */
+    std::string GetContent() { return m_content; }
+    void SetContent(std::string content) { m_content = content; }
+
     //----------//
     // Functors //
     //----------//
@@ -58,6 +64,8 @@ class GenericLayerElement : public LayerElement {
     std::string m_className;
     /** The MEI element name */
     std::string m_meiName;
+    /** The MEI element content */
+    std::string m_content;
 
 public:
     //
diff --git a/src/iomei.cpp b/src/iomei.cpp
index 2fb82d8e41f..3ea22a2dc3f 100644
--- a/src/iomei.cpp
+++ b/src/iomei.cpp
@@ -2522,6 +2522,13 @@ void MEIOutput::WriteGenericLayerElement(pugi::xml_node currentNode, GenericLaye
 
     currentNode.set_name(element->GetMEIName().c_str());
 
+    // Reparse the original content stored as a string document
+    pugi::xml_document content;
+    content.load_string(element->GetContent().c_str());
+    for (pugi::xml_node child : content.first_child().children()) {
+        currentNode.append_copy(child);
+    }
+
     this->WriteLayerElement(currentNode, element);
 }
 
@@ -6656,6 +6663,13 @@ bool MEIInput::ReadGenericLayerElement(Object *parent, pugi::xml_node element)
     GenericLayerElement *vrvElement = new GenericLayerElement(element.name());
     this->ReadLayerElement(element, vrvElement);
 
+    // Store the content as a string document
+    pugi::xml_document content;
+    content.append_copy(element);
+    std::ostringstream oss;
+    content.save(oss);
+    vrvElement->SetContent(oss.str());
+
     parent->AddChild(vrvElement);
     this->ReadUnsupportedAttr(element, vrvElement);
     return true;

From 38d4d5da9ae674b3bb3246630434effb93c35aef Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Thu, 19 Sep 2024 16:32:37 +0200
Subject: [PATCH 055/105] Fix formatting

---
 src/iocmme.cpp | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index ccadfafb1d6..aafc23787b3 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -292,9 +292,9 @@ void CmmeInput::ReadEvents(pugi::xml_node eventsNode)
         else if (name == "Dot") {
             CreateDot(eventNode);
         }
-	else if (name == "LineEnd") {
-	  CreateBreak(eventNode);
-	}
+        else if (name == "LineEnd") {
+            CreateBreak(eventNode);
+        }
         else if (name == "Mensuration") {
             CreateMensuration(eventNode);
         }
@@ -449,12 +449,13 @@ void CmmeInput::CreateBreak(pugi::xml_node breakNode)
 {
     assert(m_currentContainer);
 
-    // This is either a system or page break (usually only 
+    // This is either a system or page break (usually only
     // in one part, so not easy to visualise in score)
-    if (breakNode.select_node("./PageEnd")){
+    if (breakNode.select_node("./PageEnd")) {
         GenericLayerElement *pb = new GenericLayerElement("pb");
         m_currentContainer->AddChild(pb);
-    } else {
+    }
+    else {
         GenericLayerElement *sb = new GenericLayerElement("sb");
         m_currentContainer->AddChild(sb);
     }

From 10e9a1fb4d3008be87995bd3c8a08db3553b61b5 Mon Sep 17 00:00:00 2001
From: martha-thomae <thomaemartha@gmail.com>
Date: Thu, 19 Sep 2024 16:35:00 +0200
Subject: [PATCH 056/105] Fix formatting

---
 src/iocmme.cpp | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index ccadfafb1d6..aafc23787b3 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -292,9 +292,9 @@ void CmmeInput::ReadEvents(pugi::xml_node eventsNode)
         else if (name == "Dot") {
             CreateDot(eventNode);
         }
-	else if (name == "LineEnd") {
-	  CreateBreak(eventNode);
-	}
+        else if (name == "LineEnd") {
+            CreateBreak(eventNode);
+        }
         else if (name == "Mensuration") {
             CreateMensuration(eventNode);
         }
@@ -449,12 +449,13 @@ void CmmeInput::CreateBreak(pugi::xml_node breakNode)
 {
     assert(m_currentContainer);
 
-    // This is either a system or page break (usually only 
+    // This is either a system or page break (usually only
     // in one part, so not easy to visualise in score)
-    if (breakNode.select_node("./PageEnd")){
+    if (breakNode.select_node("./PageEnd")) {
         GenericLayerElement *pb = new GenericLayerElement("pb");
         m_currentContainer->AddChild(pb);
-    } else {
+    }
+    else {
         GenericLayerElement *sb = new GenericLayerElement("sb");
         m_currentContainer->AddChild(sb);
     }

From 8536d5ff5dbd92b65240eb3ab4795f90cedfdf1b Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Thu, 19 Sep 2024 16:36:23 +0200
Subject: [PATCH 057/105] Add missing call to write durationratio in proport

---
 src/iomei.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/iomei.cpp b/src/iomei.cpp
index 3ea22a2dc3f..5eb935cdce0 100644
--- a/src/iomei.cpp
+++ b/src/iomei.cpp
@@ -2803,6 +2803,8 @@ void MEIOutput::WriteProport(pugi::xml_node currentNode, Proport *proport)
     assert(proport);
 
     this->WriteLayerElement(currentNode, proport);
+
+    proport->WriteDurationRatio(currentNode);
 }
 
 void MEIOutput::WriteQuilisma(pugi::xml_node currentNode, Quilisma *quilisma)

From 979e97528a6f441a4b022659d998b8a936b1e89e Mon Sep 17 00:00:00 2001
From: martha-thomae <thomaemartha@gmail.com>
Date: Thu, 19 Sep 2024 16:41:05 +0200
Subject: [PATCH 058/105] Add support for CMME NoScoreEffect

---
 src/iocmme.cpp | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index aafc23787b3..f3e3fed6d7d 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -668,6 +668,12 @@ void CmmeInput::CreateMensuration(pugi::xml_node mensurationNode)
         mensur->m_unsupported.push_back(std::make_pair("fontsize", "small"));
     }
 
+    /// Mesuration/NoScoreEffect to @type = cmme_no_score_effect
+    pugi::xml_node noScoreEffect = mensurationNode.child("NoScoreEffect");
+    if (noScoreEffect != NULL) {
+        mensur->SetType("cmme_no_score_effect");
+    }
+
     /// Mensuration/Number/Num to @num and Number/Den to @numbase
     /// However, Number/Den cannot be entered in the CMME Editor.
     /// It can only be added in the XML manually and imported into the CMME Editor,

From 589dbe343fd105534166a83c956c81dc968288c4 Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Thu, 19 Sep 2024 17:13:46 +0200
Subject: [PATCH 059/105] Remove drawing proportions

---
 src/view_mensural.cpp | 29 +----------------------------
 1 file changed, 1 insertion(+), 28 deletions(-)

diff --git a/src/view_mensural.cpp b/src/view_mensural.cpp
index 3bce2133234..f0db3da4a74 100644
--- a/src/view_mensural.cpp
+++ b/src/view_mensural.cpp
@@ -546,39 +546,12 @@ void View::DrawProportFigures(DeviceContext *dc, int x, int y, int num, int numB
 
 void View::DrawProport(DeviceContext *dc, LayerElement *element, Layer *layer, Staff *staff, Measure *measure)
 {
+    assert(element);
     assert(layer);
     assert(staff);
-    assert(dynamic_cast<Proport *>(element)); // Element must be a Proport"
-
-    int x1, x2, y1, y2;
-
-    Proport *proport = dynamic_cast<Proport *>(element);
 
     dc->StartGraphic(element, "", element->GetID());
 
-    int y = staff->GetDrawingY() - (m_doc->GetDrawingUnit(staff->m_drawingStaffSize) * 4);
-    int x = element->GetDrawingX();
-
-    x1 = x + 120;
-    x2 = x1 + 150; // ??TEST: JUST DRAW AN ARBITRARY RECTANGLE
-    y1 = y;
-    y2 = y + 50 + (50 * proport->GetNum());
-    // DrawFilledRectangle(dc,x1,y1,x2,y2);
-    this->DrawPartFilledRectangle(dc, x1, y1, x2, y2, 0);
-
-    if (proport->HasNum()) {
-        x = element->GetDrawingX();
-        // if (proport->GetSign() || proport->HasTempus())           // ??WHAT SHOULD THIS BE?
-        {
-            x += m_doc->GetDrawingUnit(staff->m_drawingStaffSize)
-                * 5; // step forward because we have a sign or a meter symbol
-        }
-        int numbase = proport->HasNumbase() ? proport->GetNumbase() : 0;
-        this->DrawProportFigures(dc, x,
-            staff->GetDrawingY() - m_doc->GetDrawingUnit(staff->m_drawingStaffSize) * (staff->m_drawingLines - 1),
-            proport->GetNum(), numbase, staff);
-    }
-
     dc->EndGraphic(element, this);
 }
 

From 759958046401f6c4f1179060cba5ed2a61acd78a Mon Sep 17 00:00:00 2001
From: David Lewis <d.lewis@gold.ac.uk>
Date: Thu, 19 Sep 2024 17:24:33 +0200
Subject: [PATCH 060/105] Add CMME Ellipsis as <gap>

---
 include/vrv/iocmme.h |  1 +
 src/iocmme.cpp       | 30 ++++++++++++++++++++++++------
 src/iomei.cpp        |  3 +++
 3 files changed, 28 insertions(+), 6 deletions(-)

diff --git a/include/vrv/iocmme.h b/include/vrv/iocmme.h
index ed8d809f8db..395843a5455 100644
--- a/include/vrv/iocmme.h
+++ b/include/vrv/iocmme.h
@@ -67,6 +67,7 @@ class CmmeInput : public Input {
     void CreateClef(pugi::xml_node clefNode);
     void CreateCustos(pugi::xml_node custosNode);
     void CreateDot(pugi::xml_node dotNode);
+    void CreateEllipsis();
     void CreateKeySig(pugi::xml_node keyNode);
     void CreateMensuration(pugi::xml_node mensurationNode);
     void CreateOriginalText(pugi::xml_node originalTextNode);
diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index ccadfafb1d6..542d4576474 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -292,9 +292,9 @@ void CmmeInput::ReadEvents(pugi::xml_node eventsNode)
         else if (name == "Dot") {
             CreateDot(eventNode);
         }
-	else if (name == "LineEnd") {
-	  CreateBreak(eventNode);
-	}
+        else if (name == "LineEnd") {
+            CreateBreak(eventNode);
+        }
         else if (name == "Mensuration") {
             CreateMensuration(eventNode);
         }
@@ -304,6 +304,13 @@ void CmmeInput::ReadEvents(pugi::xml_node eventsNode)
                 pugi::xml_node barlineNode = eventNode.select_node("./Barline").node();
                 CreateBarline(barlineNode);
             }
+            else if (eventNode.select_node("./Ellipsis")) {
+                CreateEllipsis();
+            }
+            else
+            {
+                LogWarning("Unsupported MiscItem content");
+            }
         }
         else if (name == "MultiEvent") {
             /// Assuming that a multievent contains a key signature, all events are key signatures
@@ -449,12 +456,13 @@ void CmmeInput::CreateBreak(pugi::xml_node breakNode)
 {
     assert(m_currentContainer);
 
-    // This is either a system or page break (usually only 
+    // This is either a system or page break (usually only
     // in one part, so not easy to visualise in score)
-    if (breakNode.select_node("./PageEnd")){
+    if (breakNode.select_node("./PageEnd")) {
         GenericLayerElement *pb = new GenericLayerElement("pb");
         m_currentContainer->AddChild(pb);
-    } else {
+    }
+    else {
         GenericLayerElement *sb = new GenericLayerElement("sb");
         m_currentContainer->AddChild(sb);
     }
@@ -553,6 +561,16 @@ void CmmeInput::CreateDot(pugi::xml_node dotNode)
     return;
 }
 
+void CmmeInput::CreateEllipsis()
+{
+    assert(m_currentContainer);
+
+    GenericLayerElement *gap = new GenericLayerElement("gap");
+    gap->SetType("cmme_ellipsis");
+    gap->m_unsupported.push_back(std::make_pair("reason", "incipit"));
+    m_currentContainer->AddChild(gap);
+}
+
 void CmmeInput::CreateKeySig(pugi::xml_node keyNode)
 {
     static const std::map<std::string, data_ACCIDENTAL_WRITTEN> shapeMap{
diff --git a/src/iomei.cpp b/src/iomei.cpp
index 3ea22a2dc3f..743031e326f 100644
--- a/src/iomei.cpp
+++ b/src/iomei.cpp
@@ -6270,6 +6270,9 @@ bool MEIInput::ReadLayerChildren(Object *parent, pugi::xml_node parentNode, Obje
         else if (elementName == "fTrem") {
             success = this->ReadFTrem(parent, xmlElement);
         }
+        else if (elementName == "gap") {
+            success = this->ReadGenericLayerElement(parent, xmlElement);
+        }
         else if (elementName == "graceGrp") {
             success = this->ReadGraceGrp(parent, xmlElement);
         }

From d5ffe5463b8765795043e7207af13aa7e2529a5c Mon Sep 17 00:00:00 2001
From: David Lewis <d.lewis@gold.ac.uk>
Date: Fri, 20 Sep 2024 09:27:09 +0200
Subject: [PATCH 061/105] Add support for CMME Lacuna (currently doesn't
 compile because the Supplied class lacks addChild

---
 include/vrv/iocmme.h |  1 +
 src/iocmme.cpp       | 28 ++++++++++++++++++++++++++--
 2 files changed, 27 insertions(+), 2 deletions(-)

diff --git a/include/vrv/iocmme.h b/include/vrv/iocmme.h
index 395843a5455..7df84ef55ee 100644
--- a/include/vrv/iocmme.h
+++ b/include/vrv/iocmme.h
@@ -69,6 +69,7 @@ class CmmeInput : public Input {
     void CreateDot(pugi::xml_node dotNode);
     void CreateEllipsis();
     void CreateKeySig(pugi::xml_node keyNode);
+    void CreateLacuna(pugi::xml_node lacunaNode);
     void CreateMensuration(pugi::xml_node mensurationNode);
     void CreateOriginalText(pugi::xml_node originalTextNode);
     void CreateProport(pugi::xml_node proportNode);
diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index 542d4576474..1b9c2d5be63 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -42,9 +42,11 @@
 #include "rest.h"
 #include "score.h"
 #include "section.h"
+#include "space.h"
 #include "staff.h"
 #include "staffdef.h"
 #include "staffgrp.h"
+#include "supplied.h"
 #include "syl.h"
 #include "text.h"
 #include "verse.h"
@@ -307,8 +309,11 @@ void CmmeInput::ReadEvents(pugi::xml_node eventsNode)
             else if (eventNode.select_node("./Ellipsis")) {
                 CreateEllipsis();
             }
-            else
-            {
+            else if (eventNode.select_node("./Lacuna")) {
+                pugi::xml_node lacunaNode = eventNode.select_node("./Lacuna").node();
+                CreateLacuna(lacunaNode);
+            }
+            else {
                 LogWarning("Unsupported MiscItem content");
             }
         }
@@ -619,6 +624,25 @@ void CmmeInput::CreateKeySig(pugi::xml_node keyNode)
     m_currentSignature->AddChild(keyAccid);
 }
 
+void CmmeInput::CreateLacuna(pugi::xml_node lacunaNode)
+{
+    // A lacuna is used in CMME to pad a part where
+    // the scribe's version is temporally incomplete.
+    // We use mei:space, but since this is not explicit
+    // in the source, we wrap it in mei:supplied
+    assert(m_currentContainer);
+    Space *space = new Space();
+    Supplied *supplied = new Supplied();
+    supplied->addChild(space);
+    int num;
+    int numbase;
+    data_DURATION duration = this->ReadDuration(lacunaNode, num, numbase);
+    space->SetDur(duration);
+    space->SetType("cmme_lacuna");
+    supplied->SetType("cmme_lacuna");
+    m_currentContainer->AddChild(supplied);
+}
+
 void CmmeInput::CreateMensuration(pugi::xml_node mensurationNode)
 {
     assert(m_currentContainer);

From a74f479dd8ee1ea6e58905c4056325adf8c3233c Mon Sep 17 00:00:00 2001
From: David Lewis <d.lewis@gold.ac.uk>
Date: Fri, 20 Sep 2024 09:34:45 +0200
Subject: [PATCH 062/105] Fix typo for CMME Lacuna

---
 src/iocmme.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index 7c6ab47ed30..7ea9ae55f2b 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -633,7 +633,7 @@ void CmmeInput::CreateLacuna(pugi::xml_node lacunaNode)
     assert(m_currentContainer);
     Space *space = new Space();
     Supplied *supplied = new Supplied();
-    supplied->addChild(space);
+    supplied->AddChild(space);
     int num;
     int numbase;
     data_DURATION duration = this->ReadDuration(lacunaNode, num, numbase);

From b688ee61edc2f3a1e56ddd5d1f8cc579f9b16276 Mon Sep 17 00:00:00 2001
From: David Lewis <d.lewis@gold.ac.uk>
Date: Fri, 20 Sep 2024 10:03:55 +0200
Subject: [PATCH 063/105] CMME Signum congruentiae (realised as @type)

---
 src/iocmme.cpp | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index 7ea9ae55f2b..21b376c5315 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -842,6 +842,12 @@ void CmmeInput::CreateNote(pugi::xml_node noteNode)
         note->AddChild(accid);
     }
 
+    if (noteNode.child("Signum")) {
+        // MEI currently lacks signum congruentiae, so we warn and set not type
+        LogWarning("Signum Congruentiae in CMME mapped to @type");
+        note->SetType("cmme_signum_congruentiae");
+    }
+
     if (noteNode.child("Stem")) {
         std::string dir = this->ChildAsString(noteNode.child("Stem"), "Dir");
         if (dir == "Barline") {
@@ -936,6 +942,12 @@ void CmmeInput::CreateRest(pugi::xml_node restNode)
 
     this->ReadEditorialCommentary(restNode, rest);
 
+    if (restNode.child("Signum")) {
+        // MEI currently lacks signum congruentiae, so we warn and set not type
+        LogWarning("Signum Congruentiae in CMME mapped to @type");
+        rest->SetType("cmme_signum_congruentiae");
+    }
+
     m_currentContainer->AddChild(rest);
 
     return;

From 1bb94ef75468311d6fc45d0fa5740d1c71908ce1 Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Fri, 20 Sep 2024 12:20:25 +0200
Subject: [PATCH 064/105] Fix post merge change

---
 src/layerelement.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/layerelement.cpp b/src/layerelement.cpp
index 075b549ed35..c2b77daac8c 100644
--- a/src/layerelement.cpp
+++ b/src/layerelement.cpp
@@ -684,7 +684,7 @@ double LayerElement::GetAlignmentDuration(
         ListOfConstObjects notes = this->FindAllDescendantsByType(NOTE);
         for (const Object *object : notes) {
             const Note *note = vrv_cast<const Note *>(object);
-            double noteDuration = note->GetAlignmentDuration(mensur, meterSig, notGraceOnly, notationType);
+            double noteDuration = note->GetAlignmentDuration(params, notGraceOnly, notationType);
             duration = std::max(duration, noteDuration);
         }
         return duration;

From b6e82eb800b26506e353b3c27e999326e493439e Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Fri, 20 Sep 2024 12:20:50 +0200
Subject: [PATCH 065/105] Reset mensuration to binary with cmme mensInfo is
 missing

---
 src/iocmme.cpp | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index f3e3fed6d7d..6631eef1f9a 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -614,6 +614,13 @@ void CmmeInput::CreateMensuration(pugi::xml_node mensurationNode)
         m_mensInfo->modusminor = this->ChildAsInt(mensInfo, "ModusMinor");
         m_mensInfo->modusmaior = this->ChildAsInt(mensInfo, "ModusMaior");
     }
+    // If there is no <MensInfo> then resets everything to binary
+    else {
+        m_mensInfo->prolatio = 2;
+        m_mensInfo->tempus = 2;
+        m_mensInfo->modusminor = 2;
+        m_mensInfo->modusmaior = 2;
+    }
 
     /// Mensuration: logical domain
     Mensur *mensur = new Mensur();

From f988cd204d8845c74709fd91842089f5af20be7c Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Fri, 20 Sep 2024 14:21:47 +0200
Subject: [PATCH 066/105] Add proport to AlignMeterParams

---
 include/vrv/alignfunctor.h | 6 ++++--
 src/alignfunctor.cpp       | 2 --
 src/layerelement.cpp       | 4 ----
 src/midifunctor.cpp        | 2 --
 4 files changed, 4 insertions(+), 10 deletions(-)

diff --git a/include/vrv/alignfunctor.h b/include/vrv/alignfunctor.h
index cd4dabf1757..e2257f49897 100644
--- a/include/vrv/alignfunctor.h
+++ b/include/vrv/alignfunctor.h
@@ -14,6 +14,7 @@ namespace vrv {
 
 class Mensur;
 class MeterSig;
+class Proport;
 
 //----------------------------------------------------------------------------
 // AlignmentParams
@@ -22,8 +23,9 @@ class MeterSig;
  * Regroup pointers to meterSig, mensur and proport objects
  */
 struct AlignMeterParams {
-    const MeterSig *meterSig;
-    const Mensur *mensur;
+    const MeterSig *meterSig = NULL;
+    const Mensur *mensur = NULL;
+    const Proport *proport = NULL;
 };
 
 //----------------------------------------------------------------------------
diff --git a/src/alignfunctor.cpp b/src/alignfunctor.cpp
index 655fcd2034f..2e6f0ae0887 100644
--- a/src/alignfunctor.cpp
+++ b/src/alignfunctor.cpp
@@ -41,8 +41,6 @@ AlignHorizontallyFunctor::AlignHorizontallyFunctor(Doc *doc) : DocFunctor(doc)
 {
     m_measureAligner = NULL;
     m_time = 0.0;
-    m_currentParams.mensur = NULL;
-    m_currentParams.meterSig = NULL;
     m_notationType = NOTATIONTYPE_cmn;
     m_scoreDefRole = SCOREDEF_NONE;
     m_isFirstMeasure = false;
diff --git a/src/layerelement.cpp b/src/layerelement.cpp
index c2b77daac8c..b69b2b48a51 100644
--- a/src/layerelement.cpp
+++ b/src/layerelement.cpp
@@ -782,8 +782,6 @@ double LayerElement::GetAlignmentDuration(
 double LayerElement::GetAlignmentDuration(bool notGraceOnly, data_NOTATIONTYPE notationType) const
 {
     AlignMeterParams params;
-    params.meterSig = NULL;
-    params.mensur = NULL;
     return this->GetAlignmentDuration(params, notGraceOnly, notationType);
 }
 
@@ -825,8 +823,6 @@ double LayerElement::GetContentAlignmentDuration(
 double LayerElement::GetContentAlignmentDuration(bool notGraceOnly, data_NOTATIONTYPE notationType) const
 {
     AlignMeterParams params;
-    params.meterSig = NULL;
-    params.mensur = NULL;
     return this->GetContentAlignmentDuration(params, notGraceOnly, notationType);
 }
 
diff --git a/src/midifunctor.cpp b/src/midifunctor.cpp
index 05f91a550da..7639723d726 100644
--- a/src/midifunctor.cpp
+++ b/src/midifunctor.cpp
@@ -44,8 +44,6 @@ InitOnsetOffsetFunctor::InitOnsetOffsetFunctor() : Functor()
 {
     m_currentScoreTime = 0.0;
     m_currentRealTimeSeconds = 0.0;
-    m_meterParams.mensur = NULL;
-    m_meterParams.meterSig = NULL;
     m_notationType = NOTATIONTYPE_cmn;
     m_currentTempo = MIDI_TEMPO;
 }

From ea1e9e9e51aef105af9be7df71ed7696d4c653cf Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Fri, 20 Sep 2024 17:15:25 +0200
Subject: [PATCH 067/105] Add utility to reduce a fraction

---
 include/vrv/vrv.h |  5 +++++
 src/vrv.cpp       | 36 ++++++++++++++++++++++++++++++++++++
 2 files changed, 41 insertions(+)

diff --git a/include/vrv/vrv.h b/include/vrv/vrv.h
index daec31198e2..e6104524c0b 100644
--- a/include/vrv/vrv.h
+++ b/include/vrv/vrv.h
@@ -71,6 +71,11 @@ bool IsValidDouble(const std::string &value);
  */
 bool IsDigits(const std::string &value);
 
+/**
+ * Utility to reduce are faction of two integers
+ */
+void Reduce(int &numerator, int &denominator);
+
 /**
  * Extract the ID from any URI
  */
diff --git a/src/vrv.cpp b/src/vrv.cpp
index 764e29370bf..c4f686dc492 100644
--- a/src/vrv.cpp
+++ b/src/vrv.cpp
@@ -253,6 +253,42 @@ bool IsDigits(const std::string &value)
     return std::regex_match(value, re);
 }
 
+// Function to compute the Greatest Common Divisor (GCD)
+int GCD(int a, int b)
+{
+    if (b == 0) {
+        return std::abs(a);
+    }
+    return GCD(b, a % b);
+}
+
+// Function to reduce the fraction
+void Reduce(int &numerator, int &denominator)
+{
+    // Handle cases with zero denominator or numerator
+    if ((denominator == 0) || (denominator == VRV_UNSET)) {
+        return;
+    }
+
+    if ((numerator == 0) || (denominator == VRV_UNSET)) {
+        denominator = 1; // A fraction with 0 numerator is 0/1
+        return;
+    }
+
+    // Get the greatest common divisor
+    int divisor = GCD(numerator, denominator);
+
+    // Divide numerator and denominator by GCD
+    numerator /= divisor;
+    denominator /= divisor;
+
+    // Ensure denominator is always positive
+    if (denominator < 0) {
+        numerator = -numerator;
+        denominator = -denominator;
+    }
+}
+
 std::string ExtractIDFragment(std::string refID)
 {
     size_t pos = refID.find_last_of("#");

From de74afbb7530e9eecbab865ad692e3633696e892 Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Fri, 20 Sep 2024 17:18:05 +0200
Subject: [PATCH 068/105] Store cumulated values in proportion

---
 include/vrv/proport.h |  8 ++++++++
 src/proport.cpp       | 24 ++++++++++++++++++++++++
 2 files changed, 32 insertions(+)

diff --git a/include/vrv/proport.h b/include/vrv/proport.h
index ad3db4c2f2b..8aa0c240ca4 100644
--- a/include/vrv/proport.h
+++ b/include/vrv/proport.h
@@ -34,6 +34,11 @@ class Proport : public LayerElement, public AttDurationRatio {
     std::string GetClassName() const override { return "Proport"; }
     ///@}
 
+    int GetCumulatedNum() const;
+    int GetCumulatedNumbase() const;
+
+    void Cumulate(const Proport *proport);
+
     /** Override the method since alignment is required */
     bool HasToBeAligned() const override { return true; }
 
@@ -52,6 +57,9 @@ class Proport : public LayerElement, public AttDurationRatio {
 public:
     //
 private:
+    /** the cumulated num and numbase */
+    int m_cumulatedNum;
+    int m_cumulatedNumbase;
 };
 
 } // namespace vrv
diff --git a/src/proport.cpp b/src/proport.cpp
index 0ee5336561e..614d2e08b37 100644
--- a/src/proport.cpp
+++ b/src/proport.cpp
@@ -32,6 +32,30 @@ void Proport::Reset()
 {
     LayerElement::Reset();
     this->ResetDurationRatio();
+
+    m_cumulatedNum = VRV_UNSET;
+    m_cumulatedNumbase = VRV_UNSET;
+}
+
+int Proport::GetCumulatedNum() const
+{
+    return (m_cumulatedNum != VRV_UNSET) ? m_cumulatedNum : this->GetNum();
+}
+
+int Proport::GetCumulatedNumbase() const
+{
+    return (m_cumulatedNumbase != VRV_UNSET) ? m_cumulatedNumbase : this->GetNumbase();
+}
+
+void Proport::Cumulate(const Proport *proport)
+{
+    // Unset values are not cumulated
+    if (proport->HasNum() && this->HasNum()) {
+        m_cumulatedNum = this->GetNum() * proport->GetCumulatedNum();
+    }
+    if (proport->HasNumbase() && this->HasNumbase()) {
+        m_cumulatedNumbase = this->GetNumbase() * proport->GetCumulatedNumbase();
+    }
 }
 
 FunctorCode Proport::Accept(Functor &functor)

From 710fe00fff619a5cf4e6dfdae06ff368c6ad44f5 Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Fri, 20 Sep 2024 17:19:24 +0200
Subject: [PATCH 069/105] Add proportion to drawing staffdef propagated values

---
 include/vrv/drawinginterface.h   |  6 ++++++
 include/vrv/layer.h              |  2 ++
 include/vrv/setscoredeffunctor.h |  1 +
 src/drawinginterface.cpp         |  8 ++++++++
 src/layer.cpp                    | 12 ++++++++++++
 src/setscoredeffunctor.cpp       |  9 +++++++++
 6 files changed, 38 insertions(+)

diff --git a/include/vrv/drawinginterface.h b/include/vrv/drawinginterface.h
index 32d2e88e253..79efbaa2fd7 100644
--- a/include/vrv/drawinginterface.h
+++ b/include/vrv/drawinginterface.h
@@ -14,6 +14,7 @@
 #include "mensur.h"
 #include "metersig.h"
 #include "metersiggrp.h"
+#include "proport.h"
 #include "vrvdef.h"
 
 namespace vrv {
@@ -260,6 +261,7 @@ class StaffDefDrawingInterface {
     void SetCurrentMeterSig(const MeterSig *meterSig);
     void SetCurrentMeterSigGrp(const MeterSigGrp *meterSigGrp);
     void AlternateCurrentMeterSig(const Measure *measure);
+    void SetCurrentProport(const Proport *proport);
     ///@}
 
     /**
@@ -277,6 +279,8 @@ class StaffDefDrawingInterface {
     const MeterSig *GetCurrentMeterSig() const { return &m_currentMeterSig; }
     MeterSigGrp *GetCurrentMeterSigGrp() { return &m_currentMeterSigGrp; }
     const MeterSigGrp *GetCurrentMeterSigGrp() const { return &m_currentMeterSigGrp; }
+    Proport *GetCurrentProport() { return &m_currentProport; }
+    const Proport *GetCurrentProport() const { return &m_currentProport; }
     ///@}
 
 private:
@@ -290,6 +294,8 @@ class StaffDefDrawingInterface {
     MeterSig m_currentMeterSig;
     /** The meter signature group */
     MeterSigGrp m_currentMeterSigGrp;
+    /** The proport */
+    Proport m_currentProport;
 
     /**
      *  @name Flags for indicating whether the clef, keysig and mensur needs to be drawn or not
diff --git a/include/vrv/layer.h b/include/vrv/layer.h
index 45fefba99a1..16aed1c9595 100644
--- a/include/vrv/layer.h
+++ b/include/vrv/layer.h
@@ -167,6 +167,8 @@ class Layer : public Object,
     const Mensur *GetCurrentMensur() const;
     MeterSig *GetCurrentMeterSig();
     const MeterSig *GetCurrentMeterSig() const;
+    Proport *GetCurrentProport();
+    const Proport *GetCurrentProport() const;
     ///@}
 
     void ResetStaffDefObjects();
diff --git a/include/vrv/setscoredeffunctor.h b/include/vrv/setscoredeffunctor.h
index 60e3d9af41d..6c4eaa18a1b 100644
--- a/include/vrv/setscoredeffunctor.h
+++ b/include/vrv/setscoredeffunctor.h
@@ -136,6 +136,7 @@ class ScoreDefSetCurrentFunctor : public DocFunctor {
     FunctorCode VisitMeasure(Measure *measure) override;
     FunctorCode VisitMensur(Mensur *mensur) override;
     FunctorCode VisitPage(Page *page) override;
+    FunctorCode VisitProport(Proport *proport) override;
     FunctorCode VisitScore(Score *score) override;
     FunctorCode VisitScoreDef(ScoreDef *scoreDef) override;
     FunctorCode VisitStaff(Staff *staff) override;
diff --git a/src/drawinginterface.cpp b/src/drawinginterface.cpp
index c843d0431fb..c9825ea1475 100644
--- a/src/drawinginterface.cpp
+++ b/src/drawinginterface.cpp
@@ -650,6 +650,14 @@ void StaffDefDrawingInterface::AlternateCurrentMeterSig(const Measure *measure)
     }
 }
 
+void StaffDefDrawingInterface::SetCurrentProport(const Proport *proport)
+{
+    if (proport) {
+        m_currentProport = *proport;
+        m_currentProport.CloneReset();
+    }
+}
+
 //----------------------------------------------------------------------------
 // StemmedDrawingInterface
 //----------------------------------------------------------------------------
diff --git a/src/layer.cpp b/src/layer.cpp
index 560e5642bb2..6e1ea6b1681 100644
--- a/src/layer.cpp
+++ b/src/layer.cpp
@@ -541,6 +541,18 @@ const MeterSig *Layer::GetCurrentMeterSig() const
     return staff->m_drawingStaffDef->GetCurrentMeterSig();
 }
 
+Proport *Layer::GetCurrentProport()
+{
+    return const_cast<Proport *>(std::as_const(*this).GetCurrentProport());
+}
+
+const Proport *Layer::GetCurrentProport() const
+{
+    const Staff *staff = vrv_cast<const Staff *>(this->GetFirstAncestor(STAFF));
+    assert(staff && staff->m_drawingStaffDef);
+    return staff->m_drawingStaffDef->GetCurrentProport();
+}
+
 void Layer::SetDrawingStaffDefValues(StaffDef *currentStaffDef)
 {
     if (!currentStaffDef) {
diff --git a/src/setscoredeffunctor.cpp b/src/setscoredeffunctor.cpp
index 29f6548c8ce..6bbab04cd80 100644
--- a/src/setscoredeffunctor.cpp
+++ b/src/setscoredeffunctor.cpp
@@ -230,6 +230,15 @@ FunctorCode ScoreDefSetCurrentFunctor::VisitPage(Page *page)
     return FUNCTOR_CONTINUE;
 }
 
+FunctorCode ScoreDefSetCurrentFunctor::VisitProport(Proport *proport)
+{
+    assert(m_currentStaffDef);
+    StaffDef *upcomingStaffDef = m_upcomingScoreDef.GetStaffDef(m_currentStaffDef->GetN());
+    assert(upcomingStaffDef);
+    upcomingStaffDef->SetCurrentProport(proport);
+    return FUNCTOR_CONTINUE;
+}
+
 FunctorCode ScoreDefSetCurrentFunctor::VisitScore(Score *score)
 {
     m_currentScore = score;

From 22cbf404ac083acbdf0cdf989481b2d8a6061954 Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Fri, 20 Sep 2024 17:20:06 +0200
Subject: [PATCH 070/105] Take proportion into account when aligning data

---
 include/vrv/alignfunctor.h      |  7 ++-----
 include/vrv/horizontalaligner.h |  1 +
 include/vrv/layerelement.h      |  2 +-
 src/alignfunctor.cpp            | 12 ++++++++++++
 src/layerelement.cpp            |  7 +++++++
 5 files changed, 23 insertions(+), 6 deletions(-)

diff --git a/include/vrv/alignfunctor.h b/include/vrv/alignfunctor.h
index e2257f49897..746fbcafe38 100644
--- a/include/vrv/alignfunctor.h
+++ b/include/vrv/alignfunctor.h
@@ -12,10 +12,6 @@
 
 namespace vrv {
 
-class Mensur;
-class MeterSig;
-class Proport;
-
 //----------------------------------------------------------------------------
 // AlignmentParams
 //----------------------------------------------------------------------------
@@ -25,7 +21,8 @@ class Proport;
 struct AlignMeterParams {
     const MeterSig *meterSig = NULL;
     const Mensur *mensur = NULL;
-    const Proport *proport = NULL;
+    // Not const since we are cumulating proportion
+    Proport *proport = NULL;
 };
 
 //----------------------------------------------------------------------------
diff --git a/include/vrv/horizontalaligner.h b/include/vrv/horizontalaligner.h
index 0ace9f9cbfe..7351c1f9ed5 100644
--- a/include/vrv/horizontalaligner.h
+++ b/include/vrv/horizontalaligner.h
@@ -42,6 +42,7 @@ enum AlignmentType {
     ALIGNMENT_KEYSIG,
     ALIGNMENT_MENSUR,
     ALIGNMENT_METERSIG,
+    ALIGNMENT_PROPORT,
     ALIGNMENT_DOT,
     ALIGNMENT_CUSTOS,
     ALIGNMENT_ACCID,
diff --git a/include/vrv/layerelement.h b/include/vrv/layerelement.h
index 80d8b26c541..881b5922e9b 100644
--- a/include/vrv/layerelement.h
+++ b/include/vrv/layerelement.h
@@ -20,7 +20,7 @@
 namespace vrv {
 
 class Alignment;
-class AlignMeterParams;
+struct AlignMeterParams;
 class Beam;
 class BeamElementCoord;
 class FTrem;
diff --git a/src/alignfunctor.cpp b/src/alignfunctor.cpp
index 2e6f0ae0887..6c146eacd8f 100644
--- a/src/alignfunctor.cpp
+++ b/src/alignfunctor.cpp
@@ -18,6 +18,7 @@
 #include "nc.h"
 #include "neume.h"
 #include "page.h"
+#include "proport.h"
 #include "rend.h"
 #include "rest.h"
 #include "runningelement.h"
@@ -51,6 +52,7 @@ FunctorCode AlignHorizontallyFunctor::VisitLayer(Layer *layer)
 {
     m_currentParams.mensur = layer->GetCurrentMensur();
     m_currentParams.meterSig = layer->GetCurrentMeterSig();
+    m_currentParams.proport = layer->GetCurrentProport();
 
     // We are starting a new layer, reset the time;
     // We set it to -1.0 for the scoreDef attributes since they have to be aligned before any timestamp event (-1.0)
@@ -232,6 +234,16 @@ FunctorCode AlignHorizontallyFunctor::VisitLayerElement(LayerElement *layerEleme
             type = ALIGNMENT_SCOREDEF_METERSIG;
         }
     }
+    else if (layerElement->Is(PROPORT)) {
+        // replace the current proport
+        const Proport *previous = (m_currentParams.proport) ? (m_currentParams.proport) : NULL;
+        m_currentParams.proport = vrv_cast<Proport *>(layerElement);
+        assert(m_currentParams.proport);
+        if (previous) {
+            m_currentParams.proport->Cumulate(previous);
+        }
+        type = ALIGNMENT_PROPORT;
+    }
     else if (layerElement->Is({ MULTIREST, MREST, MRPT })) {
         type = ALIGNMENT_FULLMEASURE;
     }
diff --git a/src/layerelement.cpp b/src/layerelement.cpp
index b69b2b48a51..9d12ffcab45 100644
--- a/src/layerelement.cpp
+++ b/src/layerelement.cpp
@@ -50,6 +50,7 @@
 #include "neume.h"
 #include "note.h"
 #include "page.h"
+#include "proport.h"
 #include "rest.h"
 #include "slur.h"
 #include "smufl.h"
@@ -699,6 +700,12 @@ double LayerElement::GetAlignmentDuration(
     if (this->HasInterface(INTERFACE_DURATION)) {
         int num = 1;
         int numbase = 1;
+
+        if (params.proport) {
+            if (params.proport->HasNum()) num *= params.proport->GetCumulatedNum();
+            if (params.proport->HasNumbase()) numbase *= params.proport->GetCumulatedNumbase();
+        }
+
         const Tuplet *tuplet = vrv_cast<const Tuplet *>(this->GetFirstAncestor(TUPLET, MAX_TUPLET_DEPTH));
         if (tuplet) {
             ListOfConstObjects objects;

From 7406a0944e29773c6a71f710b8b5c1fce2ae1369 Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Fri, 20 Sep 2024 17:48:30 +0200
Subject: [PATCH 071/105] Move cmme mensur@num/numbase to type

* It does  seem for Mensuration/Number/Num to be only visual
---
 src/iocmme.cpp | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index 6631eef1f9a..a887e52293f 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -689,12 +689,16 @@ void CmmeInput::CreateMensuration(pugi::xml_node mensurationNode)
     if (numberNode != NULL) {
         int numValue = this->ChildAsInt(numberNode, "Num");
         int denValue = this->ChildAsInt(numberNode, "Den");
+        std::string mensurType;
         if (numValue != VRV_UNSET and numValue != 0) {
-            mensur->SetNum(numValue);
+            // mensur->SetNum(numValue);
+            mensurType += StringFormat("cmme_mensur_num_%d", numValue);
         }
         if (denValue != VRV_UNSET and denValue != 0) {
-            mensur->SetNumbase(denValue);
+            // mensur->SetNumbase(denValue);
+            mensurType += StringFormat(" cmme_mensur_den_%d", denValue);
         }
+        mensur->SetType(mensurType);
     }
 
     /// Mensuration/StaffLoc to @loc

From f28f0ab225038e2af1f204272b4cb52fdf7ef179 Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Fri, 20 Sep 2024 17:49:04 +0200
Subject: [PATCH 072/105] Cumulate proportion in duration calculation in cmme

---
 include/vrv/iocmme.h |  2 ++
 src/iocmme.cpp       | 20 +++++++++++++++-----
 2 files changed, 17 insertions(+), 5 deletions(-)

diff --git a/include/vrv/iocmme.h b/include/vrv/iocmme.h
index ed8d809f8db..29b67aadff3 100644
--- a/include/vrv/iocmme.h
+++ b/include/vrv/iocmme.h
@@ -35,6 +35,8 @@ namespace cmme {
         int tempus = 2;
         int modusminor = 2;
         int modusmaior = 2;
+        int proportNum = 1;
+        int proportDen = 1;
     };
 
 } // namespace cmme
diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index a887e52293f..6c4b48dec65 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -873,6 +873,7 @@ void CmmeInput::CreateOriginalText(pugi::xml_node originalTextNode)
 void CmmeInput::CreateProport(pugi::xml_node proportNode)
 {
     assert(m_currentContainer);
+    assert(m_mensInfo);
 
     /// Proportion part coming from CMME <Proportion>. In this case, create an MEI <proport> element is created alone
     /// (not following an MEI <mensuration> element)
@@ -881,10 +882,15 @@ void CmmeInput::CreateProport(pugi::xml_node proportNode)
     int denVal = this->ChildAsInt(proportNode, "Den");
     if (numVal != VRV_UNSET) {
         proport->SetNum(numVal);
+        // Cumulated it
+        m_mensInfo->proportNum *= numVal;
     }
     if (denVal != VRV_UNSET) {
         proport->SetNumbase(denVal);
+        // Cumulated it
+        m_mensInfo->proportDen *= denVal;
     }
+    vrv::Reduce(m_mensInfo->proportNum, m_mensInfo->proportDen);
     proport->SetType("cmme_proportion");
     m_currentContainer->AddChild(proport);
     return;
@@ -968,6 +974,10 @@ data_DURATION CmmeInput::ReadDuration(pugi::xml_node durationNode, int &num, int
         num = this->ChildAsInt(durationNode.child("Length"), "Num");
         numbase = this->ChildAsInt(durationNode.child("Length"), "Den");
 
+        // Apply the proportion
+        num *= m_mensInfo->proportNum;
+        numbase *= m_mensInfo->proportDen;
+
         std::pair<int, int> ratio = { 1, 1 };
 
         if (type == "Maxima") {
@@ -992,11 +1002,11 @@ data_DURATION CmmeInput::ReadDuration(pugi::xml_node durationNode, int &num, int
             ratio.second = 8;
         }
 
-        if (ratio.first != num || ratio.second != numbase) {
-            num *= ratio.second;
-            numbase *= ratio.first;
-        }
-        else {
+        num *= ratio.second;
+        numbase *= ratio.first;
+        vrv::Reduce(numbase, num);
+
+        if (num == numbase) {
             num = VRV_UNSET;
             numbase = VRV_UNSET;
         }

From ae9d6ff86bb4c91e55ec2d0a451bc259991c52b1 Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Sat, 21 Sep 2024 10:53:18 +0200
Subject: [PATCH 073/105] Adjust the way proportions are applied

---
 src/durationinterface.cpp | 14 +++++++-------
 src/iocmme.cpp            | 10 +++++-----
 src/layerelement.cpp      |  5 +++--
 src/proport.cpp           |  3 +++
 4 files changed, 18 insertions(+), 14 deletions(-)

diff --git a/src/durationinterface.cpp b/src/durationinterface.cpp
index 27c4ec698e0..d8ffcbae7bc 100644
--- a/src/durationinterface.cpp
+++ b/src/durationinterface.cpp
@@ -111,8 +111,8 @@ double DurationInterface::GetInterfaceAlignmentMensuralDuration(int num, int num
             || ((this->GetDur() == DURATION_semibrevis) && (currentMensur->GetProlatio() == PROLATIO_2))
             || (this->GetDur() == DURATION_minima) || (this->GetDur() == DURATION_semiminima)
             || (this->GetDur() == DURATION_fusa) || (this->GetDur() == DURATION_semifusa)) {
-            num *= 2;
-            numBase *= 3;
+            num *= 3;
+            numBase *= 2;
         }
     }
     // imperfecta in perfect mensuration (three imperfectas in the place of the two original perfectas)
@@ -120,16 +120,16 @@ double DurationInterface::GetInterfaceAlignmentMensuralDuration(int num, int num
         if (((this->GetDur() == DURATION_longa) && (currentMensur->GetModusminor() != MODUSMINOR_2))
             || ((this->GetDur() == DURATION_brevis) && (currentMensur->GetTempus() != TEMPUS_2))
             || ((this->GetDur() == DURATION_semibrevis) && (currentMensur->GetProlatio() != PROLATIO_2))) {
-            num *= 3;
-            numBase *= 2;
+            num *= 2;
+            numBase *= 3;
         }
     }
     // altera, maior, or duplex
     else if (this->HasDurQuality()
         && (this->GetDurQuality() == DURQUALITY_mensural_altera || this->GetDurQuality() == DURQUALITY_mensural_maior
             || this->GetDurQuality() == DURQUALITY_mensural_duplex)) {
-        num *= 1;
-        numBase *= 2;
+        num *= 2;
+        numBase *= 1;
     } // Any other case (minor, perfecta in tempus perfectum, and imperfecta in tempus imperfectum) follows the
       // mensuration and has no @num and @numbase attributes
 
@@ -150,7 +150,7 @@ double DurationInterface::GetInterfaceAlignmentMensuralDuration(int num, int num
             duration /= (double)abs(currentMensur->GetTempus()) * (double)abs(currentMensur->GetProlatio()) * ratio;
             break;
     }
-    duration *= (double)numBase / (double)num;
+    duration *= (double)num / (double)numBase;
     // LogDebug("Duration %d; %d/%d; Alignment %f; Ratio %f", noteDur, num, numbase, duration, ratio);
     duration = durRound(duration);
     return duration;
diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index 6c4b48dec65..78962f238d0 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -774,8 +774,8 @@ void CmmeInput::CreateNote(pugi::xml_node noteNode)
     data_DURATION duration = this->ReadDuration(noteNode, num, numbase);
     note->SetDur(duration);
     if (num != VRV_UNSET && numbase != VRV_UNSET) {
-        note->SetNumbase(num);
-        note->SetNum(numbase);
+        note->SetNum(num);
+        note->SetNumbase(numbase);
     }
 
     int oct = this->ChildAsInt(noteNode, "OctaveNum");
@@ -906,8 +906,8 @@ void CmmeInput::CreateRest(pugi::xml_node restNode)
     data_DURATION duration = this->ReadDuration(restNode, num, numbase);
     rest->SetDur(duration);
     if (num != VRV_UNSET && numbase != VRV_UNSET) {
-        rest->SetNumbase(num);
-        rest->SetNum(numbase);
+        rest->SetNum(num);
+        rest->SetNumbase(numbase);
     }
 
     this->ReadEditorialCommentary(restNode, rest);
@@ -1004,7 +1004,7 @@ data_DURATION CmmeInput::ReadDuration(pugi::xml_node durationNode, int &num, int
 
         num *= ratio.second;
         numbase *= ratio.first;
-        vrv::Reduce(numbase, num);
+        vrv::Reduce(num, numbase);
 
         if (num == numbase) {
             num = VRV_UNSET;
diff --git a/src/layerelement.cpp b/src/layerelement.cpp
index 9d12ffcab45..df50215c52b 100644
--- a/src/layerelement.cpp
+++ b/src/layerelement.cpp
@@ -702,8 +702,9 @@ double LayerElement::GetAlignmentDuration(
         int numbase = 1;
 
         if (params.proport) {
-            if (params.proport->HasNum()) num *= params.proport->GetCumulatedNum();
-            if (params.proport->HasNumbase()) numbase *= params.proport->GetCumulatedNumbase();
+            // Proportion are applied reversly - higher ratio means shorter values
+            if (params.proport->HasNum()) numbase *= params.proport->GetCumulatedNum();
+            if (params.proport->HasNumbase()) num *= params.proport->GetCumulatedNumbase();
         }
 
         const Tuplet *tuplet = vrv_cast<const Tuplet *>(this->GetFirstAncestor(TUPLET, MAX_TUPLET_DEPTH));
diff --git a/src/proport.cpp b/src/proport.cpp
index 614d2e08b37..80ee2ad2f54 100644
--- a/src/proport.cpp
+++ b/src/proport.cpp
@@ -56,6 +56,9 @@ void Proport::Cumulate(const Proport *proport)
     if (proport->HasNumbase() && this->HasNumbase()) {
         m_cumulatedNumbase = this->GetNumbase() * proport->GetCumulatedNumbase();
     }
+    if ((m_cumulatedNum != VRV_UNSET) && (m_cumulatedNumbase != VRV_UNSET)) {
+        vrv::Reduce(m_cumulatedNum, m_cumulatedNumbase);
+    }
 }
 
 FunctorCode Proport::Accept(Functor &functor)

From ea48ada2572227b857da18ed118aa1bc9aaaf409 Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Mon, 23 Sep 2024 08:39:29 +0200
Subject: [PATCH 074/105] Revert changes in num / numbase proportion handling

---
 src/durationinterface.cpp | 14 +++++++-------
 src/iocmme.cpp            | 10 +++++-----
 src/layerelement.cpp      |  4 ++--
 3 files changed, 14 insertions(+), 14 deletions(-)

diff --git a/src/durationinterface.cpp b/src/durationinterface.cpp
index d8ffcbae7bc..27c4ec698e0 100644
--- a/src/durationinterface.cpp
+++ b/src/durationinterface.cpp
@@ -111,8 +111,8 @@ double DurationInterface::GetInterfaceAlignmentMensuralDuration(int num, int num
             || ((this->GetDur() == DURATION_semibrevis) && (currentMensur->GetProlatio() == PROLATIO_2))
             || (this->GetDur() == DURATION_minima) || (this->GetDur() == DURATION_semiminima)
             || (this->GetDur() == DURATION_fusa) || (this->GetDur() == DURATION_semifusa)) {
-            num *= 3;
-            numBase *= 2;
+            num *= 2;
+            numBase *= 3;
         }
     }
     // imperfecta in perfect mensuration (three imperfectas in the place of the two original perfectas)
@@ -120,16 +120,16 @@ double DurationInterface::GetInterfaceAlignmentMensuralDuration(int num, int num
         if (((this->GetDur() == DURATION_longa) && (currentMensur->GetModusminor() != MODUSMINOR_2))
             || ((this->GetDur() == DURATION_brevis) && (currentMensur->GetTempus() != TEMPUS_2))
             || ((this->GetDur() == DURATION_semibrevis) && (currentMensur->GetProlatio() != PROLATIO_2))) {
-            num *= 2;
-            numBase *= 3;
+            num *= 3;
+            numBase *= 2;
         }
     }
     // altera, maior, or duplex
     else if (this->HasDurQuality()
         && (this->GetDurQuality() == DURQUALITY_mensural_altera || this->GetDurQuality() == DURQUALITY_mensural_maior
             || this->GetDurQuality() == DURQUALITY_mensural_duplex)) {
-        num *= 2;
-        numBase *= 1;
+        num *= 1;
+        numBase *= 2;
     } // Any other case (minor, perfecta in tempus perfectum, and imperfecta in tempus imperfectum) follows the
       // mensuration and has no @num and @numbase attributes
 
@@ -150,7 +150,7 @@ double DurationInterface::GetInterfaceAlignmentMensuralDuration(int num, int num
             duration /= (double)abs(currentMensur->GetTempus()) * (double)abs(currentMensur->GetProlatio()) * ratio;
             break;
     }
-    duration *= (double)num / (double)numBase;
+    duration *= (double)numBase / (double)num;
     // LogDebug("Duration %d; %d/%d; Alignment %f; Ratio %f", noteDur, num, numbase, duration, ratio);
     duration = durRound(duration);
     return duration;
diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index 78962f238d0..6c4b48dec65 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -774,8 +774,8 @@ void CmmeInput::CreateNote(pugi::xml_node noteNode)
     data_DURATION duration = this->ReadDuration(noteNode, num, numbase);
     note->SetDur(duration);
     if (num != VRV_UNSET && numbase != VRV_UNSET) {
-        note->SetNum(num);
-        note->SetNumbase(numbase);
+        note->SetNumbase(num);
+        note->SetNum(numbase);
     }
 
     int oct = this->ChildAsInt(noteNode, "OctaveNum");
@@ -906,8 +906,8 @@ void CmmeInput::CreateRest(pugi::xml_node restNode)
     data_DURATION duration = this->ReadDuration(restNode, num, numbase);
     rest->SetDur(duration);
     if (num != VRV_UNSET && numbase != VRV_UNSET) {
-        rest->SetNum(num);
-        rest->SetNumbase(numbase);
+        rest->SetNumbase(num);
+        rest->SetNum(numbase);
     }
 
     this->ReadEditorialCommentary(restNode, rest);
@@ -1004,7 +1004,7 @@ data_DURATION CmmeInput::ReadDuration(pugi::xml_node durationNode, int &num, int
 
         num *= ratio.second;
         numbase *= ratio.first;
-        vrv::Reduce(num, numbase);
+        vrv::Reduce(numbase, num);
 
         if (num == numbase) {
             num = VRV_UNSET;
diff --git a/src/layerelement.cpp b/src/layerelement.cpp
index df50215c52b..32c060264b0 100644
--- a/src/layerelement.cpp
+++ b/src/layerelement.cpp
@@ -703,8 +703,8 @@ double LayerElement::GetAlignmentDuration(
 
         if (params.proport) {
             // Proportion are applied reversly - higher ratio means shorter values
-            if (params.proport->HasNum()) numbase *= params.proport->GetCumulatedNum();
-            if (params.proport->HasNumbase()) num *= params.proport->GetCumulatedNumbase();
+            if (params.proport->HasNum()) num *= params.proport->GetCumulatedNum();
+            if (params.proport->HasNumbase()) numbase *= params.proport->GetCumulatedNumbase();
         }
 
         const Tuplet *tuplet = vrv_cast<const Tuplet *>(this->GetFirstAncestor(TUPLET, MAX_TUPLET_DEPTH));

From 469a61b2bf09596e89a7e989f4e7f1f5af3c1111 Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Mon, 23 Sep 2024 10:43:01 +0200
Subject: [PATCH 075/105] Add `@dot` only when true

---
 src/iocmme.cpp | 13 +++++--------
 1 file changed, 5 insertions(+), 8 deletions(-)

diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index 6c4b48dec65..b2db55aaea9 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -648,8 +648,9 @@ void CmmeInput::CreateMensuration(pugi::xml_node mensurationNode)
     }
 
     /// Mensuration/Sign/Dot to @dot
-    pugi::xml_node dotNode = (signNode) ? signNode.child("Dot") : pugi::xml_node(NULL);
-    mensur->SetDot(((dotNode) ? BOOLEAN_true : BOOLEAN_false));
+    if (signNode.child("Dot")) {
+        mensur->SetDot(BOOLEAN_true);
+    }
 
     /// Mensuration/Sign/Strokes to @slash
     int strokes = this->ChildAsInt(signNode, "Strokes");
@@ -689,16 +690,12 @@ void CmmeInput::CreateMensuration(pugi::xml_node mensurationNode)
     if (numberNode != NULL) {
         int numValue = this->ChildAsInt(numberNode, "Num");
         int denValue = this->ChildAsInt(numberNode, "Den");
-        std::string mensurType;
         if (numValue != VRV_UNSET and numValue != 0) {
-            // mensur->SetNum(numValue);
-            mensurType += StringFormat("cmme_mensur_num_%d", numValue);
+            mensur->SetNum(numValue);
         }
         if (denValue != VRV_UNSET and denValue != 0) {
-            // mensur->SetNumbase(denValue);
-            mensurType += StringFormat(" cmme_mensur_den_%d", denValue);
+            mensur->SetNumbase(denValue);
         }
-        mensur->SetType(mensurType);
     }
 
     /// Mensuration/StaffLoc to @loc

From ded5957c7657621e735a269cb4ff7d0acf920d40 Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Mon, 23 Sep 2024 10:43:26 +0200
Subject: [PATCH 076/105] Treat mensur@num as visual only

---
 include/vrv/drawinginterface.h | 2 +-
 src/durationinterface.cpp      | 3 ---
 src/view_mensural.cpp          | 2 +-
 3 files changed, 2 insertions(+), 5 deletions(-)

diff --git a/include/vrv/drawinginterface.h b/include/vrv/drawinginterface.h
index 79efbaa2fd7..384c415c432 100644
--- a/include/vrv/drawinginterface.h
+++ b/include/vrv/drawinginterface.h
@@ -240,7 +240,7 @@ class StaffDefDrawingInterface {
     void SetDrawClef(bool drawClef) { m_drawClef = drawClef; }
     bool DrawKeySig() const { return (m_drawKeySig); }
     void SetDrawKeySig(bool drawKeySig) { m_drawKeySig = drawKeySig; }
-    bool DrawMensur() const { return (m_drawMensur && m_currentMensur.HasSign()); }
+    bool DrawMensur() const { return (m_drawMensur && (m_currentMensur.HasSign() || m_currentMensur.HasNum())); }
     void SetDrawMensur(bool drawMensur) { m_drawMensur = drawMensur; }
     bool DrawMeterSig() const
     {
diff --git a/src/durationinterface.cpp b/src/durationinterface.cpp
index 27c4ec698e0..fdde51deecd 100644
--- a/src/durationinterface.cpp
+++ b/src/durationinterface.cpp
@@ -133,9 +133,6 @@ double DurationInterface::GetInterfaceAlignmentMensuralDuration(int num, int num
     } // Any other case (minor, perfecta in tempus perfectum, and imperfecta in tempus imperfectum) follows the
       // mensuration and has no @num and @numbase attributes
 
-    if (currentMensur->HasNum()) num *= currentMensur->GetNum();
-    if (currentMensur->HasNumbase()) numBase *= currentMensur->GetNumbase();
-
     double ratio = 0.0;
     double duration = (double)DUR_MENSURAL_REF;
     switch (noteDur) {
diff --git a/src/view_mensural.cpp b/src/view_mensural.cpp
index f0db3da4a74..231caba9f0f 100644
--- a/src/view_mensural.cpp
+++ b/src/view_mensural.cpp
@@ -85,7 +85,7 @@ void View::DrawMensur(DeviceContext *dc, LayerElement *element, Layer *layer, St
     Mensur *mensur = vrv_cast<Mensur *>(element);
     assert(mensur);
 
-    if (!mensur->HasSign()) {
+    if (!mensur->HasSign() && !mensur->HasNum()) {
         // only react to visual attributes
         return;
     }

From 45ea4275a888f39fe6160843cd9cd6a935d7436f Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Mon, 23 Sep 2024 11:03:21 +0200
Subject: [PATCH 077/105] Update src/iocmme.cpp

---
 src/iocmme.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index 21b376c5315..b7e7dd74ef0 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -306,7 +306,7 @@ void CmmeInput::ReadEvents(pugi::xml_node eventsNode)
                 pugi::xml_node barlineNode = eventNode.select_node("./Barline").node();
                 CreateBarline(barlineNode);
             }
-            else if (eventNode.select_node("./Ellipsis")) {
+            else if (eventNode.child("Ellipsis")) {
                 CreateEllipsis();
             }
             else if (eventNode.select_node("./Lacuna")) {

From 7f25b38f371be2cd47fefdf6c59060c94423a59a Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Mon, 23 Sep 2024 11:03:55 +0200
Subject: [PATCH 078/105] Update src/iocmme.cpp

---
 src/iocmme.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index b7e7dd74ef0..3dd95928601 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -309,7 +309,7 @@ void CmmeInput::ReadEvents(pugi::xml_node eventsNode)
             else if (eventNode.child("Ellipsis")) {
                 CreateEllipsis();
             }
-            else if (eventNode.select_node("./Lacuna")) {
+            else if (eventNode.child("Lacuna")) {
                 pugi::xml_node lacunaNode = eventNode.select_node("./Lacuna").node();
                 CreateLacuna(lacunaNode);
             }

From f6b4461060d3bf1c27ad2924576aea6a9319a444 Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Mon, 23 Sep 2024 11:04:42 +0200
Subject: [PATCH 079/105] Update src/iocmme.cpp

---
 src/iocmme.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index 3dd95928601..91be35af73d 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -310,7 +310,6 @@ void CmmeInput::ReadEvents(pugi::xml_node eventsNode)
                 CreateEllipsis();
             }
             else if (eventNode.child("Lacuna")) {
-                pugi::xml_node lacunaNode = eventNode.select_node("./Lacuna").node();
                 CreateLacuna(lacunaNode);
             }
             else {

From dc67c93f6f01eedfe132d8fc4b03d869582915a6 Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Mon, 23 Sep 2024 11:05:35 +0200
Subject: [PATCH 080/105] Read all cmme variant versions

---
 src/iocmme.cpp | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index b2db55aaea9..bce74192d82 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -245,7 +245,18 @@ void CmmeInput::CreateLemOrRdg(pugi::xml_node lemOrRdgNode, bool isFirst)
     }
     lemOrRdg->m_visibility = (isFirst) ? Visible : Hidden;
 
-    if (lemOrRdg->Is(RDG)) lemOrRdg->SetLabel(versionId);
+    if (lemOrRdg->Is(RDG)) {
+        std::string label;
+        // Loop through the event lists
+        pugi::xpath_node_set variants = lemOrRdgNode.select_nodes("./VariantVersionID");
+        bool isFirst = true;
+        for (pugi::xpath_node variant : variants) {
+            if (!isFirst) label += "; ";
+            label += this->AsString(variant.node());
+            isFirst = false;
+        }
+        lemOrRdg->SetLabel(label);
+    }
 
     if (lemOrRdgNode.child("Error")) {
         lemOrRdg->SetType("Error");

From 556263bf2fb2065e0a65cd997d570edbd2ac018a Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Mon, 23 Sep 2024 11:21:23 +0200
Subject: [PATCH 081/105] Post-merge fix in cmme import

---
 src/iocmme.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index 5b888513e36..d751c144a6c 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -321,7 +321,7 @@ void CmmeInput::ReadEvents(pugi::xml_node eventsNode)
                 CreateEllipsis();
             }
             else if (eventNode.child("Lacuna")) {
-                CreateLacuna(lacunaNode);
+                CreateLacuna(eventNode.child("Lacuna"));
             }
             else {
                 LogWarning("Unsupported MiscItem content");

From d799c73f70566cf4d00e7c6d49f1e475e34dbc8a Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Mon, 23 Sep 2024 11:41:43 +0200
Subject: [PATCH 082/105] Use local variables for clarifying cmme duration
 processing

---
 src/iocmme.cpp | 31 ++++++++++++++++++++-----------
 1 file changed, 20 insertions(+), 11 deletions(-)

diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index d751c144a6c..92cea2b092c 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -822,8 +822,8 @@ void CmmeInput::CreateNote(pugi::xml_node noteNode)
     data_DURATION duration = this->ReadDuration(noteNode, num, numbase);
     note->SetDur(duration);
     if (num != VRV_UNSET && numbase != VRV_UNSET) {
-        note->SetNumbase(num);
-        note->SetNum(numbase);
+        note->SetNum(num);
+        note->SetNumbase(numbase);
     }
 
     int oct = this->ChildAsInt(noteNode, "OctaveNum");
@@ -960,8 +960,8 @@ void CmmeInput::CreateRest(pugi::xml_node restNode)
     data_DURATION duration = this->ReadDuration(restNode, num, numbase);
     rest->SetDur(duration);
     if (num != VRV_UNSET && numbase != VRV_UNSET) {
-        rest->SetNumbase(num);
-        rest->SetNum(numbase);
+        rest->SetNum(num);
+        rest->SetNumbase(numbase);
     }
 
     this->ReadEditorialCommentary(restNode, rest);
@@ -1031,12 +1031,16 @@ data_DURATION CmmeInput::ReadDuration(pugi::xml_node durationNode, int &num, int
     numbase = VRV_UNSET;
 
     if (durationNode.child("Length")) {
-        num = this->ChildAsInt(durationNode.child("Length"), "Num");
-        numbase = this->ChildAsInt(durationNode.child("Length"), "Den");
+        int cmmeNum = this->ChildAsInt(durationNode.child("Length"), "Num");
+        int cmmeDen = this->ChildAsInt(durationNode.child("Length"), "Den");
+
+        if ((cmmeNum == VRV_UNSET) || (cmmeDen == VRV_UNSET)) {
+            return duration;
+        }
 
         // Apply the proportion
-        num *= m_mensInfo->proportNum;
-        numbase *= m_mensInfo->proportDen;
+        cmmeNum *= m_mensInfo->proportNum;
+        cmmeDen *= m_mensInfo->proportDen;
 
         std::pair<int, int> ratio = { 1, 1 };
 
@@ -1062,9 +1066,14 @@ data_DURATION CmmeInput::ReadDuration(pugi::xml_node durationNode, int &num, int
             ratio.second = 8;
         }
 
-        num *= ratio.second;
-        numbase *= ratio.first;
-        vrv::Reduce(numbase, num);
+        cmmeNum *= ratio.second;
+        cmmeDen *= ratio.first;
+
+        // MEI num and numabase are cmme den and num respectively
+        num = cmmeDen;
+        numbase = cmmeNum;
+
+        vrv::Reduce(num, numbase);
 
         if (num == numbase) {
             num = VRV_UNSET;

From 3ee90f8d5a400bc4ee8797c95ee2c5bf258a3dbf Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Wed, 2 Oct 2024 19:01:14 +0200
Subject: [PATCH 083/105] Initial refactoring for using fraction in alignment
 calculation

---
 include/vrv/alignfunctor.h             |   2 +-
 include/vrv/calcalignmentxposfunctor.h |   2 +-
 include/vrv/findlayerelementsfunctor.h |  12 +--
 include/vrv/horizontalaligner.h        |  76 ++++++++++++---
 include/vrv/layer.h                    |  10 +-
 include/vrv/layerelement.h             |  12 +--
 src/alignfunctor.cpp                   |  14 +--
 src/calcalignmentxposfunctor.cpp       |  12 +--
 src/findlayerelementsfunctor.cpp       |  18 ++--
 src/horizontalaligner.cpp              | 128 +++++++++++++++++++++----
 src/iopae.cpp                          |   2 +-
 src/layer.cpp                          |  22 +++--
 src/layerelement.cpp                   |  26 ++---
 src/measure.cpp                        |   4 +-
 src/midifunctor.cpp                    |  25 ++---
 src/view_element.cpp                   |   6 +-
 16 files changed, 260 insertions(+), 111 deletions(-)

diff --git a/include/vrv/alignfunctor.h b/include/vrv/alignfunctor.h
index a4fadfef313..2b9aa627331 100644
--- a/include/vrv/alignfunctor.h
+++ b/include/vrv/alignfunctor.h
@@ -75,7 +75,7 @@ class AlignHorizontallyFunctor : public DocFunctor {
     // The measureAligner
     MeasureAligner *m_measureAligner;
     // The time
-    double m_time;
+    Fraction m_time;
     // The current MeterSig, Mensur and Proport
     AlignMeterParams m_currentParams;
     // The current notation type
diff --git a/include/vrv/calcalignmentxposfunctor.h b/include/vrv/calcalignmentxposfunctor.h
index cce4a6e6ac4..5eda96449a8 100644
--- a/include/vrv/calcalignmentxposfunctor.h
+++ b/include/vrv/calcalignmentxposfunctor.h
@@ -61,7 +61,7 @@ class CalcAlignmentXPosFunctor : public DocFunctor {
     //
 private:
     // The previous time position
-    double m_previousTime;
+    Fraction m_previousTime;
     // The previous x rel position
     int m_previousXRel;
     // Duration of the longest note
diff --git a/include/vrv/findlayerelementsfunctor.h b/include/vrv/findlayerelementsfunctor.h
index 8d1ec87b1a1..7bdf1eeb657 100644
--- a/include/vrv/findlayerelementsfunctor.h
+++ b/include/vrv/findlayerelementsfunctor.h
@@ -41,7 +41,7 @@ class LayersInTimeSpanFunctor : public ConstFunctor {
     /*
      * Set the time and duration of the event
      */
-    void SetEvent(double time, double duration);
+    void SetEvent(const Fraction &time, const Fraction &duration);
 
     /*
      * Retrieve the search result
@@ -65,9 +65,9 @@ class LayersInTimeSpanFunctor : public ConstFunctor {
     //
 private:
     // The time of the event
-    double m_time;
+    Fraction m_time;
     // The duration of the event
-    double m_duration;
+    Fraction m_duration;
     // The layers (layerN) found
     std::set<int> m_layers;
     // The current time alignment parameters
@@ -99,7 +99,7 @@ class LayerElementsInTimeSpanFunctor : public ConstFunctor {
     /*
      * Set the time and duration of the event
      */
-    void SetEvent(double time, double duration);
+    void SetEvent(const Fraction &time, const Fraction &duration);
 
     /*
      * Consider all layers except the current one
@@ -126,9 +126,9 @@ class LayerElementsInTimeSpanFunctor : public ConstFunctor {
     //
 private:
     // The time of the event
-    double m_time;
+    Fraction m_time;
     // The duration of the event
-    double m_duration;
+    Fraction m_duration;
     // The list of layer elements found
     ListOfConstObjects m_elements;
     // The current time alignment parameters
diff --git a/include/vrv/horizontalaligner.h b/include/vrv/horizontalaligner.h
index 73f8e09c03d..ff6a97da218 100644
--- a/include/vrv/horizontalaligner.h
+++ b/include/vrv/horizontalaligner.h
@@ -12,6 +12,8 @@
 #include "object.h"
 #include "vrv.h"
 
+#include <numeric>
+
 namespace vrv {
 
 class Accid;
@@ -60,6 +62,53 @@ enum AlignmentType {
 #define BARLINE_REFERENCES -1
 #define TSTAMP_REFERENCES -2
 
+//----------------------------------------------------------------------------
+// Fraction
+//----------------------------------------------------------------------------
+
+class Fraction {
+
+public:
+    // Constructors
+    Fraction(int num = 0, int denom = 1);
+
+    /** Addition operator */
+    Fraction operator+(const Fraction &other) const;
+    /** Subtraction operator */
+    Fraction operator-(const Fraction &other) const;
+    /** Multiplication operator */
+    Fraction operator*(const Fraction &other) const;
+    /** Division operator */
+    Fraction operator/(const Fraction &other) const;
+
+    /** Equality operator */
+    bool operator==(const Fraction &other) const;
+    /** Less than operator */
+    bool operator<(const Fraction &other) const;
+    /** Less than or equal operator */
+    bool operator<=(const Fraction &other) const;
+    /** Greater than operator */
+    bool operator>(const Fraction &other) const;
+    /** Greater than or equal operator */
+    bool operator>=(const Fraction &other) const;
+
+    /** Convert fraction to a double */
+    double ToDouble() const;
+
+    /** Convert fraction to a string */
+    std::string ToString() const;
+
+private:
+    /** Reduce the fraction */
+    void Reduce();
+
+public:
+    //
+private:
+    int m_numerator;
+    int m_denominator;
+};
+
 //----------------------------------------------------------------------------
 // Alignment
 //----------------------------------------------------------------------------
@@ -75,7 +124,7 @@ class Alignment : public Object {
      */
     ///@{
     Alignment();
-    Alignment(double time, AlignmentType type = ALIGNMENT_DEFAULT);
+    Alignment(const Fraction &time, AlignmentType type = ALIGNMENT_DEFAULT);
     virtual ~Alignment();
     void Reset() override;
     ///@}
@@ -102,8 +151,8 @@ class Alignment : public Object {
      * @name Set and get the time value of the alignment
      */
     ///@{
-    void SetTime(double time) { m_time = time; }
-    double GetTime() const { return m_time; }
+    void SetTime(const Fraction &time) { m_time = time; }
+    Fraction GetTime() const { return m_time; }
     ///@}
 
     /**
@@ -188,7 +237,10 @@ class Alignment : public Object {
     /**
      * Debug message
      */
-    std::string LogDebugTreeMsg() override { return StringFormat("%d %f", this->GetXRel(), this->GetTime()); }
+    std::string LogDebugTreeMsg() override
+    {
+        return StringFormat("%d %s", this->GetXRel(), this->GetTime().ToString().c_str());
+    }
 
     //----------------//
     // Static methods //
@@ -210,7 +262,7 @@ class Alignment : public Object {
      * formula with parameters can come close and has other advantages.
      */
     static int HorizontalSpaceForDuration(
-        double intervalTime, int maxActualDur, double spacingLinear, double spacingNonLinear);
+        const Fraction &intervalTime, int maxActualDur, double spacingLinear, double spacingNonLinear);
 
     //----------//
     // Functors //
@@ -247,7 +299,7 @@ class Alignment : public Object {
      * Stores the time at which the alignment occur.
      * It is set by the AlignHorizontallyFunctor.
      */
-    double m_time;
+    Fraction m_time;
     /**
      * Defines the type of alignment (see the AlignmentType enum).
      * We have different types because we want some events occuring at the same
@@ -384,8 +436,8 @@ class HorizontalAligner : public Object {
      * If not, return in idx the position where it needs to be inserted (-1 if it is the end)
      */
     ///@{
-    Alignment *SearchAlignmentAtTime(double time, AlignmentType type, int &idx);
-    const Alignment *SearchAlignmentAtTime(double time, AlignmentType type, int &idx) const;
+    Alignment *SearchAlignmentAtTime(const Fraction &time, AlignmentType type, int &idx);
+    const Alignment *SearchAlignmentAtTime(const Fraction &time, AlignmentType type, int &idx) const;
     ///@}
 
     /**
@@ -430,19 +482,19 @@ class MeasureAligner : public HorizontalAligner {
      * The alignment object is added if not found.
      * The maximum time position is also adjusted accordingly for end barline positioning
      */
-    Alignment *GetAlignmentAtTime(double time, AlignmentType type);
+    Alignment *GetAlignmentAtTime(const Fraction &time, AlignmentType type);
 
     /**
      * Keep the maximum time of the measure.
      * This corresponds to the whole duration of the measure and
      * should be the same for all staves/layers.
      */
-    void SetMaxTime(double time);
+    void SetMaxTime(const Fraction &time);
 
     /**
      * Return the max time of the measure (i.e., the right measure alignment time)
      */
-    double GetMaxTime() const;
+    Fraction GetMaxTime() const;
 
     /**
      * @name Set and Get the non-justifiable margin (right and left scoreDefs)
@@ -569,7 +621,7 @@ class GraceAligner : public HorizontalAligner {
      * Retrieve the alignmnet of the type at that time.
      * The alignment object is added if not found.
      */
-    Alignment *GetAlignmentAtTime(double time, AlignmentType type);
+    Alignment *GetAlignmentAtTime(const Fraction &time, AlignmentType type);
 
     /**
      * Because the grace notes appear from left to right but need to be aligned
diff --git a/include/vrv/layer.h b/include/vrv/layer.h
index 45fefba99a1..c450619d864 100644
--- a/include/vrv/layer.h
+++ b/include/vrv/layer.h
@@ -130,8 +130,10 @@ class Layer : public Object,
      * Takes into account cross-staff situations: cross staff layers have negative N.
      */
     ///@{
-    std::set<int> GetLayersNInTimeSpan(double time, double duration, const Measure *measure, int staff) const;
-    int GetLayerCountInTimeSpan(double time, double duration, const Measure *measure, int staff) const;
+    std::set<int> GetLayersNInTimeSpan(
+        const Fraction &time, const Fraction &duration, const Measure *measure, int staff) const;
+    int GetLayerCountInTimeSpan(
+        const Fraction &time, const Fraction &duration, const Measure *measure, int staff) const;
     ///@}
 
     /**
@@ -150,9 +152,9 @@ class Layer : public Object,
      */
     ///@{
     ListOfObjects GetLayerElementsInTimeSpan(
-        double time, double duration, const Measure *measure, int staff, bool excludeCurrent);
+        const Fraction &time, const Fraction &duration, const Measure *measure, int staff, bool excludeCurrent);
     ListOfConstObjects GetLayerElementsInTimeSpan(
-        double time, double duration, const Measure *measure, int staff, bool excludeCurrent) const;
+        const Fraction &time, const Fraction &duration, const Measure *measure, int staff, bool excludeCurrent) const;
     ///@}
 
     /**
diff --git a/include/vrv/layerelement.h b/include/vrv/layerelement.h
index da9cdc01a0e..30f8e1323c1 100644
--- a/include/vrv/layerelement.h
+++ b/include/vrv/layerelement.h
@@ -14,12 +14,12 @@
 
 #include "atts_shared.h"
 #include "facsimileinterface.h"
+#include "horizontalaligner.h"
 #include "linkinginterface.h"
 #include "object.h"
 
 namespace vrv {
 
-class Alignment;
 class Beam;
 class BeamElementCoord;
 class FTrem;
@@ -262,26 +262,26 @@ class LayerElement : public Object,
     /**
      * Return the duration if the element has a DurationInterface.
      */
-    double GetAlignmentDuration(const AlignMeterParams &params, bool notGraceOnly = true,
+    Fraction GetAlignmentDuration(const AlignMeterParams &params, bool notGraceOnly = true,
         data_NOTATIONTYPE notationType = NOTATIONTYPE_cmn) const;
 
     /**
      * Return the duration if the element has a DurationInterface.
      * Shortcut assigning default values for AlignParameter.
      */
-    double GetAlignmentDuration(bool notGraceOnly = true, data_NOTATIONTYPE notationType = NOTATIONTYPE_cmn) const;
+    Fraction GetAlignmentDuration(bool notGraceOnly = true, data_NOTATIONTYPE notationType = NOTATIONTYPE_cmn) const;
 
     /**
      * Return the duration if the content of the layer element with a @sameas attribute.
      * Used only on beam, tuplet or ftrem have.
      */
-    double GetSameAsContentAlignmentDuration(const AlignMeterParams &params, bool notGraceOnly = true,
+    Fraction GetSameAsContentAlignmentDuration(const AlignMeterParams &params, bool notGraceOnly = true,
         data_NOTATIONTYPE notationType = NOTATIONTYPE_cmn) const;
 
-    double GetContentAlignmentDuration(const AlignMeterParams &params, bool notGraceOnly = true,
+    Fraction GetContentAlignmentDuration(const AlignMeterParams &params, bool notGraceOnly = true,
         data_NOTATIONTYPE notationType = NOTATIONTYPE_cmn) const;
 
-    double GetContentAlignmentDuration(
+    Fraction GetContentAlignmentDuration(
         bool notGraceOnly = true, data_NOTATIONTYPE notationType = NOTATIONTYPE_cmn) const;
 
     /**
diff --git a/src/alignfunctor.cpp b/src/alignfunctor.cpp
index 779518f07de..329b8b2b104 100644
--- a/src/alignfunctor.cpp
+++ b/src/alignfunctor.cpp
@@ -169,15 +169,15 @@ FunctorCode AlignHorizontallyFunctor::VisitLayerElement(LayerElement *layerEleme
             Alignment *alignment = firstNote->GetAlignment();
             layerElement->SetAlignment(alignment);
             alignment->AddLayerElementRef(layerElement);
-            double duration = layerElement->GetAlignmentDuration(m_currentParams, true, m_notationType);
-            m_time += duration;
+            Fraction duration = layerElement->GetAlignmentDuration(m_currentParams, true, m_notationType);
+            m_time = m_time + duration;
             return FUNCTOR_CONTINUE;
         }
     }
     // We do not align these (container). Any other?
     else if (layerElement->Is({ BEAM, LIGATURE, FTREM, TUPLET })) {
-        double duration = layerElement->GetSameAsContentAlignmentDuration(m_currentParams, true, m_notationType);
-        m_time += duration;
+        Fraction duration = layerElement->GetSameAsContentAlignmentDuration(m_currentParams, true, m_notationType);
+        m_time = m_time + duration;
         return FUNCTOR_CONTINUE;
     }
     else if (layerElement->Is(BARLINE)) {
@@ -304,12 +304,14 @@ FunctorCode AlignHorizontallyFunctor::VisitLayerElement(LayerElement *layerEleme
         type = ALIGNMENT_GRACENOTE;
     }
 
-    double duration = 0.0;
+    Fraction duration;
     // We have already an alignment with grace note children - skip this
     if (!layerElement->GetAlignment()) {
         // get the duration of the event
         duration = layerElement->GetAlignmentDuration(m_currentParams, true, m_notationType);
 
+        //LogDebug("duration %s %f", duration.ToString().c_str(), duration.ToDouble());
+
         // For timestamp, what we get from GetAlignmentDuration is actually the position of the timestamp
         // So use it as current time - we can do this because the timestamp loop is redirected from the measure
         // The time will be reset to 0.0 when starting a new layer anyway
@@ -350,7 +352,7 @@ FunctorCode AlignHorizontallyFunctor::VisitLayerElement(LayerElement *layerEleme
 
     if (!layerElement->Is(TIMESTAMP_ATTR)) {
         // increase the time position, but only when not a timestamp (it would actually do nothing)
-        m_time += duration;
+        m_time = m_time + duration;
     }
 
     return FUNCTOR_CONTINUE;
diff --git a/src/calcalignmentxposfunctor.cpp b/src/calcalignmentxposfunctor.cpp
index 780c076f5b9..cad0ee57f3d 100644
--- a/src/calcalignmentxposfunctor.cpp
+++ b/src/calcalignmentxposfunctor.cpp
@@ -36,7 +36,7 @@ FunctorCode CalcAlignmentXPosFunctor::VisitAlignment(Alignment *alignment)
     if (alignment->GetType() <= ALIGNMENT_MEASURE_LEFT_BARLINE) return FUNCTOR_CONTINUE;
 
     int intervalXRel = 0;
-    double intervalTime = alignment->GetTime() - m_previousTime;
+    Fraction intervalTime = alignment->GetTime() - m_previousTime;
 
     if (alignment->GetType() > ALIGNMENT_MEASURE_RIGHT_BARLINE) {
         intervalTime = 0.0;
@@ -68,22 +68,22 @@ FunctorCode CalcAlignmentXPosFunctor::VisitAlignment(Alignment *alignment)
     // alignments, then we now need to move them appropriately
     if (!m_timestamps.empty() && m_lastNonTimestamp) {
         int startXRel = m_lastNonTimestamp->GetXRel();
-        double startTime = m_lastNonTimestamp->GetTime();
-        double endTime = alignment->GetTime();
+        Fraction startTime = m_lastNonTimestamp->GetTime();
+        Fraction endTime = alignment->GetTime();
         // We have timestamp alignments between the left barline and the first beat. We need
         // to use the MeasureAligner::m_initialTstampDur to calculate the time (percentage) position
         if (m_lastNonTimestamp->GetType() == ALIGNMENT_MEASURE_LEFT_BARLINE) {
             startTime = m_measureAligner->GetInitialTstampDur();
         }
         // The duration since the last alignment and the current one
-        double duration = endTime - startTime;
+        Fraction duration = endTime - startTime;
         int space = alignment->GetXRel() - m_lastNonTimestamp->GetXRel();
         // For each timestamp alignment, move them proportionally to the space we currently have
         for (auto &tsAlignment : m_timestamps) {
             // Avoid division by zero (nothing to move with the alignment anyway
             if (duration == 0.0) break;
-            double percent = (tsAlignment->GetTime() - startTime) / duration;
-            tsAlignment->SetXRel(startXRel + space * percent);
+            Fraction percent = (tsAlignment->GetTime() - startTime) / duration;
+            tsAlignment->SetXRel(startXRel + space * percent.ToDouble());
         }
         m_timestamps.clear();
     }
diff --git a/src/findlayerelementsfunctor.cpp b/src/findlayerelementsfunctor.cpp
index 8c5b53fe942..0ebba8830fa 100644
--- a/src/findlayerelementsfunctor.cpp
+++ b/src/findlayerelementsfunctor.cpp
@@ -22,13 +22,11 @@ namespace vrv {
 
 LayersInTimeSpanFunctor::LayersInTimeSpanFunctor(const MeterSig *meterSig, const Mensur *mensur) : ConstFunctor()
 {
-    m_time = 0.0;
-    m_duration = 0.0;
     m_meterParams.meterSig = meterSig;
     m_meterParams.mensur = mensur;
 }
 
-void LayersInTimeSpanFunctor::SetEvent(double time, double duration)
+void LayersInTimeSpanFunctor::SetEvent(const Fraction &time, const Fraction &duration)
 {
     m_time = time;
     m_duration = duration;
@@ -51,15 +49,15 @@ FunctorCode LayersInTimeSpanFunctor::VisitLayerElement(const LayerElement *layer
         return FUNCTOR_CONTINUE;
     if (layerElement->Is(NOTE) && layerElement->GetParent()->Is(CHORD)) return FUNCTOR_CONTINUE;
 
-    double duration = layerElement->GetAlignmentDuration(m_meterParams);
-    double time = layerElement->GetAlignment()->GetTime();
+    Fraction duration = layerElement->GetAlignmentDuration(m_meterParams);
+    Fraction time = layerElement->GetAlignment()->GetTime();
 
     // The event is starting after the end of the element
-    if (time + duration <= m_time) {
+    if ((time + duration) <= m_time) {
         return FUNCTOR_CONTINUE;
     }
     // The element is starting after the event end - we can stop here
-    else if (time >= m_time + m_duration) {
+    else if (time >= (m_time + m_duration)) {
         return FUNCTOR_STOP;
     }
 
@@ -100,7 +98,7 @@ LayerElementsInTimeSpanFunctor::LayerElementsInTimeSpanFunctor(
     m_allLayersButCurrent = false;
 }
 
-void LayerElementsInTimeSpanFunctor::SetEvent(double time, double duration)
+void LayerElementsInTimeSpanFunctor::SetEvent(const Fraction &time, const Fraction &duration)
 {
     m_time = time;
     m_duration = duration;
@@ -124,11 +122,11 @@ FunctorCode LayerElementsInTimeSpanFunctor::VisitLayerElement(const LayerElement
 
     if (!layerElement->GetDurationInterface() || layerElement->Is({ MSPACE, SPACE })) return FUNCTOR_CONTINUE;
 
-    const double duration = !layerElement->GetFirstAncestor(CHORD)
+    Fraction duration = !layerElement->GetFirstAncestor(CHORD)
         ? layerElement->GetAlignmentDuration(m_meterParams)
         : vrv_cast<const Chord *>(layerElement->GetFirstAncestor(CHORD))->GetAlignmentDuration(m_meterParams);
 
-    const double time = layerElement->GetAlignment()->GetTime();
+    Fraction time = layerElement->GetAlignment()->GetTime();
 
     // The event is starting after the end of the element
     if ((time + duration) <= m_time) return FUNCTOR_CONTINUE;
diff --git a/src/horizontalaligner.cpp b/src/horizontalaligner.cpp
index f0b86cb2497..b6b2df1fd80 100644
--- a/src/horizontalaligner.cpp
+++ b/src/horizontalaligner.cpp
@@ -33,6 +33,97 @@
 
 namespace vrv {
 
+//----------------------------------------------------------------------------
+// Fraction
+//----------------------------------------------------------------------------
+
+Fraction::Fraction(int num, int denom) : m_numerator(num), m_denominator(denom)
+{
+    if (denom == 0) {
+        LogDebug("Denominator cannot be zero.");
+        denom = 1;
+    }
+    Reduce();
+}
+
+Fraction Fraction::operator+(const Fraction &other) const
+{
+    int num = m_numerator * other.m_denominator + other.m_numerator * m_denominator;
+    int denom = m_denominator * other.m_denominator;
+    return Fraction(num, denom);
+}
+
+Fraction Fraction::operator-(const Fraction &other) const
+{
+    int num = m_numerator * other.m_denominator - other.m_numerator * m_denominator;
+    int denom = m_denominator * other.m_denominator;
+    return Fraction(num, denom);
+}
+
+Fraction Fraction::operator*(const Fraction &other) const
+{
+    int num = m_numerator * other.m_numerator;
+    int denom = m_denominator * other.m_denominator;
+    return Fraction(num, denom);
+}
+
+Fraction Fraction::operator/(const Fraction &other) const
+{
+    if (other.m_numerator == 0) {
+        LogDebug("Cannot divide by zero.");
+        return *this;
+    }
+    int num = m_numerator * other.m_denominator;
+    int denom = m_denominator * other.m_numerator;
+    return Fraction(num, denom);
+}
+
+bool Fraction::operator==(const Fraction &other) const
+{
+    return (m_numerator == other.m_numerator) && (m_denominator == other.m_denominator);
+}
+
+bool Fraction::operator<(const Fraction &other) const
+{
+    return m_numerator * other.m_denominator < other.m_numerator * m_denominator;
+}
+
+bool Fraction::operator<=(const Fraction &other) const
+{
+    return m_numerator * other.m_denominator <= other.m_numerator * m_denominator;
+}
+
+bool Fraction::operator>(const Fraction &other) const
+{
+    return m_numerator * other.m_denominator > other.m_numerator * m_denominator;
+}
+
+bool Fraction::operator>=(const Fraction &other) const
+{
+    return m_numerator * other.m_denominator >= other.m_numerator * m_denominator;
+}
+
+double Fraction::ToDouble() const
+{
+    return static_cast<double>(m_numerator) / m_denominator;
+}
+
+std::string Fraction::ToString() const
+{
+    return StringFormat("%d/%d", m_numerator, m_denominator);
+}
+
+void Fraction::Reduce()
+{
+    if (m_denominator < 0) { // Keep the denominator positive
+        m_numerator = -m_numerator;
+        m_denominator = -m_denominator;
+    }
+    int gcdVal = std::gcd(abs(m_numerator), abs(m_denominator));
+    m_numerator /= gcdVal;
+    m_denominator /= gcdVal;
+}
+
 //----------------------------------------------------------------------------
 // HorizontalAligner
 //----------------------------------------------------------------------------
@@ -49,12 +140,12 @@ void HorizontalAligner::Reset()
     Object::Reset();
 }
 
-Alignment *HorizontalAligner::SearchAlignmentAtTime(double time, AlignmentType type, int &idx)
+Alignment *HorizontalAligner::SearchAlignmentAtTime(const Fraction &time, AlignmentType type, int &idx)
 {
     return const_cast<Alignment *>(std::as_const(*this).SearchAlignmentAtTime(time, type, idx));
 }
 
-const Alignment *HorizontalAligner::SearchAlignmentAtTime(double time, AlignmentType type, int &idx) const
+const Alignment *HorizontalAligner::SearchAlignmentAtTime(const Fraction &time, AlignmentType type, int &idx) const
 {
     idx = -1; // the index if we reach the end.
     const Alignment *alignment = NULL;
@@ -63,8 +154,8 @@ const Alignment *HorizontalAligner::SearchAlignmentAtTime(double time, Alignment
         alignment = vrv_cast<const Alignment *>(this->GetChild(i));
         assert(alignment);
 
-        double alignment_time = alignment->GetTime();
-        if (AreEqual(alignment_time, time)) {
+        Fraction alignment_time = alignment->GetTime();
+        if (alignment_time == time) {
             if (alignment->GetType() == type) {
                 return alignment;
             }
@@ -150,10 +241,10 @@ bool MeasureAligner::IsSupportedChild(Object *child)
     return true;
 }
 
-Alignment *MeasureAligner::GetAlignmentAtTime(double time, AlignmentType type)
+Alignment *MeasureAligner::GetAlignmentAtTime(const Fraction &time, AlignmentType type)
 {
     int idx; // the index if we reach the end.
-    time = durRound(time);
+    // time = durRound(time);
     Alignment *alignment = this->SearchAlignmentAtTime(time, type, idx);
     // we already have a alignment of the type at that time
     if (alignment != NULL) return alignment;
@@ -175,7 +266,7 @@ Alignment *MeasureAligner::GetAlignmentAtTime(double time, AlignmentType type)
     return newAlignment;
 }
 
-void MeasureAligner::SetMaxTime(double time)
+void MeasureAligner::SetMaxTime(const Fraction &time)
 {
     // we have to have a m_rightBarLineAlignment
     assert(m_rightBarLineAlignment);
@@ -194,7 +285,7 @@ void MeasureAligner::SetMaxTime(double time)
     }
 }
 
-double MeasureAligner::GetMaxTime() const
+Fraction MeasureAligner::GetMaxTime() const
 {
     // we have to have a m_rightBarLineAlignment
     assert(m_rightBarLineAlignment);
@@ -360,10 +451,10 @@ void GraceAligner::Reset()
     m_totalWidth = 0;
 }
 
-Alignment *GraceAligner::GetAlignmentAtTime(double time, AlignmentType type)
+Alignment *GraceAligner::GetAlignmentAtTime(const Fraction &time, AlignmentType type)
 {
     int idx; // the index if we reach the end.
-    time = round(time);
+    // time = round(time);
     Alignment *alignment = this->SearchAlignmentAtTime(time, type, idx);
     // we already have a alignment of the type at that time
     if (alignment != NULL) return alignment;
@@ -392,14 +483,14 @@ void GraceAligner::StackGraceElement(LayerElement *element)
 
 void GraceAligner::AlignStack()
 {
-    double time = 0.0;
+    Fraction time;
     for (int i = (int)m_graceStack.size(); i > 0; --i) {
         LayerElement *element = vrv_cast<LayerElement *>(m_graceStack.at(i - 1));
         assert(element);
         // get the duration of the event
-        double duration = element->GetAlignmentDuration(false);
+        Fraction duration = element->GetAlignmentDuration(false);
         // Time goes backward with grace notes
-        time -= duration;
+        time = time - duration;
         Alignment *alignment = this->GetAlignmentAtTime(time, ALIGNMENT_DEFAULT);
         element->SetGraceAlignment(alignment);
 
@@ -506,7 +597,7 @@ Alignment::Alignment() : Object(ALIGNMENT)
     this->Reset();
 }
 
-Alignment::Alignment(double time, AlignmentType type) : Object(ALIGNMENT)
+Alignment::Alignment(const Fraction &time, AlignmentType type) : Object(ALIGNMENT)
 {
     this->Reset();
     m_time = time;
@@ -518,7 +609,7 @@ void Alignment::Reset()
     Object::Reset();
 
     m_xRel = 0;
-    m_time = 0.0;
+    m_time = Fraction(0, 1);
     m_type = ALIGNMENT_DEFAULT;
 
     ClearGraceAligners();
@@ -736,13 +827,14 @@ std::pair<int, int> Alignment::GetAlignmentTopBottom() const
 }
 
 int Alignment::HorizontalSpaceForDuration(
-    double intervalTime, int maxActualDur, double spacingLinear, double spacingNonLinear)
+    const Fraction &intervalTime, int maxActualDur, double spacingLinear, double spacingNonLinear)
 {
+    double doubleIntervalTime = intervalTime.ToDouble();
     /* If the longest duration interval in the score is longer than semibreve, adjust spacing so
      that interval gets the space a semibreve would ordinarily get. */
-    if (maxActualDur < DUR_1) intervalTime /= pow(2.0, DUR_1 - maxActualDur);
+    if (maxActualDur < DUR_1) doubleIntervalTime /= pow(2.0, DUR_1 - maxActualDur);
 
-    return pow(intervalTime, spacingNonLinear) * spacingLinear * 10.0; // numbers are experimental constants
+    return pow(doubleIntervalTime, spacingNonLinear) * spacingLinear * 10.0; // numbers are experimental constants
 }
 
 FunctorCode Alignment::Accept(Functor &functor)
diff --git a/src/iopae.cpp b/src/iopae.cpp
index 7f7cf315322..5aa0bbd3fc8 100644
--- a/src/iopae.cpp
+++ b/src/iopae.cpp
@@ -521,7 +521,7 @@ void PAEOutput::WriteTuplet(Tuplet *tuplet)
 
     Staff *staff = tuplet->GetAncestorStaff();
 
-    double content = tuplet->GetContentAlignmentDuration(true, staff->m_drawingNotationType);
+    double content = tuplet->GetContentAlignmentDuration(true, staff->m_drawingNotationType).ToDouble();
     // content = DUR_MAX / 2^(dur - 2)
     int tupletDur = (content != 0.0) ? log2(DUR_MAX / content) + 2 : 4;
     // We should be looking for dotted values
diff --git a/src/layer.cpp b/src/layer.cpp
index 560e5642bb2..e76afa8c94d 100644
--- a/src/layer.cpp
+++ b/src/layer.cpp
@@ -351,8 +351,8 @@ data_STEMDIRECTION Layer::GetDrawingStemDir(const ArrayOfBeamElementCoords *coor
     // We are ignoring cross-staff situation here because this should not be called if we have one
     const Staff *staff = first->GetAncestorStaff();
 
-    double time = alignmentFirst->GetTime();
-    double duration = 0.0;
+    Fraction time = alignmentFirst->GetTime();
+    Fraction duration;
     // For the sake of counting number of layers consider only current measure. If first and last elements' layers are
     // different, take only time within current measure to run GetLayerCountInTimeSpan.
     const Measure *lastMeasure = vrv_cast<const Measure *>(last->GetFirstAncestor(MEASURE));
@@ -362,7 +362,7 @@ data_STEMDIRECTION Layer::GetDrawingStemDir(const ArrayOfBeamElementCoords *coor
     else {
         duration = measure->m_measureAligner.GetRightAlignment()->GetTime() - time;
     }
-    duration = durRound(duration);
+    // duration = durRound(duration);
 
     if (this->GetLayerCountInTimeSpan(time, duration, measure, staff->GetN()) < 2) {
         return STEMDIRECTION_NONE;
@@ -392,7 +392,8 @@ int Layer::GetLayerCountForTimeSpanOf(const LayerElement *element) const
     return static_cast<int>(this->GetLayersNForTimeSpanOf(element).size());
 }
 
-std::set<int> Layer::GetLayersNInTimeSpan(double time, double duration, const Measure *measure, int staff) const
+std::set<int> Layer::GetLayersNInTimeSpan(
+    const Fraction &time, const Fraction &duration, const Measure *measure, int staff) const
 {
     assert(measure);
 
@@ -409,7 +410,8 @@ std::set<int> Layer::GetLayersNInTimeSpan(double time, double duration, const Me
     return layersInTimeSpan.GetLayers();
 }
 
-int Layer::GetLayerCountInTimeSpan(double time, double duration, const Measure *measure, int staff) const
+int Layer::GetLayerCountInTimeSpan(
+    const Fraction &time, const Fraction &duration, const Measure *measure, int staff) const
 {
     return static_cast<int>(this->GetLayersNInTimeSpan(time, duration, measure, staff).size());
 }
@@ -430,8 +432,8 @@ ListOfConstObjects Layer::GetLayerElementsForTimeSpanOf(const LayerElement *elem
     const Measure *measure = vrv_cast<const Measure *>(this->GetFirstAncestor(MEASURE));
     assert(measure);
 
-    double time = 0.0;
-    double duration = 0.0;
+    Fraction time;
+    Fraction duration;
     const Alignment *alignment = element->GetAlignment();
     // Get duration and time if element has alignment
     if (alignment) {
@@ -449,7 +451,7 @@ ListOfConstObjects Layer::GetLayerElementsForTimeSpanOf(const LayerElement *elem
         if (!first || !last) return {};
 
         time = first->GetAlignment()->GetTime();
-        double lastTime = last->GetAlignment()->GetTime();
+        Fraction lastTime = last->GetAlignment()->GetTime();
         duration = lastTime - time + last->GetAlignmentDuration();
     }
     else {
@@ -462,7 +464,7 @@ ListOfConstObjects Layer::GetLayerElementsForTimeSpanOf(const LayerElement *elem
 }
 
 ListOfObjects Layer::GetLayerElementsInTimeSpan(
-    double time, double duration, const Measure *measure, int staff, bool excludeCurrent)
+    const Fraction &time, const Fraction &duration, const Measure *measure, int staff, bool excludeCurrent)
 {
     ListOfConstObjects elements
         = std::as_const(*this).GetLayerElementsInTimeSpan(time, duration, measure, staff, excludeCurrent);
@@ -473,7 +475,7 @@ ListOfObjects Layer::GetLayerElementsInTimeSpan(
 }
 
 ListOfConstObjects Layer::GetLayerElementsInTimeSpan(
-    double time, double duration, const Measure *measure, int staff, bool excludeCurrent) const
+    const Fraction &time, const Fraction &duration, const Measure *measure, int staff, bool excludeCurrent) const
 {
     assert(measure);
 
diff --git a/src/layerelement.cpp b/src/layerelement.cpp
index 454c91fd311..d7bd8b4cd16 100644
--- a/src/layerelement.cpp
+++ b/src/layerelement.cpp
@@ -671,11 +671,11 @@ int LayerElement::GetDrawingRadius(const Doc *doc, bool isInLigature) const
     return doc->GetGlyphWidth(code, staff->m_drawingStaffSize, this->GetDrawingCueSize()) / 2;
 }
 
-double LayerElement::GetAlignmentDuration(
+Fraction LayerElement::GetAlignmentDuration(
     const AlignMeterParams &params, bool notGraceOnly, data_NOTATIONTYPE notationType) const
 {
     if (this->IsGraceNote() && notGraceOnly) {
-        return 0.0;
+        return Fraction(0, 1);
     }
 
     // Only resolve simple sameas links to avoid infinite recursion
@@ -719,11 +719,11 @@ double LayerElement::GetAlignmentDuration(
                 return NEUME_SMALL_SPACE;
             }
         }
-        double durationValue = duration->GetInterfaceAlignmentDuration(num, numbase);
+        Fraction durationValue = duration->GetInterfaceAlignmentDuration(num, numbase);
         // With fTrem we need to divide the duration by two
         const FTrem *fTrem = vrv_cast<const FTrem *>(this->GetFirstAncestor(FTREM, MAX_FTREM_DEPTH));
         if (fTrem) {
-            durationValue /= 2.0;
+            durationValue = durationValue * Fraction(1, 2);
         }
         return durationValue;
     }
@@ -763,11 +763,11 @@ double LayerElement::GetAlignmentDuration(
         return (syllable->GetLast() == this) ? NEUME_MEDIUM_SPACE : NEUME_SMALL_SPACE;
     }
     else {
-        return 0.0;
+        return Fraction(0, 1);
     }
 }
 
-double LayerElement::GetAlignmentDuration(bool notGraceOnly, data_NOTATIONTYPE notationType) const
+Fraction LayerElement::GetAlignmentDuration(bool notGraceOnly, data_NOTATIONTYPE notationType) const
 {
     AlignMeterParams params;
     params.meterSig = NULL;
@@ -775,11 +775,11 @@ double LayerElement::GetAlignmentDuration(bool notGraceOnly, data_NOTATIONTYPE n
     return this->GetAlignmentDuration(params, notGraceOnly, notationType);
 }
 
-double LayerElement::GetSameAsContentAlignmentDuration(
+Fraction LayerElement::GetSameAsContentAlignmentDuration(
     const AlignMeterParams &params, bool notGraceOnly, data_NOTATIONTYPE notationType) const
 {
     if (!this->HasSameasLink() || !this->GetSameasLink()->Is({ BEAM, FTREM, TUPLET })) {
-        return 0.0;
+        return Fraction(0, 1);
     }
 
     const LayerElement *sameas = vrv_cast<const LayerElement *>(this->GetSameasLink());
@@ -788,14 +788,14 @@ double LayerElement::GetSameAsContentAlignmentDuration(
     return sameas->GetContentAlignmentDuration(params, notGraceOnly, notationType);
 }
 
-double LayerElement::GetContentAlignmentDuration(
+Fraction LayerElement::GetContentAlignmentDuration(
     const AlignMeterParams &params, bool notGraceOnly, data_NOTATIONTYPE notationType) const
 {
     if (!this->Is({ BEAM, FTREM, TUPLET })) {
-        return 0.0;
+        return Fraction(0, 1);
     }
 
-    double duration = 0.0;
+    Fraction duration;
 
     for (const Object *child : this->GetChildren()) {
         // Skip everything that does not have a duration interface and notes in chords
@@ -804,13 +804,13 @@ double LayerElement::GetContentAlignmentDuration(
         }
         const LayerElement *element = vrv_cast<const LayerElement *>(child);
         assert(element);
-        duration += element->GetAlignmentDuration(params, notGraceOnly, notationType);
+        duration = duration + element->GetAlignmentDuration(params, notGraceOnly, notationType);
     }
 
     return duration;
 }
 
-double LayerElement::GetContentAlignmentDuration(bool notGraceOnly, data_NOTATIONTYPE notationType) const
+Fraction LayerElement::GetContentAlignmentDuration(bool notGraceOnly, data_NOTATIONTYPE notationType) const
 {
     AlignMeterParams params;
     params.meterSig = NULL;
diff --git a/src/measure.cpp b/src/measure.cpp
index 13347082ca6..48bd70e42af 100644
--- a/src/measure.cpp
+++ b/src/measure.cpp
@@ -475,8 +475,8 @@ const Staff *Measure::GetBottomVisibleStaff() const
 int Measure::EnclosesTime(int time) const
 {
     int repeat = 1;
-    double timeDuration = m_measureAligner.GetRightAlignment()->GetTime() * static_cast<int>(DURATION_4) / DUR_MAX
-            * 60.0 / m_currentTempo * 1000.0
+    double timeDuration = m_measureAligner.GetRightAlignment()->GetTime().ToDouble() * static_cast<int>(DURATION_4)
+            / DUR_MAX * 60.0 / m_currentTempo * 1000.0
         + 0.5;
     std::vector<double>::const_iterator iter;
     for (iter = m_realTimeOffsetMilliseconds.begin(); iter != m_realTimeOffsetMilliseconds.end(); ++iter) {
diff --git a/src/midifunctor.cpp b/src/midifunctor.cpp
index 05f91a550da..45ea551835c 100644
--- a/src/midifunctor.cpp
+++ b/src/midifunctor.cpp
@@ -54,7 +54,7 @@ FunctorCode InitOnsetOffsetFunctor::VisitChordEnd(Chord *chord)
 {
     LayerElement *element = chord->ThisOrSameasLink();
 
-    double incrementScoreTime = element->GetAlignmentDuration(m_meterParams, true, m_notationType);
+    double incrementScoreTime = element->GetAlignmentDuration(m_meterParams, true, m_notationType).ToDouble();
     incrementScoreTime = incrementScoreTime / (DUR_MAX / DURATION_4);
     double realTimeIncrementSeconds = incrementScoreTime * 60.0 / m_currentTempo;
 
@@ -84,7 +84,7 @@ FunctorCode InitOnsetOffsetFunctor::VisitLayerElement(LayerElement *layerElement
     double incrementScoreTime;
 
     if (element->Is(REST) || element->Is(SPACE)) {
-        incrementScoreTime = element->GetAlignmentDuration(m_meterParams, true, m_notationType);
+        incrementScoreTime = element->GetAlignmentDuration(m_meterParams, true, m_notationType).ToDouble();
         incrementScoreTime = incrementScoreTime / (DUR_MAX / DURATION_4);
         // For rests to be possibly added to the timemap
         if (element->Is(REST)) {
@@ -111,13 +111,13 @@ FunctorCode InitOnsetOffsetFunctor::VisitLayerElement(LayerElement *layerElement
         // If the note has a @dur or a @dur.ges, take it into account
         // This means that overwriting only @dots or @dots.ges will not be taken into account
         if (chord && !note->HasDur() && !note->HasDurGes()) {
-            incrementScoreTime = chord->GetAlignmentDuration(m_meterParams, true, m_notationType);
+            incrementScoreTime = chord->GetAlignmentDuration(m_meterParams, true, m_notationType).ToDouble();
         }
         else if (tabGrp && !note->HasDur() && !note->HasDurGes()) {
-            incrementScoreTime = tabGrp->GetAlignmentDuration(m_meterParams, true, m_notationType);
+            incrementScoreTime = tabGrp->GetAlignmentDuration(m_meterParams, true, m_notationType).ToDouble();
         }
         else {
-            incrementScoreTime = note->GetAlignmentDuration(m_meterParams, true, m_notationType);
+            incrementScoreTime = note->GetAlignmentDuration(m_meterParams, true, m_notationType).ToDouble();
         }
         incrementScoreTime = incrementScoreTime / (DUR_MAX / DURATION_4);
         double realTimeIncrementSeconds = incrementScoreTime * 60.0 / m_currentTempo;
@@ -145,14 +145,15 @@ FunctorCode InitOnsetOffsetFunctor::VisitLayerElement(LayerElement *layerElement
         BeatRpt *rpt = vrv_cast<BeatRpt *>(element);
         assert(rpt);
 
-        incrementScoreTime = rpt->GetAlignmentDuration(m_meterParams, true, m_notationType);
+        incrementScoreTime = rpt->GetAlignmentDuration(m_meterParams, true, m_notationType).ToDouble();
         incrementScoreTime = incrementScoreTime / (DUR_MAX / DURATION_4);
         rpt->SetScoreTimeOnset(m_currentScoreTime);
         m_currentScoreTime += incrementScoreTime;
         m_currentRealTimeSeconds += incrementScoreTime * 60.0 / m_currentTempo;
     }
     else if (layerElement->Is({ BEAM, LIGATURE, FTREM, TUPLET }) && layerElement->HasSameasLink()) {
-        incrementScoreTime = layerElement->GetSameAsContentAlignmentDuration(m_meterParams, true, m_notationType);
+        incrementScoreTime
+            = layerElement->GetSameAsContentAlignmentDuration(m_meterParams, true, m_notationType).ToDouble();
         incrementScoreTime = incrementScoreTime / (DUR_MAX / DURATION_4);
         m_currentScoreTime += incrementScoreTime;
         m_currentRealTimeSeconds += incrementScoreTime * 60.0 / m_currentTempo;
@@ -188,7 +189,7 @@ FunctorCode InitOnsetOffsetFunctor::VisitTabGrpEnd(TabGrp *tabGrp)
 {
     LayerElement *element = tabGrp->ThisOrSameasLink();
 
-    double incrementScoreTime = element->GetAlignmentDuration(m_meterParams, true, m_notationType);
+    double incrementScoreTime = element->GetAlignmentDuration(m_meterParams, true, m_notationType).ToDouble();
     incrementScoreTime = incrementScoreTime / (DUR_MAX / DURATION_4);
     double realTimeIncrementSeconds = incrementScoreTime * 60.0 / m_currentTempo;
 
@@ -238,8 +239,8 @@ FunctorCode InitMaxMeasureDurationFunctor::VisitMeasureEnd(Measure *measure)
     const double tempo = this->GetAdjustedTempo();
     measure->SetCurrentTempo(tempo);
 
-    const double scoreTimeIncrement = measure->m_measureAligner.GetRightAlignment()->GetTime() * m_multiRestFactor
-        * static_cast<int>(DURATION_4) / DUR_MAX;
+    const double scoreTimeIncrement = measure->m_measureAligner.GetRightAlignment()->GetTime().ToDouble()
+        * m_multiRestFactor * static_cast<int>(DURATION_4) / DUR_MAX;
     m_currentScoreTime += scoreTimeIncrement;
     m_currentRealTimeSeconds += scoreTimeIncrement * 60.0 / tempo;
     m_multiRestFactor = 1;
@@ -362,7 +363,7 @@ FunctorCode GenerateMIDIFunctor::VisitBeatRpt(const BeatRpt *beatRpt)
 {
     // Sameas not taken into account for now
     AlignMeterParams params;
-    double beatLength = beatRpt->GetAlignmentDuration(params) / (DUR_MAX / DURATION_4);
+    double beatLength = beatRpt->GetAlignmentDuration(params).ToDouble() / (DUR_MAX / DURATION_4);
     double startTime = m_totalTime + beatRpt->GetScoreTimeOnset();
     int tpq = m_midiFile->getTPQ();
 
@@ -702,7 +703,7 @@ FunctorCode GenerateMIDIFunctor::VisitPedal(const Pedal *pedal)
 {
     if (!pedal->HasDir()) return FUNCTOR_CONTINUE;
 
-    double pedalTime = pedal->GetStart()->GetAlignment()->GetTime() * static_cast<int>(DURATION_4) / DUR_MAX;
+    double pedalTime = pedal->GetStart()->GetAlignment()->GetTime().ToDouble() * static_cast<int>(DURATION_4) / DUR_MAX;
     double startTime = m_totalTime + pedalTime;
     int tpq = m_midiFile->getTPQ();
 
diff --git a/src/view_element.cpp b/src/view_element.cpp
index 325723616c7..ee980ff0d13 100644
--- a/src/view_element.cpp
+++ b/src/view_element.cpp
@@ -1165,11 +1165,11 @@ void View::DrawMRest(DeviceContext *dc, LayerElement *element, Layer *layer, Sta
 
     const bool drawingCueSize = mRest->GetDrawingCueSize();
     int x = mRest->GetDrawingX();
-    int y = (measure->m_measureAligner.GetMaxTime() >= (DUR_MAX * 2))
+    int y = (measure->m_measureAligner.GetMaxTime().ToDouble() >= (DUR_MAX * 2))
         ? element->GetDrawingY() - m_doc->GetDrawingDoubleUnit(staffSize)
         : element->GetDrawingY();
-    char32_t rest
-        = (measure->m_measureAligner.GetMaxTime() >= (DUR_MAX * 2)) ? SMUFL_E4E2_restDoubleWhole : SMUFL_E4E3_restWhole;
+    char32_t rest = (measure->m_measureAligner.GetMaxTime().ToDouble() >= (DUR_MAX * 2)) ? SMUFL_E4E2_restDoubleWhole
+                                                                                         : SMUFL_E4E3_restWhole;
 
     x -= m_doc->GetGlyphWidth(rest, staffSize, drawingCueSize) / 2;
 

From 3605499a6e38ad330db334b11e0a03a127035160 Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Wed, 2 Oct 2024 19:33:51 +0200
Subject: [PATCH 084/105] Formatting

---
 src/alignfunctor.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/alignfunctor.cpp b/src/alignfunctor.cpp
index 329b8b2b104..fad1c8874f4 100644
--- a/src/alignfunctor.cpp
+++ b/src/alignfunctor.cpp
@@ -310,7 +310,7 @@ FunctorCode AlignHorizontallyFunctor::VisitLayerElement(LayerElement *layerEleme
         // get the duration of the event
         duration = layerElement->GetAlignmentDuration(m_currentParams, true, m_notationType);
 
-        //LogDebug("duration %s %f", duration.ToString().c_str(), duration.ToDouble());
+        // LogDebug("duration %s %f", duration.ToString().c_str(), duration.ToDouble());
 
         // For timestamp, what we get from GetAlignmentDuration is actually the position of the timestamp
         // So use it as current time - we can do this because the timestamp loop is redirected from the measure

From 75cc51b3541a7b28660bb9d31f709c637552a15c Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Thu, 3 Oct 2024 12:44:36 +0200
Subject: [PATCH 085/105] Change additional time variable to Fraction

---
 include/vrv/durationinterface.h | 7 ++++---
 include/vrv/horizontalaligner.h | 2 --
 src/durationinterface.cpp       | 6 +++---
 src/horizontalaligner.cpp       | 1 +
 4 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/include/vrv/durationinterface.h b/include/vrv/durationinterface.h
index 17d0ec0cc22..bf6b9f3b4b2 100644
--- a/include/vrv/durationinterface.h
+++ b/include/vrv/durationinterface.h
@@ -12,6 +12,7 @@
 #include "atts_gestural.h"
 #include "atts_mensural.h"
 #include "atts_shared.h"
+#include "horizontalaligner.h"
 #include "interface.h"
 
 namespace vrv {
@@ -57,12 +58,12 @@ class DurationInterface : public Interface,
     ///@}
 
     /**
-     * Returns the duration (in double) for the element.
-     * It returns 0.0 for grace notes.
+     * Returns the duration (in Fraction) for the element.
+     * It returns 0/1 for grace notes.
      * Careful: this method is not overriding LayerElement::GetAlignmentDuration since
      * LayerElement and DurationInterface have no inheritance link.
      */
-    double GetInterfaceAlignmentDuration(int num, int numBase) const;
+    Fraction GetInterfaceAlignmentDuration(int num, int numBase) const;
 
     /**
      * Returns the duration (in double) for the element for mensural notation
diff --git a/include/vrv/horizontalaligner.h b/include/vrv/horizontalaligner.h
index ff6a97da218..efa5a210895 100644
--- a/include/vrv/horizontalaligner.h
+++ b/include/vrv/horizontalaligner.h
@@ -12,8 +12,6 @@
 #include "object.h"
 #include "vrv.h"
 
-#include <numeric>
-
 namespace vrv {
 
 class Accid;
diff --git a/src/durationinterface.cpp b/src/durationinterface.cpp
index 27c4ec698e0..c0b526a5e22 100644
--- a/src/durationinterface.cpp
+++ b/src/durationinterface.cpp
@@ -72,7 +72,7 @@ void DurationInterface::Reset()
     m_scoreTimeTiedDuration = 0.0;
 }
 
-double DurationInterface::GetInterfaceAlignmentDuration(int num, int numBase) const
+Fraction DurationInterface::GetInterfaceAlignmentDuration(int num, int numBase) const
 {
     int noteDur = this->GetDurGes() != DURATION_NONE ? this->GetActualDurGes() : this->GetActualDur();
     if (noteDur == DUR_NONE) noteDur = DUR_4;
@@ -80,11 +80,11 @@ double DurationInterface::GetInterfaceAlignmentDuration(int num, int numBase) co
     if (this->HasNum()) num *= this->GetNum();
     if (this->HasNumbase()) numBase *= this->GetNumbase();
 
-    double duration = DUR_MAX / pow(2.0, (double)(noteDur - 2.0)) * numBase / num;
+    Fraction duration = DUR_MAX / pow(2.0, (double)(noteDur - 2.0)) * numBase / num;
 
     int noteDots = (this->HasDotsGes()) ? this->GetDotsGes() : this->GetDots();
     if (noteDots != VRV_UNSET) {
-        duration = 2 * duration - (duration / pow(2, noteDots));
+        duration = duration * 2 - (duration / pow(2, noteDots));
     }
     // LogDebug("Duration %d; Dot %d; Alignment %f", noteDur, this->GetDots(), duration);
     return duration;
diff --git a/src/horizontalaligner.cpp b/src/horizontalaligner.cpp
index b6b2df1fd80..5d7371f9044 100644
--- a/src/horizontalaligner.cpp
+++ b/src/horizontalaligner.cpp
@@ -11,6 +11,7 @@
 
 #include <cassert>
 #include <math.h>
+#include <numeric>
 
 //----------------------------------------------------------------------------
 

From cfcf7f379b45d5478d1bcc314566b8d11fd39418 Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Thu, 3 Oct 2024 14:03:31 +0200
Subject: [PATCH 086/105] Remove unnecessary duration defined values

---
 include/vrv/durationinterface.h       |   2 +-
 libmei/addons/attdef.h                |  19 +--
 src/adjustbeamsfunctor.cpp            |   7 +-
 src/adjustxposfunctor.cpp             |   4 +-
 src/beam.cpp                          |  51 ++++----
 src/btrem.cpp                         |   4 +-
 src/calcalignmentpitchposfunctor.cpp  |   6 +-
 src/calcdotsfunctor.cpp               |  18 +--
 src/calcligatureorneumeposfunctor.cpp |  22 ++--
 src/calcstemfunctor.cpp               |  18 +--
 src/drawinginterface.cpp              |   4 +-
 src/durationinterface.cpp             |  16 +--
 src/horizontalaligner.cpp             |   2 +-
 src/iopae.cpp                         |  20 +--
 src/layerelement.cpp                  |  32 ++---
 src/note.cpp                          |  26 ++--
 src/page.cpp                          |   2 +-
 src/preparedatafunctor.cpp            |  14 +--
 src/rest.cpp                          | 172 ++++++++++++++------------
 src/view_beam.cpp                     |  14 +--
 src/view_element.cpp                  |  25 ++--
 src/view_mensural.cpp                 |  14 +--
 src/view_tab.cpp                      |  22 ++--
 23 files changed, 263 insertions(+), 251 deletions(-)

diff --git a/include/vrv/durationinterface.h b/include/vrv/durationinterface.h
index bf6b9f3b4b2..b52e34d8953 100644
--- a/include/vrv/durationinterface.h
+++ b/include/vrv/durationinterface.h
@@ -85,7 +85,7 @@ class DurationInterface : public Interface,
     /**
      * @name Return the actual (gestural) duration of the note, for both CMN and mensural durations
      * See data_DURATION
-     * For CMN, it is the same (DURATION_1 == DUR_1)
+     * For CMN, it is the same (DURATION_1 == DURATION_1)
      * For mensural, we need to apply the DUR_MENSURAL_MASK
      */
     ///@{
diff --git a/libmei/addons/attdef.h b/libmei/addons/attdef.h
index fd991b11345..d04af822ac6 100644
--- a/libmei/addons/attdef.h
+++ b/libmei/addons/attdef.h
@@ -39,19 +39,6 @@ typedef double data_VU;
  */
 #define DUR_NONE -32
 #define DUR_MX -1 // maxima
-#define DUR_LG 0 // longa
-#define DUR_BR 1 // brevis
-#define DUR_1 2 // whole note (semibrevis)
-#define DUR_2 3 // half note (minima)
-#define DUR_4 4 // ...
-#define DUR_8 5
-#define DUR_16 6
-#define DUR_32 7
-#define DUR_64 8
-#define DUR_128 9
-#define DUR_256 10
-#define DUR_512 11
-#define DUR_1024 12 // this is it for now
 // used for alignement
 #define DUR_MAX 1024
 // mensural duration
@@ -102,7 +89,8 @@ typedef std::vector<std::pair<double, double>> data_BULGE;
  */
 enum data_DURATION {
     DURATION_NONE = DUR_NONE,
-    DURATION_long = DUR_LG,
+    DURATION_maxima = DUR_MX,
+    DURATION_long,
     DURATION_breve,
     DURATION_1,
     DURATION_2,
@@ -116,8 +104,7 @@ enum data_DURATION {
     DURATION_512,
     DURATION_1024,
     DURATION_2048,
-    DURATION_maxima = DUR_MX,
-    DURATION_longa = DUR_MENSURAL_OFFSET + DUR_LG,
+    DURATION_longa = DUR_MENSURAL_OFFSET + DURATION_long,
     DURATION_brevis,
     DURATION_semibrevis,
     DURATION_minima,
diff --git a/src/adjustbeamsfunctor.cpp b/src/adjustbeamsfunctor.cpp
index c887831d837..86c2f41b524 100644
--- a/src/adjustbeamsfunctor.cpp
+++ b/src/adjustbeamsfunctor.cpp
@@ -69,7 +69,8 @@ FunctorCode AdjustBeamsFunctor::VisitBeam(Beam *beam)
     }
 
     int leftMargin = 0, rightMargin = 0;
-    const int beamCount = m_outerBeam->GetBeamPartDuration((*beamSegment.m_beamElementCoordRefs.begin())->m_x) - DUR_8;
+    const int beamCount
+        = m_outerBeam->GetBeamPartDuration((*beamSegment.m_beamElementCoordRefs.begin())->m_x) - DURATION_8;
     const int currentBeamYLeft = m_y1 + m_beamSlope * ((*beamSegment.m_beamElementCoordRefs.begin())->m_x - m_x1);
     const int currentBeamYRight = m_y1 + m_beamSlope * (beamSegment.m_beamElementCoordRefs.back()->m_x - m_x1);
     leftMargin = (*beamSegment.m_beamElementCoordRefs.begin())->m_yBeam - currentBeamYLeft
@@ -130,7 +131,7 @@ FunctorCode AdjustBeamsFunctor::VisitClef(Clef *clef)
 
     Staff *staff = clef->GetAncestorStaff();
     // find number of beams at current position
-    const int beams = m_outerBeam->GetBeamPartDuration(clef) - DUR_4;
+    const int beams = m_outerBeam->GetBeamPartDuration(clef) - DURATION_4;
     const int beamWidth = m_outerBeam->m_beamWidth;
     // find beam Y positions that are relevant to current clef
     const int currentBeamYLeft = m_y1 + m_beamSlope * (clef->GetContentLeft() - m_x1);
@@ -298,7 +299,7 @@ FunctorCode AdjustBeamsFunctor::VisitRest(Rest *rest)
     if (!m_outerBeam) return FUNCTOR_SIBLINGS;
 
     // Calculate possible overlap for the rest with beams
-    const int beams = m_outerBeam->GetBeamPartDuration(rest, false) - DUR_4;
+    const int beams = m_outerBeam->GetBeamPartDuration(rest, false) - DURATION_4;
     const int beamWidth = m_outerBeam->m_beamWidth;
     const int overlapMargin = rest->Intersects(m_outerBeam, SELF, beams * beamWidth, true) * m_directionBias;
 
diff --git a/src/adjustxposfunctor.cpp b/src/adjustxposfunctor.cpp
index 1aa27238bf7..eb923f0a974 100644
--- a/src/adjustxposfunctor.cpp
+++ b/src/adjustxposfunctor.cpp
@@ -406,8 +406,8 @@ std::pair<int, int> AdjustXPosFunctor::CalculateXPosOffset(LayerElement *layerEl
             if (layerElement->Is({ NOTE, CHORD }) && !layerElement->GetFirstAncestor(TUPLET) && bboxElement->Is(REST)
                 && bboxElement->GetFirstAncestor(TUPLET)) {
                 Rest *rest = vrv_cast<Rest *>(bboxElement);
-                if (rest->GetDur() > DUR_8) {
-                    overlap = 1.5 * (rest->GetDur() - DUR_8) * drawingUnit;
+                if (rest->GetDur() > DURATION_8) {
+                    overlap = 1.5 * (rest->GetDur() - DURATION_8) * drawingUnit;
                 }
             }
         }
diff --git a/src/beam.cpp b/src/beam.cpp
index 062426112f6..297674fe23a 100644
--- a/src/beam.cpp
+++ b/src/beam.cpp
@@ -204,7 +204,7 @@ void BeamSegment::CalcSetStemValues(const Staff *staff, const Doc *doc, const Be
             int stemOffset = 0;
             const int unit = doc->GetDrawingUnit(staff->m_drawingStaffSize);
             if (coord->m_partialFlagPlace == coord->m_beamRelativePlace) {
-                stemOffset = (coord->m_dur - DUR_8) * beamInterface->m_beamWidth;
+                stemOffset = (coord->m_dur - DURATION_8) * beamInterface->m_beamWidth;
             }
             else if (el->GetIsInBeamSpan() && (coord->m_partialFlagPlace != BEAMPLACE_above)
                 && (coord->m_stem->GetDrawingStemDir() == STEMDIRECTION_up)) {
@@ -547,7 +547,7 @@ void BeamSegment::AdjustBeamToTremolos(const Doc *doc, const Staff *staff, const
         if (!stemmedInterface) continue;
 
         Stem *stem = stemmedInterface->GetDrawingStem();
-        const int offset = (coord->m_dur - DUR_8) * beamInterface->m_beamWidth + beamInterface->m_beamWidthBlack;
+        const int offset = (coord->m_dur - DURATION_8) * beamInterface->m_beamWidth + beamInterface->m_beamWidthBlack;
         const int currentAdjustment = stem->CalculateStemModAdjustment(doc, staff, offset);
         if (std::abs(currentAdjustment) > std::abs(maxAdjustment)) maxAdjustment = currentAdjustment;
     }
@@ -590,7 +590,7 @@ void BeamSegment::CalcBeamInit(
     beamInterface->m_beamWidthBlack = doc->GetDrawingBeamWidth(staff->m_drawingStaffSize, beamInterface->m_cueSize);
     beamInterface->m_beamWidthWhite
         = doc->GetDrawingBeamWhiteWidth(staff->m_drawingStaffSize, beamInterface->m_cueSize);
-    if (beamInterface->m_shortestDur == DUR_64) {
+    if (beamInterface->m_shortestDur == DURATION_64) {
         beamInterface->m_beamWidthWhite *= 4;
         beamInterface->m_beamWidthWhite /= 3;
     }
@@ -886,7 +886,7 @@ int BeamSegment::CalcBeamSlopeStep(
     // duration
     const int dur = beamInterface->m_shortestDur;
     // Prevent short step with values not shorter than a 16th
-    if (shortStep && (dur >= DUR_32)) {
+    if (shortStep && (dur >= DURATION_32)) {
         step = unit * 2;
         shortStep = false;
     }
@@ -1002,7 +1002,7 @@ void BeamSegment::CalcAdjustSlope(const Staff *staff, const Doc *doc, BeamDrawin
                 break;
             }
             // Here we should look at duration too because longer values in the middle could actually be OK as they are
-            else if (((coord != m_lastNoteOrChord) || (coord != m_firstNoteOrChord)) && (coord->m_dur > DUR_8)) {
+            else if (((coord != m_lastNoteOrChord) || (coord != m_firstNoteOrChord)) && (coord->m_dur > DURATION_8)) {
                 const int durLen = len - 0.9 * unit;
                 if (durLen < refLen) {
                     lengthen = true;
@@ -1268,9 +1268,9 @@ int BeamSegment::CalcMixedBeamCenterY(int step, int unit) const
 
 std::tuple<int, int, int> BeamSegment::CalcStemDefiningNote(const Staff *staff, data_BEAMPLACE place) const
 {
-    int shortestDuration = DUR_4;
+    int shortestDuration = DURATION_4;
     int shortestLoc = VRV_UNSET;
-    int relevantDuration = DUR_4;
+    int relevantDuration = DURATION_4;
     int relevantLoc = VRV_UNSET;
     const data_STEMDIRECTION globalStemDir = (place == BEAMPLACE_below) ? STEMDIRECTION_down : STEMDIRECTION_up;
     for (BeamElementCoord *coord : m_beamElementCoordRefs) {
@@ -1410,9 +1410,10 @@ void BeamSegment::CalcMixedBeamPlace(const Staff *staff)
 
 void BeamSegment::CalcPartialFlagPlace()
 {
-    // Start from note that is shorter than DUR_8 - we do not care otherwise, since those do not have additional beams
+    // Start from note that is shorter than DURATION_8 - we do not care otherwise, since those do not have additional
+    // beams
     auto start = std::find_if(m_beamElementCoordRefs.begin(), m_beamElementCoordRefs.end(),
-        [](BeamElementCoord *coord) { return coord->m_dur >= DUR_16; });
+        [](BeamElementCoord *coord) { return coord->m_dur >= DURATION_16; });
     if (m_beamElementCoordRefs.end() == start) return;
     while (start != m_beamElementCoordRefs.end()) {
         auto subdivision = start;
@@ -1426,14 +1427,14 @@ void BeamSegment::CalcPartialFlagPlace()
             // Find first note longer than 8th or first note that is cross-staff
             auto found = std::find_if(subdivision, m_beamElementCoordRefs.end(), [&](BeamElementCoord *coord) {
                 if (coord->m_element->Is(REST)) return false;
-                return ((coord->m_beamRelativePlace != place) || (coord->m_dur <= DUR_8) || (coord->m_breaksec));
+                return ((coord->m_beamRelativePlace != place) || (coord->m_dur <= DURATION_8) || (coord->m_breaksec));
             });
             subdivision = found;
 
             // Handle different cases, where we either don't want to proceed (e.g. end of the beam reached) or we want
             // to process them separately (e.g. on direction change from shorter to longer notes, or vice versa, we do
             // not want last note of the subdivision to have additional beam, so that it's clearly distinguishable).
-            if ((m_beamElementCoordRefs.end() == found) || ((*found)->m_dur <= DUR_8)) break;
+            if ((m_beamElementCoordRefs.end() == found) || ((*found)->m_dur <= DURATION_8)) break;
             if (((*found)->m_breaksec)) breakSec = true;
             if ((m_beamElementCoordRefs.end() - 1) == found) {
                 subdivision = m_beamElementCoordRefs.end();
@@ -1467,14 +1468,14 @@ void BeamSegment::CalcSetValues()
 int BeamSegment::GetAdjacentElementsDuration(int elementX) const
 {
     if ((elementX < m_beamElementCoordRefs.front()->m_x) || (elementX > m_beamElementCoordRefs.back()->m_x)) {
-        return DUR_8;
+        return DURATION_8;
     }
     for (int i = 0; i < int(m_beamElementCoordRefs.size()) - 1; ++i) {
         if ((m_beamElementCoordRefs.at(i)->m_x < elementX) && (m_beamElementCoordRefs.at(i + 1)->m_x > elementX)) {
             return std::min(m_beamElementCoordRefs.at(i)->m_dur, m_beamElementCoordRefs.at(i + 1)->m_dur);
         }
     }
-    return DUR_8;
+    return DURATION_8;
 }
 
 int BeamSegment::GetStartingX() const
@@ -1960,7 +1961,7 @@ int BeamElementCoord::CalculateStemLength(
     const int directionBias = (stemDir == STEMDIRECTION_up) ? 1 : -1;
     int stemLen = directionBias;
     // For 8th notes, use the shortened stem (if shortened)
-    if (preferredDur == DUR_8) {
+    if (preferredDur == DURATION_8) {
         if (stemLenInHalfUnits != standardStemLen) {
             stemLen *= stemLenInHalfUnits;
         }
@@ -1971,13 +1972,13 @@ int BeamElementCoord::CalculateStemLength(
     else {
         const bool isOddLength = (extend || !isHorizontal);
         switch (m_dur) {
-            case (DUR_16): stemLen *= isOddLength ? 14 : 13; break;
-            case (DUR_32): stemLen *= isOddLength ? 18 : 16; break;
-            case (DUR_64): stemLen *= isOddLength ? 22 : 20; break;
-            case (DUR_128): stemLen *= isOddLength ? 26 : 24; break;
-            case (DUR_256): stemLen *= isOddLength ? 30 : 28; break;
-            case (DUR_512): stemLen *= isOddLength ? 34 : 32; break;
-            case (DUR_1024): stemLen *= isOddLength ? 38 : 36; break;
+            case (DURATION_16): stemLen *= isOddLength ? 14 : 13; break;
+            case (DURATION_32): stemLen *= isOddLength ? 18 : 16; break;
+            case (DURATION_64): stemLen *= isOddLength ? 22 : 20; break;
+            case (DURATION_128): stemLen *= isOddLength ? 26 : 24; break;
+            case (DURATION_256): stemLen *= isOddLength ? 30 : 28; break;
+            case (DURATION_512): stemLen *= isOddLength ? 34 : 32; break;
+            case (DURATION_1024): stemLen *= isOddLength ? 38 : 36; break;
             default: stemLen *= 14;
         }
     }
@@ -2080,8 +2081,8 @@ void BeamElementCoord::UpdateStemLength(
 
 std::pair<int, int> Beam::GetAdditionalBeamCount() const
 {
-    int topShortestDur = DUR_8;
-    int bottomShortestDur = DUR_8;
+    int topShortestDur = DURATION_8;
+    int bottomShortestDur = DURATION_8;
     std::for_each(m_beamElementCoords.begin(), m_beamElementCoords.end(), [&](BeamElementCoord *coord) {
         if (coord->m_partialFlagPlace == BEAMPLACE_above) {
             topShortestDur = std::max(topShortestDur, coord->m_dur);
@@ -2091,7 +2092,7 @@ std::pair<int, int> Beam::GetAdditionalBeamCount() const
         }
     });
 
-    return { topShortestDur - DUR_8, bottomShortestDur - DUR_8 };
+    return { topShortestDur - DURATION_8, bottomShortestDur - DURATION_8 };
 }
 
 void Beam::SetElementShortening(int shortening)
@@ -2109,7 +2110,7 @@ int Beam::GetBeamPartDuration(int x, bool includeRests) const
         });
     // handle cases when coordinate is outside of the beam
     if (it == m_beamSegment.m_beamElementCoordRefs.end()) {
-        return DUR_8;
+        return DURATION_8;
     }
     else if (it == m_beamSegment.m_beamElementCoordRefs.begin()) {
         return (*it)->m_dur;
diff --git a/src/btrem.cpp b/src/btrem.cpp
index f26464cc33c..97496ef0dcf 100644
--- a/src/btrem.cpp
+++ b/src/btrem.cpp
@@ -144,11 +144,11 @@ data_STEMMODIFIER BTrem::GetDrawingStemMod() const
     const int drawingDur = duration->GetActualDur();
 
     if (!this->HasUnitdur()) {
-        if (drawingDur < DUR_2) return STEMMODIFIER_3slash;
+        if (drawingDur < DURATION_2) return STEMMODIFIER_3slash;
         return STEMMODIFIER_NONE;
     }
     int slashDur = this->GetUnitdur() - drawingDur;
-    if (drawingDur < DUR_4) slashDur = this->GetUnitdur() - DUR_4;
+    if (drawingDur < DURATION_4) slashDur = this->GetUnitdur() - DURATION_4;
     switch (slashDur) {
         case 0: return STEMMODIFIER_NONE;
         case 1: return STEMMODIFIER_1slash;
diff --git a/src/calcalignmentpitchposfunctor.cpp b/src/calcalignmentpitchposfunctor.cpp
index 188095fc068..b7a95489439 100644
--- a/src/calcalignmentpitchposfunctor.cpp
+++ b/src/calcalignmentpitchposfunctor.cpp
@@ -169,10 +169,10 @@ FunctorCode CalcAlignmentPitchPosFunctor::VisitLayerElement(LayerElement *layerE
             // set default location to the middle of the staff
             Staff *staff = layerElement->GetAncestorStaff();
             loc = staff->m_drawingLines - 1;
-            if ((durInterface->GetDur() < DUR_4) && (loc % 2 != 0)) --loc;
+            if ((durInterface->GetDur() < DURATION_4) && (loc % 2 != 0)) --loc;
             // Adjust special cases
-            if ((durInterface->GetDur() == DUR_1) && (staff->m_drawingLines > 1)) loc += 2;
-            if ((durInterface->GetDur() == DUR_BR) && (staff->m_drawingLines < 2)) loc -= 2;
+            if ((durInterface->GetDur() == DURATION_1) && (staff->m_drawingLines > 1)) loc += 2;
+            if ((durInterface->GetDur() == DURATION_breve) && (staff->m_drawingLines < 2)) loc -= 2;
 
             // If within a beam, calculate the rest's height based on it's relationship to the notes that surround it
             Beam *beam = vrv_cast<Beam *>(layerElement->GetFirstAncestor(BEAM, 1));
diff --git a/src/calcdotsfunctor.cpp b/src/calcdotsfunctor.cpp
index 2be321aa7a9..f2bc3cddba8 100644
--- a/src/calcdotsfunctor.cpp
+++ b/src/calcdotsfunctor.cpp
@@ -80,7 +80,7 @@ FunctorCode CalcDotsFunctor::VisitNote(Note *note)
         assert(dots);
 
         // Stem up, shorter than 4th and not in beam
-        if ((note->GetDots() > 0) && (m_chordStemDir == STEMDIRECTION_up) && (note->GetDrawingDur() > DUR_4)
+        if ((note->GetDots() > 0) && (m_chordStemDir == STEMDIRECTION_up) && (note->GetDrawingDur() > DURATION_4)
             && !note->IsInBeam()) {
             // Shift according to the flag width if the top note is not flipped
             if ((note == chord->GetTopNote()) && !note->GetFlippedNotehead()) {
@@ -129,7 +129,7 @@ FunctorCode CalcDotsFunctor::VisitRest(Rest *rest)
     }
 
     // Nothing to do
-    if ((rest->GetDur() <= DUR_BR) || (rest->GetDots() < 1)) {
+    if ((rest->GetDur() <= DURATION_breve) || (rest->GetDots() < 1)) {
         return FUNCTOR_SIBLINGS;
     }
 
@@ -150,12 +150,12 @@ FunctorCode CalcDotsFunctor::VisitRest(Rest *rest)
     }
 
     switch (rest->GetActualDur()) {
-        case DUR_32:
-        case DUR_64: loc += 2; break;
-        case DUR_128:
-        case DUR_256: loc += 4; break;
-        case DUR_512: loc += 6; break;
-        case DUR_1024: loc += 8; break;
+        case DURATION_32:
+        case DURATION_64: loc += 2; break;
+        case DURATION_128:
+        case DURATION_256: loc += 4; break;
+        case DURATION_512: loc += 6; break;
+        case DURATION_1024: loc += 8; break;
         default: break;
     }
 
@@ -164,7 +164,7 @@ FunctorCode CalcDotsFunctor::VisitRest(Rest *rest)
     // HARDCODED
     int xRel = m_doc->GetDrawingUnit(staffSize) * 2.5;
     if (drawingCueSize) xRel = m_doc->GetCueSize(xRel);
-    if (rest->GetDur() > DUR_2) {
+    if (rest->GetDur() > DURATION_2) {
         xRel = m_doc->GetGlyphWidth(rest->GetRestGlyph(), staff->m_drawingStaffSize, drawingCueSize);
     }
     dots->SetDrawingXRel(std::max(dots->GetDrawingXRel(), xRel));
diff --git a/src/calcligatureorneumeposfunctor.cpp b/src/calcligatureorneumeposfunctor.cpp
index efefc3dc3f5..5fb5576ff4f 100644
--- a/src/calcligatureorneumeposfunctor.cpp
+++ b/src/calcligatureorneumeposfunctor.cpp
@@ -67,18 +67,18 @@ FunctorCode CalcLigatureOrNeumePosFunctor::VisitLigature(Ligature *ligature)
         // Same treatment for Mx and LG except for positioning, which is done above
         // We still need to avoid oblique, so keep a flag.
         bool isMaxima = false;
-        if (dur1 == DUR_MX) {
-            dur1 = DUR_LG;
+        if (dur1 == DURATION_maxima) {
+            dur1 = DURATION_long;
             isMaxima = true;
         }
-        if (dur2 == DUR_MX) dur2 = DUR_LG;
+        if (dur2 == DURATION_maxima) dur2 = DURATION_long;
 
         int diatonicStep = note->GetDiatonicPitch() - previousNote->GetDiatonicPitch();
         bool up = (diatonicStep > 0);
         bool isLastNote = (note == lastNote);
 
         // L - L
-        if ((dur1 == DUR_LG) && (dur2 == DUR_LG)) {
+        if ((dur1 == DURATION_long) && (dur2 == DURATION_long)) {
             if (up) {
                 ligature->m_drawingShapes.at(n1) = LIGATURE_STEM_RIGHT_DOWN;
                 ligature->m_drawingShapes.at(n2) = LIGATURE_STEM_RIGHT_DOWN;
@@ -88,7 +88,7 @@ FunctorCode CalcLigatureOrNeumePosFunctor::VisitLigature(Ligature *ligature)
             }
         }
         // L - B
-        else if ((dur1 == DUR_LG) && (dur2 == DUR_BR)) {
+        else if ((dur1 == DURATION_long) && (dur2 == DURATION_breve)) {
             if (up) {
                 ligature->m_drawingShapes.at(n1) = LIGATURE_STEM_RIGHT_DOWN;
             }
@@ -102,7 +102,7 @@ FunctorCode CalcLigatureOrNeumePosFunctor::VisitLigature(Ligature *ligature)
             }
         }
         // B - B
-        else if ((dur1 == DUR_BR) && (dur2 == DUR_BR)) {
+        else if ((dur1 == DURATION_breve) && (dur2 == DURATION_breve)) {
             if (up) {
                 // nothing to change
             }
@@ -119,7 +119,7 @@ FunctorCode CalcLigatureOrNeumePosFunctor::VisitLigature(Ligature *ligature)
             }
         }
         // B - L
-        else if ((dur1 == DUR_BR) && (dur2 == DUR_LG)) {
+        else if ((dur1 == DURATION_breve) && (dur2 == DURATION_long)) {
             if (up) {
                 ligature->m_drawingShapes.at(n2) = LIGATURE_STEM_RIGHT_DOWN;
             }
@@ -133,11 +133,11 @@ FunctorCode CalcLigatureOrNeumePosFunctor::VisitLigature(Ligature *ligature)
             }
         }
         // SB - SB
-        else if ((dur1 == DUR_1) && (dur2 == DUR_1)) {
+        else if ((dur1 == DURATION_1) && (dur2 == DURATION_1)) {
             ligature->m_drawingShapes.at(n1) = LIGATURE_STEM_LEFT_UP;
         }
         // SB - L (this should not happen on the first two notes, but this is an encoding problem)
-        else if ((dur1 == DUR_1) && (dur2 == DUR_LG)) {
+        else if ((dur1 == DURATION_1) && (dur2 == DURATION_long)) {
             if (up) {
                 ligature->m_drawingShapes.at(n2) = LIGATURE_STEM_RIGHT_DOWN;
             }
@@ -146,7 +146,7 @@ FunctorCode CalcLigatureOrNeumePosFunctor::VisitLigature(Ligature *ligature)
             }
         }
         // SB - B (this should not happen on the first two notes, but this is an encoding problem)
-        else if ((dur1 == DUR_1) && (dur2 == DUR_BR)) {
+        else if ((dur1 == DURATION_1) && (dur2 == DURATION_breve)) {
             if (up) {
                 // nothing to change
             }
@@ -168,7 +168,7 @@ FunctorCode CalcLigatureOrNeumePosFunctor::VisitLigature(Ligature *ligature)
         }
 
         // With mensural black notation, stack longa going up
-        if (isLastNote && isMensuralBlack && (dur2 == DUR_LG) && up) {
+        if (isLastNote && isMensuralBlack && (dur2 == DURATION_long) && up) {
             // Stack only if at least a third
             int stackThreshold = 1;
             // If the previous was going down, adjust the threshold
diff --git a/src/calcstemfunctor.cpp b/src/calcstemfunctor.cpp
index 36210118578..c5f483ddb31 100644
--- a/src/calcstemfunctor.cpp
+++ b/src/calcstemfunctor.cpp
@@ -32,7 +32,7 @@ CalcStemFunctor::CalcStemFunctor(Doc *doc) : DocFunctor(doc)
 {
     m_chordStemLength = 0;
     m_verticalCenter = 0;
-    m_dur = DUR_1;
+    m_dur = DURATION_1;
     m_isGraceNote = false;
     m_isStemSameasSecondary = false;
     m_tabGrpWithNoNote = false;
@@ -348,7 +348,7 @@ FunctorCode CalcStemFunctor::VisitStem(Stem *stem)
     const bool drawingCueSize = stem->GetDrawingCueSize();
 
     // For notes longer than half notes the stem is always 0
-    if (m_dur < DUR_2) {
+    if (m_dur < DURATION_2) {
         stem->SetDrawingXRel(0);
         stem->SetDrawingYRel(0);
         stem->SetDrawingStemLen(0);
@@ -409,7 +409,7 @@ FunctorCode CalcStemFunctor::VisitStem(Stem *stem)
     int flagOffset = 0;
     Flag *flag = NULL;
     // There is never a flag with a duration longer than 8th notes
-    if (m_dur > DUR_4) {
+    if (m_dur > DURATION_4) {
         flag = vrv_cast<Flag *>(stem->GetFirst(FLAG));
         assert(flag);
         // There is never a flag with stem sameas notes
@@ -417,7 +417,7 @@ FunctorCode CalcStemFunctor::VisitStem(Stem *stem)
             flag->m_drawingNbFlags = 0;
         }
         else {
-            flag->m_drawingNbFlags = m_dur - DUR_4;
+            flag->m_drawingNbFlags = m_dur - DURATION_4;
             flagOffset = unit * (flag->m_drawingNbFlags + 1);
         }
     }
@@ -441,7 +441,7 @@ FunctorCode CalcStemFunctor::VisitStem(Stem *stem)
     int flagHeight = 0;
 
     // SMUFL flags cover some additional stem length from the 32th only
-    if (m_dur > DUR_16) {
+    if (m_dur > DURATION_16) {
         assert(flag);
         Point stemEnd;
         if (stem->GetDrawingStemDir() == STEMDIRECTION_up) {
@@ -450,7 +450,7 @@ FunctorCode CalcStemFunctor::VisitStem(Stem *stem)
         else {
             stemEnd = flag->GetStemDownNW(m_doc, staffSize, drawingCueSize);
         }
-        // Trick for shortening the stem with DUR_8
+        // Trick for shortening the stem with DURATION_8
         flagHeight = stemEnd.y;
     }
 
@@ -496,7 +496,7 @@ FunctorCode CalcStemFunctor::VisitTabDurSym(TabDurSym *tabDurSym)
     assert(stem);
 
     // Do not draw virtual (e.g., whole note) stems
-    if (m_dur < DUR_2 || m_tabGrpWithNoNote) {
+    if (m_dur < DURATION_2 || m_tabGrpWithNoNote) {
         stem->IsVirtual(true);
         return FUNCTOR_SIBLINGS;
     }
@@ -554,7 +554,7 @@ FunctorCode CalcStemFunctor::VisitTabDurSym(TabDurSym *tabDurSym)
     int stemSize = tabDurSym->CalcStemLenInThirdUnits(m_staff, stemDir) * m_doc->GetDrawingUnit(staffSize);
     stemSize /= (3 * stemDirFactor);
 
-    if (m_dur == DUR_2) {
+    if (m_dur == DURATION_2) {
         // Stems for half notes twice shorter
         stemSize /= 2;
     }
@@ -565,7 +565,7 @@ FunctorCode CalcStemFunctor::VisitTabDurSym(TabDurSym *tabDurSym)
     if (m_staff->IsTabGuitar()) {
         Flag *flag = vrv_cast<Flag *>(stem->GetFirst(FLAG));
         if (flag) {
-            flag->m_drawingNbFlags = m_dur - DUR_4;
+            flag->m_drawingNbFlags = m_dur - DURATION_4;
             flag->SetDrawingYRel(-stemSize);
         }
     }
diff --git a/src/drawinginterface.cpp b/src/drawinginterface.cpp
index ebd382c517f..d4be4bf9d35 100644
--- a/src/drawinginterface.cpp
+++ b/src/drawinginterface.cpp
@@ -108,7 +108,7 @@ void BeamDrawingInterface::Reset()
 
 int BeamDrawingInterface::GetTotalBeamWidth() const
 {
-    return m_beamWidthBlack + (m_shortestDur - DUR_8) * m_beamWidth;
+    return m_beamWidthBlack + (m_shortestDur - DURATION_8) * m_beamWidth;
 }
 
 void BeamDrawingInterface::ClearCoords()
@@ -459,7 +459,7 @@ bool BeamDrawingInterface::IsRepeatedPattern() const
 
 bool BeamDrawingInterface::HasOneStepHeight() const
 {
-    if (m_shortestDur < DUR_32) return false;
+    if (m_shortestDur < DURATION_32) return false;
 
     int top = -128;
     int bottom = 128;
diff --git a/src/durationinterface.cpp b/src/durationinterface.cpp
index c0b526a5e22..e39fad14f98 100644
--- a/src/durationinterface.cpp
+++ b/src/durationinterface.cpp
@@ -75,7 +75,7 @@ void DurationInterface::Reset()
 Fraction DurationInterface::GetInterfaceAlignmentDuration(int num, int numBase) const
 {
     int noteDur = this->GetDurGes() != DURATION_NONE ? this->GetActualDurGes() : this->GetActualDur();
-    if (noteDur == DUR_NONE) noteDur = DUR_4;
+    if (noteDur == DUR_NONE) noteDur = DURATION_4;
 
     if (this->HasNum()) num *= this->GetNum();
     if (this->HasNumbase()) numBase *= this->GetNumbase();
@@ -93,7 +93,7 @@ Fraction DurationInterface::GetInterfaceAlignmentDuration(int num, int numBase)
 double DurationInterface::GetInterfaceAlignmentMensuralDuration(int num, int numBase, const Mensur *currentMensur) const
 {
     int noteDur = this->GetDurGes() != DURATION_NONE ? this->GetActualDurGes() : this->GetActualDur();
-    if (noteDur == DUR_NONE) noteDur = DUR_4;
+    if (noteDur == DUR_NONE) noteDur = DURATION_4;
 
     if (!currentMensur) {
         LogWarning("No current mensur for calculating duration");
@@ -139,14 +139,14 @@ double DurationInterface::GetInterfaceAlignmentMensuralDuration(int num, int num
     double ratio = 0.0;
     double duration = (double)DUR_MENSURAL_REF;
     switch (noteDur) {
-        case DUR_MX:
+        case DURATION_maxima:
             duration *= (double)abs(currentMensur->GetModusminor()) * (double)abs(currentMensur->GetModusmaior());
             break;
-        case DUR_LG: duration *= (double)abs(currentMensur->GetModusminor()); break;
-        case DUR_BR: break;
-        case DUR_1: duration /= (double)abs(currentMensur->GetTempus()); break;
+        case DURATION_long: duration *= (double)abs(currentMensur->GetModusminor()); break;
+        case DURATION_breve: break;
+        case DURATION_1: duration /= (double)abs(currentMensur->GetTempus()); break;
         default:
-            ratio = pow(2.0, (double)(noteDur - DUR_2));
+            ratio = pow(2.0, (double)(noteDur - DURATION_2));
             duration /= (double)abs(currentMensur->GetTempus()) * (double)abs(currentMensur->GetProlatio()) * ratio;
             break;
     }
@@ -190,7 +190,7 @@ int DurationInterface::CalcActualDur(data_DURATION dur) const
 {
     if (dur == DURATION_NONE) return DUR_NONE;
     // maxima (-1) is a mensural only value
-    if (dur == DURATION_maxima) return DUR_MX;
+    if (dur == DURATION_maxima) return DURATION_maxima;
     return (dur & DUR_MENSURAL_MASK);
 }
 
diff --git a/src/horizontalaligner.cpp b/src/horizontalaligner.cpp
index 5d7371f9044..8315fabfaee 100644
--- a/src/horizontalaligner.cpp
+++ b/src/horizontalaligner.cpp
@@ -833,7 +833,7 @@ int Alignment::HorizontalSpaceForDuration(
     double doubleIntervalTime = intervalTime.ToDouble();
     /* If the longest duration interval in the score is longer than semibreve, adjust spacing so
      that interval gets the space a semibreve would ordinarily get. */
-    if (maxActualDur < DUR_1) doubleIntervalTime /= pow(2.0, DUR_1 - maxActualDur);
+    if (maxActualDur < DURATION_1) doubleIntervalTime /= pow(2.0, DURATION_1 - maxActualDur);
 
     return pow(doubleIntervalTime, spacingNonLinear) * spacingLinear * 10.0; // numbers are experimental constants
 }
diff --git a/src/iopae.cpp b/src/iopae.cpp
index 5aa0bbd3fc8..5c654356e48 100644
--- a/src/iopae.cpp
+++ b/src/iopae.cpp
@@ -528,16 +528,16 @@ void PAEOutput::WriteTuplet(Tuplet *tuplet)
 
     std::string dur;
     switch (tupletDur) {
-        case (DUR_LG): dur = "0"; break;
-        case (DUR_BR): dur = "9"; break;
-        case (DUR_1): dur = "1"; break;
-        case (DUR_2): dur = "2"; break;
-        case (DUR_4): dur = "4"; break;
-        case (DUR_8): dur = "8"; break;
-        case (DUR_16): dur = "6"; break;
-        case (DUR_32): dur = "3"; break;
-        case (DUR_64): dur = "5"; break;
-        case (DUR_128): dur = "7"; break;
+        case (DURATION_long): dur = "0"; break;
+        case (DURATION_breve): dur = "9"; break;
+        case (DURATION_1): dur = "1"; break;
+        case (DURATION_2): dur = "2"; break;
+        case (DURATION_4): dur = "4"; break;
+        case (DURATION_8): dur = "8"; break;
+        case (DURATION_16): dur = "6"; break;
+        case (DURATION_32): dur = "3"; break;
+        case (DURATION_64): dur = "5"; break;
+        case (DURATION_128): dur = "7"; break;
         default: LogWarning("Unsupported tuplet duration"); dur = "4";
     }
 
diff --git a/src/layerelement.cpp b/src/layerelement.cpp
index d7bd8b4cd16..3a0f99bb6e2 100644
--- a/src/layerelement.cpp
+++ b/src/layerelement.cpp
@@ -73,7 +73,7 @@
 namespace vrv {
 
 // Large spacing between syllables is a quarter note space
-// MAX_DURATION / pow(2.0, (DUR_4 - 2.0))
+// MAX_DURATION / pow(2.0, (DURATION_4 - 2.0))
 #define NEUME_LARGE_SPACE 256
 // Medium spacing between neume is a 8th note space
 // MAX_DURATION / pow(2.0, (DUR_5 - 2.0))
@@ -554,7 +554,7 @@ int LayerElement::GetDrawingTop(const Doc *doc, int staffSize, bool withArtic, A
     if (note) {
         const DurationInterface *durationInterface = this->GetDurationInterface();
         assert(durationInterface);
-        if (durationInterface->GetNoteOrChordDur(this) < DUR_2) {
+        if (durationInterface->GetNoteOrChordDur(this) < DURATION_2) {
             return note->GetDrawingY() + doc->GetDrawingUnit(staffSize);
         }
         // We should also take into account the stem shift to the right
@@ -592,7 +592,7 @@ int LayerElement::GetDrawingBottom(const Doc *doc, int staffSize, bool withArtic
     if (note) {
         const DurationInterface *durationInterface = this->GetDurationInterface();
         assert(durationInterface);
-        if (durationInterface->GetNoteOrChordDur(this) < DUR_2) {
+        if (durationInterface->GetNoteOrChordDur(this) < DURATION_2) {
             return note->GetDrawingY() - doc->GetDrawingUnit(staffSize);
         }
         // We should also take into account the stem shift to the right
@@ -616,7 +616,7 @@ int LayerElement::GetDrawingRadius(const Doc *doc, bool isInLigature) const
     if (!this->Is({ CHORD, NC, NOTE, REST })) return 0;
 
     char32_t code = 0;
-    int dur = DUR_4;
+    int dur = DURATION_4;
     const Staff *staff = this->GetAncestorStaff();
     bool isMensuralDur = false;
     if (this->Is(NOTE)) {
@@ -636,13 +636,13 @@ int LayerElement::GetDrawingRadius(const Doc *doc, bool isInLigature) const
         assert(chord);
         dur = chord->GetActualDur();
         isMensuralDur = chord->IsMensuralDur();
-        if (dur == DUR_BR) {
+        if (dur == DURATION_breve) {
             code = SMUFL_E0A1_noteheadDoubleWholeSquare;
         }
-        else if (dur == DUR_1) {
+        else if (dur == DURATION_1) {
             code = SMUFL_E0A2_noteheadWhole;
         }
-        else if (dur == DUR_2) {
+        else if (dur == DURATION_2) {
             code = SMUFL_E0A3_noteheadHalf;
         }
         else {
@@ -653,9 +653,9 @@ int LayerElement::GetDrawingRadius(const Doc *doc, bool isInLigature) const
         code = SMUFL_E0A4_noteheadBlack;
     }
 
-    // Mensural note shorter than DUR_BR
-    if ((isMensuralDur && (dur <= DUR_BR)) || ((dur == DUR_1) && isInLigature)) {
-        int widthFactor = (dur == DUR_MX) ? 2 : 1;
+    // Mensural note shorter than DURATION_breve
+    if ((isMensuralDur && (dur <= DURATION_breve)) || ((dur == DURATION_1) && isInLigature)) {
+        int widthFactor = (dur == DURATION_maxima) ? 2 : 1;
         if (staff->m_drawingNotationType == NOTATIONTYPE_mensural_black) {
             return widthFactor * doc->GetDrawingBrevisWidth(staff->m_drawingStaffSize) * 0.7;
         }
@@ -1212,11 +1212,11 @@ std::pair<int, bool> LayerElement::CalcElementHorizontalOverlap(const Doc *doc,
                         || ((STEMDIRECTION_up == stemDir) && (parentChord->GetTopNote() == previousNote));
                 }
                 // Reduce the margin to 0 for whole notes unisson
-                else if ((currentNote->GetDrawingDur() == DUR_1) && (previousDuration == DUR_1)) {
+                else if ((currentNote->GetDrawingDur() == DURATION_1) && (previousDuration == DURATION_1)) {
                     horizontalMargin = 0;
                 }
                 if (!isPreviousChord || isEdgeElement || isChordElement) {
-                    if ((currentNote->GetDrawingDur() == DUR_2) && (previousDuration == DUR_2)) {
+                    if ((currentNote->GetDrawingDur() == DURATION_2) && (previousDuration == DURATION_2)) {
                         isInUnison = true;
                     }
                     else if ((!currentNote->IsGraceNote() && !currentNote->GetDrawingCueSize())
@@ -1232,7 +1232,7 @@ std::pair<int, bool> LayerElement::CalcElementHorizontalOverlap(const Doc *doc,
                         isInUnison = true;
                         continue;
                     }
-                    else if ((currentNote->GetDrawingDur() > DUR_2) && (previousDuration > DUR_2)) {
+                    else if ((currentNote->GetDrawingDur() > DURATION_2) && (previousDuration > DURATION_2)) {
                         isInUnison = true;
                     }
                     if (isInUnison && (currentNote->GetDots() == previousNote->GetDots())) {
@@ -1240,7 +1240,8 @@ std::pair<int, bool> LayerElement::CalcElementHorizontalOverlap(const Doc *doc,
                     }
                     else {
                         isInUnison = false;
-                        if ((currentNote->GetDrawingDur() <= DUR_1) || (previousNote->GetDrawingDur() <= DUR_1)) {
+                        if ((currentNote->GetDrawingDur() <= DURATION_1)
+                            || (previousNote->GetDrawingDur() <= DURATION_1)) {
                             horizontalMargin *= -1;
                         }
                         else {
@@ -1263,7 +1264,8 @@ std::pair<int, bool> LayerElement::CalcElementHorizontalOverlap(const Doc *doc,
                 if (previousNote->GetDrawingLoc() - currentNote->GetDrawingLoc() == -1) {
                     horizontalMargin *= -1;
                 }
-                else if ((currentNote->GetDrawingDur() <= DUR_1) && (previousNote->GetDrawingDur() <= DUR_1)) {
+                else if ((currentNote->GetDrawingDur() <= DURATION_1)
+                    && (previousNote->GetDrawingDur() <= DURATION_1)) {
                     continue;
                 }
                 else if (previousNote->m_crossStaff || m_crossStaff)
diff --git a/src/note.cpp b/src/note.cpp
index 08b8ad11ca2..1037dd17315 100644
--- a/src/note.cpp
+++ b/src/note.cpp
@@ -473,7 +473,7 @@ int Note::CalcStemLenInThirdUnits(const Staff *staff, data_STEMDIRECTION stemDir
 
     // Limit shortening with duration shorter than quarter not when not in a beam
 
-    if ((this->GetDrawingDur() > DUR_4) && !this->IsInBeam()) {
+    if ((this->GetDrawingDur() > DURATION_4) && !this->IsInBeam()) {
         if (this->GetDrawingStemDir() == STEMDIRECTION_up) {
             shortening = std::min(4, shortening);
         }
@@ -494,7 +494,7 @@ char32_t Note::GetMensuralNoteheadGlyph() const
     int drawingDur = this->GetDrawingDur();
 
     // No SMuFL code used for these values
-    if (drawingDur < DUR_1) {
+    if (drawingDur < DURATION_1) {
         return 0;
     }
 
@@ -507,7 +507,7 @@ char32_t Note::GetMensuralNoteheadGlyph() const
     }
     else {
         if (this->GetColored() == BOOLEAN_true) {
-            if (drawingDur > DUR_2) {
+            if (drawingDur > DURATION_2) {
                 code = SMUFL_E93C_mensuralNoteheadMinimaWhite;
             }
             else {
@@ -515,7 +515,7 @@ char32_t Note::GetMensuralNoteheadGlyph() const
             }
         }
         else {
-            if (drawingDur > DUR_2) {
+            if (drawingDur > DURATION_2) {
                 code = SMUFL_E93D_mensuralNoteheadSemiminimaWhite;
             }
             else {
@@ -549,7 +549,7 @@ char32_t Note::GetNoteheadGlyph(const int duration) const
         // case HEADSHAPE_circle: return SMUFL_E0B3_noteheadCircleX;
         case HEADSHAPE_plus: return SMUFL_E0AF_noteheadPlusBlack;
         case HEADSHAPE_diamond: {
-            if (duration < DUR_4) {
+            if (duration < DURATION_4) {
                 return (this->GetHeadFill() == FILL_solid) ? SMUFL_E0DB_noteheadDiamondBlack
                                                            : SMUFL_E0D9_noteheadDiamondHalf;
             }
@@ -562,7 +562,7 @@ char32_t Note::GetNoteheadGlyph(const int duration) const
         // case HEADSHAPE_oval: return SMUFL_noteheadOval;
         // case HEADSHAPE_piewedge: return SMUFL_noteheadPieWedge;
         case HEADSHAPE_rectangle:
-            if (duration < DUR_4) {
+            if (duration < DURATION_4) {
                 return (this->GetHeadFill() == FILL_solid) ? SMUFL_E0B9_noteheadSquareBlack
                                                            : SMUFL_E0B8_noteheadSquareWhite;
             }
@@ -573,14 +573,14 @@ char32_t Note::GetNoteheadGlyph(const int duration) const
         // case HEADSHAPE_rtriangle: return SMUFL_noteheadRTriangle;
         // case HEADSHAPE_semicircle: return SMUFL_noteheadSemicircle;
         case HEADSHAPE_slash: {
-            if (DUR_1 >= duration) return SMUFL_E102_noteheadSlashWhiteWhole;
-            if (DUR_2 == duration) return SMUFL_E103_noteheadSlashWhiteHalf;
+            if (DURATION_1 >= duration) return SMUFL_E102_noteheadSlashWhiteWhole;
+            if (DURATION_2 == duration) return SMUFL_E103_noteheadSlashWhiteHalf;
             return SMUFL_E101_noteheadSlashHorizontalEnds;
         }
         // case HEADSHAPE_square: return SMUFL_noteheadSquare;
         case HEADSHAPE_x: {
-            if (DUR_1 == duration) return SMUFL_E0B5_noteheadWholeWithX;
-            if (DUR_2 == duration) return SMUFL_E0B6_noteheadHalfWithX;
+            if (DURATION_1 == duration) return SMUFL_E0B5_noteheadWholeWithX;
+            if (DURATION_2 == duration) return SMUFL_E0B6_noteheadHalfWithX;
             return SMUFL_E0A9_noteheadXBlack;
         }
         default: break;
@@ -591,10 +591,10 @@ char32_t Note::GetNoteheadGlyph(const int duration) const
         default: break;
     }
 
-    if (DUR_BR == duration) return SMUFL_E0A1_noteheadDoubleWholeSquare;
-    if (DUR_1 == duration) return SMUFL_E0A2_noteheadWhole;
+    if (DURATION_breve == duration) return SMUFL_E0A1_noteheadDoubleWholeSquare;
+    if (DURATION_1 == duration) return SMUFL_E0A2_noteheadWhole;
     // We support solid on half notes or void on quarter and shorter notes
-    if (DUR_2 == duration) {
+    if (DURATION_2 == duration) {
         return (this->GetHeadFill() == FILL_solid) ? SMUFL_E0A4_noteheadBlack : SMUFL_E0A3_noteheadHalf;
     }
     else {
diff --git a/src/page.cpp b/src/page.cpp
index 17388407eb8..cab9e92fea6 100644
--- a/src/page.cpp
+++ b/src/page.cpp
@@ -318,7 +318,7 @@ void Page::ResetAligners()
     // Unless duration-based spacing is disabled, set the X position of each Alignment.
     // Does non-linear spacing based on the duration space between two Alignment objects.
     if (!doc->GetOptions()->m_evenNoteSpacing.GetValue()) {
-        int longestActualDur = DUR_4;
+        int longestActualDur = DURATION_4;
 
         // Detect the longest duration in order to adjust the spacing (false by default)
         if (doc->GetOptions()->m_spacingDurDetection.GetValue()) {
diff --git a/src/preparedatafunctor.cpp b/src/preparedatafunctor.cpp
index 1bb1389a6a6..091b6be6d54 100644
--- a/src/preparedatafunctor.cpp
+++ b/src/preparedatafunctor.cpp
@@ -1102,11 +1102,11 @@ FunctorCode PrepareLayerElementPartsFunctor::VisitChord(Chord *chord)
     currentStem->FillAttributes(*chord);
 
     int duration = chord->GetNoteOrChordDur(chord);
-    if ((duration < DUR_2) || (chord->GetStemVisible() == BOOLEAN_false)) {
+    if ((duration < DURATION_2) || (chord->GetStemVisible() == BOOLEAN_false)) {
         currentStem->IsVirtual(true);
     }
 
-    const bool shouldHaveFlag = ((duration > DUR_4) && !chord->IsInBeam() && !chord->GetAncestorFTrem());
+    const bool shouldHaveFlag = ((duration > DURATION_4) && !chord->IsInBeam() && !chord->GetAncestorFTrem());
     currentFlag = this->ProcessFlag(currentFlag, currentStem, shouldHaveFlag);
 
     chord->SetDrawingStem(currentStem);
@@ -1150,7 +1150,7 @@ FunctorCode PrepareLayerElementPartsFunctor::VisitNote(Note *note)
         currentStem->AttGraced::operator=(*note);
         currentStem->FillAttributes(*note);
 
-        if (note->GetActualDur() < DUR_2 || (note->GetStemVisible() == BOOLEAN_false)) {
+        if (note->GetActualDur() < DURATION_2 || (note->GetStemVisible() == BOOLEAN_false)) {
             currentStem->IsVirtual(true);
         }
     }
@@ -1177,8 +1177,8 @@ FunctorCode PrepareLayerElementPartsFunctor::VisitNote(Note *note)
     if (note->IsMensuralDur()) return FUNCTOR_CONTINUE;
 
     if (currentStem) {
-        const bool shouldHaveFlag = ((note->GetActualDur() > DUR_4) && !note->IsInBeam() && !note->GetAncestorFTrem()
-            && !note->IsChordTone() && !note->IsTabGrpNote());
+        const bool shouldHaveFlag = ((note->GetActualDur() > DURATION_4) && !note->IsInBeam()
+            && !note->GetAncestorFTrem() && !note->IsChordTone() && !note->IsTabGrpNote());
         currentFlag = this->ProcessFlag(currentFlag, currentStem, shouldHaveFlag);
 
         if (!chord) note->SetDrawingStem(currentStem);
@@ -1196,7 +1196,7 @@ FunctorCode PrepareLayerElementPartsFunctor::VisitRest(Rest *rest)
 {
     Dots *currentDots = vrv_cast<Dots *>(rest->FindDescendantByType(DOTS, 1));
 
-    const bool shouldHaveDots = (rest->GetDur() > DUR_BR) && (rest->GetDots() > 0);
+    const bool shouldHaveDots = (rest->GetDur() > DURATION_breve) && (rest->GetDots() > 0);
     currentDots = this->ProcessDots(currentDots, rest, shouldHaveDots);
 
     /************ Prepare the drawing cue size ************/
@@ -1222,7 +1222,7 @@ FunctorCode PrepareLayerElementPartsFunctor::VisitTabDurSym(TabDurSym *tabDurSym
     assert(tabGrp);
 
     // No flag within beam for durations longer than 8th notes
-    const bool shouldHaveFlag = (!tabDurSym->IsInBeam() && (tabGrp->GetActualDur() > DUR_4));
+    const bool shouldHaveFlag = (!tabDurSym->IsInBeam() && (tabGrp->GetActualDur() > DURATION_4));
     currentFlag = this->ProcessFlag(currentFlag, currentStem, shouldHaveFlag);
 
     return FUNCTOR_SIBLINGS;
diff --git a/src/rest.cpp b/src/rest.cpp
index 5f0779dd004..444159f8e63 100644
--- a/src/rest.cpp
+++ b/src/rest.cpp
@@ -37,100 +37,118 @@ RestOffsets g_defaultRests{
         { { RA_none,
               { { RLP_restOnTopLayer,
                     { { RNP_noteInSpace,
-                          { { DUR_1, 3 }, { DUR_2, 3 }, { DUR_4, 5 }, { DUR_8, 5 }, { DUR_16, 7 }, { DUR_32, 7 },
-                              { DUR_64, 9 }, { DUR_128, 9 }, { DUR_LG, 5 }, { DUR_BR, 5 } } },
+                          { { DURATION_1, 3 }, { DURATION_2, 3 }, { DURATION_4, 5 }, { DURATION_8, 5 },
+                              { DURATION_16, 7 }, { DURATION_32, 7 }, { DURATION_64, 9 }, { DURATION_128, 9 },
+                              { DURATION_long, 5 }, { DURATION_breve, 5 } } },
                         { RNP_noteOnLine,
-                            { { DUR_1, 2 }, { DUR_2, 4 }, { DUR_4, 6 }, { DUR_8, 4 }, { DUR_16, 6 }, { DUR_32, 6 },
-                                { DUR_64, 8 }, { DUR_128, 8 }, { DUR_LG, 6 }, { DUR_BR, 4 } } } } },
+                            { { DURATION_1, 2 }, { DURATION_2, 4 }, { DURATION_4, 6 }, { DURATION_8, 4 },
+                                { DURATION_16, 6 }, { DURATION_32, 6 }, { DURATION_64, 8 }, { DURATION_128, 8 },
+                                { DURATION_long, 6 }, { DURATION_breve, 4 } } } } },
                   { RLP_restOnBottomLayer,
                       { { RNP_noteInSpace,
-                            { { DUR_1, -5 }, { DUR_2, -5 }, { DUR_4, -5 }, { DUR_8, -5 }, { DUR_16, -5 },
-                                { DUR_32, -7 }, { DUR_64, -7 }, { DUR_128, -9 }, { DUR_LG, -5 }, { DUR_BR, -5 } } },
+                            { { DURATION_1, -5 }, { DURATION_2, -5 }, { DURATION_4, -5 }, { DURATION_8, -5 },
+                                { DURATION_16, -5 }, { DURATION_32, -7 }, { DURATION_64, -7 }, { DURATION_128, -9 },
+                                { DURATION_long, -5 }, { DURATION_breve, -5 } } },
                           { RNP_noteOnLine,
-                              { { DUR_1, -6 }, { DUR_2, -6 }, { DUR_4, -6 }, { DUR_8, -4 }, { DUR_16, -4 },
-                                  { DUR_32, -6 }, { DUR_64, -6 }, { DUR_128, -8 }, { DUR_LG, -6 },
-                                  { DUR_BR, -6 } } } } } } },
+                              { { DURATION_1, -6 }, { DURATION_2, -6 }, { DURATION_4, -6 }, { DURATION_8, -4 },
+                                  { DURATION_16, -4 }, { DURATION_32, -6 }, { DURATION_64, -6 }, { DURATION_128, -8 },
+                                  { DURATION_long, -6 }, { DURATION_breve, -6 } } } } } } },
             { RA_s,
                 { { RLP_restOnTopLayer,
                       { { RNP_noteInSpace,
-                            { { DUR_1, 3 }, { DUR_2, 5 }, { DUR_4, 7 }, { DUR_8, 5 }, { DUR_16, 7 }, { DUR_32, 7 },
-                                { DUR_64, 9 }, { DUR_128, 9 }, { DUR_LG, 5 }, { DUR_BR, 5 } } },
+                            { { DURATION_1, 3 }, { DURATION_2, 5 }, { DURATION_4, 7 }, { DURATION_8, 5 },
+                                { DURATION_16, 7 }, { DURATION_32, 7 }, { DURATION_64, 9 }, { DURATION_128, 9 },
+                                { DURATION_long, 5 }, { DURATION_breve, 5 } } },
                           { RNP_noteOnLine,
-                              { { DUR_1, 2 }, { DUR_2, 4 }, { DUR_4, 6 }, { DUR_8, 6 }, { DUR_16, 8 }, { DUR_32, 8 },
-                                  { DUR_64, 10 }, { DUR_128, 10 }, { DUR_LG, 6 }, { DUR_BR, 4 } } } } },
+                              { { DURATION_1, 2 }, { DURATION_2, 4 }, { DURATION_4, 6 }, { DURATION_8, 6 },
+                                  { DURATION_16, 8 }, { DURATION_32, 8 }, { DURATION_64, 10 }, { DURATION_128, 10 },
+                                  { DURATION_long, 6 }, { DURATION_breve, 4 } } } } },
                     { RLP_restOnBottomLayer,
                         { { RNP_noteInSpace,
-                              { { DUR_1, -5 }, { DUR_2, -5 }, { DUR_4, -5 }, { DUR_8, -5 }, { DUR_16, -5 },
-                                  { DUR_32, -7 }, { DUR_64, -7 }, { DUR_128, -9 }, { DUR_LG, -5 }, { DUR_BR, -5 } } },
+                              { { DURATION_1, -5 }, { DURATION_2, -5 }, { DURATION_4, -5 }, { DURATION_8, -5 },
+                                  { DURATION_16, -5 }, { DURATION_32, -7 }, { DURATION_64, -7 }, { DURATION_128, -9 },
+                                  { DURATION_long, -5 }, { DURATION_breve, -5 } } },
                             { RNP_noteOnLine,
-                                { { DUR_1, -6 }, { DUR_2, -6 }, { DUR_4, -6 }, { DUR_8, -6 }, { DUR_16, -6 },
-                                    { DUR_32, -6 }, { DUR_64, -6 }, { DUR_128, -8 }, { DUR_LG, -6 },
-                                    { DUR_BR, -6 } } } } } } },
+                                { { DURATION_1, -6 }, { DURATION_2, -6 }, { DURATION_4, -6 }, { DURATION_8, -6 },
+                                    { DURATION_16, -6 }, { DURATION_32, -6 }, { DURATION_64, -6 }, { DURATION_128, -8 },
+                                    { DURATION_long, -6 }, { DURATION_breve, -6 } } } } } } },
             { RA_f,
                 { { RLP_restOnTopLayer,
                       { { RNP_noteInSpace,
-                            { { DUR_1, 3 }, { DUR_2, 5 }, { DUR_4, 5 }, { DUR_8, 5 }, { DUR_16, 7 }, { DUR_32, 7 },
-                                { DUR_64, 9 }, { DUR_128, 9 }, { DUR_LG, 5 }, { DUR_BR, 5 } } },
+                            { { DURATION_1, 3 }, { DURATION_2, 5 }, { DURATION_4, 5 }, { DURATION_8, 5 },
+                                { DURATION_16, 7 }, { DURATION_32, 7 }, { DURATION_64, 9 }, { DURATION_128, 9 },
+                                { DURATION_long, 5 }, { DURATION_breve, 5 } } },
                           { RNP_noteOnLine,
-                              { { DUR_1, 4 }, { DUR_2, 4 }, { DUR_4, 6 }, { DUR_8, 6 }, { DUR_16, 8 }, { DUR_32, 8 },
-                                  { DUR_64, 10 }, { DUR_128, 10 }, { DUR_LG, 6 }, { DUR_BR, 4 } } } } },
+                              { { DURATION_1, 4 }, { DURATION_2, 4 }, { DURATION_4, 6 }, { DURATION_8, 6 },
+                                  { DURATION_16, 8 }, { DURATION_32, 8 }, { DURATION_64, 10 }, { DURATION_128, 10 },
+                                  { DURATION_long, 6 }, { DURATION_breve, 4 } } } } },
                     { RLP_restOnBottomLayer,
                         { { RNP_noteInSpace,
-                              { { DUR_1, -5 }, { DUR_2, -5 }, { DUR_4, -5 }, { DUR_8, -5 }, { DUR_16, -5 },
-                                  { DUR_32, -7 }, { DUR_64, -7 }, { DUR_128, -9 }, { DUR_LG, -5 }, { DUR_BR, -5 } } },
+                              { { DURATION_1, -5 }, { DURATION_2, -5 }, { DURATION_4, -5 }, { DURATION_8, -5 },
+                                  { DURATION_16, -5 }, { DURATION_32, -7 }, { DURATION_64, -7 }, { DURATION_128, -9 },
+                                  { DURATION_long, -5 }, { DURATION_breve, -5 } } },
                             { RNP_noteOnLine,
-                                { { DUR_1, -6 }, { DUR_2, -6 }, { DUR_4, -6 }, { DUR_8, -4 }, { DUR_16, -4 },
-                                    { DUR_32, -6 }, { DUR_64, -6 }, { DUR_128, -8 }, { DUR_LG, -6 },
-                                    { DUR_BR, -6 } } } } } } },
+                                { { DURATION_1, -6 }, { DURATION_2, -6 }, { DURATION_4, -6 }, { DURATION_8, -4 },
+                                    { DURATION_16, -4 }, { DURATION_32, -6 }, { DURATION_64, -6 }, { DURATION_128, -8 },
+                                    { DURATION_long, -6 }, { DURATION_breve, -6 } } } } } } },
             { RA_x,
                 { { RLP_restOnTopLayer,
                       { { RNP_noteInSpace,
-                            { { DUR_1, 3 }, { DUR_2, 3 }, { DUR_4, 5 }, { DUR_8, 5 }, { DUR_16, 7 }, { DUR_32, 7 },
-                                { DUR_64, 9 }, { DUR_128, 9 }, { DUR_LG, 5 }, { DUR_BR, 5 } } },
+                            { { DURATION_1, 3 }, { DURATION_2, 3 }, { DURATION_4, 5 }, { DURATION_8, 5 },
+                                { DURATION_16, 7 }, { DURATION_32, 7 }, { DURATION_64, 9 }, { DURATION_128, 9 },
+                                { DURATION_long, 5 }, { DURATION_breve, 5 } } },
                           { RNP_noteOnLine,
-                              { { DUR_1, 2 }, { DUR_2, 4 }, { DUR_4, 6 }, { DUR_8, 6 }, { DUR_16, 8 }, { DUR_32, 8 },
-                                  { DUR_64, 10 }, { DUR_128, 10 }, { DUR_LG, 6 }, { DUR_BR, 4 } } } } },
+                              { { DURATION_1, 2 }, { DURATION_2, 4 }, { DURATION_4, 6 }, { DURATION_8, 6 },
+                                  { DURATION_16, 8 }, { DURATION_32, 8 }, { DURATION_64, 10 }, { DURATION_128, 10 },
+                                  { DURATION_long, 6 }, { DURATION_breve, 4 } } } } },
                     { RLP_restOnBottomLayer,
                         { { RNP_noteInSpace,
-                              { { DUR_1, -5 }, { DUR_2, -5 }, { DUR_4, -5 }, { DUR_8, -5 }, { DUR_16, -5 },
-                                  { DUR_32, -7 }, { DUR_64, -7 }, { DUR_128, -9 }, { DUR_LG, -5 }, { DUR_BR, -5 } } },
+                              { { DURATION_1, -5 }, { DURATION_2, -5 }, { DURATION_4, -5 }, { DURATION_8, -5 },
+                                  { DURATION_16, -5 }, { DURATION_32, -7 }, { DURATION_64, -7 }, { DURATION_128, -9 },
+                                  { DURATION_long, -5 }, { DURATION_breve, -5 } } },
                             { RNP_noteOnLine,
-                                { { DUR_1, -6 }, { DUR_2, -4 }, { DUR_4, -6 }, { DUR_8, -4 }, { DUR_16, -4 },
-                                    { DUR_32, -6 }, { DUR_64, -6 }, { DUR_128, -8 }, { DUR_LG, -6 },
-                                    { DUR_BR, -6 } } } } } } },
+                                { { DURATION_1, -6 }, { DURATION_2, -4 }, { DURATION_4, -6 }, { DURATION_8, -4 },
+                                    { DURATION_16, -4 }, { DURATION_32, -6 }, { DURATION_64, -6 }, { DURATION_128, -8 },
+                                    { DURATION_long, -6 }, { DURATION_breve, -6 } } } } } } },
             { RA_n,
                 { { RLP_restOnTopLayer,
                       { { RNP_noteInSpace,
-                            { { DUR_1, 3 }, { DUR_2, 3 }, { DUR_4, 5 }, { DUR_8, 5 }, { DUR_16, 7 }, { DUR_32, 7 },
-                                { DUR_64, 9 }, { DUR_128, 9 }, { DUR_LG, 5 }, { DUR_BR, 5 } } },
+                            { { DURATION_1, 3 }, { DURATION_2, 3 }, { DURATION_4, 5 }, { DURATION_8, 5 },
+                                { DURATION_16, 7 }, { DURATION_32, 7 }, { DURATION_64, 9 }, { DURATION_128, 9 },
+                                { DURATION_long, 5 }, { DURATION_breve, 5 } } },
                           { RNP_noteOnLine,
-                              { { DUR_1, 2 }, { DUR_2, 6 }, { DUR_4, 6 }, { DUR_8, 6 }, { DUR_16, 8 }, { DUR_32, 8 },
-                                  { DUR_64, 10 }, { DUR_128, 10 }, { DUR_LG, 6 }, { DUR_BR, 4 } } } } },
+                              { { DURATION_1, 2 }, { DURATION_2, 6 }, { DURATION_4, 6 }, { DURATION_8, 6 },
+                                  { DURATION_16, 8 }, { DURATION_32, 8 }, { DURATION_64, 10 }, { DURATION_128, 10 },
+                                  { DURATION_long, 6 }, { DURATION_breve, 4 } } } } },
                     { RLP_restOnBottomLayer,
                         { { RNP_noteInSpace,
-                              { { DUR_1, -7 }, { DUR_2, -5 }, { DUR_4, -7 }, { DUR_8, -5 }, { DUR_16, -5 },
-                                  { DUR_32, -7 }, { DUR_64, -7 }, { DUR_128, -9 }, { DUR_LG, -5 }, { DUR_BR, -5 } } },
+                              { { DURATION_1, -7 }, { DURATION_2, -5 }, { DURATION_4, -7 }, { DURATION_8, -5 },
+                                  { DURATION_16, -5 }, { DURATION_32, -7 }, { DURATION_64, -7 }, { DURATION_128, -9 },
+                                  { DURATION_long, -5 }, { DURATION_breve, -5 } } },
                             { RNP_noteOnLine,
-                                { { DUR_1, -6 }, { DUR_2, -6 }, { DUR_4, -6 }, { DUR_8, -6 }, { DUR_16, -6 },
-                                    { DUR_32, -6 }, { DUR_64, -6 }, { DUR_128, -8 }, { DUR_LG, -6 },
-                                    { DUR_BR, -6 } } } } } } } } },
+                                { { DURATION_1, -6 }, { DURATION_2, -6 }, { DURATION_4, -6 }, { DURATION_8, -6 },
+                                    { DURATION_16, -6 }, { DURATION_32, -6 }, { DURATION_64, -6 }, { DURATION_128, -8 },
+                                    { DURATION_long, -6 }, { DURATION_breve, -6 } } } } } } } } },
     { RL_sameLayer,
         { { RA_none,
             { { RLP_restOnTopLayer,
                   { { RNP_noteInSpace,
-                        { { DUR_1, -1 }, { DUR_2, 1 }, { DUR_4, 1 }, { DUR_8, 1 }, { DUR_16, 3 }, { DUR_32, 3 },
-                            { DUR_64, 5 }, { DUR_128, 5 }, { DUR_LG, 3 }, { DUR_BR, 1 } } },
+                        { { DURATION_1, -1 }, { DURATION_2, 1 }, { DURATION_4, 1 }, { DURATION_8, 1 },
+                            { DURATION_16, 3 }, { DURATION_32, 3 }, { DURATION_64, 5 }, { DURATION_128, 5 },
+                            { DURATION_long, 3 }, { DURATION_breve, 1 } } },
                       { RNP_noteOnLine,
-                          { { DUR_1, 0 }, { DUR_2, 0 }, { DUR_4, 2 }, { DUR_8, 2 }, { DUR_16, 2 }, { DUR_32, 2 },
-                              { DUR_64, 4 }, { DUR_128, 4 }, { DUR_LG, 2 }, { DUR_BR, 2 } } } } },
+                          { { DURATION_1, 0 }, { DURATION_2, 0 }, { DURATION_4, 2 }, { DURATION_8, 2 },
+                              { DURATION_16, 2 }, { DURATION_32, 2 }, { DURATION_64, 4 }, { DURATION_128, 4 },
+                              { DURATION_long, 2 }, { DURATION_breve, 2 } } } } },
                 { RLP_restOnBottomLayer,
                     { { RNP_noteInSpace,
-                          { { DUR_1, -3 }, { DUR_2, -1 }, { DUR_4, -1 }, { DUR_8, -1 }, { DUR_16, -1 }, { DUR_32, -3 },
-                              { DUR_64, -3 }, { DUR_128, -5 }, { DUR_LG, -3 }, { DUR_BR, -3 } } },
+                          { { DURATION_1, -3 }, { DURATION_2, -1 }, { DURATION_4, -1 }, { DURATION_8, -1 },
+                              { DURATION_16, -1 }, { DURATION_32, -3 }, { DURATION_64, -3 }, { DURATION_128, -5 },
+                              { DURATION_long, -3 }, { DURATION_breve, -3 } } },
                         { RNP_noteOnLine,
-                            { { DUR_1, -2 }, { DUR_2, -2 }, { DUR_4, -2 }, { DUR_8, -2 }, { DUR_16, -2 },
-                                { DUR_32, -4 }, { DUR_64, -4 }, { DUR_128, -6 }, { DUR_LG, -2 },
-                                { DUR_BR, -2 } } } } } } } } }
+                            { { DURATION_1, -2 }, { DURATION_2, -2 }, { DURATION_4, -2 }, { DURATION_8, -2 },
+                                { DURATION_16, -2 }, { DURATION_32, -4 }, { DURATION_64, -4 }, { DURATION_128, -6 },
+                                { DURATION_long, -2 }, { DURATION_breve, -2 } } } } } } } } }
 };
 
 // helper function for conversion
@@ -244,31 +262,31 @@ char32_t Rest::GetRestGlyph(const int duration) const
 
     if (this->IsMensuralDur()) {
         switch (duration) {
-            case DUR_MX: return SMUFL_E9F0_mensuralRestMaxima; break;
-            case DUR_LG: return SMUFL_E9F2_mensuralRestLongaImperfecta; break;
-            case DUR_BR: return SMUFL_E9F3_mensuralRestBrevis; break;
-            case DUR_1: return SMUFL_E9F4_mensuralRestSemibrevis; break;
-            case DUR_2: return SMUFL_E9F5_mensuralRestMinima; break;
-            case DUR_4: return SMUFL_E9F6_mensuralRestSemiminima; break;
-            case DUR_8: return SMUFL_E9F7_mensuralRestFusa; break;
-            case DUR_16: return SMUFL_E9F8_mensuralRestSemifusa; break;
+            case DURATION_maxima: return SMUFL_E9F0_mensuralRestMaxima; break;
+            case DURATION_long: return SMUFL_E9F2_mensuralRestLongaImperfecta; break;
+            case DURATION_breve: return SMUFL_E9F3_mensuralRestBrevis; break;
+            case DURATION_1: return SMUFL_E9F4_mensuralRestSemibrevis; break;
+            case DURATION_2: return SMUFL_E9F5_mensuralRestMinima; break;
+            case DURATION_4: return SMUFL_E9F6_mensuralRestSemiminima; break;
+            case DURATION_8: return SMUFL_E9F7_mensuralRestFusa; break;
+            case DURATION_16: return SMUFL_E9F8_mensuralRestSemifusa; break;
         }
     }
     else {
         switch (duration) {
-            case DUR_LG: return SMUFL_E4E1_restLonga; break;
-            case DUR_BR: return SMUFL_E4E2_restDoubleWhole; break;
-            case DUR_1: return SMUFL_E4E3_restWhole; break;
-            case DUR_2: return SMUFL_E4E4_restHalf; break;
-            case DUR_4: return SMUFL_E4E5_restQuarter; break;
-            case DUR_8: return SMUFL_E4E6_rest8th; break;
-            case DUR_16: return SMUFL_E4E7_rest16th; break;
-            case DUR_32: return SMUFL_E4E8_rest32nd; break;
-            case DUR_64: return SMUFL_E4E9_rest64th; break;
-            case DUR_128: return SMUFL_E4EA_rest128th; break;
-            case DUR_256: return SMUFL_E4EB_rest256th; break;
-            case DUR_512: return SMUFL_E4EC_rest512th; break;
-            case DUR_1024: return SMUFL_E4ED_rest1024th; break;
+            case DURATION_long: return SMUFL_E4E1_restLonga; break;
+            case DURATION_breve: return SMUFL_E4E2_restDoubleWhole; break;
+            case DURATION_1: return SMUFL_E4E3_restWhole; break;
+            case DURATION_2: return SMUFL_E4E4_restHalf; break;
+            case DURATION_4: return SMUFL_E4E5_restQuarter; break;
+            case DURATION_8: return SMUFL_E4E6_rest8th; break;
+            case DURATION_16: return SMUFL_E4E7_rest16th; break;
+            case DURATION_32: return SMUFL_E4E8_rest32nd; break;
+            case DURATION_64: return SMUFL_E4E9_rest64th; break;
+            case DURATION_128: return SMUFL_E4EA_rest128th; break;
+            case DURATION_256: return SMUFL_E4EB_rest256th; break;
+            case DURATION_512: return SMUFL_E4EC_rest512th; break;
+            case DURATION_1024: return SMUFL_E4ED_rest1024th; break;
         }
     }
 
@@ -543,8 +561,8 @@ int Rest::GetRestOffsetFromOptions(
 {
     int duration = this->GetActualDur();
     // Make sure we are in the boundaries of g_defaultRests
-    if (duration > DUR_128) duration = DUR_128;
-    if (duration < DUR_LG) duration = DUR_LG;
+    if (duration > DURATION_128) duration = DURATION_128;
+    if (duration < DURATION_long) duration = DURATION_long;
     return g_defaultRests.at(layer)
         .at(RL_sameLayer == layer ? location.second : RA_none)
         .at(isTopLayer ? RLP_restOnTopLayer : RLP_restOnBottomLayer)
diff --git a/src/view_beam.cpp b/src/view_beam.cpp
index 34d04de9f06..566234d1cc3 100644
--- a/src/view_beam.cpp
+++ b/src/view_beam.cpp
@@ -152,7 +152,7 @@ void View::DrawFTremSegment(DeviceContext *dc, Staff *staff, FTrem *fTrem)
     if (!durationElement) return;
     const int dur = durationElement->GetDur();
 
-    if (dur > DUR_1) {
+    if (dur > DURATION_1) {
         // Adjust the x position of the first and last element for taking into account the stem width
         firstElement->m_x -= (m_doc->GetDrawingStemWidth(staff->m_drawingStaffSize)) / 2;
         secondElement->m_x += (m_doc->GetDrawingStemWidth(staff->m_drawingStaffSize)) / 2;
@@ -177,7 +177,7 @@ void View::DrawFTremSegment(DeviceContext *dc, Staff *staff, FTrem *fTrem)
 
     int space = m_doc->GetDrawingBeamWidth(staff->m_drawingStaffSize, fTrem->m_cueSize);
     // for non-stem notes the bar should be shortened
-    if (dur < DUR_2) {
+    if (dur < DURATION_2) {
         if (fTrem->m_drawingPlace == BEAMPLACE_below) x1 += 2 * space;
         y1 += 2 * space * fTrem->m_beamSegment.m_beamSlope;
         if (fTrem->m_drawingPlace == BEAMPLACE_above) x2 -= 2 * space;
@@ -186,7 +186,7 @@ void View::DrawFTremSegment(DeviceContext *dc, Staff *staff, FTrem *fTrem)
         fullBars = allBars;
         floatingBars = 0;
     }
-    else if ((dur > DUR_2) && !floatingBars) {
+    else if ((dur > DURATION_2) && !floatingBars) {
         fullBars = dur - 4;
         floatingBars = allBars - fullBars;
     }
@@ -289,12 +289,12 @@ void View::DrawBeamSegment(
     }
     int noteCount = (int)noteIndexes.size();
 
-    int durRef = DUR_8;
-    int durRef2 = DUR_16;
+    int durRef = DURATION_8;
+    int durRef2 = DURATION_16;
 
     if (staff->IsTabLuteFrench() || staff->IsTabLuteItalian()) {
-        durRef = DUR_4;
-        durRef2 = DUR_8;
+        durRef = DURATION_4;
+        durRef2 = DURATION_8;
     }
 
     int barY = 0;
diff --git a/src/view_element.cpp b/src/view_element.cpp
index ee980ff0d13..0f15863eab9 100644
--- a/src/view_element.cpp
+++ b/src/view_element.cpp
@@ -285,7 +285,7 @@ void View::DrawAccid(DeviceContext *dc, LayerElement *element, Layer *layer, Sta
                 if (accid->GetFunc() != accidLog_FUNC_edit) onStaff = (accid->GetOnstaff() != BOOLEAN_false);
                 const int verticalCenter = staffTop - (staff->m_drawingLines - 1) * unit;
                 const data_STEMDIRECTION stemDir = this->GetMensuralStemDir(layer, note, verticalCenter);
-                if ((drawingDur > DUR_1) || (drawingDur < DUR_BR)) {
+                if ((drawingDur > DURATION_1) || (drawingDur < DURATION_breve)) {
                     if (stemDir == STEMDIRECTION_up) {
                         noteTop = note->GetDrawingY() + unit * STANDARD_STEMLENGTH;
                         noteBottom -= unit;
@@ -600,7 +600,7 @@ void View::DrawChordCluster(DeviceContext *dc, Chord *chord, Layer *layer, Staff
 
     dc->StartCustomGraphic("notehead");
 
-    if (chord->GetActualDur() < DUR_4) {
+    if (chord->GetActualDur() < DURATION_4) {
         const int line = unit / 2;
         this->DrawNotFilledRectangle(dc, x + line / 2, y1 - line / 2, x + width - line / 2, y2 + line / 2, line, 0);
     }
@@ -1459,19 +1459,19 @@ void View::DrawNote(DeviceContext *dc, LayerElement *element, Layer *layer, Staf
             if (note->IsInBeam() && !dc->Is(BBOX_DEVICE_CONTEXT)) {
                 LogWarning("Missing duration for note '%s' in beam", note->GetID().c_str());
             }
-            drawingDur = DUR_4;
+            drawingDur = DURATION_4;
         }
-        if (drawingDur < DUR_BR) {
+        if (drawingDur < DURATION_breve) {
             this->DrawMaximaToBrevis(dc, noteY, element, layer, staff);
         }
         else {
             // Whole notes
             char32_t fontNo;
             if (note->GetColored() == BOOLEAN_true) {
-                if (DUR_1 == drawingDur) {
+                if (DURATION_1 == drawingDur) {
                     fontNo = SMUFL_E0FA_noteheadWholeFilled;
                 }
-                else if (DUR_2 == drawingDur) {
+                else if (DURATION_2 == drawingDur) {
                     fontNo = SMUFL_E0FB_noteheadHalfFilled;
                 }
                 else {
@@ -1544,7 +1544,7 @@ void View::DrawRest(DeviceContext *dc, LayerElement *element, Layer *layer, Staf
         if (!dc->Is(BBOX_DEVICE_CONTEXT)) {
             LogWarning("Missing duration for rest '%s'", rest->GetID().c_str());
         }
-        drawingDur = DUR_4;
+        drawingDur = DURATION_4;
     }
     const char32_t drawingGlyph = rest->GetRestGlyph(drawingDur);
 
@@ -1553,7 +1553,7 @@ void View::DrawRest(DeviceContext *dc, LayerElement *element, Layer *layer, Staf
 
     this->DrawSmuflCode(dc, x, y, drawingGlyph, staffSize, drawingCueSize);
 
-    if ((drawingDur == DUR_1 || drawingDur == DUR_2 || drawingDur == DUR_BR)) {
+    if ((drawingDur == DURATION_1 || drawingDur == DURATION_2 || drawingDur == DURATION_breve)) {
         const int width = m_doc->GetGlyphWidth(drawingGlyph, staffSize, drawingCueSize);
         int ledgerLineThickness
             = m_doc->GetOptions()->m_ledgerLineThickness.GetValue() * m_doc->GetDrawingUnit(staffSize);
@@ -1569,14 +1569,14 @@ void View::DrawRest(DeviceContext *dc, LayerElement *element, Layer *layer, Staf
 
         dc->StartCustomGraphic("ledgerLines");
         // single legder line for half and whole rests
-        if ((drawingDur == DUR_1 || drawingDur == DUR_2) && (y > topMargin || y < bottomMargin)) {
+        if ((drawingDur == DURATION_1 || drawingDur == DURATION_2) && (y > topMargin || y < bottomMargin)) {
             dc->DeactivateGraphicX();
             this->DrawHorizontalLine(
                 dc, x - ledgerLineExtension, x + width + ledgerLineExtension, y, ledgerLineThickness);
             dc->ReactivateGraphic();
         }
         // double ledger line for breve rests
-        else if (drawingDur == DUR_BR && (y >= topMargin || y <= bottomMargin)) {
+        else if (drawingDur == DURATION_breve && (y >= topMargin || y <= bottomMargin)) {
             const int height = m_doc->GetGlyphHeight(drawingGlyph, staffSize, drawingCueSize);
             dc->DeactivateGraphicX();
             if (y != topMargin) {
@@ -1623,7 +1623,7 @@ void View::DrawStem(DeviceContext *dc, LayerElement *element, Layer *layer, Staf
     // We check if this belongs to a mensural note
     Note *parent = vrv_cast<Note *>(stem->GetFirstAncestor(NOTE));
     if (parent && parent->IsMensuralDur()) {
-        if (parent->GetDrawingDur() > DUR_1) {
+        if (parent->GetDrawingDur() > DURATION_1) {
             /************** Stem/notehead direction: **************/
             const int staffCenter
                 = staff->GetDrawingY() - m_doc->GetDrawingUnit(staff->m_drawingStaffSize) * (staff->m_drawingLines - 1);
@@ -1717,7 +1717,8 @@ void View::DrawStemMod(DeviceContext *dc, LayerElement *element, Staff *staff)
 
     // calculate position for the stem mod
     const int y = note->GetDrawingY() + stemRelY;
-    const int x = (drawingDur <= DUR_1) ? childElement->GetDrawingX() + childElement->GetDrawingRadius(m_doc) : stemX;
+    const int x
+        = (drawingDur <= DURATION_1) ? childElement->GetDrawingX() + childElement->GetDrawingRadius(m_doc) : stemX;
 
     if ((code != SMUFL_E645_vocalSprechgesang) || !element->Is(BTREM)) {
         int adjust = 0;
diff --git a/src/view_mensural.cpp b/src/view_mensural.cpp
index 2423f321eaf..a02bc5f9cad 100644
--- a/src/view_mensural.cpp
+++ b/src/view_mensural.cpp
@@ -58,7 +58,7 @@ void View::DrawMensuralNote(DeviceContext *dc, LayerElement *element, Layer *lay
     if (note->IsInLigature() && !m_options->m_ligatureAsBracket.GetValue()) {
         this->DrawLigatureNote(dc, element, layer, staff);
     }
-    else if (drawingDur < DUR_1) {
+    else if (drawingDur < DURATION_1) {
         this->DrawMaximaToBrevis(dc, yNote, element, layer, staff);
     }
     // Semibrevis and shorter
@@ -169,7 +169,7 @@ void View::DrawMensuralStem(DeviceContext *dc, Note *note, Staff *staff, data_ST
 
     /* In black notation, the semiminima gets one flag; in white notation, it gets none.
         In both cases, as in CWMN, each shorter duration gets one additional flag. */
-    const int nbFlags = (mensural_black ? drawingDur - DUR_2 : drawingDur - DUR_4);
+    const int nbFlags = (mensural_black ? drawingDur - DURATION_2 : drawingDur - DURATION_4);
 
     // SMuFL's mensural stems are not centered
     const int halfStemWidth
@@ -218,7 +218,7 @@ void View::DrawMaximaToBrevis(DeviceContext *dc, int y, LayerElement *element, L
     const int staffSize = staff->m_drawingStaffSize;
 
     int shape = LIGATURE_DEFAULT;
-    if (note->GetActualDur() != DUR_BR) {
+    if (note->GetActualDur() != DURATION_breve) {
         bool up = false;
         // Mensural notes have no Stem child - rely on the MEI @stem.dir
         if (note->GetStemDir() != STEMDIRECTION_NONE) {
@@ -263,7 +263,7 @@ void View::DrawMaximaToBrevis(DeviceContext *dc, int y, LayerElement *element, L
     // serifs and / or stem
     this->DrawFilledRectangle(dc, topLeft.x, sides[0], topLeft.x + stemWidth, sides[1]);
 
-    if (note->GetActualDur() != DUR_BR) {
+    if (note->GetActualDur() != DURATION_breve) {
         // Right side is a stem - end the notehead first
         dc->EndCustomGraphic();
         dc->StartCustomGraphic("stem");
@@ -449,7 +449,7 @@ void View::DrawDotInLigature(DeviceContext *dc, LayerElement *element, Layer *la
         isVerticalDot = !isLast && (shape & LIGATURE_OBLIQUE);
     }
     else {
-        if (note->GetActualDur() == DUR_1) shiftMultiplier = 3.5;
+        if (note->GetActualDur() == DURATION_1) shiftMultiplier = 3.5;
     }
 
     int y = note->GetDrawingY();
@@ -484,7 +484,7 @@ void View::DrawPlica(DeviceContext *dc, LayerElement *element, Layer *layer, Sta
     const bool isMensuralBlack = (staff->m_drawingNotationType == NOTATIONTYPE_mensural_black);
     const int stemWidth = m_doc->GetDrawingStemWidth(staff->m_drawingStaffSize);
 
-    const bool isLonga = (note->GetActualDur() == DUR_LG);
+    const bool isLonga = (note->GetActualDur() == DURATION_long);
     const bool up = (plica->GetDir() == STEMDIRECTION_basic_up);
 
     int shape = LIGATURE_DEFAULT;
@@ -722,7 +722,7 @@ data_STEMDIRECTION View::GetMensuralStemDir(Layer *layer, Note *note, int vertic
         stemDir = layerStemDir;
     }
     else {
-        if (drawingDur < DUR_1) {
+        if (drawingDur < DURATION_1) {
             stemDir = STEMDIRECTION_down;
         }
         else {
diff --git a/src/view_tab.cpp b/src/view_tab.cpp
index dd28a98fe09..6fa926578e3 100644
--- a/src/view_tab.cpp
+++ b/src/view_tab.cpp
@@ -177,13 +177,13 @@ void View::DrawTabDurSym(DeviceContext *dc, LayerElement *element, Layer *layer,
     if (!tabGrp->IsInBeam() && !staff->IsTabGuitar()) {
         int symc = 0;
         switch (drawingDur) {
-            case DUR_1: symc = SMUFL_EBA6_luteDurationDoubleWhole; break; // 1 back flag */
-            case DUR_2: symc = SMUFL_EBA7_luteDurationWhole; break; // 0 flags
-            case DUR_4: symc = SMUFL_EBA8_luteDurationHalf; break; // 1 flag
-            case DUR_8: symc = SMUFL_EBA9_luteDurationQuarter; break; // 2 flags
-            case DUR_16: symc = SMUFL_EBAA_luteDuration8th; break; // 3 flags
-            case DUR_32: symc = SMUFL_EBAB_luteDuration16th; break; // 4 flags
-            case DUR_64: symc = SMUFL_EBAC_luteDuration32nd; break; // 5 flags
+            case DURATION_1: symc = SMUFL_EBA6_luteDurationDoubleWhole; break; // 1 back flag */
+            case DURATION_2: symc = SMUFL_EBA7_luteDurationWhole; break; // 0 flags
+            case DURATION_4: symc = SMUFL_EBA8_luteDurationHalf; break; // 1 flag
+            case DURATION_8: symc = SMUFL_EBA9_luteDurationQuarter; break; // 2 flags
+            case DURATION_16: symc = SMUFL_EBAA_luteDuration8th; break; // 3 flags
+            case DURATION_32: symc = SMUFL_EBAB_luteDuration16th; break; // 4 flags
+            case DURATION_64: symc = SMUFL_EBAC_luteDuration32nd; break; // 5 flags
             default: symc = SMUFL_EBA9_luteDurationQuarter; // 2 flags
         }
 
@@ -205,9 +205,11 @@ void View::DrawTabDurSym(DeviceContext *dc, LayerElement *element, Layer *layer,
         }
         else {
             // Vertical: the more flags the lower the dots
-            const int durfactor = DUR_64 - std::min(std::max(drawingDur, DUR_2), DUR_64) + 1;
-            static_assert(DUR_64 - DUR_2 + 1 == 6);
-            static_assert(DUR_64 - DUR_64 + 1 == 1);
+            int durOffset = (drawingDur > DURATION_2) ? drawingDur : DURATION_2;
+            durOffset = (durOffset < DURATION_64) ? durOffset : DURATION_64;
+            const int durfactor = DURATION_64 - durOffset + 1;
+            static_assert(DURATION_64 - DURATION_2 + 1 == 6);
+            static_assert(DURATION_64 - DURATION_64 + 1 == 1);
 
             y += m_doc->GetDrawingUnit(glyphSize) * stemDirFactor * durfactor * 2 / 5;
 

From 66a275b4cb069f237cb21c9e5d2ad1266d26aba7 Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Thu, 3 Oct 2024 14:27:41 +0200
Subject: [PATCH 087/105] Additional change to data_DURATION type

---
 include/vrv/durationinterface.h |  8 ++++----
 src/durationinterface.cpp       | 25 +++++++++++++------------
 src/view_element.cpp            |  4 ++--
 3 files changed, 19 insertions(+), 18 deletions(-)

diff --git a/include/vrv/durationinterface.h b/include/vrv/durationinterface.h
index b52e34d8953..4265aedcfd7 100644
--- a/include/vrv/durationinterface.h
+++ b/include/vrv/durationinterface.h
@@ -89,15 +89,15 @@ class DurationInterface : public Interface,
      * For mensural, we need to apply the DUR_MENSURAL_MASK
      */
     ///@{
-    int GetActualDur() const;
-    int GetActualDurGes() const;
+    data_DURATION GetActualDur() const;
+    data_DURATION GetActualDurGes() const;
     ///@}
 
     /**
      * If the element is part of a chord, return the chord actual duration, otherwise the note actual duration.
      * Since we need to check what the element is, we need to pass it as parameter.
      */
-    int GetNoteOrChordDur(const LayerElement *element) const;
+    data_DURATION GetNoteOrChordDur(const LayerElement *element) const;
 
     /**
      * Return true if the value is a mensural (DURATION_longa, brevis, etc.)
@@ -141,7 +141,7 @@ class DurationInterface : public Interface,
     /**
      * Calculate the actual duration => translate mensural values
      */
-    int CalcActualDur(data_DURATION dur) const;
+    data_DURATION CalcActualDur(data_DURATION dur) const;
 
 public:
     //
diff --git a/src/durationinterface.cpp b/src/durationinterface.cpp
index e39fad14f98..07aafa0f0c8 100644
--- a/src/durationinterface.cpp
+++ b/src/durationinterface.cpp
@@ -74,8 +74,8 @@ void DurationInterface::Reset()
 
 Fraction DurationInterface::GetInterfaceAlignmentDuration(int num, int numBase) const
 {
-    int noteDur = this->GetDurGes() != DURATION_NONE ? this->GetActualDurGes() : this->GetActualDur();
-    if (noteDur == DUR_NONE) noteDur = DURATION_4;
+    data_DURATION noteDur = (this->GetDurGes() != DURATION_NONE) ? this->GetActualDurGes() : this->GetActualDur();
+    if (noteDur == DURATION_NONE) noteDur = DURATION_4;
 
     if (this->HasNum()) num *= this->GetNum();
     if (this->HasNumbase()) numBase *= this->GetNumbase();
@@ -93,7 +93,7 @@ Fraction DurationInterface::GetInterfaceAlignmentDuration(int num, int numBase)
 double DurationInterface::GetInterfaceAlignmentMensuralDuration(int num, int numBase, const Mensur *currentMensur) const
 {
     int noteDur = this->GetDurGes() != DURATION_NONE ? this->GetActualDurGes() : this->GetActualDur();
-    if (noteDur == DUR_NONE) noteDur = DURATION_4;
+    if (noteDur == DURATION_NONE) noteDur = DURATION_4;
 
     if (!currentMensur) {
         LogWarning("No current mensur for calculating duration");
@@ -174,36 +174,37 @@ bool DurationInterface::IsLastInBeam(const LayerElement *noteOrRest) const
     return (noteOrRest == beam->GetListBack());
 }
 
-int DurationInterface::GetActualDur() const
+data_DURATION DurationInterface::GetActualDur() const
 {
     const data_DURATION dur = this->HasDur() ? this->GetDur() : this->GetDurDefault();
     return this->CalcActualDur(dur);
 }
 
-int DurationInterface::GetActualDurGes() const
+data_DURATION DurationInterface::GetActualDurGes() const
 {
     const data_DURATION dur = this->HasDurGes() ? this->GetDurGes() : DURATION_NONE;
     return this->CalcActualDur(dur);
 }
 
-int DurationInterface::CalcActualDur(data_DURATION dur) const
+data_DURATION DurationInterface::CalcActualDur(data_DURATION dur) const
 {
-    if (dur == DURATION_NONE) return DUR_NONE;
+    if (dur == DURATION_NONE) return DURATION_NONE;
     // maxima (-1) is a mensural only value
     if (dur == DURATION_maxima) return DURATION_maxima;
-    return (dur & DUR_MENSURAL_MASK);
+    // return (dur & DUR_MENSURAL_MASK);
+    return DURATION_breve;
 }
 
-int DurationInterface::GetNoteOrChordDur(const LayerElement *element) const
+data_DURATION DurationInterface::GetNoteOrChordDur(const LayerElement *element) const
 {
     if (element->Is(CHORD)) {
-        int duration = this->GetActualDur();
-        if (duration != DUR_NONE) return duration;
+        data_DURATION duration = this->GetActualDur();
+        if (duration != DURATION_NONE) return duration;
 
         const Chord *chord = vrv_cast<const Chord *>(element);
         for (const Note *note : { chord->GetTopNote(), chord->GetBottomNote() }) {
             duration = note->GetActualDur();
-            if (duration != DUR_NONE) {
+            if (duration != DURATION_NONE) {
                 return duration;
             }
         }
diff --git a/src/view_element.cpp b/src/view_element.cpp
index 0f15863eab9..fe041d76cc4 100644
--- a/src/view_element.cpp
+++ b/src/view_element.cpp
@@ -1455,7 +1455,7 @@ void View::DrawNote(DeviceContext *dc, LayerElement *element, Layer *layer, Staf
     if (!(note->GetHeadVisible() == BOOLEAN_false)) {
         /************** Noteheads: **************/
         int drawingDur = note->GetDrawingDur();
-        if (drawingDur == DUR_NONE) {
+        if (drawingDur == DURATION_NONE) {
             if (note->IsInBeam() && !dc->Is(BBOX_DEVICE_CONTEXT)) {
                 LogWarning("Missing duration for note '%s' in beam", note->GetID().c_str());
             }
@@ -1540,7 +1540,7 @@ void View::DrawRest(DeviceContext *dc, LayerElement *element, Layer *layer, Staf
     const bool drawingCueSize = rest->GetDrawingCueSize();
     const int staffSize = staff->GetDrawingStaffNotationSize();
     int drawingDur = rest->GetActualDur();
-    if (drawingDur == DUR_NONE) {
+    if (drawingDur == DURATION_NONE) {
         if (!dc->Is(BBOX_DEVICE_CONTEXT)) {
             LogWarning("Missing duration for rest '%s'", rest->GetID().c_str());
         }

From 0113f25c4f4215f90b02f3efcd1ba55e6d858b08 Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Thu, 3 Oct 2024 16:08:21 +0200
Subject: [PATCH 088/105] Change durations from int to data_DURATION for better
 typing

* 2 static_cast left in BeamSegment::AdjustBeamToFrenchStyle
* 1 static_cast left in BeamSegment::CalcStemDefiningNote
---
 include/vrv/beam.h                     |  4 ++--
 include/vrv/calcalignmentxposfunctor.h |  4 ++--
 include/vrv/calcstemfunctor.h          |  4 ++--
 include/vrv/drawinginterface.h         |  2 +-
 include/vrv/note.h                     |  4 ++--
 include/vrv/rest.h                     |  2 +-
 include/vrv/vrv.h                      |  7 ++++++
 src/beam.cpp                           | 33 ++++++++++++++------------
 src/calcalignmentxposfunctor.cpp       |  2 +-
 src/calcligatureorneumeposfunctor.cpp  |  4 ++--
 src/calcstemfunctor.cpp                |  2 +-
 src/drawinginterface.cpp               |  6 ++---
 src/durationinterface.cpp              | 16 +++++++++----
 src/layerelement.cpp                   |  4 ++--
 src/note.cpp                           |  6 ++---
 src/page.cpp                           |  2 +-
 src/preparedatafunctor.cpp             |  2 +-
 src/rest.cpp                           |  4 ++--
 src/view_beam.cpp                      |  6 ++---
 src/view_element.cpp                   |  6 ++---
 src/view_mensural.cpp                  |  6 ++---
 src/vrv.cpp                            | 10 ++++++++
 22 files changed, 82 insertions(+), 54 deletions(-)

diff --git a/include/vrv/beam.h b/include/vrv/beam.h
index 5ae37892360..855d556417d 100644
--- a/include/vrv/beam.h
+++ b/include/vrv/beam.h
@@ -156,7 +156,7 @@ class BeamSegment {
     int CalcMixedBeamCenterY(int step, int unit) const;
 
     // Helper to calculate location and duration of the note that would be setting highest/lowest point for the beam
-    std::tuple<int, int, int> CalcStemDefiningNote(const Staff *staff, data_BEAMPLACE place) const;
+    std::tuple<int, int, data_DURATION> CalcStemDefiningNote(const Staff *staff, data_BEAMPLACE place) const;
 
     // Calculate positioning for the horizontal beams
     void CalcHorizontalBeam(const Doc *doc, const Staff *staff, const BeamDrawingInterface *beamInterface);
@@ -454,7 +454,7 @@ class BeamElementCoord {
 
     int m_x;
     int m_yBeam; // y value of stem top position
-    int m_dur; // drawing duration
+    data_DURATION m_dur; // drawing duration
     int m_breaksec;
     int m_overlapMargin;
     int m_maxShortening; // maximum allowed shortening in half units
diff --git a/include/vrv/calcalignmentxposfunctor.h b/include/vrv/calcalignmentxposfunctor.h
index 5eda96449a8..2680dc611e5 100644
--- a/include/vrv/calcalignmentxposfunctor.h
+++ b/include/vrv/calcalignmentxposfunctor.h
@@ -40,7 +40,7 @@ class CalcAlignmentXPosFunctor : public DocFunctor {
      */
     ///@{
     int GetLongestActualDur() const { return m_longestActualDur; }
-    void SetLongestActualDur(int dur) { m_longestActualDur = dur; }
+    void SetLongestActualDur(data_DURATION dur) { m_longestActualDur = dur; }
     ///@}
 
     /*
@@ -65,7 +65,7 @@ class CalcAlignmentXPosFunctor : public DocFunctor {
     // The previous x rel position
     int m_previousXRel;
     // Duration of the longest note
-    int m_longestActualDur;
+    data_DURATION m_longestActualDur;
     // The estimated justification ratio of the system
     double m_estimatedJustificationRatio;
     // The last alignment that was not timestamp-only
diff --git a/include/vrv/calcstemfunctor.h b/include/vrv/calcstemfunctor.h
index ee414083668..c90f8db60a8 100644
--- a/include/vrv/calcstemfunctor.h
+++ b/include/vrv/calcstemfunctor.h
@@ -62,7 +62,7 @@ class CalcStemFunctor : public DocFunctor {
      * Adjusts the flag placement and stem length if they are crossing notehead or ledger lines
      */
     void AdjustFlagPlacement(
-        const Doc *doc, Stem *stem, Flag *flag, int staffSize, int verticalCenter, int duration) const;
+        const Doc *doc, Stem *stem, Flag *flag, int staffSize, int verticalCenter, data_DURATION duration) const;
 
 public:
     //
@@ -72,7 +72,7 @@ class CalcStemFunctor : public DocFunctor {
     // The vertical center of the staff
     int m_verticalCenter;
     // The actual duration of the chord / note
-    int m_dur;
+    data_DURATION m_dur;
     // The flag for grace notes (stem is not extended)
     bool m_isGraceNote;
     // The flag for stem.sameas notes
diff --git a/include/vrv/drawinginterface.h b/include/vrv/drawinginterface.h
index 32d2e88e253..49b85b6c421 100644
--- a/include/vrv/drawinginterface.h
+++ b/include/vrv/drawinginterface.h
@@ -186,7 +186,7 @@ class BeamDrawingInterface : public ObjectListInterface {
     Staff *m_crossStaffContent;
     data_STAFFREL_basic m_crossStaffRel;
     bool m_isSpanningElement;
-    int m_shortestDur;
+    data_DURATION m_shortestDur;
     data_STEMDIRECTION m_notesStemDir;
     data_BEAMPLACE m_drawingPlace;
     Staff *m_beamStaff;
diff --git a/include/vrv/note.h b/include/vrv/note.h
index b6b8261ac6f..2ab4cc48bcf 100644
--- a/include/vrv/note.h
+++ b/include/vrv/note.h
@@ -146,7 +146,7 @@ class Note : public LayerElement,
     ///@{
     Chord *IsChordTone();
     const Chord *IsChordTone() const;
-    int GetDrawingDur() const;
+    data_DURATION GetDrawingDur() const;
     bool IsNoteGroupExtreme() const; // used to find if it is the highest or lowest note in a note group
     ///@}
 
@@ -211,7 +211,7 @@ class Note : public LayerElement,
     /**
      * Return a SMuFL code for the notehead
      */
-    char32_t GetNoteheadGlyph(const int duration) const;
+    char32_t GetNoteheadGlyph(const data_DURATION duration) const;
 
     /**
      * Check whether current note is enharmonic with another
diff --git a/include/vrv/rest.h b/include/vrv/rest.h
index b737eebc0d7..506df84a891 100644
--- a/include/vrv/rest.h
+++ b/include/vrv/rest.h
@@ -84,7 +84,7 @@ class Rest : public LayerElement,
      */
     ///@{
     char32_t GetRestGlyph() const;
-    char32_t GetRestGlyph(const int duration) const;
+    char32_t GetRestGlyph(const data_DURATION duration) const;
     ///@}
 
     /**
diff --git a/include/vrv/vrv.h b/include/vrv/vrv.h
index d103807cd37..9a8a80937cf 100644
--- a/include/vrv/vrv.h
+++ b/include/vrv/vrv.h
@@ -22,6 +22,7 @@
 #include <time.h>
 #endif
 
+#include "attdef.h"
 #include "atttypes.h"
 #include "toolkitdef.h"
 
@@ -131,6 +132,12 @@ std::string FromCamelCase(const std::string &s);
  */
 std::string ToCamelCase(const std::string &s);
 
+/*
+ * Min / Max for data_DURATION (std::min/max not possible)
+ */
+data_DURATION DurationMin(data_DURATION dur1, data_DURATION dur2);
+data_DURATION DurationMax(data_DURATION dur1, data_DURATION dur2);
+
 /**
  *
  */
diff --git a/src/beam.cpp b/src/beam.cpp
index 297674fe23a..8eddeb4968a 100644
--- a/src/beam.cpp
+++ b/src/beam.cpp
@@ -457,7 +457,7 @@ void BeamSegment::AdjustBeamToFrenchStyle(const BeamDrawingInterface *beamInterf
     assert(beamInterface);
 
     // set to store durations of relevant notes (it's ordered, so min duration is going to be first)
-    std::set<int> noteDurations;
+    std::set<data_DURATION> noteDurations;
     // lambda check whether coord has element set and whether that element is CHORD or NOTE
     const auto isNoteOrChord
         = [](BeamElementCoord *coord) { return (coord->m_element && coord->m_element->Is({ CHORD, NOTE })); };
@@ -470,7 +470,9 @@ void BeamSegment::AdjustBeamToFrenchStyle(const BeamDrawingInterface *beamInterf
         if (!isNoteOrChord(*it)) continue;
 
         // get current element duration
-        const int val = (*it)->m_breaksec ? std::min((*it)->m_breaksec + DURATION_4, (*it)->m_dur) : (*it)->m_dur;
+        const data_DURATION val = (*it)->m_breaksec
+            ? vrv::DurationMin(static_cast<data_DURATION>((*it)->m_breaksec + DURATION_4), (*it)->m_dur)
+            : (*it)->m_dur;
         noteDurations.insert(val);
         // get next element duration
         CoordIt nextElement = std::find_if(it + 1, m_beamElementCoordRefs.end(), isNoteOrChord);
@@ -481,8 +483,9 @@ void BeamSegment::AdjustBeamToFrenchStyle(const BeamDrawingInterface *beamInterf
         CoordReverseIt reverse = std::make_reverse_iterator(it);
         CoordReverseIt prevElement = std::find_if(reverse, m_beamElementCoordRefs.rend(), isNoteOrChord);
         if (prevElement != m_beamElementCoordRefs.rend()) {
-            const int prevVal = (*prevElement)->m_breaksec
-                ? std::min((*prevElement)->m_breaksec + DURATION_4, (*prevElement)->m_dur)
+            const data_DURATION prevVal = (*prevElement)->m_breaksec
+                ? vrv::DurationMin(
+                      static_cast<data_DURATION>((*prevElement)->m_breaksec + DURATION_4), (*prevElement)->m_dur)
                 : (*prevElement)->m_dur;
             noteDurations.insert(prevVal);
         }
@@ -884,7 +887,7 @@ int BeamSegment::CalcBeamSlopeStep(
     }
 
     // duration
-    const int dur = beamInterface->m_shortestDur;
+    const data_DURATION dur = beamInterface->m_shortestDur;
     // Prevent short step with values not shorter than a 16th
     if (shortStep && (dur >= DURATION_32)) {
         step = unit * 2;
@@ -1212,7 +1215,7 @@ void BeamSegment::CalcBeamStemLength(const Staff *staff, data_BEAMPLACE place, b
         // skip current element if it's longer that minDuration and is not a part of fTrem
         if ((coord->m_dur < noteDur) && !(coord->m_element && coord->m_element->GetFirstAncestor(FTREM))) continue;
         // adjust stem length if location matches
-        const int dur = (preferredDur != 0) ? preferredDur : coord->m_dur;
+        const data_DURATION dur = (preferredDur != 0) ? preferredDur : coord->m_dur;
         const int coordStemLength = coord->CalculateStemLength(staff, stemDir, isHorizontal, dur);
         if (coord->m_closestNote->GetDrawingLoc() == noteLoc) {
             m_uniformStemLength = coordStemLength;
@@ -1266,11 +1269,11 @@ int BeamSegment::CalcMixedBeamCenterY(int step, int unit) const
     return centerY;
 }
 
-std::tuple<int, int, int> BeamSegment::CalcStemDefiningNote(const Staff *staff, data_BEAMPLACE place) const
+std::tuple<int, int, data_DURATION> BeamSegment::CalcStemDefiningNote(const Staff *staff, data_BEAMPLACE place) const
 {
-    int shortestDuration = DURATION_4;
+    data_DURATION shortestDuration = DURATION_4;
     int shortestLoc = VRV_UNSET;
-    int relevantDuration = DURATION_4;
+    data_DURATION relevantDuration = DURATION_4;
     int relevantLoc = VRV_UNSET;
     const data_STEMDIRECTION globalStemDir = (place == BEAMPLACE_below) ? STEMDIRECTION_down : STEMDIRECTION_up;
     for (BeamElementCoord *coord : m_beamElementCoordRefs) {
@@ -1313,7 +1316,7 @@ std::tuple<int, int, int> BeamSegment::CalcStemDefiningNote(const Staff *staff,
         }
     }
 
-    int adjusted_duration = 0;
+    data_DURATION adjusted_duration = DURATION_NONE;
     // if shortest note location does not offset its duration (shorter notes need more space for additional beams) then
     // give preference to the its location
     if ((shortestDuration - relevantDuration) > (std::abs(relevantLoc - shortestLoc) + 1)) {
@@ -1328,7 +1331,7 @@ std::tuple<int, int, int> BeamSegment::CalcStemDefiningNote(const Staff *staff,
         }
     }
     else if ((shortestDuration - relevantDuration) == std::abs(relevantLoc - shortestLoc)) {
-        adjusted_duration = (relevantDuration + shortestDuration) / 2;
+        adjusted_duration = static_cast<data_DURATION>((relevantDuration + shortestDuration) / 2);
     }
 
     return { relevantLoc, relevantDuration, adjusted_duration };
@@ -2081,14 +2084,14 @@ void BeamElementCoord::UpdateStemLength(
 
 std::pair<int, int> Beam::GetAdditionalBeamCount() const
 {
-    int topShortestDur = DURATION_8;
-    int bottomShortestDur = DURATION_8;
+    data_DURATION topShortestDur = DURATION_8;
+    data_DURATION bottomShortestDur = DURATION_8;
     std::for_each(m_beamElementCoords.begin(), m_beamElementCoords.end(), [&](BeamElementCoord *coord) {
         if (coord->m_partialFlagPlace == BEAMPLACE_above) {
-            topShortestDur = std::max(topShortestDur, coord->m_dur);
+            topShortestDur = vrv::DurationMax(topShortestDur, coord->m_dur);
         }
         else if (coord->m_partialFlagPlace == BEAMPLACE_below) {
-            bottomShortestDur = std::max(bottomShortestDur, coord->m_dur);
+            bottomShortestDur = vrv::DurationMax(bottomShortestDur, coord->m_dur);
         }
     });
 
diff --git a/src/calcalignmentxposfunctor.cpp b/src/calcalignmentxposfunctor.cpp
index cad0ee57f3d..1aaf14eeddc 100644
--- a/src/calcalignmentxposfunctor.cpp
+++ b/src/calcalignmentxposfunctor.cpp
@@ -24,7 +24,7 @@ CalcAlignmentXPosFunctor::CalcAlignmentXPosFunctor(Doc *doc) : DocFunctor(doc)
 {
     m_previousTime = 0.0;
     m_previousXRel = 0;
-    m_longestActualDur = 0;
+    m_longestActualDur = DURATION_NONE;
     m_estimatedJustificationRatio = 1.0;
     m_lastNonTimestamp = NULL;
     m_measureAligner = NULL;
diff --git a/src/calcligatureorneumeposfunctor.cpp b/src/calcligatureorneumeposfunctor.cpp
index 5fb5576ff4f..4ed08423439 100644
--- a/src/calcligatureorneumeposfunctor.cpp
+++ b/src/calcligatureorneumeposfunctor.cpp
@@ -62,8 +62,8 @@ FunctorCode CalcLigatureOrNeumePosFunctor::VisitLigature(Ligature *ligature)
 
         // Look at the @lig attribute on the previous note
         if (previousNote->GetLig() == LIGATUREFORM_obliqua) oblique = true;
-        int dur1 = previousNote->GetActualDur();
-        int dur2 = note->GetActualDur();
+        data_DURATION dur1 = previousNote->GetActualDur();
+        data_DURATION dur2 = note->GetActualDur();
         // Same treatment for Mx and LG except for positioning, which is done above
         // We still need to avoid oblique, so keep a flag.
         bool isMaxima = false;
diff --git a/src/calcstemfunctor.cpp b/src/calcstemfunctor.cpp
index c5f483ddb31..17a65040e5a 100644
--- a/src/calcstemfunctor.cpp
+++ b/src/calcstemfunctor.cpp
@@ -621,7 +621,7 @@ data_STEMDIRECTION CalcStemFunctor::CalcStemDirection(const Chord *chord, int ve
 }
 
 void CalcStemFunctor::AdjustFlagPlacement(
-    const Doc *doc, Stem *stem, Flag *flag, int staffSize, int verticalCenter, int duration) const
+    const Doc *doc, Stem *stem, Flag *flag, int staffSize, int verticalCenter, data_DURATION duration) const
 {
     assert(stem->GetParent());
     assert(stem->GetParent()->IsLayerElement());
diff --git a/src/drawinginterface.cpp b/src/drawinginterface.cpp
index d4be4bf9d35..facd2c57737 100644
--- a/src/drawinginterface.cpp
+++ b/src/drawinginterface.cpp
@@ -94,7 +94,7 @@ void BeamDrawingInterface::Reset()
     m_crossStaffContent = NULL;
     m_crossStaffRel = STAFFREL_basic_NONE;
     m_isSpanningElement = false;
-    m_shortestDur = 0;
+    m_shortestDur = DURATION_NONE;
     m_notesStemDir = STEMDIRECTION_NONE;
     m_drawingPlace = BEAMPLACE_NONE;
     m_beamStaff = NULL;
@@ -148,7 +148,7 @@ void BeamDrawingInterface::InitCoords(const ListOfObjects &childList, Staff *sta
     // Beam list should contain only DurationInterface objects
     assert(current->GetDurationInterface());
 
-    int lastDur = (current->GetDurationInterface())->GetActualDur();
+    data_DURATION lastDur = (current->GetDurationInterface())->GetActualDur();
 
     /******************************************************************/
     // Populate BeamElementCoord for each element in the beam
@@ -164,7 +164,7 @@ void BeamDrawingInterface::InitCoords(const ListOfObjects &childList, Staff *sta
     do {
         // Beam list should contain only DurationInterface objects
         assert(current->GetDurationInterface());
-        const int currentDur = (current->GetDurationInterface())->GetActualDur();
+        const data_DURATION currentDur = (current->GetDurationInterface())->GetActualDur();
 
         if (current->Is(CHORD)) {
             m_beamHasChord = true;
diff --git a/src/durationinterface.cpp b/src/durationinterface.cpp
index 07aafa0f0c8..e6675538482 100644
--- a/src/durationinterface.cpp
+++ b/src/durationinterface.cpp
@@ -188,11 +188,19 @@ data_DURATION DurationInterface::GetActualDurGes() const
 
 data_DURATION DurationInterface::CalcActualDur(data_DURATION dur) const
 {
-    if (dur == DURATION_NONE) return DURATION_NONE;
     // maxima (-1) is a mensural only value
-    if (dur == DURATION_maxima) return DURATION_maxima;
-    // return (dur & DUR_MENSURAL_MASK);
-    return DURATION_breve;
+    if (dur < DUR_MAX) return dur;
+    // Mensural duration (except maxima)
+    switch (dur) {
+        case (DURATION_longa): return DURATION_long;
+        case (DURATION_brevis): return DURATION_breve;
+        case (DURATION_semibrevis): return DURATION_1;
+        case (DURATION_minima): return DURATION_2;
+        case (DURATION_semiminima): return DURATION_4;
+        case (DURATION_fusa): return DURATION_8;
+        case (DURATION_semifusa): return DURATION_16;
+        default: return DURATION_NONE;
+    }
 }
 
 data_DURATION DurationInterface::GetNoteOrChordDur(const LayerElement *element) const
diff --git a/src/layerelement.cpp b/src/layerelement.cpp
index 3a0f99bb6e2..cd6c0da8a44 100644
--- a/src/layerelement.cpp
+++ b/src/layerelement.cpp
@@ -616,7 +616,7 @@ int LayerElement::GetDrawingRadius(const Doc *doc, bool isInLigature) const
     if (!this->Is({ CHORD, NC, NOTE, REST })) return 0;
 
     char32_t code = 0;
-    int dur = DURATION_4;
+    data_DURATION dur = DURATION_4;
     const Staff *staff = this->GetAncestorStaff();
     bool isMensuralDur = false;
     if (this->Is(NOTE)) {
@@ -1200,7 +1200,7 @@ std::pair<int, bool> LayerElement::CalcElementHorizontalOverlap(const Doc *doc,
             isUnisonElement = currentNote->IsUnisonWith(previousNote, true);
             // Unisson, look at the duration for the note heads
             if (unison && currentNote->IsUnisonWith(previousNote, false)) {
-                int previousDuration = previousNote->GetDrawingDur();
+                data_DURATION previousDuration = previousNote->GetDrawingDur();
                 assert(previousNote->GetParent());
                 const bool isPreviousChord = previousNote->GetParent()->Is(CHORD);
                 bool isEdgeElement = false;
diff --git a/src/note.cpp b/src/note.cpp
index 1037dd17315..54af2ee5695 100644
--- a/src/note.cpp
+++ b/src/note.cpp
@@ -239,7 +239,7 @@ const Chord *Note::IsChordTone() const
     return vrv_cast<const Chord *>(this->GetFirstAncestor(CHORD, MAX_CHORD_DEPTH));
 }
 
-int Note::GetDrawingDur() const
+data_DURATION Note::GetDrawingDur() const
 {
     const Chord *chordParent = vrv_cast<const Chord *>(this->GetFirstAncestor(CHORD, MAX_CHORD_DEPTH));
     if (chordParent && !this->HasDur()) {
@@ -491,7 +491,7 @@ char32_t Note::GetMensuralNoteheadGlyph() const
 {
     assert(this->IsMensuralDur());
 
-    int drawingDur = this->GetDrawingDur();
+    data_DURATION drawingDur = this->GetDrawingDur();
 
     // No SMuFL code used for these values
     if (drawingDur < DURATION_1) {
@@ -526,7 +526,7 @@ char32_t Note::GetMensuralNoteheadGlyph() const
     return code;
 }
 
-char32_t Note::GetNoteheadGlyph(const int duration) const
+char32_t Note::GetNoteheadGlyph(const data_DURATION duration) const
 {
     static std::map<std::string, char32_t> additionalNoteheadSymbols
         = { { "noteheadDiamondBlackWide", SMUFL_E0DC_noteheadDiamondBlackWide },
diff --git a/src/page.cpp b/src/page.cpp
index cab9e92fea6..a5de8172a24 100644
--- a/src/page.cpp
+++ b/src/page.cpp
@@ -318,7 +318,7 @@ void Page::ResetAligners()
     // Unless duration-based spacing is disabled, set the X position of each Alignment.
     // Does non-linear spacing based on the duration space between two Alignment objects.
     if (!doc->GetOptions()->m_evenNoteSpacing.GetValue()) {
-        int longestActualDur = DURATION_4;
+        data_DURATION longestActualDur = DURATION_4;
 
         // Detect the longest duration in order to adjust the spacing (false by default)
         if (doc->GetOptions()->m_spacingDurDetection.GetValue()) {
diff --git a/src/preparedatafunctor.cpp b/src/preparedatafunctor.cpp
index 091b6be6d54..8666862c692 100644
--- a/src/preparedatafunctor.cpp
+++ b/src/preparedatafunctor.cpp
@@ -1101,7 +1101,7 @@ FunctorCode PrepareLayerElementPartsFunctor::VisitChord(Chord *chord)
     currentStem->AttGraced::operator=(*chord);
     currentStem->FillAttributes(*chord);
 
-    int duration = chord->GetNoteOrChordDur(chord);
+    data_DURATION duration = chord->GetNoteOrChordDur(chord);
     if ((duration < DURATION_2) || (chord->GetStemVisible() == BOOLEAN_false)) {
         currentStem->IsVirtual(true);
     }
diff --git a/src/rest.cpp b/src/rest.cpp
index 444159f8e63..323cb970a86 100644
--- a/src/rest.cpp
+++ b/src/rest.cpp
@@ -244,7 +244,7 @@ char32_t Rest::GetRestGlyph() const
     return this->GetRestGlyph(this->GetActualDur());
 }
 
-char32_t Rest::GetRestGlyph(const int duration) const
+char32_t Rest::GetRestGlyph(const data_DURATION duration) const
 {
     const Resources *resources = this->GetDocResources();
     if (!resources) return 0;
@@ -559,7 +559,7 @@ int Rest::GetMarginLayerLocation(bool isTopLayer, bool restOverlap) const
 int Rest::GetRestOffsetFromOptions(
     RestLayer layer, const std::pair<int, RestAccidental> &location, bool isTopLayer) const
 {
-    int duration = this->GetActualDur();
+    data_DURATION duration = this->GetActualDur();
     // Make sure we are in the boundaries of g_defaultRests
     if (duration > DURATION_128) duration = DURATION_128;
     if (duration < DURATION_long) duration = DURATION_long;
diff --git a/src/view_beam.cpp b/src/view_beam.cpp
index 566234d1cc3..046e24e190e 100644
--- a/src/view_beam.cpp
+++ b/src/view_beam.cpp
@@ -150,7 +150,7 @@ void View::DrawFTremSegment(DeviceContext *dc, Staff *staff, FTrem *fTrem)
     // We look only at the first one for the duration since both are expected to be the same
     AttDurationLog *durationElement = dynamic_cast<AttDurationLog *>(firstElement->m_element);
     if (!durationElement) return;
-    const int dur = durationElement->GetDur();
+    const data_DURATION dur = durationElement->GetDur();
 
     if (dur > DURATION_1) {
         // Adjust the x position of the first and last element for taking into account the stem width
@@ -289,8 +289,8 @@ void View::DrawBeamSegment(
     }
     int noteCount = (int)noteIndexes.size();
 
-    int durRef = DURATION_8;
-    int durRef2 = DURATION_16;
+    data_DURATION durRef = DURATION_8;
+    data_DURATION durRef2 = DURATION_16;
 
     if (staff->IsTabLuteFrench() || staff->IsTabLuteItalian()) {
         durRef = DURATION_4;
diff --git a/src/view_element.cpp b/src/view_element.cpp
index fe041d76cc4..b6bf6101623 100644
--- a/src/view_element.cpp
+++ b/src/view_element.cpp
@@ -275,7 +275,7 @@ void View::DrawAccid(DeviceContext *dc, LayerElement *element, Layer *layer, Sta
         // look at the note position and adjust it if necessary
         Note *note = vrv_cast<Note *>(accid->GetFirstAncestor(NOTE, MAX_ACCID_DEPTH));
         if (note) {
-            const int drawingDur = note->GetDrawingDur();
+            const data_DURATION drawingDur = note->GetDrawingDur();
             int noteTop = note->GetDrawingTop(m_doc, staff->m_drawingStaffSize);
             int noteBottom = note->GetDrawingBottom(m_doc, staff->m_drawingStaffSize);
             bool onStaff = (accid->GetOnstaff() == BOOLEAN_true);
@@ -1454,7 +1454,7 @@ void View::DrawNote(DeviceContext *dc, LayerElement *element, Layer *layer, Staf
 
     if (!(note->GetHeadVisible() == BOOLEAN_false)) {
         /************** Noteheads: **************/
-        int drawingDur = note->GetDrawingDur();
+        data_DURATION drawingDur = note->GetDrawingDur();
         if (drawingDur == DURATION_NONE) {
             if (note->IsInBeam() && !dc->Is(BBOX_DEVICE_CONTEXT)) {
                 LogWarning("Missing duration for note '%s' in beam", note->GetID().c_str());
@@ -1539,7 +1539,7 @@ void View::DrawRest(DeviceContext *dc, LayerElement *element, Layer *layer, Staf
 
     const bool drawingCueSize = rest->GetDrawingCueSize();
     const int staffSize = staff->GetDrawingStaffNotationSize();
-    int drawingDur = rest->GetActualDur();
+    data_DURATION drawingDur = rest->GetActualDur();
     if (drawingDur == DURATION_NONE) {
         if (!dc->Is(BBOX_DEVICE_CONTEXT)) {
             LogWarning("Missing duration for rest '%s'", rest->GetID().c_str());
diff --git a/src/view_mensural.cpp b/src/view_mensural.cpp
index a02bc5f9cad..5222b8d916d 100644
--- a/src/view_mensural.cpp
+++ b/src/view_mensural.cpp
@@ -50,7 +50,7 @@ void View::DrawMensuralNote(DeviceContext *dc, LayerElement *element, Layer *lay
 
     const int yNote = element->GetDrawingY();
     const int xNote = element->GetDrawingX();
-    const int drawingDur = note->GetDrawingDur();
+    const data_DURATION drawingDur = note->GetDrawingDur();
 
     /************** Noteheads: **************/
 
@@ -161,7 +161,7 @@ void View::DrawMensuralStem(DeviceContext *dc, Note *note, Staff *staff, data_ST
     assert(note);
 
     const int staffSize = staff->m_drawingStaffSize;
-    const int drawingDur = note->GetDrawingDur();
+    const data_DURATION drawingDur = note->GetDrawingDur();
     const int radius = note->GetDrawingRadius(m_doc);
     // Cue size is currently disabled
     const bool drawingCueSize = false;
@@ -710,7 +710,7 @@ void View::CalcObliquePoints(Note *note1, Note *note2, Staff *staff, Point point
 data_STEMDIRECTION View::GetMensuralStemDir(Layer *layer, Note *note, int verticalCenter)
 {
     // constants
-    const int drawingDur = note->GetDrawingDur();
+    const data_DURATION drawingDur = note->GetDrawingDur();
     const int yNote = note->GetDrawingY();
 
     data_STEMDIRECTION layerStemDir;
diff --git a/src/vrv.cpp b/src/vrv.cpp
index 06340f6ecff..2378a542dd8 100644
--- a/src/vrv.cpp
+++ b/src/vrv.cpp
@@ -459,6 +459,16 @@ std::string ToCamelCase(const std::string &s)
     return result;
 }
 
+data_DURATION DurationMin(data_DURATION dur1, data_DURATION dur2)
+{
+    return (dur1 < dur2) ? dur1 : dur2;
+}
+
+data_DURATION DurationMax(data_DURATION dur1, data_DURATION dur2)
+{
+    return (dur1 > dur2) ? dur1 : dur2;
+}
+
 //----------------------------------------------------------------------------
 // Notation type checks
 //----------------------------------------------------------------------------

From 25e2d10f460b2bf53e43972f68056e69e68e2a0b Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Thu, 3 Oct 2024 16:13:14 +0200
Subject: [PATCH 089/105] Fix missing switch default and remove ()

---
 src/durationinterface.cpp | 14 +++++++-------
 src/rest.cpp              |  2 ++
 2 files changed, 9 insertions(+), 7 deletions(-)

diff --git a/src/durationinterface.cpp b/src/durationinterface.cpp
index e6675538482..24297314f3a 100644
--- a/src/durationinterface.cpp
+++ b/src/durationinterface.cpp
@@ -192,13 +192,13 @@ data_DURATION DurationInterface::CalcActualDur(data_DURATION dur) const
     if (dur < DUR_MAX) return dur;
     // Mensural duration (except maxima)
     switch (dur) {
-        case (DURATION_longa): return DURATION_long;
-        case (DURATION_brevis): return DURATION_breve;
-        case (DURATION_semibrevis): return DURATION_1;
-        case (DURATION_minima): return DURATION_2;
-        case (DURATION_semiminima): return DURATION_4;
-        case (DURATION_fusa): return DURATION_8;
-        case (DURATION_semifusa): return DURATION_16;
+        case DURATION_longa: return DURATION_long;
+        case DURATION_brevis: return DURATION_breve;
+        case DURATION_semibrevis: return DURATION_1;
+        case DURATION_minima: return DURATION_2;
+        case DURATION_semiminima: return DURATION_4;
+        case DURATION_fusa: return DURATION_8;
+        case DURATION_semifusa: return DURATION_16;
         default: return DURATION_NONE;
     }
 }
diff --git a/src/rest.cpp b/src/rest.cpp
index 323cb970a86..7e622d604d1 100644
--- a/src/rest.cpp
+++ b/src/rest.cpp
@@ -270,6 +270,7 @@ char32_t Rest::GetRestGlyph(const data_DURATION duration) const
             case DURATION_4: return SMUFL_E9F6_mensuralRestSemiminima; break;
             case DURATION_8: return SMUFL_E9F7_mensuralRestFusa; break;
             case DURATION_16: return SMUFL_E9F8_mensuralRestSemifusa; break;
+            default: return 0;
         }
     }
     else {
@@ -287,6 +288,7 @@ char32_t Rest::GetRestGlyph(const data_DURATION duration) const
             case DURATION_256: return SMUFL_E4EB_rest256th; break;
             case DURATION_512: return SMUFL_E4EC_rest512th; break;
             case DURATION_1024: return SMUFL_E4ED_rest1024th; break;
+            default: return 0;
         }
     }
 

From ae4ab4abd11e6896d0f426ca5990d89f2d3f7120 Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Thu, 3 Oct 2024 16:14:29 +0200
Subject: [PATCH 090/105] Add Fraction constructor from data_DURATION

---
 include/vrv/horizontalaligner.h | 1 +
 src/durationinterface.cpp       | 2 ++
 src/horizontalaligner.cpp       | 8 ++++++++
 3 files changed, 11 insertions(+)

diff --git a/include/vrv/horizontalaligner.h b/include/vrv/horizontalaligner.h
index efa5a210895..824cfa6b7ba 100644
--- a/include/vrv/horizontalaligner.h
+++ b/include/vrv/horizontalaligner.h
@@ -69,6 +69,7 @@ class Fraction {
 public:
     // Constructors
     Fraction(int num = 0, int denom = 1);
+    Fraction(data_DURATION duration);
 
     /** Addition operator */
     Fraction operator+(const Fraction &other) const;
diff --git a/src/durationinterface.cpp b/src/durationinterface.cpp
index 24297314f3a..75a42299beb 100644
--- a/src/durationinterface.cpp
+++ b/src/durationinterface.cpp
@@ -80,6 +80,8 @@ Fraction DurationInterface::GetInterfaceAlignmentDuration(int num, int numBase)
     if (this->HasNum()) num *= this->GetNum();
     if (this->HasNumbase()) numBase *= this->GetNumbase();
 
+    // Fraction duration(noteDur);
+    // duration = duration * numBase / num;
     Fraction duration = DUR_MAX / pow(2.0, (double)(noteDur - 2.0)) * numBase / num;
 
     int noteDots = (this->HasDotsGes()) ? this->GetDotsGes() : this->GetDots();
diff --git a/src/horizontalaligner.cpp b/src/horizontalaligner.cpp
index 8315fabfaee..955443e5ab9 100644
--- a/src/horizontalaligner.cpp
+++ b/src/horizontalaligner.cpp
@@ -47,6 +47,14 @@ Fraction::Fraction(int num, int denom) : m_numerator(num), m_denominator(denom)
     Reduce();
 }
 
+Fraction::Fraction(data_DURATION duration)
+{
+    int den = pow(2, (duration + 1));
+    m_numerator = 8;
+    m_denominator = den;
+    Reduce();
+}
+
 Fraction Fraction::operator+(const Fraction &other) const
 {
     int num = m_numerator * other.m_denominator + other.m_numerator * m_denominator;

From 7479d5da2eccab68a19ca3a2a03f481d27f2f1f0 Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Thu, 3 Oct 2024 16:44:12 +0200
Subject: [PATCH 091/105] Fix regression on grace note stems

---
 include/vrv/beam.h | 3 ++-
 src/beam.cpp       | 4 ++--
 2 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/include/vrv/beam.h b/include/vrv/beam.h
index 855d556417d..2ab3afd24cc 100644
--- a/include/vrv/beam.h
+++ b/include/vrv/beam.h
@@ -429,7 +429,8 @@ class BeamElementCoord {
     void SetClosestNoteOrTabDurSym(data_STEMDIRECTION stemDir, bool outsideStaff);
 
     /** Helper for calculating the stem length for staff notation and tablature beams within the staff */
-    int CalculateStemLength(const Staff *staff, data_STEMDIRECTION stemDir, bool isHorizontal, int preferredDur) const;
+    int CalculateStemLength(
+        const Staff *staff, data_STEMDIRECTION stemDir, bool isHorizontal, data_DURATION preferredDur) const;
 
     /** Helper for calculating the stem length for tablature beam placed outside the staff */
     int CalculateStemLengthTab(const Staff *staff, data_STEMDIRECTION stemDir) const;
diff --git a/src/beam.cpp b/src/beam.cpp
index 8eddeb4968a..5cffc7fb905 100644
--- a/src/beam.cpp
+++ b/src/beam.cpp
@@ -1215,7 +1215,7 @@ void BeamSegment::CalcBeamStemLength(const Staff *staff, data_BEAMPLACE place, b
         // skip current element if it's longer that minDuration and is not a part of fTrem
         if ((coord->m_dur < noteDur) && !(coord->m_element && coord->m_element->GetFirstAncestor(FTREM))) continue;
         // adjust stem length if location matches
-        const data_DURATION dur = (preferredDur != 0) ? preferredDur : coord->m_dur;
+        const data_DURATION dur = (preferredDur != DURATION_NONE) ? preferredDur : coord->m_dur;
         const int coordStemLength = coord->CalculateStemLength(staff, stemDir, isHorizontal, dur);
         if (coord->m_closestNote->GetDrawingLoc() == noteLoc) {
             m_uniformStemLength = coordStemLength;
@@ -1942,7 +1942,7 @@ void BeamElementCoord::SetDrawingStemDir(data_STEMDIRECTION stemDir, const Staff
 }
 
 int BeamElementCoord::CalculateStemLength(
-    const Staff *staff, data_STEMDIRECTION stemDir, bool isHorizontal, int preferredDur) const
+    const Staff *staff, data_STEMDIRECTION stemDir, bool isHorizontal, data_DURATION preferredDur) const
 {
     if (!m_closestNote) return 0;
 

From 22629d166645d415cf00514070004419b8a34dff Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Thu, 3 Oct 2024 19:37:55 +0200
Subject: [PATCH 092/105] Change alignment to Fraction with whole note == 1/1

---
 include/vrv/beatrpt.h            |  2 +-
 include/vrv/durationinterface.h  |  2 +-
 include/vrv/horizontalaligner.h  | 24 +++++++++++++++++-------
 include/vrv/metersig.h           |  6 ++++++
 include/vrv/timestamp.h          |  2 +-
 src/alignfunctor.cpp             | 10 ++++++----
 src/beatrpt.cpp                  |  8 ++++----
 src/calcalignmentxposfunctor.cpp | 10 +++++-----
 src/durationinterface.cpp        | 17 ++++++++++-------
 src/findlayerelementsfunctor.cpp |  4 ++--
 src/horizontalaligner.cpp        | 23 ++++++++++++-----------
 src/layerelement.cpp             | 27 ++++++++++-----------------
 src/metersig.cpp                 | 13 +++++++++++++
 src/timestamp.cpp                |  5 +++--
 src/view_control.cpp             |  4 ++--
 15 files changed, 93 insertions(+), 64 deletions(-)

diff --git a/include/vrv/beatrpt.h b/include/vrv/beatrpt.h
index d4d8a4d9b5d..8db95215c38 100644
--- a/include/vrv/beatrpt.h
+++ b/include/vrv/beatrpt.h
@@ -43,7 +43,7 @@ class BeatRpt : public LayerElement, public AttColor, public AttBeatRptLog, publ
      * Returns the duration (in double) for the BeatRpt.
      */
 
-    double GetBeatRptAlignmentDuration(int meterUnit) const;
+    Fraction GetBeatRptAlignmentDuration(data_DURATION meterUnit) const;
 
     /**
      * MIDI timing information
diff --git a/include/vrv/durationinterface.h b/include/vrv/durationinterface.h
index 4265aedcfd7..b1cd3d3760c 100644
--- a/include/vrv/durationinterface.h
+++ b/include/vrv/durationinterface.h
@@ -70,7 +70,7 @@ class DurationInterface : public Interface,
      * Currently this assume brevis equality (through DUR_MENSURAL_REF) and would
      * need to be modified for shorter equality in later repertoire.
      */
-    double GetInterfaceAlignmentMensuralDuration(int num, int numBase, const Mensur *currentMensur) const;
+    Fraction GetInterfaceAlignmentMensuralDuration(int num, int numBase, const Mensur *currentMensur) const;
 
     /**
      * Return true if the note or rest is the first of a beam.
diff --git a/include/vrv/horizontalaligner.h b/include/vrv/horizontalaligner.h
index 824cfa6b7ba..d28fa25c731 100644
--- a/include/vrv/horizontalaligner.h
+++ b/include/vrv/horizontalaligner.h
@@ -67,9 +67,15 @@ enum AlignmentType {
 class Fraction {
 
 public:
-    // Constructors
-    Fraction(int num = 0, int denom = 1);
-    Fraction(data_DURATION duration);
+    // Constructors - make them explicit to avoid type conversion
+    explicit Fraction(int num = 0, int denom = 1);
+    explicit Fraction(data_DURATION duration);
+
+    // Enable implicit conversion constructor for `int`
+    template <typename T, typename = std::enable_if_t<std::is_same_v<T, int>>>
+    Fraction(T num) : m_numerator(num), m_denominator(1)
+    {
+    }
 
     /** Addition operator */
     Fraction operator+(const Fraction &other) const;
@@ -91,6 +97,10 @@ class Fraction {
     /** Greater than or equal operator */
     bool operator>=(const Fraction &other) const;
 
+    /** Getters */
+    int GetNumerator() const { return m_numerator; }
+    int GetDenominator() const { return m_denominator; }
+
     /** Convert fraction to a double */
     double ToDouble() const;
 
@@ -261,7 +271,7 @@ class Alignment : public Object {
      * formula with parameters can come close and has other advantages.
      */
     static int HorizontalSpaceForDuration(
-        const Fraction &intervalTime, int maxActualDur, double spacingLinear, double spacingNonLinear);
+        const Fraction &intervalTime, data_DURATION maxActualDur, double spacingLinear, double spacingNonLinear);
 
     //----------//
     // Functors //
@@ -507,8 +517,8 @@ class MeasureAligner : public HorizontalAligner {
      * Setter takes a meter unit parameter.
      */
     ///@{
-    void SetInitialTstamp(int meterUnit);
-    double GetInitialTstampDur() const { return m_initialTstampDur; }
+    void SetInitialTstamp(data_DURATION meterUnit);
+    Fraction GetInitialTstampDur() const { return m_initialTstampDur; }
     ///@}
 
     /**
@@ -593,7 +603,7 @@ class MeasureAligner : public HorizontalAligner {
      * The time duration of the timestamp between 0.0 and 1.0.
      * This depends on the meter signature in the preceeding scoreDef
      */
-    double m_initialTstampDur;
+    Fraction m_initialTstampDur;
 };
 
 //----------------------------------------------------------------------------
diff --git a/include/vrv/metersig.h b/include/vrv/metersig.h
index 585c259b9c4..4e3363e4863 100644
--- a/include/vrv/metersig.h
+++ b/include/vrv/metersig.h
@@ -54,6 +54,12 @@ class MeterSig : public LayerElement,
     /** Evaluate additive meter counts */
     int GetTotalCount() const;
 
+    /**
+     * Return the unit (int) as data_DURATION (up to 32).
+     * Return DURATION_4 if no match.
+     */
+    data_DURATION GetUnitAsDur() const;
+
     /** Retrieves the symbol glyph */
     char32_t GetSymbolGlyph() const;
 
diff --git a/include/vrv/timestamp.h b/include/vrv/timestamp.h
index c715b569ec0..fe537184dad 100644
--- a/include/vrv/timestamp.h
+++ b/include/vrv/timestamp.h
@@ -41,7 +41,7 @@ class TimestampAttr : public LayerElement {
     /**
      * Returns the duration (in double) for the Timestamp.
      */
-    double GetTimestampAttrAlignmentDuration(int meterUnit) const;
+    Fraction GetTimestampAttrAlignmentDuration(data_DURATION meterUnit) const;
 
     //----------//
     // Functors //
diff --git a/src/alignfunctor.cpp b/src/alignfunctor.cpp
index fad1c8874f4..4dd8fe4a3ca 100644
--- a/src/alignfunctor.cpp
+++ b/src/alignfunctor.cpp
@@ -40,7 +40,7 @@ namespace vrv {
 AlignHorizontallyFunctor::AlignHorizontallyFunctor(Doc *doc) : DocFunctor(doc)
 {
     m_measureAligner = NULL;
-    m_time = 0.0;
+    m_time = 0;
     m_currentParams.mensur = NULL;
     m_currentParams.meterSig = NULL;
     m_notationType = NOTATIONTYPE_cmn;
@@ -56,7 +56,7 @@ FunctorCode AlignHorizontallyFunctor::VisitLayer(Layer *layer)
 
     // We are starting a new layer, reset the time;
     // We set it to -1.0 for the scoreDef attributes since they have to be aligned before any timestamp event (-1.0)
-    m_time = DUR_MAX * -1.0;
+    m_time = -1;
 
     m_scoreDefRole = m_isFirstMeasure ? SCOREDEF_SYSTEM : SCOREDEF_INTERMEDIATE;
 
@@ -87,7 +87,7 @@ FunctorCode AlignHorizontallyFunctor::VisitLayer(Layer *layer)
     m_scoreDefRole = SCOREDEF_NONE;
 
     // Now we have to set it to 0.0 since we will start aligning musical content
-    m_time = 0.0;
+    m_time = 0;
 
     return FUNCTOR_CONTINUE;
 }
@@ -378,7 +378,9 @@ FunctorCode AlignHorizontallyFunctor::VisitMeasure(Measure *measure)
 
 FunctorCode AlignHorizontallyFunctor::VisitMeasureEnd(Measure *measure)
 {
-    int meterUnit = m_currentParams.meterSig ? m_currentParams.meterSig->GetUnit() : 4;
+    data_DURATION meterUnit = (m_currentParams.meterSig && m_currentParams.meterSig->HasUnit())
+        ? m_currentParams.meterSig->GetUnitAsDur()
+        : DURATION_4;
     measure->m_measureAligner.SetInitialTstamp(meterUnit);
 
     // We also need to align the timestamps - we do it at the end since we need the *meterSig to be initialized by a
diff --git a/src/beatrpt.cpp b/src/beatrpt.cpp
index 3b72624333c..26dc321bc07 100644
--- a/src/beatrpt.cpp
+++ b/src/beatrpt.cpp
@@ -54,11 +54,11 @@ void BeatRpt::Reset()
     m_scoreTimeOnset = 0.0;
 }
 
-double BeatRpt::GetBeatRptAlignmentDuration(int meterUnit) const
+Fraction BeatRpt::GetBeatRptAlignmentDuration(data_DURATION meterUnit) const
 {
-    double dur = DUR_MAX / meterUnit;
-    if (this->HasBeatdef()) dur *= this->GetBeatdef();
-    return dur;
+    Fraction duration(meterUnit);
+    if (this->HasBeatdef()) duration = duration * Fraction(this->GetBeatdef() * DUR_MAX, DUR_MAX);
+    return duration;
 }
 
 void BeatRpt::SetScoreTimeOnset(double scoreTime)
diff --git a/src/calcalignmentxposfunctor.cpp b/src/calcalignmentxposfunctor.cpp
index 1aaf14eeddc..6f8c291dc8e 100644
--- a/src/calcalignmentxposfunctor.cpp
+++ b/src/calcalignmentxposfunctor.cpp
@@ -22,7 +22,7 @@ namespace vrv {
 
 CalcAlignmentXPosFunctor::CalcAlignmentXPosFunctor(Doc *doc) : DocFunctor(doc)
 {
-    m_previousTime = 0.0;
+    m_previousTime = 0;
     m_previousXRel = 0;
     m_longestActualDur = DURATION_NONE;
     m_estimatedJustificationRatio = 1.0;
@@ -39,7 +39,7 @@ FunctorCode CalcAlignmentXPosFunctor::VisitAlignment(Alignment *alignment)
     Fraction intervalTime = alignment->GetTime() - m_previousTime;
 
     if (alignment->GetType() > ALIGNMENT_MEASURE_RIGHT_BARLINE) {
-        intervalTime = 0.0;
+        intervalTime = 0;
     }
 
     // Do not move aligners that are only time-stamps at this stage but add it to the pending list
@@ -48,7 +48,7 @@ FunctorCode CalcAlignmentXPosFunctor::VisitAlignment(Alignment *alignment)
         return FUNCTOR_CONTINUE;
     }
 
-    if (intervalTime > 0.0) {
+    if (intervalTime > 0) {
         intervalXRel = Alignment::HorizontalSpaceForDuration(intervalTime, m_longestActualDur,
             m_doc->GetOptions()->m_spacingLinear.GetValue(), m_doc->GetOptions()->m_spacingNonLinear.GetValue());
         // LogDebug("CalcAlignmentXPos: intervalTime=%.2f intervalXRel=%d", intervalTime, intervalXRel);
@@ -81,7 +81,7 @@ FunctorCode CalcAlignmentXPosFunctor::VisitAlignment(Alignment *alignment)
         // For each timestamp alignment, move them proportionally to the space we currently have
         for (auto &tsAlignment : m_timestamps) {
             // Avoid division by zero (nothing to move with the alignment anyway
-            if (duration == 0.0) break;
+            if (duration == 0) break;
             Fraction percent = (tsAlignment->GetTime() - startTime) / duration;
             tsAlignment->SetXRel(startXRel + space * percent.ToDouble());
         }
@@ -98,7 +98,7 @@ FunctorCode CalcAlignmentXPosFunctor::VisitMeasure(Measure *measure)
 {
     // We start a new Measure
     // Reset the previous time position and x_rel to 0;
-    m_previousTime = 0.0;
+    m_previousTime = 0;
     // We un-measured music we never have a left barline, so do not add a default space
     m_previousXRel = (measure->IsMeasuredMusic()) ? m_doc->GetDrawingUnit(100) : 0;
 
diff --git a/src/durationinterface.cpp b/src/durationinterface.cpp
index 75a42299beb..68c05cea91e 100644
--- a/src/durationinterface.cpp
+++ b/src/durationinterface.cpp
@@ -80,26 +80,29 @@ Fraction DurationInterface::GetInterfaceAlignmentDuration(int num, int numBase)
     if (this->HasNum()) num *= this->GetNum();
     if (this->HasNumbase()) numBase *= this->GetNumbase();
 
-    // Fraction duration(noteDur);
-    // duration = duration * numBase / num;
-    Fraction duration = DUR_MAX / pow(2.0, (double)(noteDur - 2.0)) * numBase / num;
+    Fraction duration(noteDur);
+    duration = duration * numBase / num;
+    // double old = DUR_MAX / pow(2.0, (double)(noteDur - 2.0)) * numBase / num;
+    // duration = DUR_MAX / pow(2.0, (double)(noteDur - 2.0)) * numBase / num;
 
     int noteDots = (this->HasDotsGes()) ? this->GetDotsGes() : this->GetDots();
     if (noteDots != VRV_UNSET) {
-        duration = duration * 2 - (duration / pow(2, noteDots));
+        Fraction durationReduction(duration.GetNumerator(), duration.GetDenominator() * pow(2, noteDots));
+        duration = duration * 2 - durationReduction;
     }
     // LogDebug("Duration %d; Dot %d; Alignment %f", noteDur, this->GetDots(), duration);
     return duration;
 }
 
-double DurationInterface::GetInterfaceAlignmentMensuralDuration(int num, int numBase, const Mensur *currentMensur) const
+Fraction DurationInterface::GetInterfaceAlignmentMensuralDuration(
+    int num, int numBase, const Mensur *currentMensur) const
 {
     int noteDur = this->GetDurGes() != DURATION_NONE ? this->GetActualDurGes() : this->GetActualDur();
     if (noteDur == DURATION_NONE) noteDur = DURATION_4;
 
     if (!currentMensur) {
         LogWarning("No current mensur for calculating duration");
-        return DUR_MENSURAL_REF;
+        return Fraction(1, 1);
     }
 
     if (this->HasNum() || this->HasNumbase()) {
@@ -155,7 +158,7 @@ double DurationInterface::GetInterfaceAlignmentMensuralDuration(int num, int num
     duration *= (double)numBase / (double)num;
     // LogDebug("Duration %d; %d/%d; Alignment %f; Ratio %f", noteDur, num, numbase, duration, ratio);
     duration = durRound(duration);
-    return duration;
+    return Fraction(DUR_MAX * duration, DUR_MAX * DUR_MAX);
 }
 
 bool DurationInterface::IsFirstInBeam(const LayerElement *noteOrRest) const
diff --git a/src/findlayerelementsfunctor.cpp b/src/findlayerelementsfunctor.cpp
index 0ebba8830fa..35129d8b0af 100644
--- a/src/findlayerelementsfunctor.cpp
+++ b/src/findlayerelementsfunctor.cpp
@@ -90,8 +90,8 @@ LayerElementsInTimeSpanFunctor::LayerElementsInTimeSpanFunctor(
     const MeterSig *meterSig, const Mensur *mensur, const Layer *layer)
     : ConstFunctor()
 {
-    m_time = 0.0;
-    m_duration = 0.0;
+    m_time = 0;
+    m_duration = 0;
     m_meterParams.meterSig = meterSig;
     m_meterParams.mensur = mensur;
     m_layer = layer;
diff --git a/src/horizontalaligner.cpp b/src/horizontalaligner.cpp
index 955443e5ab9..8cbf97a20ab 100644
--- a/src/horizontalaligner.cpp
+++ b/src/horizontalaligner.cpp
@@ -49,6 +49,8 @@ Fraction::Fraction(int num, int denom) : m_numerator(num), m_denominator(denom)
 
 Fraction::Fraction(data_DURATION duration)
 {
+    duration = vrv::DurationMin(duration, DURATION_1024);
+    duration = vrv::DurationMax(duration, DURATION_NONE);
     int den = pow(2, (duration + 1));
     m_numerator = 8;
     m_denominator = den;
@@ -232,16 +234,16 @@ void MeasureAligner::Reset()
 {
     HorizontalAligner::Reset();
     m_nonJustifiableLeftMargin = 0;
-    m_leftAlignment = new Alignment(-1.0 * DUR_MAX, ALIGNMENT_MEASURE_START);
+    m_leftAlignment = new Alignment(-1, ALIGNMENT_MEASURE_START);
     AddAlignment(m_leftAlignment);
-    m_leftBarLineAlignment = new Alignment(-1.0 * DUR_MAX, ALIGNMENT_MEASURE_LEFT_BARLINE);
+    m_leftBarLineAlignment = new Alignment(-1, ALIGNMENT_MEASURE_LEFT_BARLINE);
     AddAlignment(m_leftBarLineAlignment);
-    m_rightBarLineAlignment = new Alignment(0.0 * DUR_MAX, ALIGNMENT_MEASURE_RIGHT_BARLINE);
+    m_rightBarLineAlignment = new Alignment(0, ALIGNMENT_MEASURE_RIGHT_BARLINE);
     AddAlignment(m_rightBarLineAlignment);
-    m_rightAlignment = new Alignment(0.0 * DUR_MAX, ALIGNMENT_MEASURE_END);
+    m_rightAlignment = new Alignment(0, ALIGNMENT_MEASURE_END);
     AddAlignment(m_rightAlignment);
 
-    m_initialTstampDur = -DUR_MAX;
+    m_initialTstampDur = -1;
 }
 
 bool MeasureAligner::IsSupportedChild(Object *child)
@@ -302,11 +304,9 @@ Fraction MeasureAligner::GetMaxTime() const
     return m_rightAlignment->GetTime();
 }
 
-void MeasureAligner::SetInitialTstamp(int meterUnit)
+void MeasureAligner::SetInitialTstamp(data_DURATION meterUnit)
 {
-    if (meterUnit != 0) {
-        m_initialTstampDur = DUR_MAX / meterUnit * -1;
-    }
+    m_initialTstampDur = Fraction(meterUnit) * -1;
 }
 
 void MeasureAligner::AdjustProportionally(const ArrayOfAdjustmentTuples &adjustments)
@@ -836,14 +836,15 @@ std::pair<int, int> Alignment::GetAlignmentTopBottom() const
 }
 
 int Alignment::HorizontalSpaceForDuration(
-    const Fraction &intervalTime, int maxActualDur, double spacingLinear, double spacingNonLinear)
+    const Fraction &intervalTime, data_DURATION maxActualDur, double spacingLinear, double spacingNonLinear)
 {
     double doubleIntervalTime = intervalTime.ToDouble();
     /* If the longest duration interval in the score is longer than semibreve, adjust spacing so
      that interval gets the space a semibreve would ordinarily get. */
     if (maxActualDur < DURATION_1) doubleIntervalTime /= pow(2.0, DURATION_1 - maxActualDur);
 
-    return pow(doubleIntervalTime, spacingNonLinear) * spacingLinear * 10.0; // numbers are experimental constants
+    return pow(doubleIntervalTime * 1024, spacingNonLinear) * spacingLinear
+        * 10.0; // numbers are experimental constants
 }
 
 FunctorCode Alignment::Accept(Functor &functor)
diff --git a/src/layerelement.cpp b/src/layerelement.cpp
index cd6c0da8a44..20f59628df1 100644
--- a/src/layerelement.cpp
+++ b/src/layerelement.cpp
@@ -76,11 +76,9 @@ namespace vrv {
 // MAX_DURATION / pow(2.0, (DURATION_4 - 2.0))
 #define NEUME_LARGE_SPACE 256
 // Medium spacing between neume is a 8th note space
-// MAX_DURATION / pow(2.0, (DUR_5 - 2.0))
-#define NEUME_MEDIUM_SPACE 128
+#define NEUME_MEDIUM_SPACE Fraction(1, 8)
 // Small spacing between neume components is a 16th note space
-// MAX_DURATION / pow(2.0, (DUR_6 - 2.0))
-#define NEUME_SMALL_SPACE 64
+#define NEUME_SMALL_SPACE Fraction(1, 16)
 
 //----------------------------------------------------------------------------
 // LayerElement
@@ -730,30 +728,25 @@ Fraction LayerElement::GetAlignmentDuration(
     else if (this->Is(BEATRPT)) {
         const BeatRpt *beatRpt = vrv_cast<const BeatRpt *>(this);
         assert(beatRpt);
-        int meterUnit = 4;
-        if (params.meterSig && params.meterSig->HasUnit()) meterUnit = params.meterSig->GetUnit();
+        data_DURATION meterUnit = DURATION_4;
+        if (params.meterSig && params.meterSig->HasUnit()) meterUnit = params.meterSig->GetUnitAsDur();
         return beatRpt->GetBeatRptAlignmentDuration(meterUnit);
     }
     else if (this->Is(TIMESTAMP_ATTR)) {
         const TimestampAttr *timestampAttr = vrv_cast<const TimestampAttr *>(this);
         assert(timestampAttr);
-        int meterUnit = 4;
-        if (params.meterSig && params.meterSig->HasUnit()) meterUnit = params.meterSig->GetUnit();
+        data_DURATION meterUnit = DURATION_4;
+        if (params.meterSig && params.meterSig->HasUnit()) meterUnit = params.meterSig->GetUnitAsDur();
         return timestampAttr->GetTimestampAttrAlignmentDuration(meterUnit);
     }
     // We align all full measure element to the current time signature, even the ones that last longer than one measure
     else if (this->Is({ HALFMRPT, MREST, MULTIREST, MRPT, MRPT2, MULTIRPT })) {
-        int meterUnit = 4;
+        data_DURATION meterUnit = DURATION_4;
         int meterCount = 4;
-        if (params.meterSig && params.meterSig->HasUnit()) meterUnit = params.meterSig->GetUnit();
+        if (params.meterSig && params.meterSig->HasUnit()) meterUnit = params.meterSig->GetUnitAsDur();
         if (params.meterSig && params.meterSig->HasCount()) meterCount = params.meterSig->GetTotalCount();
-
-        if (this->Is(HALFMRPT)) {
-            return (DUR_MAX / meterUnit * meterCount) / 2;
-        }
-        else {
-            return DUR_MAX / meterUnit * meterCount;
-        }
+        Fraction duration = Fraction(meterUnit) * meterCount;
+        return (this->Is(HALFMRPT)) ? (duration / 2) : duration;
     }
     // This is not called with --neume-as-note since otherwise each nc has an aligner
     else if (this->Is(NEUME)) {
diff --git a/src/metersig.cpp b/src/metersig.cpp
index f3df8054d23..8ff16ecf702 100644
--- a/src/metersig.cpp
+++ b/src/metersig.cpp
@@ -108,6 +108,19 @@ int MeterSig::GetTotalCount() const
     return counts.front();
 }
 
+data_DURATION MeterSig::GetUnitAsDur() const
+{
+    switch (this->GetUnit()) {
+        case 1: return DURATION_1;
+        case 2: return DURATION_2;
+        case 4: return DURATION_4;
+        case 8: return DURATION_8;
+        case 16: return DURATION_16;
+        case 32: return DURATION_32;
+        default: return DURATION_4;
+    }
+}
+
 char32_t MeterSig::GetSymbolGlyph() const
 {
     char32_t glyph = 0;
diff --git a/src/timestamp.cpp b/src/timestamp.cpp
index da4a52e682d..c7bd31c6269 100644
--- a/src/timestamp.cpp
+++ b/src/timestamp.cpp
@@ -35,9 +35,10 @@ void TimestampAttr::Reset()
     m_actualDurPos = 0.0;
 }
 
-double TimestampAttr::GetTimestampAttrAlignmentDuration(int meterUnit) const
+Fraction TimestampAttr::GetTimestampAttrAlignmentDuration(data_DURATION meterUnit) const
 {
-    return DUR_MAX / meterUnit * m_actualDurPos;
+    Fraction duration(meterUnit);
+    return (duration * Fraction(m_actualDurPos * DUR_MAX, DUR_MAX));
 }
 
 FunctorCode TimestampAttr::Accept(Functor &functor)
diff --git a/src/view_control.cpp b/src/view_control.cpp
index 9a0065f0ce9..6c908fb21ff 100644
--- a/src/view_control.cpp
+++ b/src/view_control.cpp
@@ -1269,8 +1269,8 @@ void View::DrawSylConnector(
     }
     // We are in the system of the last note - draw the connector from the beginning of the system
     else if (spanningType == SPANNING_END) {
-        // If we do not want to show hyphens at the start of a system and the end is at time 0.0
-        if (m_options->m_lyricNoStartHyphen.GetValue() && (syl->GetEnd()->GetAlignment()->GetTime() == 0.0)) {
+        // If we do not want to show hyphens at the start of a system and the end is at time 0
+        if (m_options->m_lyricNoStartHyphen.GetValue() && (syl->GetEnd()->GetAlignment()->GetTime() == 0)) {
             // Return but only if the end is in the first measure of the system...
             Measure *measure = vrv_cast<Measure *>(syl->GetEnd()->GetFirstAncestor(MEASURE));
             assert(measure);

From 0ce9f54a4b429637af3cb6e1775b2bff30837ef3 Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Thu, 3 Oct 2024 20:04:09 +0200
Subject: [PATCH 093/105] Fix comments

---
 include/vrv/beatrpt.h           | 2 +-
 include/vrv/durationinterface.h | 2 +-
 include/vrv/timestamp.h         | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/include/vrv/beatrpt.h b/include/vrv/beatrpt.h
index 8db95215c38..bf892e589c0 100644
--- a/include/vrv/beatrpt.h
+++ b/include/vrv/beatrpt.h
@@ -40,7 +40,7 @@ class BeatRpt : public LayerElement, public AttColor, public AttBeatRptLog, publ
     bool HasToBeAligned() const override { return true; }
 
     /**
-     * Returns the duration (in double) for the BeatRpt.
+     * Returns the duration (in Fraction) for the BeatRpt.
      */
 
     Fraction GetBeatRptAlignmentDuration(data_DURATION meterUnit) const;
diff --git a/include/vrv/durationinterface.h b/include/vrv/durationinterface.h
index b1cd3d3760c..215ba4fce57 100644
--- a/include/vrv/durationinterface.h
+++ b/include/vrv/durationinterface.h
@@ -66,7 +66,7 @@ class DurationInterface : public Interface,
     Fraction GetInterfaceAlignmentDuration(int num, int numBase) const;
 
     /**
-     * Returns the duration (in double) for the element for mensural notation
+     * Returns the duration (in Fraction) for the element for mensural notation
      * Currently this assume brevis equality (through DUR_MENSURAL_REF) and would
      * need to be modified for shorter equality in later repertoire.
      */
diff --git a/include/vrv/timestamp.h b/include/vrv/timestamp.h
index fe537184dad..71e208592c9 100644
--- a/include/vrv/timestamp.h
+++ b/include/vrv/timestamp.h
@@ -39,7 +39,7 @@ class TimestampAttr : public LayerElement {
     ///@}
 
     /**
-     * Returns the duration (in double) for the Timestamp.
+     * Returns the duration (in Fraction) for the Timestamp.
      */
     Fraction GetTimestampAttrAlignmentDuration(data_DURATION meterUnit) const;
 

From 449fc72e459e932d10a5114dbfbfa2b961e875a8 Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Thu, 3 Oct 2024 20:58:34 +0200
Subject: [PATCH 094/105] Remove call to Fraction::ToDouble

---
 src/alignfunctor.cpp | 2 --
 src/view_element.cpp | 8 +++-----
 2 files changed, 3 insertions(+), 7 deletions(-)

diff --git a/src/alignfunctor.cpp b/src/alignfunctor.cpp
index 4dd8fe4a3ca..f28c7badeda 100644
--- a/src/alignfunctor.cpp
+++ b/src/alignfunctor.cpp
@@ -310,8 +310,6 @@ FunctorCode AlignHorizontallyFunctor::VisitLayerElement(LayerElement *layerEleme
         // get the duration of the event
         duration = layerElement->GetAlignmentDuration(m_currentParams, true, m_notationType);
 
-        // LogDebug("duration %s %f", duration.ToString().c_str(), duration.ToDouble());
-
         // For timestamp, what we get from GetAlignmentDuration is actually the position of the timestamp
         // So use it as current time - we can do this because the timestamp loop is redirected from the measure
         // The time will be reset to 0.0 when starting a new layer anyway
diff --git a/src/view_element.cpp b/src/view_element.cpp
index b6bf6101623..9766eac5833 100644
--- a/src/view_element.cpp
+++ b/src/view_element.cpp
@@ -1165,11 +1165,9 @@ void View::DrawMRest(DeviceContext *dc, LayerElement *element, Layer *layer, Sta
 
     const bool drawingCueSize = mRest->GetDrawingCueSize();
     int x = mRest->GetDrawingX();
-    int y = (measure->m_measureAligner.GetMaxTime().ToDouble() >= (DUR_MAX * 2))
-        ? element->GetDrawingY() - m_doc->GetDrawingDoubleUnit(staffSize)
-        : element->GetDrawingY();
-    char32_t rest = (measure->m_measureAligner.GetMaxTime().ToDouble() >= (DUR_MAX * 2)) ? SMUFL_E4E2_restDoubleWhole
-                                                                                         : SMUFL_E4E3_restWhole;
+    const bool isDouble = (measure->m_measureAligner.GetMaxTime() >= Fraction(2, 1));
+    int y = isDouble ? element->GetDrawingY() - m_doc->GetDrawingDoubleUnit(staffSize) : element->GetDrawingY();
+    char32_t rest = isDouble ? SMUFL_E4E2_restDoubleWhole : SMUFL_E4E3_restWhole;
 
     x -= m_doc->GetGlyphWidth(rest, staffSize, drawingCueSize) / 2;
 

From 501de5f8908093980e9237c8b66c486578411ace Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Thu, 3 Oct 2024 21:12:11 +0200
Subject: [PATCH 095/105] Change beam pattern detection items

---
 src/drawinginterface.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/drawinginterface.cpp b/src/drawinginterface.cpp
index facd2c57737..c4a102a9432 100644
--- a/src/drawinginterface.cpp
+++ b/src/drawinginterface.cpp
@@ -421,7 +421,7 @@ bool BeamDrawingInterface::IsRepeatedPattern() const
         if (!coord->m_stem || !coord->m_closestNote) continue;
 
         // Could this be an overflow with 32 bits?
-        items.push_back(coord->m_closestNote->GetDrawingY() * DUR_MAX + coord->m_dur);
+        items.push_back(coord->m_closestNote->GetDrawingY() + DUR_MAX * coord->m_dur);
     }
     int itemCount = (int)items.size();
 

From f569cd06ac5ca68d9e2bf52a768b1bac33b190ca Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Thu, 3 Oct 2024 21:12:45 +0200
Subject: [PATCH 096/105] Remove unnecessary defines

---
 libmei/addons/attdef.h | 14 ++++----------
 1 file changed, 4 insertions(+), 10 deletions(-)

diff --git a/libmei/addons/attdef.h b/libmei/addons/attdef.h
index d04af822ac6..6bb21ecd9b7 100644
--- a/libmei/addons/attdef.h
+++ b/libmei/addons/attdef.h
@@ -33,12 +33,6 @@ typedef double data_VU;
 // Durations
 //----------------------------------------------------------------------------
 
-/**
- * These duration values are used for internal calculation and differ from the
- * MEI data.DURATION types (see below)
- */
-#define DUR_NONE -32
-#define DUR_MX -1 // maxima
 // used for alignement
 #define DUR_MAX 1024
 // mensural duration
@@ -88,13 +82,13 @@ typedef std::vector<std::pair<double, double>> data_BULGE;
  * MEI data.DURATION
  */
 enum data_DURATION {
-    DURATION_NONE = DUR_NONE,
-    DURATION_maxima = DUR_MX,
-    DURATION_long,
+    DURATION_NONE = -2,
+    DURATION_maxima, // -1
+    DURATION_long, // 0
     DURATION_breve,
     DURATION_1,
     DURATION_2,
-    DURATION_4,
+    DURATION_4, // 4
     DURATION_8,
     DURATION_16,
     DURATION_32,

From 4b3c462a169fa73cee143144910e023247ced4ca Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Thu, 3 Oct 2024 21:14:27 +0200
Subject: [PATCH 097/105] Rename variable

---
 src/horizontalaligner.cpp | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/src/horizontalaligner.cpp b/src/horizontalaligner.cpp
index 8cbf97a20ab..7b440275c51 100644
--- a/src/horizontalaligner.cpp
+++ b/src/horizontalaligner.cpp
@@ -838,13 +838,12 @@ std::pair<int, int> Alignment::GetAlignmentTopBottom() const
 int Alignment::HorizontalSpaceForDuration(
     const Fraction &intervalTime, data_DURATION maxActualDur, double spacingLinear, double spacingNonLinear)
 {
-    double doubleIntervalTime = intervalTime.ToDouble();
+    double intervalTimeDbl = intervalTime.ToDouble();
     /* If the longest duration interval in the score is longer than semibreve, adjust spacing so
      that interval gets the space a semibreve would ordinarily get. */
-    if (maxActualDur < DURATION_1) doubleIntervalTime /= pow(2.0, DURATION_1 - maxActualDur);
+    if (maxActualDur < DURATION_1) intervalTimeDbl /= pow(2.0, DURATION_1 - maxActualDur);
 
-    return pow(doubleIntervalTime * 1024, spacingNonLinear) * spacingLinear
-        * 10.0; // numbers are experimental constants
+    return pow(intervalTimeDbl * 1024, spacingNonLinear) * spacingLinear * 10.0; // numbers are experimental constants
 }
 
 FunctorCode Alignment::Accept(Functor &functor)

From d9e5f1f625032aaa8cbf0de4e478130a57d82bd4 Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Fri, 4 Oct 2024 10:53:16 +0200
Subject: [PATCH 098/105] Add method to convert Fraction to data_DURATION

* Used in PAE output
---
 include/vrv/horizontalaligner.h |  3 +++
 libmei/addons/attdef.h          |  2 +-
 src/horizontalaligner.cpp       | 15 +++++++++++++++
 src/iopae.cpp                   |  7 ++++---
 4 files changed, 23 insertions(+), 4 deletions(-)

diff --git a/include/vrv/horizontalaligner.h b/include/vrv/horizontalaligner.h
index d28fa25c731..25ca888dbb3 100644
--- a/include/vrv/horizontalaligner.h
+++ b/include/vrv/horizontalaligner.h
@@ -107,6 +107,9 @@ class Fraction {
     /** Convert fraction to a string */
     std::string ToString() const;
 
+    /** Convert to data_DURATION and the remaining Fraction */
+    std::pair<data_DURATION, Fraction> ToDur() const;
+
 private:
     /** Reduce the fraction */
     void Reduce();
diff --git a/libmei/addons/attdef.h b/libmei/addons/attdef.h
index 6bb21ecd9b7..1bbd8de2975 100644
--- a/libmei/addons/attdef.h
+++ b/libmei/addons/attdef.h
@@ -96,7 +96,7 @@ enum data_DURATION {
     DURATION_128,
     DURATION_256,
     DURATION_512,
-    DURATION_1024,
+    DURATION_1024, // 12
     DURATION_2048,
     DURATION_longa = DUR_MENSURAL_OFFSET + DURATION_long,
     DURATION_brevis,
diff --git a/src/horizontalaligner.cpp b/src/horizontalaligner.cpp
index 7b440275c51..f1a68746279 100644
--- a/src/horizontalaligner.cpp
+++ b/src/horizontalaligner.cpp
@@ -135,6 +135,21 @@ void Fraction::Reduce()
     m_denominator /= gcdVal;
 }
 
+std::pair<data_DURATION, Fraction> Fraction::ToDur() const
+{
+    if (m_numerator == 0) return { DURATION_NONE, 0 };
+
+    int value = ceil(log2((double)m_denominator / (double)m_numerator * 8)) - 1;
+    data_DURATION dur = static_cast<data_DURATION>(value);
+    dur = vrv::DurationMax(DURATION_maxima, dur);
+    dur = vrv::DurationMin(DURATION_2048, dur);
+
+    Fraction remainder = *this - Fraction(dur);
+    // Making sure we would not be trigger an inifite loop when looping over the remainder
+    if ((remainder >= *this) || (remainder < 0)) remainder = 0;
+    return { dur, remainder };
+}
+
 //----------------------------------------------------------------------------
 // HorizontalAligner
 //----------------------------------------------------------------------------
diff --git a/src/iopae.cpp b/src/iopae.cpp
index 5c654356e48..2106985c027 100644
--- a/src/iopae.cpp
+++ b/src/iopae.cpp
@@ -521,10 +521,11 @@ void PAEOutput::WriteTuplet(Tuplet *tuplet)
 
     Staff *staff = tuplet->GetAncestorStaff();
 
-    double content = tuplet->GetContentAlignmentDuration(true, staff->m_drawingNotationType).ToDouble();
-    // content = DUR_MAX / 2^(dur - 2)
-    int tupletDur = (content != 0.0) ? log2(DUR_MAX / content) + 2 : 4;
+    auto [tupletDur, remainder] = tuplet->GetContentAlignmentDuration(true, staff->m_drawingNotationType).ToDur();
     // We should be looking for dotted values
+    if (remainder != 0) {
+        LogWarning("The tuplet content is not a single non-dotted duration");
+    }
 
     std::string dur;
     switch (tupletDur) {

From 72d18e484732ea13b07a098bf8430bd03bc1b6cf Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Fri, 4 Oct 2024 13:44:40 +0200
Subject: [PATCH 099/105] Adjust variable

---
 src/calcalignmentxposfunctor.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/calcalignmentxposfunctor.cpp b/src/calcalignmentxposfunctor.cpp
index 6f8c291dc8e..38e992e3741 100644
--- a/src/calcalignmentxposfunctor.cpp
+++ b/src/calcalignmentxposfunctor.cpp
@@ -82,8 +82,8 @@ FunctorCode CalcAlignmentXPosFunctor::VisitAlignment(Alignment *alignment)
         for (auto &tsAlignment : m_timestamps) {
             // Avoid division by zero (nothing to move with the alignment anyway
             if (duration == 0) break;
-            Fraction percent = (tsAlignment->GetTime() - startTime) / duration;
-            tsAlignment->SetXRel(startXRel + space * percent.ToDouble());
+            double percent = ((tsAlignment->GetTime() - startTime) / duration).ToDouble();
+            tsAlignment->SetXRel(startXRel + space * percent);
         }
         m_timestamps.clear();
     }

From 755402537bd453dba26fa30062b49c5ab7331480 Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Fri, 4 Oct 2024 15:17:02 +0200
Subject: [PATCH 100/105] Use Fraction for score time information

---
 include/vrv/beatrpt.h           |   6 +-
 include/vrv/durationinterface.h |  20 +++---
 include/vrv/measure.h           |   6 +-
 include/vrv/midifunctor.h       |   6 +-
 include/vrv/vrvdef.h            |  10 +++
 src/beatrpt.cpp                 |   6 +-
 src/durationinterface.cpp       |  20 +++---
 src/featureextractor.cpp        |   2 +-
 src/iomusxml.cpp                |   8 +--
 src/measure.cpp                 |   4 +-
 src/midifunctor.cpp             | 117 ++++++++++++++++----------------
 11 files changed, 106 insertions(+), 99 deletions(-)

diff --git a/include/vrv/beatrpt.h b/include/vrv/beatrpt.h
index bf892e589c0..5108e3bc67c 100644
--- a/include/vrv/beatrpt.h
+++ b/include/vrv/beatrpt.h
@@ -49,8 +49,8 @@ class BeatRpt : public LayerElement, public AttColor, public AttBeatRptLog, publ
      * MIDI timing information
      */
     ///@{
-    void SetScoreTimeOnset(double scoreTime);
-    double GetScoreTimeOnset() const;
+    void SetScoreTimeOnset(Fraction scoreTime);
+    Fraction GetScoreTimeOnset() const;
 
     //----------//
     // Functors //
@@ -75,7 +75,7 @@ class BeatRpt : public LayerElement, public AttColor, public AttBeatRptLog, publ
      * The score-time onset of the note in the measure (duration from the start of measure in
      * quarter notes).
      */
-    double m_scoreTimeOnset;
+    Fraction m_scoreTimeOnset;
 };
 
 } // namespace vrv
diff --git a/include/vrv/durationinterface.h b/include/vrv/durationinterface.h
index 215ba4fce57..6a9c21292f9 100644
--- a/include/vrv/durationinterface.h
+++ b/include/vrv/durationinterface.h
@@ -114,17 +114,17 @@ class DurationInterface : public Interface,
      * MIDI timing information
      */
     ///@{
-    void SetScoreTimeOnset(double scoreTime);
+    void SetScoreTimeOnset(Fraction scoreTime);
     void SetRealTimeOnsetSeconds(double timeInSeconds);
-    void SetScoreTimeOffset(double scoreTime);
+    void SetScoreTimeOffset(Fraction scoreTime);
     void SetRealTimeOffsetSeconds(double timeInSeconds);
-    void SetScoreTimeTiedDuration(double timeInSeconds);
-    double GetScoreTimeOnset() const;
+    void SetScoreTimeTiedDuration(Fraction timeInSeconds);
+    Fraction GetScoreTimeOnset() const;
     double GetRealTimeOnsetMilliseconds() const;
-    double GetScoreTimeOffset() const;
-    double GetScoreTimeTiedDuration() const;
+    Fraction GetScoreTimeOffset() const;
+    Fraction GetScoreTimeTiedDuration() const;
     double GetRealTimeOffsetMilliseconds() const;
-    double GetScoreTimeDuration() const;
+    Fraction GetScoreTimeDuration() const;
     ///@}
 
     //-----------------//
@@ -150,7 +150,7 @@ class DurationInterface : public Interface,
      * The score-time onset of the note in the measure (duration from the start of measure in
      * quarter notes).
      */
-    double m_scoreTimeOnset;
+    Fraction m_scoreTimeOnset;
 
     /**
      * The score-time off-time of the note in the measure (duration from the start of the measure
@@ -160,7 +160,7 @@ class DurationInterface : public Interface,
      * of the printed note, and the m_scoreTimeTiedDuration is -1.0 to indicate that it should not
      * be exported when creating a MIDI file.
      */
-    double m_scoreTimeOffset;
+    Fraction m_scoreTimeOffset;
 
     /**
      * The time in milliseconds since the start of the measure element that contains the note.
@@ -180,7 +180,7 @@ class DurationInterface : public Interface,
      * If the note is a secondary note in a tied group, then this variable is set to -1.0 to
      * indicate that it should not be written to MIDI output.
      */
-    double m_scoreTimeTiedDuration;
+    Fraction m_scoreTimeTiedDuration;
 
     /**
      * The default duration: extracted from scoreDef/staffDef and used when no duration attribute is given
diff --git a/include/vrv/measure.h b/include/vrv/measure.h
index dcf96d15243..8069f161ae0 100644
--- a/include/vrv/measure.h
+++ b/include/vrv/measure.h
@@ -316,7 +316,7 @@ class Measure : public Object,
     /**
      * Read only access to m_scoreTimeOffset
      */
-    double GetLastTimeOffset() const { return m_scoreTimeOffset.back(); }
+    Fraction GetLastTimeOffset() const { return m_scoreTimeOffset.back(); }
 
     /**
      * Return the real time offset in milliseconds
@@ -331,7 +331,7 @@ class Measure : public Object,
      */
     ///@{
     void ClearScoreTimeOffset() { m_scoreTimeOffset.clear(); }
-    void AddScoreTimeOffset(double offset) { m_scoreTimeOffset.push_back(offset); }
+    void AddScoreTimeOffset(Fraction offset) { m_scoreTimeOffset.push_back(offset); }
     void ClearRealTimeOffset() { m_realTimeOffsetMilliseconds.clear(); }
     void AddRealTimeOffset(double milliseconds) { m_realTimeOffsetMilliseconds.push_back(milliseconds); }
     ///@}
@@ -448,7 +448,7 @@ class Measure : public Object,
     /**
      * Start time state variables.
      */
-    std::vector<double> m_scoreTimeOffset;
+    std::vector<Fraction> m_scoreTimeOffset;
     std::vector<double> m_realTimeOffsetMilliseconds;
     double m_currentTempo;
 
diff --git a/include/vrv/midifunctor.h b/include/vrv/midifunctor.h
index 8d7600bdf10..47e4b47d3bf 100644
--- a/include/vrv/midifunctor.h
+++ b/include/vrv/midifunctor.h
@@ -65,7 +65,7 @@ class InitOnsetOffsetFunctor : public Functor {
     //
 private:
     // The current score time in the measure (incremented by each element)
-    double m_currentScoreTime;
+    Fraction m_currentScoreTime;
     // The current real time in seconds in the measure (incremented by each element)
     double m_currentRealTimeSeconds;
     // The current time alignment parameters
@@ -127,7 +127,7 @@ class InitMaxMeasureDurationFunctor : public Functor {
     //
 private:
     // The current score time
-    double m_currentScoreTime;
+    Fraction m_currentScoreTime;
     // The current time in seconds
     double m_currentRealTimeSeconds;
     // The current tempo
@@ -423,7 +423,7 @@ class GenerateTimemapFunctor : public ConstFunctor {
     //
 private:
     // The score time from the start of the piece to the previous barline in quarter notes
-    double m_scoreTimeOffset;
+    Fraction m_scoreTimeOffset;
     // Real time from the start of the piece to the previous barline in ms
     double m_realTimeOffsetMilliseconds;
     // The current tempo
diff --git a/include/vrv/vrvdef.h b/include/vrv/vrvdef.h
index 66c40feceb4..5e02c4d153d 100644
--- a/include/vrv/vrvdef.h
+++ b/include/vrv/vrvdef.h
@@ -670,6 +670,16 @@ enum GraphicID { PRIMARY = 0, SPANNING, SYMBOLREF };
 
 enum MeasureType { MEASURED = 0, UNMEASURED, NEUMELINE };
 
+//----------------------------------------------------------------------------
+// The score time unit (quarter note)
+//----------------------------------------------------------------------------
+
+#define SCORE_TIME_UNIT 4
+
+//----------------------------------------------------------------------------
+// Section representing a line in neon
+//----------------------------------------------------------------------------
+
 #define NEUME_LINE_TYPE "neon-neume-line"
 
 //----------------------------------------------------------------------------
diff --git a/src/beatrpt.cpp b/src/beatrpt.cpp
index 26dc321bc07..d3221f02c46 100644
--- a/src/beatrpt.cpp
+++ b/src/beatrpt.cpp
@@ -51,7 +51,7 @@ void BeatRpt::Reset()
     this->ResetBeatRptVis();
     this->ResetColor();
 
-    m_scoreTimeOnset = 0.0;
+    m_scoreTimeOnset = 0;
 }
 
 Fraction BeatRpt::GetBeatRptAlignmentDuration(data_DURATION meterUnit) const
@@ -61,12 +61,12 @@ Fraction BeatRpt::GetBeatRptAlignmentDuration(data_DURATION meterUnit) const
     return duration;
 }
 
-void BeatRpt::SetScoreTimeOnset(double scoreTime)
+void BeatRpt::SetScoreTimeOnset(Fraction scoreTime)
 {
     m_scoreTimeOnset = scoreTime;
 }
 
-double BeatRpt::GetScoreTimeOnset() const
+Fraction BeatRpt::GetScoreTimeOnset() const
 {
     return m_scoreTimeOnset;
 }
diff --git a/src/durationinterface.cpp b/src/durationinterface.cpp
index 68c05cea91e..71d7a8de98d 100644
--- a/src/durationinterface.cpp
+++ b/src/durationinterface.cpp
@@ -65,11 +65,11 @@ void DurationInterface::Reset()
 
     m_durDefault = DURATION_NONE;
 
-    m_scoreTimeOnset = 0.0;
-    m_scoreTimeOffset = 0.0;
+    m_scoreTimeOnset = 0;
+    m_scoreTimeOffset = 0;
     m_realTimeOnsetMilliseconds = 0;
     m_realTimeOffsetMilliseconds = 0;
-    m_scoreTimeTiedDuration = 0.0;
+    m_scoreTimeTiedDuration = 0;
 }
 
 Fraction DurationInterface::GetInterfaceAlignmentDuration(int num, int numBase) const
@@ -251,7 +251,7 @@ bool DurationInterface::HasIdenticalDurationInterface(const DurationInterface *o
     */
 }
 
-void DurationInterface::SetScoreTimeOnset(double scoreTime)
+void DurationInterface::SetScoreTimeOnset(Fraction scoreTime)
 {
     m_scoreTimeOnset = scoreTime;
 }
@@ -262,7 +262,7 @@ void DurationInterface::SetRealTimeOnsetSeconds(double timeInSeconds)
     m_realTimeOnsetMilliseconds = timeInSeconds * 1000.0;
 }
 
-void DurationInterface::SetScoreTimeOffset(double scoreTime)
+void DurationInterface::SetScoreTimeOffset(Fraction scoreTime)
 {
     m_scoreTimeOffset = scoreTime;
 }
@@ -273,12 +273,12 @@ void DurationInterface::SetRealTimeOffsetSeconds(double timeInSeconds)
     m_realTimeOffsetMilliseconds = timeInSeconds * 1000.0;
 }
 
-void DurationInterface::SetScoreTimeTiedDuration(double scoreTime)
+void DurationInterface::SetScoreTimeTiedDuration(Fraction scoreTime)
 {
     m_scoreTimeTiedDuration = scoreTime;
 }
 
-double DurationInterface::GetScoreTimeOnset() const
+Fraction DurationInterface::GetScoreTimeOnset() const
 {
     return m_scoreTimeOnset;
 }
@@ -288,7 +288,7 @@ double DurationInterface::GetRealTimeOnsetMilliseconds() const
     return m_realTimeOnsetMilliseconds;
 }
 
-double DurationInterface::GetScoreTimeOffset() const
+Fraction DurationInterface::GetScoreTimeOffset() const
 {
     return m_scoreTimeOffset;
 }
@@ -298,12 +298,12 @@ double DurationInterface::GetRealTimeOffsetMilliseconds() const
     return m_realTimeOffsetMilliseconds;
 }
 
-double DurationInterface::GetScoreTimeTiedDuration() const
+Fraction DurationInterface::GetScoreTimeTiedDuration() const
 {
     return m_scoreTimeTiedDuration;
 }
 
-double DurationInterface::GetScoreTimeDuration() const
+Fraction DurationInterface::GetScoreTimeDuration() const
 {
     return this->GetScoreTimeOffset() - this->GetScoreTimeOnset();
 }
diff --git a/src/featureextractor.cpp b/src/featureextractor.cpp
index fad1e07b47b..a1c2a9e9a40 100644
--- a/src/featureextractor.cpp
+++ b/src/featureextractor.cpp
@@ -59,7 +59,7 @@ void FeatureExtractor::Extract(const Object *object)
         if (chord && (note != chord->GetTopNote())) return;
 
         // Check if the note is tied to a previous one and skip it if yes
-        if (note->GetScoreTimeTiedDuration() == -1.0) {
+        if (note->GetScoreTimeTiedDuration() == -1) {
             // Check if we need to add it to the previous interval ids
             const int intervalsIdsSize = (int)m_intervalsIds.size();
             if (intervalsIdsSize > 0) m_intervalsIds.get<jsonxx::Array>(intervalsIdsSize - 1) << note->GetID();
diff --git a/src/iomusxml.cpp b/src/iomusxml.cpp
index 0432bf0caf1..98a09782a54 100644
--- a/src/iomusxml.cpp
+++ b/src/iomusxml.cpp
@@ -1761,11 +1761,11 @@ void MusicXmlInput::MatchTies(bool matchLayers)
             // match tie stop with pitch/oct identity, with start note earlier than end note,
             // and with earliest end note.
             if ((iter->m_note->IsEnharmonicWith(jter->m_note))
-                && (iter->m_note->GetScoreTimeOnset() < jter->m_note->GetScoreTimeOnset())
-                && (jter->m_note->GetScoreTimeOnset() < lastScoreTimeOnset)
+                && (iter->m_note->GetRealTimeOnsetMilliseconds() < jter->m_note->GetRealTimeOnsetMilliseconds())
+                && (jter->m_note->GetRealTimeOnsetMilliseconds() < lastScoreTimeOnset)
                 && (!matchLayers || (iter->m_layerNum == jter->m_layerNum))) {
                 iter->m_tie->SetEndid("#" + jter->m_note->GetID());
-                lastScoreTimeOnset = jter->m_note->GetScoreTimeOnset();
+                lastScoreTimeOnset = jter->m_note->GetRealTimeOnsetMilliseconds();
                 tieMatched = true;
                 break;
             }
@@ -2830,7 +2830,7 @@ void MusicXmlInput::ReadMusicXmlNote(
         if (!noteID.empty()) {
             note->SetID(noteID);
         }
-        note->SetScoreTimeOnset(onset); // remember the MIDI onset within that measure
+        note->SetRealTimeOnsetSeconds(onset); // remember the MIDI onset within that measure
         // set @staff attribute, if existing and different from parent staff number
         if (noteStaffNum > 0 && noteStaffNum + staffOffset != staff->GetN())
             note->SetStaff(
diff --git a/src/measure.cpp b/src/measure.cpp
index 48bd70e42af..ea7b78ae3fd 100644
--- a/src/measure.cpp
+++ b/src/measure.cpp
@@ -475,8 +475,8 @@ const Staff *Measure::GetBottomVisibleStaff() const
 int Measure::EnclosesTime(int time) const
 {
     int repeat = 1;
-    double timeDuration = m_measureAligner.GetRightAlignment()->GetTime().ToDouble() * static_cast<int>(DURATION_4)
-            / DUR_MAX * 60.0 / m_currentTempo * 1000.0
+    double timeDuration
+        = m_measureAligner.GetRightAlignment()->GetTime().ToDouble() * SCORE_TIME_UNIT * 60.0 / m_currentTempo * 1000.0
         + 0.5;
     std::vector<double>::const_iterator iter;
     for (iter = m_realTimeOffsetMilliseconds.begin(); iter != m_realTimeOffsetMilliseconds.end(); ++iter) {
diff --git a/src/midifunctor.cpp b/src/midifunctor.cpp
index 45ea551835c..c05965ae93a 100644
--- a/src/midifunctor.cpp
+++ b/src/midifunctor.cpp
@@ -42,7 +42,7 @@ namespace vrv {
 
 InitOnsetOffsetFunctor::InitOnsetOffsetFunctor() : Functor()
 {
-    m_currentScoreTime = 0.0;
+    m_currentScoreTime = 0;
     m_currentRealTimeSeconds = 0.0;
     m_meterParams.mensur = NULL;
     m_meterParams.meterSig = NULL;
@@ -54,11 +54,10 @@ FunctorCode InitOnsetOffsetFunctor::VisitChordEnd(Chord *chord)
 {
     LayerElement *element = chord->ThisOrSameasLink();
 
-    double incrementScoreTime = element->GetAlignmentDuration(m_meterParams, true, m_notationType).ToDouble();
-    incrementScoreTime = incrementScoreTime / (DUR_MAX / DURATION_4);
-    double realTimeIncrementSeconds = incrementScoreTime * 60.0 / m_currentTempo;
+    Fraction incrementScoreTime = element->GetAlignmentDuration(m_meterParams, true, m_notationType) * SCORE_TIME_UNIT;
+    double realTimeIncrementSeconds = incrementScoreTime.ToDouble() * 60.0 / m_currentTempo;
 
-    m_currentScoreTime += incrementScoreTime;
+    m_currentScoreTime = m_currentScoreTime + incrementScoreTime;
     m_currentRealTimeSeconds += realTimeIncrementSeconds;
 
     return FUNCTOR_CONTINUE;
@@ -66,7 +65,7 @@ FunctorCode InitOnsetOffsetFunctor::VisitChordEnd(Chord *chord)
 
 FunctorCode InitOnsetOffsetFunctor::VisitLayer(Layer *layer)
 {
-    m_currentScoreTime = 0.0;
+    m_currentScoreTime = 0;
     m_currentRealTimeSeconds = 0.0;
 
     m_meterParams.mensur = layer->GetCurrentMensur();
@@ -81,22 +80,21 @@ FunctorCode InitOnsetOffsetFunctor::VisitLayerElement(LayerElement *layerElement
 
     LayerElement *element = layerElement->ThisOrSameasLink();
 
-    double incrementScoreTime;
+    Fraction incrementScoreTime;
 
     if (element->Is(REST) || element->Is(SPACE)) {
-        incrementScoreTime = element->GetAlignmentDuration(m_meterParams, true, m_notationType).ToDouble();
-        incrementScoreTime = incrementScoreTime / (DUR_MAX / DURATION_4);
+        incrementScoreTime = element->GetAlignmentDuration(m_meterParams, true, m_notationType) * SCORE_TIME_UNIT;
         // For rests to be possibly added to the timemap
         if (element->Is(REST)) {
             Rest *rest = vrv_cast<Rest *>(element);
-            double realTimeIncrementSeconds = incrementScoreTime * 60.0 / m_currentTempo;
+            double realTimeIncrementSeconds = incrementScoreTime.ToDouble() * 60.0 / m_currentTempo;
             rest->SetScoreTimeOnset(m_currentScoreTime);
             rest->SetRealTimeOnsetSeconds(m_currentRealTimeSeconds);
             rest->SetScoreTimeOffset(m_currentScoreTime + incrementScoreTime);
             rest->SetRealTimeOffsetSeconds(m_currentRealTimeSeconds + realTimeIncrementSeconds);
         }
-        m_currentScoreTime += incrementScoreTime;
-        m_currentRealTimeSeconds += incrementScoreTime * 60.0 / m_currentTempo;
+        m_currentScoreTime = m_currentScoreTime + incrementScoreTime;
+        m_currentRealTimeSeconds += incrementScoreTime.ToDouble() * 60.0 / m_currentTempo;
     }
     else if (element->Is(NOTE)) {
         Note *note = vrv_cast<Note *>(element);
@@ -111,16 +109,15 @@ FunctorCode InitOnsetOffsetFunctor::VisitLayerElement(LayerElement *layerElement
         // If the note has a @dur or a @dur.ges, take it into account
         // This means that overwriting only @dots or @dots.ges will not be taken into account
         if (chord && !note->HasDur() && !note->HasDurGes()) {
-            incrementScoreTime = chord->GetAlignmentDuration(m_meterParams, true, m_notationType).ToDouble();
+            incrementScoreTime = chord->GetAlignmentDuration(m_meterParams, true, m_notationType) * SCORE_TIME_UNIT;
         }
         else if (tabGrp && !note->HasDur() && !note->HasDurGes()) {
-            incrementScoreTime = tabGrp->GetAlignmentDuration(m_meterParams, true, m_notationType).ToDouble();
+            incrementScoreTime = tabGrp->GetAlignmentDuration(m_meterParams, true, m_notationType) * SCORE_TIME_UNIT;
         }
         else {
-            incrementScoreTime = note->GetAlignmentDuration(m_meterParams, true, m_notationType).ToDouble();
+            incrementScoreTime = note->GetAlignmentDuration(m_meterParams, true, m_notationType) * SCORE_TIME_UNIT;
         }
-        incrementScoreTime = incrementScoreTime / (DUR_MAX / DURATION_4);
-        double realTimeIncrementSeconds = incrementScoreTime * 60.0 / m_currentTempo;
+        double realTimeIncrementSeconds = incrementScoreTime.ToDouble() * 60.0 / m_currentTempo;
 
         // LogDebug("Note Alignment Duration %f - Dur %d - Diatonic Pitch %d - Track %d", GetAlignmentDuration(),
         // note->GetNoteOrChordDur(element), note->GetDiatonicPitch(), *midiTrack);
@@ -137,7 +134,7 @@ FunctorCode InitOnsetOffsetFunctor::VisitLayerElement(LayerElement *layerElement
 
         // increase the currentTime accordingly, but only if not in a chord or tabGrp
         if (!note->IsChordTone() && !note->IsTabGrpNote()) {
-            m_currentScoreTime += incrementScoreTime;
+            m_currentScoreTime = m_currentScoreTime + incrementScoreTime;
             m_currentRealTimeSeconds += realTimeIncrementSeconds;
         }
     }
@@ -145,18 +142,16 @@ FunctorCode InitOnsetOffsetFunctor::VisitLayerElement(LayerElement *layerElement
         BeatRpt *rpt = vrv_cast<BeatRpt *>(element);
         assert(rpt);
 
-        incrementScoreTime = rpt->GetAlignmentDuration(m_meterParams, true, m_notationType).ToDouble();
-        incrementScoreTime = incrementScoreTime / (DUR_MAX / DURATION_4);
+        incrementScoreTime = rpt->GetAlignmentDuration(m_meterParams, true, m_notationType) * SCORE_TIME_UNIT;
         rpt->SetScoreTimeOnset(m_currentScoreTime);
-        m_currentScoreTime += incrementScoreTime;
-        m_currentRealTimeSeconds += incrementScoreTime * 60.0 / m_currentTempo;
+        m_currentScoreTime = m_currentScoreTime + incrementScoreTime;
+        m_currentRealTimeSeconds += incrementScoreTime.ToDouble() * 60.0 / m_currentTempo;
     }
     else if (layerElement->Is({ BEAM, LIGATURE, FTREM, TUPLET }) && layerElement->HasSameasLink()) {
         incrementScoreTime
-            = layerElement->GetSameAsContentAlignmentDuration(m_meterParams, true, m_notationType).ToDouble();
-        incrementScoreTime = incrementScoreTime / (DUR_MAX / DURATION_4);
-        m_currentScoreTime += incrementScoreTime;
-        m_currentRealTimeSeconds += incrementScoreTime * 60.0 / m_currentTempo;
+            = layerElement->GetSameAsContentAlignmentDuration(m_meterParams, true, m_notationType) * SCORE_TIME_UNIT;
+        m_currentScoreTime = m_currentScoreTime + incrementScoreTime;
+        m_currentRealTimeSeconds += incrementScoreTime.ToDouble() * 60.0 / m_currentTempo;
     }
     else if (layerElement->Is(MENSUR)) {
         this->m_meterParams.mensur = vrv_cast<Mensur *>(layerElement);
@@ -189,11 +184,10 @@ FunctorCode InitOnsetOffsetFunctor::VisitTabGrpEnd(TabGrp *tabGrp)
 {
     LayerElement *element = tabGrp->ThisOrSameasLink();
 
-    double incrementScoreTime = element->GetAlignmentDuration(m_meterParams, true, m_notationType).ToDouble();
-    incrementScoreTime = incrementScoreTime / (DUR_MAX / DURATION_4);
-    double realTimeIncrementSeconds = incrementScoreTime * 60.0 / m_currentTempo;
+    Fraction incrementScoreTime = element->GetAlignmentDuration(m_meterParams, true, m_notationType) * SCORE_TIME_UNIT;
+    double realTimeIncrementSeconds = incrementScoreTime.ToDouble() * 60.0 / m_currentTempo;
 
-    m_currentScoreTime += incrementScoreTime;
+    m_currentScoreTime = m_currentScoreTime + incrementScoreTime;
     m_currentRealTimeSeconds += realTimeIncrementSeconds;
 
     return FUNCTOR_CONTINUE;
@@ -205,7 +199,7 @@ FunctorCode InitOnsetOffsetFunctor::VisitTabGrpEnd(TabGrp *tabGrp)
 
 InitMaxMeasureDurationFunctor::InitMaxMeasureDurationFunctor() : Functor()
 {
-    m_currentScoreTime = 0.0;
+    m_currentScoreTime = 0;
     m_currentRealTimeSeconds = 0.0;
     m_currentTempo = MIDI_TEMPO;
     m_tempoAdjustment = 1.0;
@@ -239,10 +233,10 @@ FunctorCode InitMaxMeasureDurationFunctor::VisitMeasureEnd(Measure *measure)
     const double tempo = this->GetAdjustedTempo();
     measure->SetCurrentTempo(tempo);
 
-    const double scoreTimeIncrement = measure->m_measureAligner.GetRightAlignment()->GetTime().ToDouble()
-        * m_multiRestFactor * static_cast<int>(DURATION_4) / DUR_MAX;
-    m_currentScoreTime += scoreTimeIncrement;
-    m_currentRealTimeSeconds += scoreTimeIncrement * 60.0 / tempo;
+    const Fraction scoreTimeIncrement
+        = measure->m_measureAligner.GetRightAlignment()->GetTime() * m_multiRestFactor * SCORE_TIME_UNIT;
+    m_currentScoreTime = m_currentScoreTime + scoreTimeIncrement;
+    m_currentRealTimeSeconds += scoreTimeIncrement.ToDouble() * 60.0 / tempo;
     m_multiRestFactor = 1;
 
     return FUNCTOR_CONTINUE;
@@ -287,16 +281,16 @@ FunctorCode InitTimemapTiesFunctor::VisitTie(Tie *tie)
         return FUNCTOR_CONTINUE;
     }
 
-    double sttd2 = note2->GetScoreTimeTiedDuration();
-    double std2 = note2->GetScoreTimeDuration();
+    Fraction sttd2 = note2->GetScoreTimeTiedDuration();
+    Fraction std2 = note2->GetScoreTimeDuration();
 
-    if (sttd2 > 0.0) {
+    if (sttd2 > 0) {
         note1->SetScoreTimeTiedDuration(sttd2 + std2);
     }
     else {
         note1->SetScoreTimeTiedDuration(std2);
     }
-    note2->SetScoreTimeTiedDuration(-1.0);
+    note2->SetScoreTimeTiedDuration(-1);
 
     return FUNCTOR_SIBLINGS;
 }
@@ -363,8 +357,8 @@ FunctorCode GenerateMIDIFunctor::VisitBeatRpt(const BeatRpt *beatRpt)
 {
     // Sameas not taken into account for now
     AlignMeterParams params;
-    double beatLength = beatRpt->GetAlignmentDuration(params).ToDouble() / (DUR_MAX / DURATION_4);
-    double startTime = m_totalTime + beatRpt->GetScoreTimeOnset();
+    double beatLength = beatRpt->GetAlignmentDuration(params).ToDouble() * SCORE_TIME_UNIT;
+    double startTime = m_totalTime + beatRpt->GetScoreTimeOnset().ToDouble();
     int tpq = m_midiFile->getTPQ();
 
     // filter last beat and copy all notes
@@ -418,7 +412,8 @@ FunctorCode GenerateMIDIFunctor::VisitBTrem(const BTrem *bTrem)
         const Note *note = vrv_cast<const Note *>(obj);
         assert(note);
         const int pitch = note->GetMIDIPitch(m_transSemi);
-        const double totalInQuarterDur = note->GetScoreTimeDuration() + note->GetScoreTimeTiedDuration();
+        const double totalInQuarterDur
+            = note->GetScoreTimeDuration().ToDouble() + note->GetScoreTimeTiedDuration().ToDouble();
         int multiplicity = totalInQuarterDur / noteInQuarterDur;
         double noteDuration = noteInQuarterDur;
         // if NUM has been set for the bTrem, override calculated values
@@ -490,7 +485,7 @@ FunctorCode GenerateMIDIFunctor::VisitGraceGrpEnd(const GraceGrp *graceGrp)
     // Handling of Nachschlag
     if (!m_graceNotes.empty() && (graceGrp->GetAttach() == graceGrpLog_ATTACH_pre) && !m_accentedGraceNote
         && m_lastNote) {
-        double startTime = m_totalTime + m_lastNote->GetScoreTimeOffset();
+        double startTime = m_totalTime + m_lastNote->GetScoreTimeOffset().ToDouble();
         const double graceNoteDur = UNACC_GRACENOTE_DUR * m_currentTempo / 60000.0;
         const double totalDur = graceNoteDur * m_graceNotes.size();
         startTime -= totalDur;
@@ -559,7 +554,7 @@ FunctorCode GenerateMIDIFunctor::VisitLayerElement(const LayerElement *layerElem
 FunctorCode GenerateMIDIFunctor::VisitMeasure(const Measure *measure)
 {
     // Here we need to update the m_totalTime from the starting time of the measure.
-    m_totalTime = measure->GetLastTimeOffset();
+    m_totalTime = measure->GetLastTimeOffset().ToDouble();
 
     if (measure->GetCurrentTempo() != m_currentTempo) {
         m_currentTempo = measure->GetCurrentTempo();
@@ -593,7 +588,7 @@ FunctorCode GenerateMIDIFunctor::VisitNote(const Note *note)
     }
 
     // If the note is a secondary tied note, then ignore it
-    if (note->GetScoreTimeTiedDuration() < 0.0) {
+    if (note->GetScoreTimeTiedDuration() < 0) {
         return FUNCTOR_SIBLINGS;
     }
 
@@ -621,7 +616,7 @@ FunctorCode GenerateMIDIFunctor::VisitNote(const Note *note)
     int velocity = MIDI_VELOCITY;
     if (note->HasVel()) velocity = note->GetVel();
 
-    double startTime = m_totalTime + note->GetScoreTimeOnset();
+    double startTime = m_totalTime + note->GetScoreTimeOnset().ToDouble();
     const int tpq = m_midiFile->getTPQ();
 
     // Check if some grace notes must be performed
@@ -680,13 +675,15 @@ FunctorCode GenerateMIDIFunctor::VisitNote(const Note *note)
             const double defaultHoldTime = 4; // quarter notes
             m_heldNotes[course - 1].m_pitch = pitch;
             m_heldNotes[course - 1].m_stopTime = m_totalTime
-                + std::max(defaultHoldTime, note->GetScoreTimeOffset() + note->GetScoreTimeTiedDuration());
+                + std::max(defaultHoldTime,
+                    note->GetScoreTimeOffset().ToDouble() + note->GetScoreTimeTiedDuration().ToDouble());
 
             // start this note
             m_midiFile->addNoteOn(m_midiTrack, startTime * tpq, channel, pitch, velocity);
         }
         else {
-            const double stopTime = m_totalTime + note->GetScoreTimeOffset() + note->GetScoreTimeTiedDuration();
+            const double stopTime
+                = m_totalTime + note->GetScoreTimeOffset().ToDouble() + note->GetScoreTimeTiedDuration().ToDouble();
 
             m_midiFile->addNoteOn(m_midiTrack, startTime * tpq, channel, pitch, velocity);
             m_midiFile->addNoteOff(m_midiTrack, stopTime * tpq, channel, pitch);
@@ -703,7 +700,7 @@ FunctorCode GenerateMIDIFunctor::VisitPedal(const Pedal *pedal)
 {
     if (!pedal->HasDir()) return FUNCTOR_CONTINUE;
 
-    double pedalTime = pedal->GetStart()->GetAlignment()->GetTime().ToDouble() * static_cast<int>(DURATION_4) / DUR_MAX;
+    double pedalTime = pedal->GetStart()->GetAlignment()->GetTime().ToDouble() * SCORE_TIME_UNIT;
     double startTime = m_totalTime + pedalTime;
     int tpq = m_midiFile->getTPQ();
 
@@ -730,7 +727,7 @@ FunctorCode GenerateMIDIFunctor::VisitScoreDef(const ScoreDef *scoreDef)
         const Object *next = parent->GetNext(scoreDef);
         if (next && next->Is(MEASURE)) {
             const Measure *nextMeasure = vrv_cast<const Measure *>(next);
-            totalTime = nextMeasure->GetLastTimeOffset();
+            totalTime = nextMeasure->GetLastTimeOffset().ToDouble();
         }
     }
     const double currentTick = totalTime * m_midiFile->getTPQ();
@@ -803,7 +800,7 @@ FunctorCode GenerateMIDIFunctor::VisitStaffDef(const StaffDef *staffDef)
 
 FunctorCode GenerateMIDIFunctor::VisitSyl(const Syl *syl)
 {
-    const double startTime = m_totalTime + m_lastNote->GetScoreTimeOnset();
+    const double startTime = m_totalTime + m_lastNote->GetScoreTimeOnset().ToDouble();
     const std::string sylText = UTF32to8(syl->GetText());
 
     m_midiFile->addLyric(m_midiTrack, startTime * m_midiFile->getTPQ(), sylText);
@@ -840,7 +837,7 @@ void GenerateMIDIFunctor::DeferMIDINote(const Note *refNote, double shift, bool
     }
 
     // Register the shift
-    if (shift < refNote->GetScoreTimeDuration() + refNote->GetScoreTimeTiedDuration()) {
+    if (shift < refNote->GetScoreTimeDuration().ToDouble() + refNote->GetScoreTimeTiedDuration().ToDouble()) {
         m_deferredNotes[refNote] = shift;
     }
 }
@@ -850,7 +847,7 @@ void GenerateMIDIFunctor::GenerateGraceNoteMIDI(
 {
     double graceNoteDur = 0.0;
     if (m_accentedGraceNote && !m_graceNotes.empty()) {
-        const double totalDur = refNote->GetScoreTimeDuration() / 2.0;
+        const double totalDur = refNote->GetScoreTimeDuration().ToDouble() / 2.0;
         this->DeferMIDINote(refNote, totalDur, true);
         graceNoteDur = totalDur / m_graceNotes.size();
     }
@@ -881,7 +878,7 @@ void GenerateMIDIFunctor::GenerateGraceNoteMIDI(
 
 GenerateTimemapFunctor::GenerateTimemapFunctor(Timemap *timemap) : ConstFunctor()
 {
-    m_scoreTimeOffset = 0.0;
+    m_scoreTimeOffset = 0;
     m_realTimeOffsetMilliseconds = 0.0;
     m_currentTempo = MIDI_TEMPO;
     m_cueExclusion = false;
@@ -945,10 +942,10 @@ void GenerateTimemapFunctor::AddTimemapEntry(const Object *object)
         assert(interface);
 
         double realTimeStart = round(m_realTimeOffsetMilliseconds + interface->GetRealTimeOnsetMilliseconds());
-        double scoreTimeStart = m_scoreTimeOffset + interface->GetScoreTimeOnset();
+        Fraction scoreTimeStart = m_scoreTimeOffset + interface->GetScoreTimeOnset();
 
         double realTimeEnd = round(m_realTimeOffsetMilliseconds + interface->GetRealTimeOffsetMilliseconds());
-        double scoreTimeEnd = m_scoreTimeOffset + interface->GetScoreTimeOffset();
+        Fraction scoreTimeEnd = m_scoreTimeOffset + interface->GetScoreTimeOffset();
 
         bool isRest = (object->Is(REST));
 
@@ -958,7 +955,7 @@ void GenerateTimemapFunctor::AddTimemapEntry(const Object *object)
 
         // Should check if value for realTimeStart already exists and if so, then
         // ensure that it is equal to scoreTimeStart:
-        startEntry.qstamp = scoreTimeStart;
+        startEntry.qstamp = scoreTimeStart.ToDouble();
 
         // Store the element ID in list to turn on at given time - note or rest
         if (!isRest) startEntry.notesOn.push_back(object->GetID());
@@ -973,7 +970,7 @@ void GenerateTimemapFunctor::AddTimemapEntry(const Object *object)
 
         // Should check if value for realTimeEnd already exists and if so, then
         // ensure that it is equal to scoreTimeEnd:
-        endEntry.qstamp = scoreTimeEnd;
+        endEntry.qstamp = scoreTimeEnd.ToDouble();
 
         // Store the element ID in list to turn off at given time - notes or rest
         if (!isRest) endEntry.notesOff.push_back(object->GetID());
@@ -985,14 +982,14 @@ void GenerateTimemapFunctor::AddTimemapEntry(const Object *object)
         assert(measure);
 
         // Deal with repeated music later, for now get the last times.
-        double scoreTimeStart = m_scoreTimeOffset;
+        Fraction scoreTimeStart = m_scoreTimeOffset;
         double realTimeStart = round(m_realTimeOffsetMilliseconds);
 
         TimemapEntry &startEntry = m_timemap->GetEntry(realTimeStart);
 
         // Should check if value for realTimeStart already exists and if so, then
         // ensure that it is equal to scoreTimeStart:
-        startEntry.qstamp = scoreTimeStart;
+        startEntry.qstamp = scoreTimeStart.ToDouble();
 
         // Add the measureOn
         startEntry.measureOn = measure->GetID();

From 416c8926ff6cb070c4fcc7ec052b672f7ecdb241 Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Fri, 4 Oct 2024 15:43:49 +0200
Subject: [PATCH 101/105] Add test to avoid useless division in
 Fraction::Reduce

---
 src/horizontalaligner.cpp | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/src/horizontalaligner.cpp b/src/horizontalaligner.cpp
index f1a68746279..6b6098f28eb 100644
--- a/src/horizontalaligner.cpp
+++ b/src/horizontalaligner.cpp
@@ -131,8 +131,10 @@ void Fraction::Reduce()
         m_denominator = -m_denominator;
     }
     int gcdVal = std::gcd(abs(m_numerator), abs(m_denominator));
-    m_numerator /= gcdVal;
-    m_denominator /= gcdVal;
+    if (gcdVal != 1) {
+        m_numerator /= gcdVal;
+        m_denominator /= gcdVal;
+    }
 }
 
 std::pair<data_DURATION, Fraction> Fraction::ToDur() const

From 16a3c5dba0409268a7be0d95eef196a82a10bd32 Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Sat, 5 Oct 2024 14:35:05 +0200
Subject: [PATCH 102/105] Use Fraction in mensural alignment

---
 src/durationinterface.cpp | 32 +++++++++++++++-----------------
 1 file changed, 15 insertions(+), 17 deletions(-)

diff --git a/src/durationinterface.cpp b/src/durationinterface.cpp
index 71d7a8de98d..075c3ab2dd5 100644
--- a/src/durationinterface.cpp
+++ b/src/durationinterface.cpp
@@ -82,8 +82,6 @@ Fraction DurationInterface::GetInterfaceAlignmentDuration(int num, int numBase)
 
     Fraction duration(noteDur);
     duration = duration * numBase / num;
-    // double old = DUR_MAX / pow(2.0, (double)(noteDur - 2.0)) * numBase / num;
-    // duration = DUR_MAX / pow(2.0, (double)(noteDur - 2.0)) * numBase / num;
 
     int noteDots = (this->HasDotsGes()) ? this->GetDotsGes() : this->GetDots();
     if (noteDots != VRV_UNSET) {
@@ -97,7 +95,7 @@ Fraction DurationInterface::GetInterfaceAlignmentDuration(int num, int numBase)
 Fraction DurationInterface::GetInterfaceAlignmentMensuralDuration(
     int num, int numBase, const Mensur *currentMensur) const
 {
-    int noteDur = this->GetDurGes() != DURATION_NONE ? this->GetActualDurGes() : this->GetActualDur();
+    data_DURATION noteDur = this->GetDurGes() != DURATION_NONE ? this->GetActualDurGes() : this->GetActualDur();
     if (noteDur == DURATION_NONE) noteDur = DURATION_4;
 
     if (!currentMensur) {
@@ -141,24 +139,24 @@ Fraction DurationInterface::GetInterfaceAlignmentMensuralDuration(
     if (currentMensur->HasNum()) num *= currentMensur->GetNum();
     if (currentMensur->HasNumbase()) numBase *= currentMensur->GetNumbase();
 
-    double ratio = 0.0;
-    double duration = (double)DUR_MENSURAL_REF;
+    int ratio = 0;
+    Fraction duration(DURATION_breve);
     switch (noteDur) {
         case DURATION_maxima:
-            duration *= (double)abs(currentMensur->GetModusminor()) * (double)abs(currentMensur->GetModusmaior());
+            duration = duration * abs(currentMensur->GetModusminor()) * abs(currentMensur->GetModusmaior());
             break;
-        case DURATION_long: duration *= (double)abs(currentMensur->GetModusminor()); break;
+        case DURATION_long: duration = duration * abs(currentMensur->GetModusminor()); break;
         case DURATION_breve: break;
-        case DURATION_1: duration /= (double)abs(currentMensur->GetTempus()); break;
+        case DURATION_1: duration = duration / abs(currentMensur->GetTempus()); break;
         default:
             ratio = pow(2.0, (double)(noteDur - DURATION_2));
-            duration /= (double)abs(currentMensur->GetTempus()) * (double)abs(currentMensur->GetProlatio()) * ratio;
+            assert(ratio);
+            duration = duration / abs(currentMensur->GetTempus()) / abs(currentMensur->GetProlatio()) / ratio;
             break;
     }
-    duration *= (double)numBase / (double)num;
-    // LogDebug("Duration %d; %d/%d; Alignment %f; Ratio %f", noteDur, num, numbase, duration, ratio);
-    duration = durRound(duration);
-    return Fraction(DUR_MAX * duration, DUR_MAX * DUR_MAX);
+    duration = duration * numBase / num;
+
+    return duration;
 }
 
 bool DurationInterface::IsFirstInBeam(const LayerElement *noteOrRest) const
@@ -193,9 +191,9 @@ data_DURATION DurationInterface::GetActualDurGes() const
 
 data_DURATION DurationInterface::CalcActualDur(data_DURATION dur) const
 {
-    // maxima (-1) is a mensural only value
-    if (dur < DUR_MAX) return dur;
-    // Mensural duration (except maxima)
+    // No mapping needed for values below, including maxima and NONE
+    if (dur < DURATION_longa) return dur;
+    // Mensural durations (except maxima)
     switch (dur) {
         case DURATION_longa: return DURATION_long;
         case DURATION_brevis: return DURATION_breve;
@@ -235,7 +233,7 @@ bool DurationInterface::IsMensuralDur() const
 {
     // maxima (-1) is a mensural only value
     if (this->GetDur() == DURATION_maxima) return true;
-    return (this->GetDur() > DUR_MENSURAL_MASK);
+    return (this->GetDur() >= DURATION_longa);
 }
 
 bool DurationInterface::HasIdenticalDurationInterface(const DurationInterface *otherDurationInterface) const

From a170857b4c618bb1fbd3e42ab50dd3b0d65c74ef Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Sat, 5 Oct 2024 14:36:00 +0200
Subject: [PATCH 103/105] Additional cleanup

* Remove some duration defines
* Remove some uses of DUR_MAX
* Adjust code comments
---
 include/vrv/durationinterface.h |  5 ++---
 include/vrv/horizontalaligner.h |  2 +-
 include/vrv/vrvdef.h            |  2 --
 libmei/addons/attdef.h          | 11 +++--------
 src/drawinginterface.cpp        |  2 +-
 src/horizontalaligner.cpp       |  1 -
 src/layer.cpp                   |  1 -
 src/view_element.cpp            |  2 +-
 8 files changed, 8 insertions(+), 18 deletions(-)

diff --git a/include/vrv/durationinterface.h b/include/vrv/durationinterface.h
index 6a9c21292f9..2a101f46e8c 100644
--- a/include/vrv/durationinterface.h
+++ b/include/vrv/durationinterface.h
@@ -67,7 +67,7 @@ class DurationInterface : public Interface,
 
     /**
      * Returns the duration (in Fraction) for the element for mensural notation
-     * Currently this assume brevis equality (through DUR_MENSURAL_REF) and would
+     * Currently this assume brevis equality and would
      * need to be modified for shorter equality in later repertoire.
      */
     Fraction GetInterfaceAlignmentMensuralDuration(int num, int numBase, const Mensur *currentMensur) const;
@@ -85,8 +85,7 @@ class DurationInterface : public Interface,
     /**
      * @name Return the actual (gestural) duration of the note, for both CMN and mensural durations
      * See data_DURATION
-     * For CMN, it is the same (DURATION_1 == DURATION_1)
-     * For mensural, we need to apply the DUR_MENSURAL_MASK
+     * For Mensural, it is the same (DURATION_2 == DURATION_minima)
      */
     ///@{
     data_DURATION GetActualDur() const;
diff --git a/include/vrv/horizontalaligner.h b/include/vrv/horizontalaligner.h
index 25ca888dbb3..fa761c6e549 100644
--- a/include/vrv/horizontalaligner.h
+++ b/include/vrv/horizontalaligner.h
@@ -527,7 +527,7 @@ class MeasureAligner : public HorizontalAligner {
     /**
      * Get left Alignment for the measure and for the left BarLine.
      * For each MeasureAligner, we keep and Alignment for the left position.
-     * The Alignment time will be always -1.0 * DUR_MAX and will appear first in the list.
+     * The Alignment time will be always -1.0 and will appear first in the list.
      */
     ///@{
     Alignment *GetLeftAlignment() { return m_leftAlignment; }
diff --git a/include/vrv/vrvdef.h b/include/vrv/vrvdef.h
index 5e02c4d153d..b596eac7762 100644
--- a/include/vrv/vrvdef.h
+++ b/include/vrv/vrvdef.h
@@ -430,8 +430,6 @@ typedef std::map<int, LayerN_VerserN_t> StaffN_LayerN_VerseN_t;
 
 #define isIn(x, a, b) (((x) >= std::min((a), (b))) && ((x) <= std::max((a), (b))))
 
-#define durRound(dur) round(dur *pow(10, 8)) / pow(10, 8)
-
 /**
  * Codes returned by Functors.
  * Default is FUNCTOR_CONTINUE.
diff --git a/libmei/addons/attdef.h b/libmei/addons/attdef.h
index 1bbd8de2975..04ee0b3c3ed 100644
--- a/libmei/addons/attdef.h
+++ b/libmei/addons/attdef.h
@@ -34,12 +34,7 @@ typedef double data_VU;
 //----------------------------------------------------------------------------
 
 // used for alignement
-#define DUR_MAX 1024
-// mensural duration
-#define DUR_MENSURAL_OFFSET (2 * DUR_MAX)
-#define DUR_MENSURAL_MASK (2 * DUR_MAX - 1)
-// used for mensural alignment
-#define DUR_MENSURAL_REF 1728
+#define DUR_MAX 2048
 
 //----------------------------------------------------------------------------
 // MEI data defines
@@ -98,8 +93,8 @@ enum data_DURATION {
     DURATION_512,
     DURATION_1024, // 12
     DURATION_2048,
-    DURATION_longa = DUR_MENSURAL_OFFSET + DURATION_long,
-    DURATION_brevis,
+    DURATION_longa = 100,
+    DURATION_brevis, // 101
     DURATION_semibrevis,
     DURATION_minima,
     DURATION_semiminima,
diff --git a/src/drawinginterface.cpp b/src/drawinginterface.cpp
index c4a102a9432..09f209df455 100644
--- a/src/drawinginterface.cpp
+++ b/src/drawinginterface.cpp
@@ -420,7 +420,7 @@ bool BeamDrawingInterface::IsRepeatedPattern() const
     for (BeamElementCoord *coord : m_beamElementCoords) {
         if (!coord->m_stem || !coord->m_closestNote) continue;
 
-        // Could this be an overflow with 32 bits?
+        // Could this be an overflow with 32 bits? Not sure why DUR_MAX is used here
         items.push_back(coord->m_closestNote->GetDrawingY() + DUR_MAX * coord->m_dur);
     }
     int itemCount = (int)items.size();
diff --git a/src/horizontalaligner.cpp b/src/horizontalaligner.cpp
index 6b6098f28eb..5e1a30ae9b8 100644
--- a/src/horizontalaligner.cpp
+++ b/src/horizontalaligner.cpp
@@ -272,7 +272,6 @@ bool MeasureAligner::IsSupportedChild(Object *child)
 Alignment *MeasureAligner::GetAlignmentAtTime(const Fraction &time, AlignmentType type)
 {
     int idx; // the index if we reach the end.
-    // time = durRound(time);
     Alignment *alignment = this->SearchAlignmentAtTime(time, type, idx);
     // we already have a alignment of the type at that time
     if (alignment != NULL) return alignment;
diff --git a/src/layer.cpp b/src/layer.cpp
index e76afa8c94d..eaa30b8be3d 100644
--- a/src/layer.cpp
+++ b/src/layer.cpp
@@ -362,7 +362,6 @@ data_STEMDIRECTION Layer::GetDrawingStemDir(const ArrayOfBeamElementCoords *coor
     else {
         duration = measure->m_measureAligner.GetRightAlignment()->GetTime() - time;
     }
-    // duration = durRound(duration);
 
     if (this->GetLayerCountInTimeSpan(time, duration, measure, staff->GetN()) < 2) {
         return STEMDIRECTION_NONE;
diff --git a/src/view_element.cpp b/src/view_element.cpp
index 9766eac5833..efa5fd74b63 100644
--- a/src/view_element.cpp
+++ b/src/view_element.cpp
@@ -1174,7 +1174,7 @@ void View::DrawMRest(DeviceContext *dc, LayerElement *element, Layer *layer, Sta
     this->DrawSmuflCode(dc, x, y, rest, staffSize, drawingCueSize);
 
     // single legder line for whole rest glyphs
-    if ((measure->m_measureAligner.GetMaxTime() < (DUR_MAX * 2))
+    if ((measure->m_measureAligner.GetMaxTime() < Fraction(DURATION_1))
         && (y > staff->GetDrawingY()
             || y < staff->GetDrawingY() - (staff->m_drawingLines - 1) * m_doc->GetDrawingDoubleUnit(staffSize))) {
         const int width = m_doc->GetGlyphWidth(rest, staffSize, drawingCueSize);

From dc62331aef34e7fd8110e258fa90dae16009c8e0 Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Sat, 5 Oct 2024 14:59:29 +0200
Subject: [PATCH 104/105] Add static function to reduce are fraction

---
 include/vrv/horizontalaligner.h | 7 +++++++
 src/horizontalaligner.cpp       | 7 +++++++
 2 files changed, 14 insertions(+)

diff --git a/include/vrv/horizontalaligner.h b/include/vrv/horizontalaligner.h
index fa761c6e549..d6d428ec211 100644
--- a/include/vrv/horizontalaligner.h
+++ b/include/vrv/horizontalaligner.h
@@ -110,6 +110,13 @@ class Fraction {
     /** Convert to data_DURATION and the remaining Fraction */
     std::pair<data_DURATION, Fraction> ToDur() const;
 
+    //----------------//
+    // Static methods //
+    //----------------//
+
+    /** Reduce the faction represented by the two numbers */
+    static void Reduce(int &numerator, int &denominator);
+
 private:
     /** Reduce the fraction */
     void Reduce();
diff --git a/src/horizontalaligner.cpp b/src/horizontalaligner.cpp
index 5e1a30ae9b8..aa837ebd4d2 100644
--- a/src/horizontalaligner.cpp
+++ b/src/horizontalaligner.cpp
@@ -152,6 +152,13 @@ std::pair<data_DURATION, Fraction> Fraction::ToDur() const
     return { dur, remainder };
 }
 
+void Fraction::Reduce(int &numerator, int &denominator)
+{
+    Fraction fraction(numerator, denominator);
+    numerator = fraction.GetNumerator();
+    denominator = fraction.GetDenominator();
+}
+
 //----------------------------------------------------------------------------
 // HorizontalAligner
 //----------------------------------------------------------------------------

From d58072ed27193530752f13cee84493fbc0c51dd7 Mon Sep 17 00:00:00 2001
From: Laurent Pugin <lxpugin@gmail.com>
Date: Sat, 5 Oct 2024 15:01:37 +0200
Subject: [PATCH 105/105] Remove vrv::Reduce and use Fraction::Reduce

---
 include/vrv/vrv.h |  5 -----
 src/iocmme.cpp    |  4 ++--
 src/proport.cpp   |  2 +-
 src/vrv.cpp       | 36 ------------------------------------
 4 files changed, 3 insertions(+), 44 deletions(-)

diff --git a/include/vrv/vrv.h b/include/vrv/vrv.h
index 9c4789a3386..9a8a80937cf 100644
--- a/include/vrv/vrv.h
+++ b/include/vrv/vrv.h
@@ -72,11 +72,6 @@ bool IsValidDouble(const std::string &value);
  */
 bool IsDigits(const std::string &value);
 
-/**
- * Utility to reduce are faction of two integers
- */
-void Reduce(int &numerator, int &denominator);
-
 /**
  * Extract the ID from any URI
  */
diff --git a/src/iocmme.cpp b/src/iocmme.cpp
index 92cea2b092c..87f530c3e64 100644
--- a/src/iocmme.cpp
+++ b/src/iocmme.cpp
@@ -944,7 +944,7 @@ void CmmeInput::CreateProport(pugi::xml_node proportNode)
         // Cumulated it
         m_mensInfo->proportDen *= denVal;
     }
-    vrv::Reduce(m_mensInfo->proportNum, m_mensInfo->proportDen);
+    Fraction::Reduce(m_mensInfo->proportNum, m_mensInfo->proportDen);
     proport->SetType("cmme_proportion");
     m_currentContainer->AddChild(proport);
     return;
@@ -1073,7 +1073,7 @@ data_DURATION CmmeInput::ReadDuration(pugi::xml_node durationNode, int &num, int
         num = cmmeDen;
         numbase = cmmeNum;
 
-        vrv::Reduce(num, numbase);
+        Fraction::Reduce(num, numbase);
 
         if (num == numbase) {
             num = VRV_UNSET;
diff --git a/src/proport.cpp b/src/proport.cpp
index 80ee2ad2f54..44466ca8a59 100644
--- a/src/proport.cpp
+++ b/src/proport.cpp
@@ -57,7 +57,7 @@ void Proport::Cumulate(const Proport *proport)
         m_cumulatedNumbase = this->GetNumbase() * proport->GetCumulatedNumbase();
     }
     if ((m_cumulatedNum != VRV_UNSET) && (m_cumulatedNumbase != VRV_UNSET)) {
-        vrv::Reduce(m_cumulatedNum, m_cumulatedNumbase);
+        Fraction::Reduce(m_cumulatedNum, m_cumulatedNumbase);
     }
 }
 
diff --git a/src/vrv.cpp b/src/vrv.cpp
index bde1baab791..2378a542dd8 100644
--- a/src/vrv.cpp
+++ b/src/vrv.cpp
@@ -252,42 +252,6 @@ bool IsDigits(const std::string &value)
     return std::regex_match(value, re);
 }
 
-// Function to compute the Greatest Common Divisor (GCD)
-int GCD(int a, int b)
-{
-    if (b == 0) {
-        return std::abs(a);
-    }
-    return GCD(b, a % b);
-}
-
-// Function to reduce the fraction
-void Reduce(int &numerator, int &denominator)
-{
-    // Handle cases with zero denominator or numerator
-    if ((denominator == 0) || (denominator == VRV_UNSET)) {
-        return;
-    }
-
-    if ((numerator == 0) || (denominator == VRV_UNSET)) {
-        denominator = 1; // A fraction with 0 numerator is 0/1
-        return;
-    }
-
-    // Get the greatest common divisor
-    int divisor = GCD(numerator, denominator);
-
-    // Divide numerator and denominator by GCD
-    numerator /= divisor;
-    denominator /= divisor;
-
-    // Ensure denominator is always positive
-    if (denominator < 0) {
-        numerator = -numerator;
-        denominator = -denominator;
-    }
-}
-
 std::string ExtractIDFragment(std::string refID)
 {
     size_t pos = refID.find_last_of("#");