From 43bc9cdb1a1ea7f84a9b98b7b9a3559e0f316176 Mon Sep 17 00:00:00 2001 From: Laurent Pugin Date: Thu, 18 Jul 2024 13:57:13 +0200 Subject: [PATCH 01/15] Rebase --- include/vrv/note.h | 2 +- include/vrv/tuning.h | 13 +++++- include/vrv/vrvdef.h | 1 + src/beam.cpp | 4 +- src/calcalignmentpitchposfunctor.cpp | 7 +-- src/note.cpp | 66 +++++++++++++++++++++++++++- src/staff.cpp | 2 + src/tabdursym.cpp | 7 ++- src/tuning.cpp | 3 +- src/view_beam.cpp | 2 +- src/view_page.cpp | 63 ++++++++++++++------------ src/view_tab.cpp | 29 +++++++++++- 12 files changed, 156 insertions(+), 43 deletions(-) diff --git a/include/vrv/note.h b/include/vrv/note.h index b6b8261ac6f..d44bc06d88b 100644 --- a/include/vrv/note.h +++ b/include/vrv/note.h @@ -162,7 +162,7 @@ class Note : public LayerElement, * @name Return the smufl string to use for a note give the notation type */ ///@{ - std::u32string GetTabFretString(data_NOTATIONTYPE notationType) const; + std::u32string GetTabFretString(data_NOTATIONTYPE notationType, bool &overline) const; ///@} /** diff --git a/include/vrv/tuning.h b/include/vrv/tuning.h index bdb78867b98..5a5295f45ea 100644 --- a/include/vrv/tuning.h +++ b/include/vrv/tuning.h @@ -40,9 +40,18 @@ class Tuning : public Object, public AttCourseLog { bool IsSupportedChild(Object *object) override; /** - * Return the line for a the tuning and a given course and a notation type + * Return the line for a note according to tablature type. + * Guitar, french and italian tablature: the line is based on the course. + * German tablature: the line is based on the note's index in the note list. + * + * @param[in] course + * @param[in] notationType + * @param[in] lines + * @param[in] listSize + * @param[in] index - 0 based from the bottom of the chord + * @return position in staff half lines */ - int CalcPitchPos(int course, data_NOTATIONTYPE notationType, int lines) const; + int CalcPitchPos(int course, data_NOTATIONTYPE notationType, int lines, int listSize, int index) const; /** * Calculate the MIDI note number for course/fret diff --git a/include/vrv/vrvdef.h b/include/vrv/vrvdef.h index a5c6019e334..eaee4334378 100644 --- a/include/vrv/vrvdef.h +++ b/include/vrv/vrvdef.h @@ -686,6 +686,7 @@ enum MeasureType { MEASURED = 0, UNMEASURED, NEUMELINE }; //---------------------------------------------------------------------------- #define TABLATURE_STAFF_RATIO 1.75 +#define GERMAN_TAB_STAFF_RATIO 2.2 #define SUPER_SCRIPT_FACTOR 0.58 #define SUPER_SCRIPT_POSITION -0.20 // lowered down from the midline diff --git a/src/beam.cpp b/src/beam.cpp index 6e643e4ac13..d66b04c7533 100644 --- a/src/beam.cpp +++ b/src/beam.cpp @@ -600,8 +600,8 @@ void BeamSegment::CalcBeamInit( beamInterface->m_beamWidthBlack /= 2; beamInterface->m_beamWidthWhite /= 2; - // Adjust it further for tab.lute.french and tab.lute.italian - if (staff->IsTabLuteFrench() || staff->IsTabLuteItalian()) { + // Adjust it further for tab.lute.french, tab.lute.italian and tab.lute.german + if (staff->IsTabLuteFrench() || staff->IsTabLuteItalian() || staff->IsTabLuteGerman()) { beamInterface->m_beamWidthBlack = beamInterface->m_beamWidthBlack * 2 / 5; beamInterface->m_beamWidthWhite = beamInterface->m_beamWidthWhite * 3 / 5; } diff --git a/src/calcalignmentpitchposfunctor.cpp b/src/calcalignmentpitchposfunctor.cpp index 188095fc068..3e9616c355a 100644 --- a/src/calcalignmentpitchposfunctor.cpp +++ b/src/calcalignmentpitchposfunctor.cpp @@ -17,6 +17,7 @@ #include "rest.h" #include "score.h" #include "staff.h" +#include "tabgrp.h" #include "tuning.h" //---------------------------------------------------------------------------- @@ -105,8 +106,8 @@ FunctorCode CalcAlignmentPitchPosFunctor::VisitLayerElement(LayerElement *layerE TabGrp *tabGrp = note->IsTabGrpNote(); if (tabGrp) { assert(staffY->m_drawingTuning); - loc = staffY->m_drawingTuning->CalcPitchPos( - note->GetTabCourse(), staffY->m_drawingNotationType, staffY->m_drawingLines); + loc = staffY->m_drawingTuning->CalcPitchPos(note->GetTabCourse(), staffY->m_drawingNotationType, + staffY->m_drawingLines, tabGrp->GetListSize(tabGrp), tabGrp->GetListIndex(note)); } else if ((note->HasPname() && (note->HasOct() || note->HasOctDefault())) || note->HasLoc()) { loc = PitchInterface::CalcLoc(note, layerY, layerElementY); @@ -307,7 +308,7 @@ FunctorCode CalcAlignmentPitchPosFunctor::VisitLayerElement(LayerElement *layerE else if (layerElement->Is(TABDURSYM)) { int yRel = 0; if (staffY->IsTabWithStemsOutside()) { - double spacingRatio = (staffY->IsTabLuteFrench()) ? 2.0 : 1.0; + double spacingRatio = (staffY->IsTabLuteFrench() || staffY->IsTabLuteGerman()) ? 2.0 : 1.0; yRel += m_doc->GetDrawingUnit(staffY->m_drawingStaffSize) * spacingRatio; } layerElement->SetDrawingYRel(yRel); diff --git a/src/note.cpp b/src/note.cpp index ae0175f3ea1..194724858e5 100644 --- a/src/note.cpp +++ b/src/note.cpp @@ -267,12 +267,14 @@ const TabGrp *Note::IsTabGrpNote() const return vrv_cast(this->GetFirstAncestor(TABGRP, MAX_TABGRP_DEPTH)); } -std::u32string Note::GetTabFretString(data_NOTATIONTYPE notationType) const +std::u32string Note::GetTabFretString(data_NOTATIONTYPE notationType, bool &overline) const { + overline = false; + if (notationType == NOTATIONTYPE_tab_lute_italian) { std::u32string fretStr; int fret = this->GetTabFret(); - // Maximum allowed would be 19 (always bindly addind 1 as first figure) + // Maximum allowed would be 19 (always bindly adding 1 as first figure) if (fret > 9) fretStr.push_back(SMUFL_EBE1_luteItalianFret1); switch (fret % 10) { case 0: fretStr.push_back(SMUFL_EBE0_luteItalianFret0); break; @@ -336,6 +338,66 @@ std::u32string Note::GetTabFretString(data_NOTATIONTYPE notationType) const } return fretStr; } + else if (notationType == NOTATIONTYPE_tab_lute_german) { + std::u32string fretStr; + const int fret = this->GetTabFret(); + const int course = this->GetTabCourse(); + + // SMuFL has glyphs for German lute tablature following Hans Newsider's notation + // for the 6th course. + // "German Renaissance lute tablature (U+EC00–U+EC2F)" + // https://w3c.github.io/smufl/latest/tables/german-renaissance-lute-tablature.html + // + // However, some glyphs are missing: + // + // Digit 1 with an oblique stroke for the open 6th course. + // "et" for 2nd course 5th fret. + // "con" for 1st course 5th fret. + // Gothic font digits 1-5 for the open courses <= 5. + // Second lowercase alphabet with an overline used for courses <= 5 frets 6 to 10. + // + // To overcome these omissions I've substituted missing glyphs from other + // parts of the SMuFL collection. Overlines are drawn separately. + + if (course == 6 && fret >= 0 && fret <= 13) { + if (fret == 0) { + fretStr = SMUFL_E595_ornamentLeftVerticalStrokeWithCross; // substitute for 1 with oblique stroke + } + else { + // The German tablature uppercase letters A-N are contiguous, correctly omitting J + static_assert(SMUFL_EC23_luteGermanNUpper == SMUFL_EC17_luteGermanAUpper + 13 - 1); + fretStr = SMUFL_EC17_luteGermanAUpper + fret - 1; + } + } + else if (course >= 1 && course <= 5 && fret == 0) { + // Substitute for gothic digits + static const char32_t digit[] = { SMUFL_EA51_figbass1, SMUFL_EA52_figbass2, SMUFL_EA54_figbass3, + SMUFL_EA55_figbass4, SMUFL_EA57_figbass5 }; + fretStr = digit[5 - course]; + } + else if (course >= 1 && course <= 5 && fret >= 0 && fret <= 10) { + const int firstAlphabetFret = fret <= 5 ? fret : fret - 5; // map second alphabet to first + + if (course == 2 && firstAlphabetFret == 5) { + fretStr = SMUFL_EA5F_figbass7Raised2; // substitute for "et" + } + else if (course == 1 && firstAlphabetFret == 5) { + fretStr = SMUFL_EA61_figbass9; // substitute for "con" + } + else { + // The German tablature lowercase letters a-z are contiguous, correctly omitting j u w + static_assert(SMUFL_EC16_luteGermanZLower == SMUFL_EC00_luteGermanALower + 23 - 1); + + // lowercase letters run 5th to 1st course, frets 1 to 5 + // and frets 6 to 10 with an overline + fretStr = SMUFL_EC00_luteGermanALower + (5 - course) + (firstAlphabetFret - 1) * 5; + } + + // second alphabet needs an overline + overline = (fret >= 6); + } + return fretStr; + } else { std::string str = StringFormat("%d", this->GetTabFret()); return UTF8to32(str); diff --git a/src/staff.cpp b/src/staff.cpp index 9360300b579..51e35a9051c 100644 --- a/src/staff.cpp +++ b/src/staff.cpp @@ -186,6 +186,8 @@ void Staff::AdjustDrawingStaffSize() int Staff::GetDrawingStaffNotationSize() const { + if (this->IsTabLuteGerman()) return m_drawingStaffSize / GERMAN_TAB_STAFF_RATIO; + return (this->IsTablature()) ? m_drawingStaffSize / TABLATURE_STAFF_RATIO : m_drawingStaffSize; } diff --git a/src/tabdursym.cpp b/src/tabdursym.cpp index 21d03c05f6d..1ac5721f437 100644 --- a/src/tabdursym.cpp +++ b/src/tabdursym.cpp @@ -89,7 +89,7 @@ void TabDurSym::AdjustDrawingYRel(const Staff *staff, const Doc *doc) // For stems outside add a margin to the tabDurSym - otherwise attached to the staff line if (staff->IsTabWithStemsOutside()) { - double spacingRatio = (staff->IsTabLuteFrench()) ? 2.0 : 1.0; + double spacingRatio = (staff->IsTabLuteFrench() || staff->IsTabLuteGerman()) ? 2.0 : 1.0; yRel += doc->GetDrawingUnit(staff->m_drawingStaffSize) * spacingRatio; } @@ -116,8 +116,11 @@ int TabDurSym::CalcStemLenInThirdUnits(const Staff *staff, data_STEMDIRECTION st int baseStem = STANDARD_STEMLENGTH_TAB * 3; + // Shorter for german lute tablature to match ryhthm glyphs + if (staff->IsTabLuteGerman()) baseStem -= 3; // One unit longer for guitar tablature - if (staff->IsTabGuitar()) baseStem += 3; + else if (staff->IsTabGuitar()) + baseStem += 3; // One unit longer for stems inside the staff if (!staff->IsTabWithStemsOutside()) baseStem += 3; diff --git a/src/tuning.cpp b/src/tuning.cpp index 7ebbdc1e4bf..43b3d57fd8f 100644 --- a/src/tuning.cpp +++ b/src/tuning.cpp @@ -56,13 +56,14 @@ bool Tuning::IsSupportedChild(Object *child) return true; } -int Tuning::CalcPitchPos(int course, data_NOTATIONTYPE notationType, int lines) const +int Tuning::CalcPitchPos(int course, data_NOTATIONTYPE notationType, int lines, int listSize, int index) const { switch (notationType) { case NOTATIONTYPE_tab_lute_french: // all courses >= 7 are positioned above line 0 return (lines - std::min(course, 7)) * 2 + 1; // above the line case NOTATIONTYPE_tab_lute_italian: return (course - 1) * 2; + case NOTATIONTYPE_tab_lute_german: return (lines - (listSize - index)) * 2; case NOTATIONTYPE_tab_guitar: [[fallthrough]]; default: return abs(course - lines) * 2; } diff --git a/src/view_beam.cpp b/src/view_beam.cpp index 34d04de9f06..9708c739a61 100644 --- a/src/view_beam.cpp +++ b/src/view_beam.cpp @@ -292,7 +292,7 @@ void View::DrawBeamSegment( int durRef = DUR_8; int durRef2 = DUR_16; - if (staff->IsTabLuteFrench() || staff->IsTabLuteItalian()) { + if (staff->IsTabLuteFrench() || staff->IsTabLuteItalian() || staff->IsTabLuteGerman()) { durRef = DUR_4; durRef2 = DUR_8; } diff --git a/src/view_page.cpp b/src/view_page.cpp index 626aa7f91e7..87853c41815 100644 --- a/src/view_page.cpp +++ b/src/view_page.cpp @@ -1252,7 +1252,7 @@ void View::DrawStaff(DeviceContext *dc, Staff *staff, Measure *measure, System * staff->SetFromFacsimile(m_doc); } - if (staffDef && (staffDef->GetLinesVisible() != BOOLEAN_false)) { + if ((staffDef && (staffDef->GetLinesVisible() != BOOLEAN_false)) || staff->IsTabLuteGerman()) { this->DrawStaffLines(dc, staff, measure, system); } @@ -1307,35 +1307,44 @@ void View::DrawStaffLines(DeviceContext *dc, Staff *staff, Measure *measure, Sys dc->SetPen(m_currentColor, ToDeviceContextX(lineWidth), AxSOLID); dc->SetBrush(m_currentColor, AxSOLID); - for (j = 0; j < staff->m_drawingLines; ++j) { - // Skewed lines - with Facs (neumes) only for now - if (y1 != y2) { - dc->DrawLine(ToDeviceContextX(x1), ToDeviceContextY(y1), ToDeviceContextX(x2), ToDeviceContextY(y2)); - // For drawing rectangles instead of lines - y1 -= m_doc->GetDrawingDoubleUnit(staff->m_drawingStaffSize); - y2 -= m_doc->GetDrawingDoubleUnit(staff->m_drawingStaffSize); - } - else { - const bool isFrenchOrItalianTablature = (staff->IsTabLuteFrench() || staff->IsTabLuteItalian()); - SegmentedLine line(x1, x2); - // We do not need to do this during layout calculation - and only with tablature but not for French or - // Italian tablature - if (!dc->Is(BBOX_DEVICE_CONTEXT) && staff->IsTablature() && !isFrenchOrItalianTablature) { - Object fullLine; - fullLine.SetParent(system); - fullLine.UpdateContentBBoxY(y1 + (lineWidth / 2), y1 - (lineWidth / 2)); - fullLine.UpdateContentBBoxX(x1, x2); - int margin = m_doc->GetDrawingUnit(100) / 2; - ListOfObjects notes = staff->FindAllDescendantsByType(NOTE, false); - for (Object *note : notes) { - if (note->VerticalContentOverlap(&fullLine, margin / 2)) { - line.AddGap(note->GetContentLeft() - margin, note->GetContentRight() + margin); + if (staff->IsTabLuteGerman()) { + // German tablature has no staff, just a single base line + // But internally we maintain the fiction of an invisible staff as a coordinate system + SegmentedLine line(x1, x2); + y1 -= m_doc->GetDrawingDoubleUnit(staff->m_drawingStaffSize) * staff->m_drawingLines; + this->DrawHorizontalSegmentedLine(dc, y1, line, lineWidth); + } + else { + for (j = 0; j < staff->m_drawingLines; ++j) { + // Skewed lines - with Facs (neumes) only for now + if (y1 != y2) { + dc->DrawLine(ToDeviceContextX(x1), ToDeviceContextY(y1), ToDeviceContextX(x2), ToDeviceContextY(y2)); + // For drawing rectangles instead of lines + y1 -= m_doc->GetDrawingDoubleUnit(staff->m_drawingStaffSize); + y2 -= m_doc->GetDrawingDoubleUnit(staff->m_drawingStaffSize); + } + else { + const bool isFrenchOrItalianTablature = (staff->IsTabLuteFrench() || staff->IsTabLuteItalian()); + SegmentedLine line(x1, x2); + // We do not need to do this during layout calculation - and only with tablature but not for French or + // Italian tablature + if (!dc->Is(BBOX_DEVICE_CONTEXT) && staff->IsTablature() && !isFrenchOrItalianTablature) { + Object fullLine; + fullLine.SetParent(system); + fullLine.UpdateContentBBoxY(y1 + (lineWidth / 2), y1 - (lineWidth / 2)); + fullLine.UpdateContentBBoxX(x1, x2); + int margin = m_doc->GetDrawingUnit(100) / 2; + ListOfObjects notes = staff->FindAllDescendantsByType(NOTE, false); + for (Object *note : notes) { + if (note->VerticalContentOverlap(&fullLine, margin / 2)) { + line.AddGap(note->GetContentLeft() - margin, note->GetContentRight() + margin); + } } } + this->DrawHorizontalSegmentedLine(dc, y1, line, lineWidth); + y1 -= m_doc->GetDrawingDoubleUnit(staff->m_drawingStaffSize); + y2 = y1; } - this->DrawHorizontalSegmentedLine(dc, y1, line, lineWidth); - y1 -= m_doc->GetDrawingDoubleUnit(staff->m_drawingStaffSize); - y2 = y1; } } diff --git a/src/view_tab.cpp b/src/view_tab.cpp index dd28a98fe09..6c0f4f13339 100644 --- a/src/view_tab.cpp +++ b/src/view_tab.cpp @@ -103,10 +103,11 @@ void View::DrawTabNote(DeviceContext *dc, LayerElement *element, Layer *layer, S int glyphSize = staff->GetDrawingStaffNotationSize(); bool drawingCueSize = false; + bool overline = false; if (staff->m_drawingNotationType == NOTATIONTYPE_tab_guitar) { - std::u32string fret = note->GetTabFretString(staff->m_drawingNotationType); + std::u32string fret = note->GetTabFretString(staff->m_drawingNotationType, overline); FontInfo fretTxt; if (!dc->UseGlobalStyling()) { @@ -132,7 +133,7 @@ void View::DrawTabNote(DeviceContext *dc, LayerElement *element, Layer *layer, S } else { - std::u32string fret = note->GetTabFretString(staff->m_drawingNotationType); + std::u32string fret = note->GetTabFretString(staff->m_drawingNotationType, overline); // Center for italian tablature if (staff->IsTabLuteItalian()) { y -= (m_doc->GetGlyphHeight(SMUFL_EBE0_luteItalianFret0, glyphSize, drawingCueSize) / 2); @@ -142,9 +143,32 @@ void View::DrawTabNote(DeviceContext *dc, LayerElement *element, Layer *layer, S y -= m_doc->GetDrawingUnit(staff->m_drawingStaffSize) - m_doc->GetDrawingStaffLineWidth(staff->m_drawingStaffSize); } + // Center for German tablature + else if (staff->IsTabLuteGerman()) { + y -= m_doc->GetGlyphHeight(SMUFL_EC17_luteGermanAUpper, glyphSize, drawingCueSize) / 2; + } dc->SetFont(m_doc->GetDrawingSmuflFont(glyphSize, false)); this->DrawSmuflString(dc, x, y, fret, HORIZONTALALIGNMENT_center, glyphSize); + + // Add overline if required + // TODO is this the correct way to add an overline? + if (overline && !fret.empty()) { + const int lineWidth = m_doc->GetDrawingStaffLineWidth(staff->m_drawingStaffSize); + const int width = m_doc->GetGlyphWidth(fret.at(0), glyphSize, drawingCueSize); + const int x1 = x - width / 2; + const int x2 = x + (fret.size() - 1) * width + width * 8 / 10; // trim right hand overhang on last character + const int y1 = y + m_doc->GetGlyphHeight(fret.at(0), glyphSize, drawingCueSize) + lineWidth; + const int y2 = y1; + + dc->SetPen(m_currentColour, ToDeviceContextX(lineWidth), AxSOLID); + dc->SetBrush(m_currentColour, AxSOLID); + + dc->DrawLine(ToDeviceContextX(x1), ToDeviceContextY(y1), ToDeviceContextX(x2), ToDeviceContextY(y2)); + + dc->ResetPen(); + dc->ResetBrush(); + } dc->ResetFont(); } @@ -171,6 +195,7 @@ void View::DrawTabDurSym(DeviceContext *dc, LayerElement *element, Layer *layer, int y = element->GetDrawingY(); const int glyphSize = staff->GetDrawingStaffNotationSize(); + const int drawingDur = (tabGrp->GetDurGes() != DURATION_NONE) ? tabGrp->GetActualDurGes() : tabGrp->GetActualDur(); // For beam and guitar notation, stem are drawn through the child Stem From 466fe89066eacdca382e95c8f29bc4f996d22b50 Mon Sep 17 00:00:00 2001 From: Paul Overell Date: Fri, 19 May 2023 12:24:08 +0100 Subject: [PATCH 02/15] Fixes after feedback from pull request for German lute tablature. --- src/beam.cpp | 4 ++-- src/note.cpp | 2 +- src/view_beam.cpp | 2 +- src/view_tab.cpp | 19 ++++++++++++------- 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/beam.cpp b/src/beam.cpp index d66b04c7533..f3137305ad1 100644 --- a/src/beam.cpp +++ b/src/beam.cpp @@ -600,8 +600,8 @@ void BeamSegment::CalcBeamInit( beamInterface->m_beamWidthBlack /= 2; beamInterface->m_beamWidthWhite /= 2; - // Adjust it further for tab.lute.french, tab.lute.italian and tab.lute.german - if (staff->IsTabLuteFrench() || staff->IsTabLuteItalian() || staff->IsTabLuteGerman()) { + // Adjust it further for tab.lute.french, tab.lute.german and tab.lute.italian + if (staff->IsTabLuteFrench() || staff->IsTabLuteGerman() || staff->IsTabLuteItalian()) { beamInterface->m_beamWidthBlack = beamInterface->m_beamWidthBlack * 2 / 5; beamInterface->m_beamWidthWhite = beamInterface->m_beamWidthWhite * 3 / 5; } diff --git a/src/note.cpp b/src/note.cpp index 194724858e5..2b4991c9f24 100644 --- a/src/note.cpp +++ b/src/note.cpp @@ -343,7 +343,7 @@ std::u32string Note::GetTabFretString(data_NOTATIONTYPE notationType, bool &over const int fret = this->GetTabFret(); const int course = this->GetTabCourse(); - // SMuFL has glyphs for German lute tablature following Hans Newsider's notation + // SMuFL has glyphs for German lute tablature following Hans Newsidler's notation // for the 6th course. // "German Renaissance lute tablature (U+EC00–U+EC2F)" // https://w3c.github.io/smufl/latest/tables/german-renaissance-lute-tablature.html diff --git a/src/view_beam.cpp b/src/view_beam.cpp index 9708c739a61..1bbe23a473c 100644 --- a/src/view_beam.cpp +++ b/src/view_beam.cpp @@ -292,7 +292,7 @@ void View::DrawBeamSegment( int durRef = DUR_8; int durRef2 = DUR_16; - if (staff->IsTabLuteFrench() || staff->IsTabLuteItalian() || staff->IsTabLuteGerman()) { + if (staff->IsTabLuteFrench() || staff->IsTabLuteGerman() || staff->IsTabLuteItalian()) { durRef = DUR_4; durRef2 = DUR_8; } diff --git a/src/view_tab.cpp b/src/view_tab.cpp index 6c0f4f13339..c64c76b81ee 100644 --- a/src/view_tab.cpp +++ b/src/view_tab.cpp @@ -152,16 +152,21 @@ void View::DrawTabNote(DeviceContext *dc, LayerElement *element, Layer *layer, S this->DrawSmuflString(dc, x, y, fret, HORIZONTALALIGNMENT_center, glyphSize); // Add overline if required - // TODO is this the correct way to add an overline? if (overline && !fret.empty()) { - const int lineWidth = m_doc->GetDrawingStaffLineWidth(staff->m_drawingStaffSize); - const int width = m_doc->GetGlyphWidth(fret.at(0), glyphSize, drawingCueSize); - const int x1 = x - width / 2; - const int x2 = x + (fret.size() - 1) * width + width * 8 / 10; // trim right hand overhang on last character - const int y1 = y + m_doc->GetGlyphHeight(fret.at(0), glyphSize, drawingCueSize) + lineWidth; + const int lineThickness + = m_options->m_lyricLineThickness.GetValue() * m_doc->GetDrawingUnit(staff->m_drawingStaffSize); + const int widthFront = m_doc->GetGlyphWidth(fret.front(), glyphSize, drawingCueSize); + const int widthBack = m_doc->GetGlyphWidth(fret.back(), glyphSize, drawingCueSize); + TextExtend extend; + dc->GetSmuflTextExtent(fret, &extend); + + const int x1 = x - widthFront / 2; + const int x2 = x + extend.m_width - widthBack * 3 / 10; // trim right hand overhang on last character + + const int y1 = y + extend.m_ascent + lineThickness; const int y2 = y1; - dc->SetPen(m_currentColour, ToDeviceContextX(lineWidth), AxSOLID); + dc->SetPen(m_currentColour, lineThickness, AxSOLID); dc->SetBrush(m_currentColour, AxSOLID); dc->DrawLine(ToDeviceContextX(x1), ToDeviceContextY(y1), ToDeviceContextX(x2), ToDeviceContextY(y2)); From 4d1c7ee409bff22d65b034f2ec0cc1df17fbad2f Mon Sep 17 00:00:00 2001 From: Laurent Pugin Date: Wed, 17 Jul 2024 14:18:40 +0200 Subject: [PATCH 03/15] Rebase --- include/vrv/note.h | 3 +- include/vrv/staffdef.h | 3 +- include/vrv/tabdursym.h | 2 +- include/vrv/tuning.h | 8 ++++-- src/calcalignmentpitchposfunctor.cpp | 4 ++- src/iomei.cpp | 4 +++ src/note.cpp | 39 ++++++++++++++++++-------- src/staffdef.cpp | 3 ++ src/tabdursym.cpp | 5 +++- src/tuning.cpp | 15 ++++++++-- src/view_tab.cpp | 41 +++++++++++++++++++++------- 11 files changed, 97 insertions(+), 30 deletions(-) diff --git a/include/vrv/note.h b/include/vrv/note.h index d44bc06d88b..af51ac7e17c 100644 --- a/include/vrv/note.h +++ b/include/vrv/note.h @@ -162,7 +162,8 @@ class Note : public LayerElement, * @name Return the smufl string to use for a note give the notation type */ ///@{ - std::u32string GetTabFretString(data_NOTATIONTYPE notationType, bool &overline) const; + std::u32string GetTabFretString( + data_NOTATIONTYPE notationType, const std::string ¬ationSubtype, int &overline, int &strike) const; ///@} /** diff --git a/include/vrv/staffdef.h b/include/vrv/staffdef.h index 7e779f3df01..b59d303483f 100644 --- a/include/vrv/staffdef.h +++ b/include/vrv/staffdef.h @@ -32,7 +32,8 @@ class StaffDef : public ScoreDefElement, public AttStaffDefLog, public AttStaffDefVis, public AttTimeBase, - public AttTransposition { + public AttTransposition, + public AttVerticalAlign { public: /** * @name Constructors, destructors, and other standard methods diff --git a/include/vrv/tabdursym.h b/include/vrv/tabdursym.h index 87e39ab2188..baa44140519 100644 --- a/include/vrv/tabdursym.h +++ b/include/vrv/tabdursym.h @@ -21,7 +21,7 @@ namespace vrv { /** * This class models the MEI element. */ -class TabDurSym : public LayerElement, public StemmedDrawingInterface, public AttNNumberLike { +class TabDurSym : public LayerElement, public StemmedDrawingInterface, public AttNNumberLike, public AttStaffLoc { public: /** * @name Constructors, destructors, and other standard methods diff --git a/include/vrv/tuning.h b/include/vrv/tuning.h index 5a5295f45ea..d30b842856a 100644 --- a/include/vrv/tuning.h +++ b/include/vrv/tuning.h @@ -42,16 +42,20 @@ class Tuning : public Object, public AttCourseLog { /** * Return the line for a note according to tablature type. * Guitar, french and italian tablature: the line is based on the course. - * German tablature: the line is based on the note's index in the note list. + * German tablature: the line is based on the note's index in the note list + * or by explicit @loc. * * @param[in] course * @param[in] notationType * @param[in] lines * @param[in] listSize * @param[in] index - 0 based from the bottom of the chord + * @param[in] loc - German tablature: note@loc if specified, 0 at the bottom + * @param[in] topAlign - German tablature: true => align at the top, false => align at the bottom * @return position in staff half lines */ - int CalcPitchPos(int course, data_NOTATIONTYPE notationType, int lines, int listSize, int index) const; + int CalcPitchPos( + int course, data_NOTATIONTYPE notationType, int lines, int listSize, int index, int loc, bool topAlign) const; /** * Calculate the MIDI note number for course/fret diff --git a/src/calcalignmentpitchposfunctor.cpp b/src/calcalignmentpitchposfunctor.cpp index 3e9616c355a..95ea2a41a0c 100644 --- a/src/calcalignmentpitchposfunctor.cpp +++ b/src/calcalignmentpitchposfunctor.cpp @@ -106,8 +106,10 @@ FunctorCode CalcAlignmentPitchPosFunctor::VisitLayerElement(LayerElement *layerE TabGrp *tabGrp = note->IsTabGrpNote(); if (tabGrp) { assert(staffY->m_drawingTuning); + assert(staffY->m_drawingStaffDef); loc = staffY->m_drawingTuning->CalcPitchPos(note->GetTabCourse(), staffY->m_drawingNotationType, - staffY->m_drawingLines, tabGrp->GetListSize(tabGrp), tabGrp->GetListIndex(note)); + staffY->m_drawingLines, tabGrp->GetListSize(tabGrp), tabGrp->GetListIndex(note), note->GetLoc(), + staffY->m_drawingStaffDef->GetValign() != VERTICALALIGNMENT_bottom); } else if ((note->HasPname() && (note->HasOct() || note->HasOctDefault())) || note->HasLoc()) { loc = PitchInterface::CalcLoc(note, layerY, layerElementY); diff --git a/src/iomei.cpp b/src/iomei.cpp index eeb2f763486..c6daa9ca38c 100644 --- a/src/iomei.cpp +++ b/src/iomei.cpp @@ -1833,6 +1833,7 @@ void MEIOutput::WriteStaffDef(pugi::xml_node currentNode, StaffDef *staffDef) staffDef->WriteStaffDefVis(currentNode); staffDef->WriteTimeBase(currentNode); staffDef->WriteTransposition(currentNode); + staffDef->WriteVerticalAlign(currentNode); } void MEIOutput::WriteInstrDef(pugi::xml_node currentNode, InstrDef *instrDef) @@ -2830,6 +2831,7 @@ void MEIOutput::WriteTabDurSym(pugi::xml_node currentNode, TabDurSym *tabDurSym) this->WriteLayerElement(currentNode, tabDurSym); tabDurSym->WriteNNumberLike(currentNode); + tabDurSym->WriteStaffLoc(currentNode); } void MEIOutput::WriteTabGrp(pugi::xml_node currentNode, TabGrp *tabGrp) @@ -5181,6 +5183,7 @@ bool MEIInput::ReadStaffDef(Object *parent, pugi::xml_node staffDef) vrvStaffDef->ReadStaffDefVis(staffDef); vrvStaffDef->ReadTimeBase(staffDef); vrvStaffDef->ReadTransposition(staffDef); + vrvStaffDef->ReadVerticalAlign(staffDef); if (!vrvStaffDef->HasN()) { LogWarning("No @n on might yield unpredictable results"); @@ -7091,6 +7094,7 @@ bool MEIInput::ReadTabDurSym(Object *parent, pugi::xml_node tabRhyhtm) this->ReadLayerElement(tabRhyhtm, vrvTabDurSym); vrvTabDurSym->ReadNNumberLike(tabRhyhtm); + vrvTabDurSym->ReadStaffLoc(tabRhyhtm); parent->AddChild(vrvTabDurSym); this->ReadUnsupportedAttr(tabRhyhtm, vrvTabDurSym); diff --git a/src/note.cpp b/src/note.cpp index 2b4991c9f24..3a1ca811a43 100644 --- a/src/note.cpp +++ b/src/note.cpp @@ -267,9 +267,11 @@ const TabGrp *Note::IsTabGrpNote() const return vrv_cast(this->GetFirstAncestor(TABGRP, MAX_TABGRP_DEPTH)); } -std::u32string Note::GetTabFretString(data_NOTATIONTYPE notationType, bool &overline) const +std::u32string Note::GetTabFretString( + data_NOTATIONTYPE notationType, const std::string ¬ationSubtype, int &overline, int &strike) const { - overline = false; + overline = 0; + strike = 0; if (notationType == NOTATIONTYPE_tab_lute_italian) { std::u32string fretStr; @@ -350,23 +352,38 @@ std::u32string Note::GetTabFretString(data_NOTATIONTYPE notationType, bool &over // // However, some glyphs are missing: // - // Digit 1 with an oblique stroke for the open 6th course. + // Digit 1 with a strike through for the open 6th course. + // Digit 1 with two strike throughs for the open 7th course. + // Digit 1 with three strike throughs for the open 8th course. // "et" for 2nd course 5th fret. // "con" for 1st course 5th fret. // Gothic font digits 1-5 for the open courses <= 5. // Second lowercase alphabet with an overline used for courses <= 5 frets 6 to 10. // // To overcome these omissions I've substituted missing glyphs from other - // parts of the SMuFL collection. Overlines are drawn separately. + // parts of the SMuFL collection. Overlines and strike throughs are drawn separately. - if (course == 6 && fret >= 0 && fret <= 13) { - if (fret == 0) { - fretStr = SMUFL_E595_ornamentLeftVerticalStrokeWithCross; // substitute for 1 with oblique stroke - } - else { + if (course >= 6 && fret >= 0 && fret <= 13) { + // TODO more GLT subtypes needed, is this subtype too specific? + if (notationSubtype == "judenkunig_1523") { + // A B C D ... // The German tablature uppercase letters A-N are contiguous, correctly omitting J static_assert(SMUFL_EC23_luteGermanNUpper == SMUFL_EC17_luteGermanAUpper + 13 - 1); - fretStr = SMUFL_EC17_luteGermanAUpper + fret - 1; + fretStr = SMUFL_EC17_luteGermanAUpper + fret; + overline = course - 6; // 6 course 0 overline, 7 course 1 overline, ... + } + else { + // + A B C D ... + if (fret == 0) { + fretStr = SMUFL_EA51_figbass1; // substitute for 1 with oblique stroke + strike = course - 5; // 6 course 1 strike, 7 course 2 strikes, ... + } + else { + // The German tablature uppercase letters A-N are contiguous, correctly omitting J + static_assert(SMUFL_EC23_luteGermanNUpper == SMUFL_EC17_luteGermanAUpper + 13 - 1); + fretStr = SMUFL_EC17_luteGermanAUpper + fret - 1; + overline = course - 6; // 6 course 0 overline, 7 course 1 overline, ... + } } } else if (course >= 1 && course <= 5 && fret == 0) { @@ -394,7 +411,7 @@ std::u32string Note::GetTabFretString(data_NOTATIONTYPE notationType, bool &over } // second alphabet needs an overline - overline = (fret >= 6); + overline = (fret >= 6) ? 1 : 0; } return fretStr; } diff --git a/src/staffdef.cpp b/src/staffdef.cpp index 5a86e2a6c48..5d55f3c8f80 100644 --- a/src/staffdef.cpp +++ b/src/staffdef.cpp @@ -42,6 +42,7 @@ StaffDef::StaffDef() , AttStaffDefVis() , AttTimeBase() , AttTransposition() + , AttVerticalAlign() { this->RegisterAttClass(ATT_DISTANCES); this->RegisterAttClass(ATT_LABELLED); @@ -52,6 +53,7 @@ StaffDef::StaffDef() this->RegisterAttClass(ATT_STAFFDEFVIS); this->RegisterAttClass(ATT_TIMEBASE); this->RegisterAttClass(ATT_TRANSPOSITION); + this->RegisterAttClass(ATT_VERTICALALIGN); this->Reset(); } @@ -71,6 +73,7 @@ void StaffDef::Reset() this->ResetStaffDefVis(); this->ResetTimeBase(); this->ResetTransposition(); + this->ResetVerticalAlign(); m_drawingVisibility = OPTIMIZATION_NONE; } diff --git a/src/tabdursym.cpp b/src/tabdursym.cpp index 1ac5721f437..2bb64949d1c 100644 --- a/src/tabdursym.cpp +++ b/src/tabdursym.cpp @@ -31,9 +31,11 @@ namespace vrv { static const ClassRegistrar s_factory("tabDurSym", TABDURSYM); -TabDurSym::TabDurSym() : LayerElement(TABDURSYM, "tabdursym-"), StemmedDrawingInterface(), AttNNumberLike() +TabDurSym::TabDurSym() + : LayerElement(TABDURSYM, "tabdursym-"), StemmedDrawingInterface(), AttNNumberLike(), AttStaffLoc() { this->RegisterAttClass(ATT_NNUMBERLIKE); + this->RegisterAttClass(ATT_STAFFLOC); this->Reset(); } @@ -45,6 +47,7 @@ void TabDurSym::Reset() LayerElement::Reset(); StemmedDrawingInterface::Reset(); this->ResetNNumberLike(); + this->ResetStaffLoc(); } bool TabDurSym::IsSupportedChild(Object *child) diff --git a/src/tuning.cpp b/src/tuning.cpp index 43b3d57fd8f..c7f71fa3f3d 100644 --- a/src/tuning.cpp +++ b/src/tuning.cpp @@ -56,14 +56,25 @@ bool Tuning::IsSupportedChild(Object *child) return true; } -int Tuning::CalcPitchPos(int course, data_NOTATIONTYPE notationType, int lines, int listSize, int index) const +int Tuning::CalcPitchPos( + int course, data_NOTATIONTYPE notationType, int lines, int listSize, int index, int loc, bool topAlign) const { switch (notationType) { case NOTATIONTYPE_tab_lute_french: // all courses >= 7 are positioned above line 0 return (lines - std::min(course, 7)) * 2 + 1; // above the line case NOTATIONTYPE_tab_lute_italian: return (course - 1) * 2; - case NOTATIONTYPE_tab_lute_german: return (lines - (listSize - index)) * 2; + case NOTATIONTYPE_tab_lute_german: + if (loc != MEI_UNSET) { + return loc; + } + else if (topAlign) { + return (lines - (listSize - index)) * 2; + } + else { + // bottom align + return index * 2; + } case NOTATIONTYPE_tab_guitar: [[fallthrough]]; default: return abs(course - lines) * 2; } diff --git a/src/view_tab.cpp b/src/view_tab.cpp index c64c76b81ee..e81f20c72ef 100644 --- a/src/view_tab.cpp +++ b/src/view_tab.cpp @@ -22,6 +22,7 @@ #include "rend.h" #include "smufl.h" #include "staff.h" +#include "staffdef.h" #include "stem.h" #include "system.h" #include "tabdursym.h" @@ -103,11 +104,13 @@ void View::DrawTabNote(DeviceContext *dc, LayerElement *element, Layer *layer, S int glyphSize = staff->GetDrawingStaffNotationSize(); bool drawingCueSize = false; - bool overline = false; + int overline = 0; + int strike = 0; if (staff->m_drawingNotationType == NOTATIONTYPE_tab_guitar) { - std::u32string fret = note->GetTabFretString(staff->m_drawingNotationType, overline); + std::u32string fret = note->GetTabFretString( + staff->m_drawingNotationType, staff->m_drawingStaffDef->GetNotationsubtype(), overline, strike); FontInfo fretTxt; if (!dc->UseGlobalStyling()) { @@ -133,7 +136,8 @@ void View::DrawTabNote(DeviceContext *dc, LayerElement *element, Layer *layer, S } else { - std::u32string fret = note->GetTabFretString(staff->m_drawingNotationType, overline); + std::u32string fret = note->GetTabFretString( + staff->m_drawingNotationType, staff->m_drawingStaffDef->GetNotationsubtype(), overline, strike); // Center for italian tablature if (staff->IsTabLuteItalian()) { y -= (m_doc->GetGlyphHeight(SMUFL_EBE0_luteItalianFret0, glyphSize, drawingCueSize) / 2); @@ -151,8 +155,8 @@ void View::DrawTabNote(DeviceContext *dc, LayerElement *element, Layer *layer, S dc->SetFont(m_doc->GetDrawingSmuflFont(glyphSize, false)); this->DrawSmuflString(dc, x, y, fret, HORIZONTALALIGNMENT_center, glyphSize); - // Add overline if required - if (overline && !fret.empty()) { + // Add overlines or strikethoughs if required + if ((overline > 0 || strike > 0) && !fret.empty()) { const int lineThickness = m_options->m_lyricLineThickness.GetValue() * m_doc->GetDrawingUnit(staff->m_drawingStaffSize); const int widthFront = m_doc->GetGlyphWidth(fret.front(), glyphSize, drawingCueSize); @@ -163,13 +167,24 @@ void View::DrawTabNote(DeviceContext *dc, LayerElement *element, Layer *layer, S const int x1 = x - widthFront / 2; const int x2 = x + extend.m_width - widthBack * 3 / 10; // trim right hand overhang on last character - const int y1 = y + extend.m_ascent + lineThickness; - const int y2 = y1; + dc->SetPen(m_currentColor, lineThickness, AxSOLID); + dc->SetBrush(m_currentColor, AxSOLID); - dc->SetPen(m_currentColour, lineThickness, AxSOLID); - dc->SetBrush(m_currentColour, AxSOLID); + // overlines + int y1 = y + extend.m_ascent + lineThickness; - dc->DrawLine(ToDeviceContextX(x1), ToDeviceContextY(y1), ToDeviceContextX(x2), ToDeviceContextY(y2)); + for (int i = 0; i < overline; ++i) { + dc->DrawLine(ToDeviceContextX(x1), ToDeviceContextY(y1), ToDeviceContextX(x2), ToDeviceContextY(y1)); + y1 += 2 * lineThickness; + } + + // strikethroughs + y1 = y + extend.m_ascent / 2 - (strike - 1) * lineThickness; + + for (int i = 0; i < strike; ++i) { + dc->DrawLine(ToDeviceContextX(x1), ToDeviceContextY(y1), ToDeviceContextX(x2), ToDeviceContextY(y1)); + y1 += 2 * lineThickness; + } dc->ResetPen(); dc->ResetBrush(); @@ -196,6 +211,12 @@ void View::DrawTabDurSym(DeviceContext *dc, LayerElement *element, Layer *layer, dc->StartGraphic(tabDurSym, "", tabDurSym->GetID()); + if (tabDurSym->HasLoc()) { + const int yRel = ((staff->m_drawingLines - 1) * 2 - tabDurSym->GetLoc()) + * m_doc->GetDrawingUnit(staff->m_drawingStaffSize); + tabDurSym->SetDrawingYRel(-yRel); + } + int x = element->GetDrawingX(); int y = element->GetDrawingY(); From dab05ae0015c79c57a613903366aca03cbc8d536 Mon Sep 17 00:00:00 2001 From: Paul Overell Date: Wed, 6 Sep 2023 11:49:23 +0100 Subject: [PATCH 04/15] Changed I/F for tabGrp::GetListSize --- src/calcalignmentpitchposfunctor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calcalignmentpitchposfunctor.cpp b/src/calcalignmentpitchposfunctor.cpp index 95ea2a41a0c..8508fbffd7b 100644 --- a/src/calcalignmentpitchposfunctor.cpp +++ b/src/calcalignmentpitchposfunctor.cpp @@ -108,7 +108,7 @@ FunctorCode CalcAlignmentPitchPosFunctor::VisitLayerElement(LayerElement *layerE assert(staffY->m_drawingTuning); assert(staffY->m_drawingStaffDef); loc = staffY->m_drawingTuning->CalcPitchPos(note->GetTabCourse(), staffY->m_drawingNotationType, - staffY->m_drawingLines, tabGrp->GetListSize(tabGrp), tabGrp->GetListIndex(note), note->GetLoc(), + staffY->m_drawingLines, tabGrp->GetListSize(), tabGrp->GetListIndex(note), note->GetLoc(), staffY->m_drawingStaffDef->GetValign() != VERTICALALIGNMENT_bottom); } else if ((note->HasPname() && (note->HasOct() || note->HasOctDefault())) || note->HasLoc()) { From 50c3d076e544832520c308a9a4eb7d3d54a3b75b Mon Sep 17 00:00:00 2001 From: Paul Overell Date: Tue, 23 Jan 2024 14:05:57 +0000 Subject: [PATCH 05/15] Remove @notationsubtype for German Lute Tablature --- include/vrv/note.h | 3 +-- src/note.cpp | 35 ++++++++++++++--------------------- src/view_tab.cpp | 6 ++---- 3 files changed, 17 insertions(+), 27 deletions(-) diff --git a/include/vrv/note.h b/include/vrv/note.h index af51ac7e17c..cbeae02cb40 100644 --- a/include/vrv/note.h +++ b/include/vrv/note.h @@ -162,8 +162,7 @@ class Note : public LayerElement, * @name Return the smufl string to use for a note give the notation type */ ///@{ - std::u32string GetTabFretString( - data_NOTATIONTYPE notationType, const std::string ¬ationSubtype, int &overline, int &strike) const; + std::u32string GetTabFretString(data_NOTATIONTYPE notationType, int &overline, int &strike) const; ///@} /** diff --git a/src/note.cpp b/src/note.cpp index 3a1ca811a43..a9893e0f6e1 100644 --- a/src/note.cpp +++ b/src/note.cpp @@ -267,8 +267,7 @@ const TabGrp *Note::IsTabGrpNote() const return vrv_cast(this->GetFirstAncestor(TABGRP, MAX_TABGRP_DEPTH)); } -std::u32string Note::GetTabFretString( - data_NOTATIONTYPE notationType, const std::string ¬ationSubtype, int &overline, int &strike) const +std::u32string Note::GetTabFretString(data_NOTATIONTYPE notationType, int &overline, int &strike) const { overline = 0; strike = 0; @@ -345,8 +344,8 @@ std::u32string Note::GetTabFretString( const int fret = this->GetTabFret(); const int course = this->GetTabCourse(); - // SMuFL has glyphs for German lute tablature following Hans Newsidler's notation - // for the 6th course. + // SMuFL has glyphs for German lute tablature following Hans and Melchior Newsidler's notation + // for the >= 6th courses. // "German Renaissance lute tablature (U+EC00–U+EC2F)" // https://w3c.github.io/smufl/latest/tables/german-renaissance-lute-tablature.html // @@ -364,27 +363,17 @@ std::u32string Note::GetTabFretString( // parts of the SMuFL collection. Overlines and strike throughs are drawn separately. if (course >= 6 && fret >= 0 && fret <= 13) { - // TODO more GLT subtypes needed, is this subtype too specific? - if (notationSubtype == "judenkunig_1523") { - // A B C D ... + // + A B C D ... + if (fret == 0) { + fretStr = SMUFL_EA51_figbass1; // substitute for 1 with oblique stroke + strike = course - 5; // 6 course 1 strike, 7 course 2 strikes, ... + } + else { // The German tablature uppercase letters A-N are contiguous, correctly omitting J static_assert(SMUFL_EC23_luteGermanNUpper == SMUFL_EC17_luteGermanAUpper + 13 - 1); - fretStr = SMUFL_EC17_luteGermanAUpper + fret; + fretStr = SMUFL_EC17_luteGermanAUpper + fret - 1; overline = course - 6; // 6 course 0 overline, 7 course 1 overline, ... } - else { - // + A B C D ... - if (fret == 0) { - fretStr = SMUFL_EA51_figbass1; // substitute for 1 with oblique stroke - strike = course - 5; // 6 course 1 strike, 7 course 2 strikes, ... - } - else { - // The German tablature uppercase letters A-N are contiguous, correctly omitting J - static_assert(SMUFL_EC23_luteGermanNUpper == SMUFL_EC17_luteGermanAUpper + 13 - 1); - fretStr = SMUFL_EC17_luteGermanAUpper + fret - 1; - overline = course - 6; // 6 course 0 overline, 7 course 1 overline, ... - } - } } else if (course >= 1 && course <= 5 && fret == 0) { // Substitute for gothic digits @@ -396,9 +385,13 @@ std::u32string Note::GetTabFretString( const int firstAlphabetFret = fret <= 5 ? fret : fret - 5; // map second alphabet to first if (course == 2 && firstAlphabetFret == 5) { + // TODO replace with U+EC24, luteGermanEt when available + // https://github.com/w3c/smufl/issues/274 fretStr = SMUFL_EA5F_figbass7Raised2; // substitute for "et" } else if (course == 1 && firstAlphabetFret == 5) { + // TODO replace with U+EC25, luteGermanCon when available + // https://github.com/w3c/smufl/issues/274 fretStr = SMUFL_EA61_figbass9; // substitute for "con" } else { diff --git a/src/view_tab.cpp b/src/view_tab.cpp index e81f20c72ef..1f99119122e 100644 --- a/src/view_tab.cpp +++ b/src/view_tab.cpp @@ -109,8 +109,7 @@ void View::DrawTabNote(DeviceContext *dc, LayerElement *element, Layer *layer, S if (staff->m_drawingNotationType == NOTATIONTYPE_tab_guitar) { - std::u32string fret = note->GetTabFretString( - staff->m_drawingNotationType, staff->m_drawingStaffDef->GetNotationsubtype(), overline, strike); + std::u32string fret = note->GetTabFretString(staff->m_drawingNotationType, overline, strike); FontInfo fretTxt; if (!dc->UseGlobalStyling()) { @@ -136,8 +135,7 @@ void View::DrawTabNote(DeviceContext *dc, LayerElement *element, Layer *layer, S } else { - std::u32string fret = note->GetTabFretString( - staff->m_drawingNotationType, staff->m_drawingStaffDef->GetNotationsubtype(), overline, strike); + std::u32string fret = note->GetTabFretString(staff->m_drawingNotationType, overline, strike); // Center for italian tablature if (staff->IsTabLuteItalian()) { y -= (m_doc->GetGlyphHeight(SMUFL_EBE0_luteItalianFret0, glyphSize, drawingCueSize) / 2); From 6770e4a7238a5a2e47c7076f6c98acdf04518b9a Mon Sep 17 00:00:00 2001 From: Paul Overell Date: Thu, 25 Jan 2024 15:51:00 +0000 Subject: [PATCH 06/15] Implement note@glyph.num, @glyph.name, @altsym for tablature. --- include/vrv/note.h | 4 ++++ include/vrv/view.h | 2 +- src/iomei.cpp | 5 +++++ src/note.cpp | 47 ++++++++++++++++++++++++++++++++++++++++++++++ src/symboldef.cpp | 4 ++++ src/view_page.cpp | 14 ++++++++------ 6 files changed, 69 insertions(+), 7 deletions(-) diff --git a/include/vrv/note.h b/include/vrv/note.h index cbeae02cb40..1bad7764503 100644 --- a/include/vrv/note.h +++ b/include/vrv/note.h @@ -13,6 +13,7 @@ //---------------------------------------------------------------------------- #include "accid.h" +#include "altsyminterface.h" #include "atts_analytical.h" #include "atts_externalsymbols.h" #include "atts_frettab.h" @@ -45,6 +46,7 @@ class Verse; */ class Note : public LayerElement, public StemmedDrawingInterface, + public AltSymInterface, public DurationInterface, public PitchInterface, public PositionInterface, @@ -80,6 +82,8 @@ class Note : public LayerElement, * @name Getter to interfaces */ ///@{ + AltSymInterface *GetAltSymInterface() override { return vrv_cast(this); } + const AltSymInterface *GetAltSymInterface() const override { return vrv_cast(this); } DurationInterface *GetDurationInterface() override { return vrv_cast(this); } const DurationInterface *GetDurationInterface() const override { return vrv_cast(this); } PitchInterface *GetPitchInterface() override { return vrv_cast(this); } diff --git a/include/vrv/view.h b/include/vrv/view.h index e7faedd7ad1..7abe3fc919b 100644 --- a/include/vrv/view.h +++ b/include/vrv/view.h @@ -232,7 +232,7 @@ class View { void DrawMeterSigGrp(DeviceContext *dc, Layer *layer, Staff *staff); void DrawMNum(DeviceContext *dc, MNum *mnum, Measure *measure, System *system, int yOffset); void DrawStaff(DeviceContext *dc, Staff *staff, Measure *measure, System *system); - void DrawStaffLines(DeviceContext *dc, Staff *staff, Measure *measure, System *system); + void DrawStaffLines(DeviceContext *dc, Staff *staff, StaffDef *staffDef, Measure *measure, System *system); void DrawLayer(DeviceContext *dc, Layer *layer, Staff *staff, Measure *measure); void DrawLayerList(DeviceContext *dc, Layer *layer, Staff *staff, Measure *measure, const ClassId classId); void DrawLayerDefLabels( diff --git a/src/iomei.cpp b/src/iomei.cpp index c6daa9ca38c..386c011bf74 100644 --- a/src/iomei.cpp +++ b/src/iomei.cpp @@ -2740,6 +2740,7 @@ void MEIOutput::WriteNote(pugi::xml_node currentNode, Note *note) assert(note); this->WriteLayerElement(currentNode, note); + this->WriteAltSymInterface(currentNode, note); this->WriteDurationInterface(currentNode, note); this->WritePitchInterface(currentNode, note); this->WritePositionInterface(currentNode, note); @@ -6911,6 +6912,7 @@ bool MEIInput::ReadNote(Object *parent, pugi::xml_node note) } } + this->ReadAltSymInterface(note, vrvNote); this->ReadDurationInterface(note, vrvNote); this->ReadPitchInterface(note, vrvNote); this->ReadPositionInterface(note, vrvNote); @@ -7232,6 +7234,9 @@ bool MEIInput::ReadSymbolDefChildren(Object *parent, pugi::xml_node parentNode, else if (elementName == "svg") { success = this->ReadSvg(parent, xmlElement); } + else if (elementName == "symbol") { + success = this->ReadSymbol(parent, xmlElement); + } // xml comment else if (elementName == "") { success = this->ReadXMLComment(parent, xmlElement); diff --git a/src/note.cpp b/src/note.cpp index a9893e0f6e1..7bf036cbf78 100644 --- a/src/note.cpp +++ b/src/note.cpp @@ -31,6 +31,7 @@ #include "staff.h" #include "stem.h" #include "syl.h" +#include "symboldef.h" #include "tabgrp.h" #include "tie.h" #include "tuning.h" @@ -52,6 +53,7 @@ static const ClassRegistrar s_factory("note", NOTE); Note::Note() : LayerElement(NOTE, "note-") , StemmedDrawingInterface() + , AltSymInterface() , DurationInterface() , PitchInterface() , PositionInterface() @@ -71,6 +73,7 @@ Note::Note() , AttTiePresent() , AttVisibility() { + this->RegisterInterface(AltSymInterface::GetAttClasses(), AltSymInterface::IsInterface()); this->RegisterInterface(DurationInterface::GetAttClasses(), DurationInterface::IsInterface()); this->RegisterInterface(PitchInterface::GetAttClasses(), PitchInterface::IsInterface()); this->RegisterInterface(PositionInterface::GetAttClasses(), PositionInterface::IsInterface()); @@ -99,6 +102,7 @@ void Note::Reset() { LayerElement::Reset(); StemmedDrawingInterface::Reset(); + AltSymInterface::Reset(); DurationInterface::Reset(); PitchInterface::Reset(); PositionInterface::Reset(); @@ -272,6 +276,49 @@ std::u32string Note::GetTabFretString(data_NOTATIONTYPE notationType, int &overl overline = 0; strike = 0; + // @glyph.num, @glyph.name or @altsym + const Resources *resources = this->GetDocResources(); + if (resources != NULL) { + std::u32string fretStr; + // If there is @glyph.num, return glyph based on it (first priority) + if (this->HasGlyphNum()) { + const char32_t code = this->GetGlyphNum(); + if (NULL != resources->GetGlyph(code)) fretStr.push_back(code); + } + + // If there is @glyph.name (second priority) + else if (this->HasGlyphName()) { + const char32_t code = resources->GetGlyphCode(this->GetGlyphName()); + if (NULL != resources->GetGlyph(code)) fretStr.push_back(code); + } + + // If there is @altsym (third priority) + else if (this->HasAltsym() && this->HasAltSymbolDef()) { + const SymbolDef *symbolDef = this->GetAltSymbolDef(); + + // there may be more than one + for (const Object *child : symbolDef->GetChildren()) { + if (child->Is(SYMBOL)) { + const Symbol *symbol = vrv_cast(child); + + // If there is @glyph.num, return glyph based on it (fourth priority) + if (symbol->HasGlyphNum()) { + const char32_t code = symbol->GetGlyphNum(); + if (NULL != resources->GetGlyph(code)) fretStr.push_back(code); + } + + // If there is @glyph.name (fifth priority) + else if (symbol->HasGlyphName()) { + const char32_t code = resources->GetGlyphCode(symbol->GetGlyphName()); + if (NULL != resources->GetGlyph(code)) fretStr.push_back(code); + } + } + } + } + + if (!fretStr.empty()) return fretStr; + } + if (notationType == NOTATIONTYPE_tab_lute_italian) { std::u32string fretStr; int fret = this->GetTabFret(); diff --git a/src/symboldef.cpp b/src/symboldef.cpp index a0fe1d66c73..eb58a10fcd2 100644 --- a/src/symboldef.cpp +++ b/src/symboldef.cpp @@ -16,6 +16,7 @@ #include "doc.h" #include "graphic.h" #include "svg.h" +#include "symbol.h" #include "vrv.h" namespace vrv { @@ -48,6 +49,9 @@ bool SymbolDef::IsSupportedChild(Object *child) else if (child->Is(SVG)) { assert(dynamic_cast(child)); } + else if (child->Is(SYMBOL)) { + assert(dynamic_cast(child)); + } else { return false; } diff --git a/src/view_page.cpp b/src/view_page.cpp index 87853c41815..2a6e071b36a 100644 --- a/src/view_page.cpp +++ b/src/view_page.cpp @@ -1252,9 +1252,7 @@ void View::DrawStaff(DeviceContext *dc, Staff *staff, Measure *measure, System * staff->SetFromFacsimile(m_doc); } - if ((staffDef && (staffDef->GetLinesVisible() != BOOLEAN_false)) || staff->IsTabLuteGerman()) { - this->DrawStaffLines(dc, staff, measure, system); - } + this->DrawStaffLines(dc, staff, staffDef, measure, system); if (staffDef && (m_doc->GetType() != Facs)) { this->DrawStaffDef(dc, staff, measure); @@ -1284,7 +1282,7 @@ void View::DrawStaff(DeviceContext *dc, Staff *staff, Measure *measure, System * dc->EndGraphic(staff, this); } -void View::DrawStaffLines(DeviceContext *dc, Staff *staff, Measure *measure, System *system) +void View::DrawStaffLines(DeviceContext *dc, Staff *staff, StaffDef *staffDef, Measure *measure, System *system) { assert(dc); assert(staff); @@ -1307,14 +1305,18 @@ void View::DrawStaffLines(DeviceContext *dc, Staff *staff, Measure *measure, Sys dc->SetPen(m_currentColor, ToDeviceContextX(lineWidth), AxSOLID); dc->SetBrush(m_currentColor, AxSOLID); - if (staff->IsTabLuteGerman()) { + // If German lute tablature the default is @lines.visible="false", but setting @lines.visible="true" + // will draw the staff lines. + // For anything other than German lute tablature the default is @lines.visible="true" + if (staff->IsTabLuteGerman() && staffDef->GetLinesVisible() != BOOLEAN_true) { // German tablature has no staff, just a single base line // But internally we maintain the fiction of an invisible staff as a coordinate system SegmentedLine line(x1, x2); y1 -= m_doc->GetDrawingDoubleUnit(staff->m_drawingStaffSize) * staff->m_drawingLines; this->DrawHorizontalSegmentedLine(dc, y1, line, lineWidth); } - else { + else if (staffDef->GetLinesVisible() != BOOLEAN_false) { + // draw staff lines for (j = 0; j < staff->m_drawingLines; ++j) { // Skewed lines - with Facs (neumes) only for now if (y1 != y2) { From 844f49385f3913716e79e2252b256b0b359e6e15 Mon Sep 17 00:00:00 2001 From: Paul Overell Date: Tue, 6 Feb 2024 12:44:51 +0000 Subject: [PATCH 07/15] Issue #3589 GLT de-emphasise dividing lines --- src/view_page.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/view_page.cpp b/src/view_page.cpp index 2a6e071b36a..0971e29914f 100644 --- a/src/view_page.cpp +++ b/src/view_page.cpp @@ -1312,8 +1312,9 @@ void View::DrawStaffLines(DeviceContext *dc, Staff *staff, StaffDef *staffDef, M // German tablature has no staff, just a single base line // But internally we maintain the fiction of an invisible staff as a coordinate system SegmentedLine line(x1, x2); - y1 -= m_doc->GetDrawingDoubleUnit(staff->m_drawingStaffSize) * staff->m_drawingLines; - this->DrawHorizontalSegmentedLine(dc, y1, line, lineWidth); + // Issue #3589 move base line slightly further down and reduce thickness + y1 -= (m_doc->GetDrawingDoubleUnit(staff->m_drawingStaffSize) * staff->m_drawingLines) * 11 / 10; + this->DrawHorizontalSegmentedLine(dc, y1, line, lineWidth / 2); } else if (staffDef->GetLinesVisible() != BOOLEAN_false) { // draw staff lines @@ -1326,11 +1327,12 @@ void View::DrawStaffLines(DeviceContext *dc, Staff *staff, StaffDef *staffDef, M y2 -= m_doc->GetDrawingDoubleUnit(staff->m_drawingStaffSize); } else { - const bool isFrenchOrItalianTablature = (staff->IsTabLuteFrench() || staff->IsTabLuteItalian()); + const bool isFrenchOrGermanOrItalianTablature + = (staff->IsTabLuteFrench() || staff->IsTabLuteGerman() || staff->IsTabLuteItalian()); SegmentedLine line(x1, x2); - // We do not need to do this during layout calculation - and only with tablature but not for French or - // Italian tablature - if (!dc->Is(BBOX_DEVICE_CONTEXT) && staff->IsTablature() && !isFrenchOrItalianTablature) { + // We do not need to do this during layout calculation - and only with guitar tablature but not for + // French, German or Italian lute tablature + if (!dc->Is(BBOX_DEVICE_CONTEXT) && staff->IsTablature() && !isFrenchOrGermanOrItalianTablature) { Object fullLine; fullLine.SetParent(system); fullLine.UpdateContentBBoxY(y1 + (lineWidth / 2), y1 - (lineWidth / 2)); From a38baf6d3eea10e6988297cf915c7106ce35e78b Mon Sep 17 00:00:00 2001 From: Laurent Pugin Date: Wed, 7 Feb 2024 13:39:11 +0100 Subject: [PATCH 08/15] Adding clone methods to TabDurSym and TabGrp --- include/vrv/tabdursym.h | 1 + include/vrv/tabgrp.h | 1 + 2 files changed, 2 insertions(+) diff --git a/include/vrv/tabdursym.h b/include/vrv/tabdursym.h index baa44140519..e83d6b78196 100644 --- a/include/vrv/tabdursym.h +++ b/include/vrv/tabdursym.h @@ -30,6 +30,7 @@ class TabDurSym : public LayerElement, public StemmedDrawingInterface, public At ///@{ TabDurSym(); virtual ~TabDurSym(); + Object *Clone() const override { return new TabDurSym(*this); } void Reset() override; std::string GetClassName() const override { return "TabDurSym"; } ///@} diff --git a/include/vrv/tabgrp.h b/include/vrv/tabgrp.h index 14e9f8ba4d1..cf9dd6713be 100644 --- a/include/vrv/tabgrp.h +++ b/include/vrv/tabgrp.h @@ -29,6 +29,7 @@ class TabGrp : public LayerElement, public ObjectListInterface, public DurationI ///@{ TabGrp(); virtual ~TabGrp(); + Object *Clone() const override { return new TabGrp(*this); } void Reset() override; std::string GetClassName() const override { return "TabGrp"; } ///@} From 89715f3fe5e1bbc6d159fe881f10ad2a8817b2ee Mon Sep 17 00:00:00 2001 From: Laurent Pugin Date: Wed, 17 Jul 2024 14:19:21 +0200 Subject: [PATCH 09/15] Rebase --- include/vrv/rest.h | 3 +++ src/iomei.cpp | 5 +++++ src/rest.cpp | 21 +++++++++++++++++++++ src/tabgrp.cpp | 4 ++++ src/view_element.cpp | 17 ++++++++++++++--- 5 files changed, 47 insertions(+), 3 deletions(-) diff --git a/include/vrv/rest.h b/include/vrv/rest.h index b737eebc0d7..f860b6b63d2 100644 --- a/include/vrv/rest.h +++ b/include/vrv/rest.h @@ -8,6 +8,7 @@ #ifndef __VRV_REST_H__ #define __VRV_REST_H__ +#include "altsyminterface.h" #include "atts_externalsymbols.h" #include "atts_mensural.h" #include "durationinterface.h" @@ -34,6 +35,7 @@ enum RestNotePlace { RNP_UNSET = -1, RNP_noteInSpace, RNP_noteOnLine }; * This class models the MEI element. */ class Rest : public LayerElement, + public AltSymInterface, public DurationInterface, public PositionInterface, public AttColor, @@ -69,6 +71,7 @@ class Rest : public LayerElement, * @name Getter to interfaces */ ///@{ + AltSymInterface *GetAltSymInterface() override { return vrv_cast(this); } PositionInterface *GetPositionInterface() override { return vrv_cast(this); } const PositionInterface *GetPositionInterface() const override { return vrv_cast(this); } DurationInterface *GetDurationInterface() override { return vrv_cast(this); } diff --git a/src/iomei.cpp b/src/iomei.cpp index 386c011bf74..f85c4840aab 100644 --- a/src/iomei.cpp +++ b/src/iomei.cpp @@ -2799,6 +2799,7 @@ void MEIOutput::WriteRest(pugi::xml_node currentNode, Rest *rest) assert(rest); this->WriteLayerElement(currentNode, rest); + this->WriteAltSymInterface(currentNode, rest); this->WriteDurationInterface(currentNode, rest); this->WritePositionInterface(currentNode, rest); rest->WriteColor(currentNode); @@ -3819,6 +3820,9 @@ bool MEIInput::IsAllowed(std::string element, Object *filterParent) if (element == "note") { return true; } + if (element == "rest") { + return true; + } else { return false; } @@ -6967,6 +6971,7 @@ bool MEIInput::ReadRest(Object *parent, pugi::xml_node rest) } } + this->ReadAltSymInterface(rest, vrvRest); this->ReadDurationInterface(rest, vrvRest); this->ReadPositionInterface(rest, vrvRest); vrvRest->ReadColor(rest); diff --git a/src/rest.cpp b/src/rest.cpp index c9c3db2903b..f9abe232e86 100644 --- a/src/rest.cpp +++ b/src/rest.cpp @@ -22,6 +22,7 @@ #include "layer.h" #include "smufl.h" #include "staff.h" +#include "symboldef.h" #include "system.h" #include "transposition.h" #include "vrv.h" @@ -153,6 +154,7 @@ static const ClassRegistrar s_factory("rest", REST); Rest::Rest() : LayerElement(REST, "rest-") + , AltSymInterface() , DurationInterface() , PositionInterface() , AttColor() @@ -161,6 +163,7 @@ Rest::Rest() , AttExtSymNames() , AttRestVisMensural() { + this->RegisterInterface(AltSymInterface::GetAttClasses(), AltSymInterface::IsInterface()); this->RegisterInterface(DurationInterface::GetAttClasses(), DurationInterface::IsInterface()); this->RegisterInterface(PositionInterface::GetAttClasses(), PositionInterface::IsInterface()); this->RegisterAttClass(ATT_COLOR); @@ -176,6 +179,7 @@ Rest::~Rest() {} void Rest::Reset() { LayerElement::Reset(); + AltSymInterface::Reset(); DurationInterface::Reset(); PositionInterface::Reset(); this->ResetColor(); @@ -241,6 +245,23 @@ char32_t Rest::GetRestGlyph(const int duration) const char32_t code = resources->GetGlyphCode(this->GetGlyphName()); if (NULL != resources->GetGlyph(code)) return code; } + // If there is @altsym (third priority) + else if (this->HasAltsym() && this->HasAltSymbolDef()) { + const SymbolDef *symbolDef = this->GetAltSymbolDef(); + const Symbol *symbol = vrv_cast(symbolDef->GetFirst(SYMBOL)); + if (symbol != NULL) { + // If there is @glyph.num, return glyph based on it (fourth priority) + if (symbol->HasGlyphNum()) { + const char32_t code = symbol->GetGlyphNum(); + if (NULL != resources->GetGlyph(code)) return code; + } + // If there is @glyph.name (fifth priority) + else if (symbol->HasGlyphName()) { + const char32_t code = resources->GetGlyphCode(symbol->GetGlyphName()); + if (NULL != resources->GetGlyph(code)) return code; + } + } + } if (this->IsMensuralDur()) { switch (duration) { diff --git a/src/tabgrp.cpp b/src/tabgrp.cpp index 72436718d33..d9b668b0733 100644 --- a/src/tabgrp.cpp +++ b/src/tabgrp.cpp @@ -16,6 +16,7 @@ #include "editorial.h" #include "functor.h" #include "note.h" +#include "rest.h" #include "tabdursym.h" namespace vrv { @@ -46,6 +47,9 @@ bool TabGrp::IsSupportedChild(Object *child) if (child->Is(NOTE)) { assert(dynamic_cast(child)); } + else if (child->Is(REST)) { + assert(dynamic_cast(child)); + } else if (child->Is(TABDURSYM)) { assert(dynamic_cast(child)); } diff --git a/src/view_element.cpp b/src/view_element.cpp index eb253fc26fd..77a2ab378e4 100644 --- a/src/view_element.cpp +++ b/src/view_element.cpp @@ -53,6 +53,7 @@ #include "stem.h" #include "syl.h" #include "system.h" +#include "tabgrp.h" #include "tie.h" #include "tuplet.h" #include "verse.h" @@ -1512,10 +1513,20 @@ void View::DrawRest(DeviceContext *dc, LayerElement *element, Layer *layer, Staf const int staffSize = staff->GetDrawingStaffNotationSize(); int drawingDur = rest->GetActualDur(); if (drawingDur == DUR_NONE) { - if (!dc->Is(BBOX_DEVICE_CONTEXT)) { - LogWarning("Missing duration for rest '%s'", rest->GetID().c_str()); + // in tablature the @dur is in the parent TabGrp + if (staff->IsTablature()) { + TabGrp *tabGrp = vrv_cast(rest->GetFirstAncestor(TABGRP)); + if (tabGrp != NULL) { + drawingDur = tabGrp->GetActualDur(); + } + } + + if (drawingDur == DUR_NONE) { + if (!dc->Is(BBOX_DEVICE_CONTEXT)) { + LogWarning("Missing duration for rest '%s'", rest->GetID().c_str()); + } + drawingDur = DUR_4; } - drawingDur = DUR_4; } const char32_t drawingGlyph = rest->GetRestGlyph(drawingDur); From 9dcff602abbca46e1acb55eb21b9b1d3a243e3c4 Mon Sep 17 00:00:00 2001 From: Laurent Pugin Date: Thu, 18 Jul 2024 15:56:57 +0200 Subject: [PATCH 10/15] Reset cloned beams coord list * Fixes https://github.com/rism-digital/verovio/issues/3702 * Test-suite evaluated locally --- include/vrv/beam.h | 5 +++++ src/beam.cpp | 9 +++++++++ src/drawinginterface.cpp | 2 ++ 3 files changed, 16 insertions(+) diff --git a/include/vrv/beam.h b/include/vrv/beam.h index 54683186f3d..6f6b56482c0 100644 --- a/include/vrv/beam.h +++ b/include/vrv/beam.h @@ -295,6 +295,11 @@ class Beam : public LayerElement, std::string GetClassName() const override { return "Beam"; } ///@} + /** + * Overriding CloneReset() method to be called after copy / assignment calls. + */ + void CloneReset() override; + /** * @name Getter to interfaces */ diff --git a/src/beam.cpp b/src/beam.cpp index f3137305ad1..5cc4bbf2c53 100644 --- a/src/beam.cpp +++ b/src/beam.cpp @@ -1619,6 +1619,15 @@ void Beam::Reset() m_stemSameas = NULL; } +void Beam::CloneReset() +{ + // Since these are owned by the beam we cloned from, empty the list + // Do it before Object::CloneReset since that one will reset the coord list + m_beamElementCoords.clear(); + + LayerElement::CloneReset(); +} + bool Beam::IsSupportedChild(Object *child) { if (child->Is(BEAM)) { diff --git a/src/drawinginterface.cpp b/src/drawinginterface.cpp index 7d4b2622981..c843d0431fb 100644 --- a/src/drawinginterface.cpp +++ b/src/drawinginterface.cpp @@ -102,6 +102,8 @@ void BeamDrawingInterface::Reset() m_beamWidth = 0; m_beamWidthBlack = 0; m_beamWidthWhite = 0; + + ClearCoords(); } int BeamDrawingInterface::GetTotalBeamWidth() const From 3b0fe9c03a5e6e81f2947e3adc5a994a4f0f88b5 Mon Sep 17 00:00:00 2001 From: Paul Overell Date: Mon, 14 Oct 2024 14:54:30 +0100 Subject: [PATCH 11/15] Fix #3828 Italian lute tablature bass notes. --- include/vrv/note.h | 2 +- src/note.cpp | 50 +++++++++++++++++++++++++++++++--------------- src/tuning.cpp | 4 +++- src/view_tab.cpp | 30 ++++++++++++++++++++++------ 4 files changed, 62 insertions(+), 24 deletions(-) diff --git a/include/vrv/note.h b/include/vrv/note.h index c27fb7ac026..6e90dfa0c61 100644 --- a/include/vrv/note.h +++ b/include/vrv/note.h @@ -166,7 +166,7 @@ class Note : public LayerElement, * @name Return the smufl string to use for a note give the notation type */ ///@{ - std::u32string GetTabFretString(data_NOTATIONTYPE notationType, int &overline, int &strike) const; + std::u32string GetTabFretString(data_NOTATIONTYPE notationType, int &overline, int &strike, int &underline) const; ///@} /** diff --git a/src/note.cpp b/src/note.cpp index 433300820b7..cdf254cc17d 100644 --- a/src/note.cpp +++ b/src/note.cpp @@ -224,6 +224,17 @@ bool Note::HasLedgerLines(int &linesAbove, int &linesBelow, const Staff *staff) staff = this->GetAncestorStaff(); } + if (staff->IsTabLuteFrench() || staff->IsTabLuteGerman() || staff->IsTabLuteItalian()) { + // French and German tablature do not use ledger lines. + // Italian tablature does use a single ledger line for 7th course, and compressed + // ledger lines for fretted 8th and above, but not for open 8th and above. So + // rather than use the CMN ledger line handling we draw our own. + // Guitar tablature has been left as originally implemented. + linesAbove = 0; + linesBelow = 0; + return false; + } + linesAbove = (this->GetDrawingLoc() - staff->m_drawingLines * 2 + 2) / 2; linesBelow = -(this->GetDrawingLoc()) / 2; @@ -271,10 +282,11 @@ const TabGrp *Note::IsTabGrpNote() const return vrv_cast(this->GetFirstAncestor(TABGRP, MAX_TABGRP_DEPTH)); } -std::u32string Note::GetTabFretString(data_NOTATIONTYPE notationType, int &overline, int &strike) const +std::u32string Note::GetTabFretString(data_NOTATIONTYPE notationType, int &overline, int &strike, int &underline) const { overline = 0; strike = 0; + underline = 0; // @glyph.num, @glyph.name or @altsym const Resources *resources = this->GetDocResources(); @@ -321,21 +333,27 @@ std::u32string Note::GetTabFretString(data_NOTATIONTYPE notationType, int &overl if (notationType == NOTATIONTYPE_tab_lute_italian) { std::u32string fretStr; - int fret = this->GetTabFret(); - // Maximum allowed would be 19 (always bindly adding 1 as first figure) - if (fret > 9) fretStr.push_back(SMUFL_EBE1_luteItalianFret1); - switch (fret % 10) { - case 0: fretStr.push_back(SMUFL_EBE0_luteItalianFret0); break; - case 1: fretStr.push_back(SMUFL_EBE1_luteItalianFret1); break; - case 2: fretStr.push_back(SMUFL_EBE2_luteItalianFret2); break; - case 3: fretStr.push_back(SMUFL_EBE3_luteItalianFret3); break; - case 4: fretStr.push_back(SMUFL_EBE4_luteItalianFret4); break; - case 5: fretStr.push_back(SMUFL_EBE5_luteItalianFret5); break; - case 6: fretStr.push_back(SMUFL_EBE6_luteItalianFret6); break; - case 7: fretStr.push_back(SMUFL_EBE7_luteItalianFret7); break; - case 8: fretStr.push_back(SMUFL_EBE8_luteItalianFret8); break; - case 9: fretStr.push_back(SMUFL_EBE9_luteItalianFret9); break; - default: break; + const int fret = this->GetTabFret(); + const int course = this->GetTabCourse(); + + // Italian tablature glyphs are contiguous + static_assert(SMUFL_EBE1_luteItalianFret1 == SMUFL_EBE0_luteItalianFret0 + 1); + static_assert(SMUFL_EBE2_luteItalianFret2 == SMUFL_EBE0_luteItalianFret0 + 2); + // ... + static_assert(SMUFL_EBE9_luteItalianFret9 == SMUFL_EBE0_luteItalianFret0 + 9); + + if (course <= 7 || fret != 0) { + const auto decimal = std::div(fret, 10); + if (decimal.quot > 0) fretStr.push_back(SMUFL_EBE0_luteItalianFret0 + decimal.quot); + if (decimal.rem <= 9) fretStr.push_back(SMUFL_EBE0_luteItalianFret0 + decimal.rem); + if (course >= 7) strike = 1; // course 7 and fretted courses >= 8 use a ledger line + underline = std::max(0, course - 7); // compressed ledger lines for fretted courses >= 8 + } + else { + // open courses >= 8 just use the number of the course on stave line 7 + const auto decimal = std::div(course, 10); + if (decimal.quot > 0) fretStr.push_back(SMUFL_EBE0_luteItalianFret0 + decimal.quot); + if (decimal.rem <= 9) fretStr.push_back(SMUFL_EBE0_luteItalianFret0 + decimal.rem); } return fretStr; } diff --git a/src/tuning.cpp b/src/tuning.cpp index c7f71fa3f3d..1dde76662fc 100644 --- a/src/tuning.cpp +++ b/src/tuning.cpp @@ -63,7 +63,9 @@ int Tuning::CalcPitchPos( case NOTATIONTYPE_tab_lute_french: // all courses >= 7 are positioned above line 0 return (lines - std::min(course, 7)) * 2 + 1; // above the line - case NOTATIONTYPE_tab_lute_italian: return (course - 1) * 2; + case NOTATIONTYPE_tab_lute_italian: + // all courses >= 7 are positioned on line 7 + return (std::min(course, 7) - 1) * 2; case NOTATIONTYPE_tab_lute_german: if (loc != MEI_UNSET) { return loc; diff --git a/src/view_tab.cpp b/src/view_tab.cpp index 9e82e4441b8..44a247351b6 100644 --- a/src/view_tab.cpp +++ b/src/view_tab.cpp @@ -106,10 +106,11 @@ void View::DrawTabNote(DeviceContext *dc, LayerElement *element, Layer *layer, S bool drawingCueSize = false; int overline = 0; int strike = 0; + int underline = 0; if (staff->m_drawingNotationType == NOTATIONTYPE_tab_guitar) { - std::u32string fret = note->GetTabFretString(staff->m_drawingNotationType, overline, strike); + std::u32string fret = note->GetTabFretString(staff->m_drawingNotationType, overline, strike, underline); FontInfo fretTxt; if (!dc->UseGlobalStyling()) { @@ -135,7 +136,7 @@ void View::DrawTabNote(DeviceContext *dc, LayerElement *element, Layer *layer, S } else { - std::u32string fret = note->GetTabFretString(staff->m_drawingNotationType, overline, strike); + std::u32string fret = note->GetTabFretString(staff->m_drawingNotationType, overline, strike, underline); // Center for italian tablature if (staff->IsTabLuteItalian()) { y -= (m_doc->GetGlyphHeight(SMUFL_EBE0_luteItalianFret0, glyphSize, drawingCueSize) / 2); @@ -153,8 +154,8 @@ void View::DrawTabNote(DeviceContext *dc, LayerElement *element, Layer *layer, S dc->SetFont(m_doc->GetDrawingSmuflFont(glyphSize, false)); this->DrawSmuflString(dc, x, y, fret, HORIZONTALALIGNMENT_center, glyphSize); - // Add overlines or strikethoughs if required - if ((overline > 0 || strike > 0) && !fret.empty()) { + // Add overlines, strikethoughs and underlines if required + if ((overline > 0 || strike > 0 || underline > 0) && !fret.empty()) { const int lineThickness = m_options->m_lyricLineThickness.GetValue() * m_doc->GetDrawingUnit(staff->m_drawingStaffSize); const int widthFront = m_doc->GetGlyphWidth(fret.front(), glyphSize, drawingCueSize); @@ -162,8 +163,11 @@ void View::DrawTabNote(DeviceContext *dc, LayerElement *element, Layer *layer, S TextExtend extend; dc->GetSmuflTextExtent(fret, &extend); - const int x1 = x - widthFront / 2; - const int x2 = x + extend.m_width - widthBack * 3 / 10; // trim right hand overhang on last character + // TODO These fiddle factors seem necessary to get the lines balanced on either side + // of the fret string. Can we do better? + const int x1 + = x - (fret.size() == 1 ? widthFront * 7 / 10 : widthFront * 12 / 10); // extend on the left hand side + const int x2 = x + extend.m_width - widthBack * 1 / 10; // trim right hand overhang on last character dc->SetPen(m_currentColor, lineThickness, AxSOLID); dc->SetBrush(m_currentColor, AxSOLID); @@ -184,6 +188,14 @@ void View::DrawTabNote(DeviceContext *dc, LayerElement *element, Layer *layer, S y1 += 2 * lineThickness; } + // underlines + y1 = y - extend.m_descent - lineThickness; + + for (int i = 0; i < underline; ++i) { + dc->DrawLine(ToDeviceContextX(x1), ToDeviceContextY(y1), ToDeviceContextX(x2), ToDeviceContextY(y1)); + y1 -= 2 * lineThickness; + } + dc->ResetPen(); dc->ResetBrush(); } @@ -214,6 +226,12 @@ void View::DrawTabDurSym(DeviceContext *dc, LayerElement *element, Layer *layer, * m_doc->GetDrawingUnit(staff->m_drawingStaffSize); tabDurSym->SetDrawingYRel(-yRel); } + else if (staff->IsTabLuteItalian()) { + // make space for 7th course + const int yRel + = ((staff->m_drawingLines - 1) * 2 - 7 * 2 + 1) * m_doc->GetDrawingUnit(staff->m_drawingStaffSize); + tabDurSym->SetDrawingYRel(-yRel); + } int x = element->GetDrawingX(); int y = element->GetDrawingY(); From 27b94cdd8800854aa7f33dacea67b8ab17734912 Mon Sep 17 00:00:00 2001 From: Laurent Pugin Date: Tue, 26 Nov 2024 10:52:45 +0100 Subject: [PATCH 12/15] Fix formatting --- src/positioninterface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/positioninterface.cpp b/src/positioninterface.cpp index f717de0e67a..0a20079ba12 100644 --- a/src/positioninterface.cpp +++ b/src/positioninterface.cpp @@ -75,7 +75,7 @@ int PositionInterface::CalcDrawingLoc(const Layer *layer, const LayerElement *el bool PositionInterface::HasLedgerLines(int &linesAbove, int &linesBelow, const Staff *staff) const { if (!staff) return false; - + if (staff->IsTabLuteFrench() || staff->IsTabLuteGerman() || staff->IsTabLuteItalian()) { // French and German tablature do not use ledger lines. // Italian tablature does use a single ledger line for 7th course, and compressed From 2da0dce166bc991b62e6ea9cb848445047ce3078 Mon Sep 17 00:00:00 2001 From: Laurent Pugin Date: Tue, 26 Nov 2024 11:37:20 +0100 Subject: [PATCH 13/15] Refactor setting of rest duration --- src/view_element.cpp | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/src/view_element.cpp b/src/view_element.cpp index b933e842611..cad7563ea2a 100644 --- a/src/view_element.cpp +++ b/src/view_element.cpp @@ -1539,22 +1539,17 @@ void View::DrawRest(DeviceContext *dc, LayerElement *element, Layer *layer, Staf const bool drawingCueSize = rest->GetDrawingCueSize(); const int staffSize = staff->GetDrawingStaffNotationSize(); data_DURATION drawingDur = rest->GetActualDur(); - if (drawingDur == DURATION_NONE) { - // in tablature the @dur is in the parent TabGrp - if (staff->IsTablature()) { - TabGrp *tabGrp = vrv_cast(rest->GetFirstAncestor(TABGRP)); - if (tabGrp != NULL) { - drawingDur = tabGrp->GetActualDur(); - } - } - - if (drawingDur == DURATION_NONE) { - if (!dc->Is(BBOX_DEVICE_CONTEXT)) { - LogWarning("Missing duration for rest '%s'", rest->GetID().c_str()); - } - drawingDur = DURATION_4; - } + // in tablature the @dur is in the parent TabGrp - try to get if from there + if ((drawingDur == DURATION_NONE) && staff->IsTablature()) { + TabGrp *tabGrp = vrv_cast(rest->GetFirstAncestor(TABGRP)); + if (tabGrp != NULL) drawingDur = tabGrp->GetActualDur(); } + // Make sure we have something to draw + if ((drawingDur == DURATION_NONE) && !dc->Is(BBOX_DEVICE_CONTEXT)) { + LogWarning("Missing duration for rest '%s'", rest->GetID().c_str()); + drawingDur = DURATION_4; + } + const char32_t drawingGlyph = rest->GetRestGlyph(drawingDur); const int x = element->GetDrawingX(); From 5d06ae6ed4fe6f3af0846dcc61f30e295651354c Mon Sep 17 00:00:00 2001 From: Laurent Pugin Date: Tue, 26 Nov 2024 11:48:27 +0100 Subject: [PATCH 14/15] Refactor of the drawing staff lines decision steps * Change `staffDef->GetLinesVisible() == BOOLEAN_true` for GLT (pinging @paul-bayleaf and @musicog) --- src/view_page.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/view_page.cpp b/src/view_page.cpp index 003781aaf32..e9d56bfbcba 100644 --- a/src/view_page.cpp +++ b/src/view_page.cpp @@ -1290,6 +1290,15 @@ void View::DrawStaffLines(DeviceContext *dc, Staff *staff, StaffDef *staffDef, M assert(measure); assert(system); + // If German lute tablature the default is @lines.visible="false", but setting @lines.visible="true" + // will draw the staff lines. + bool gltLines = (staff->IsTabLuteGerman() && staffDef->GetLinesVisible() == BOOLEAN_true); + // For anything other than German lute tablature the default is @lines.visible="true" + bool visibleLines = (staffDef->GetLinesVisible() != BOOLEAN_false); + + // Nothing to do if both are false + if (!gltLines && !visibleLines) return; + int j, x1, x2, y1, y2; x1 = measure->GetDrawingX(); @@ -1309,7 +1318,7 @@ void View::DrawStaffLines(DeviceContext *dc, Staff *staff, StaffDef *staffDef, M // If German lute tablature the default is @lines.visible="false", but setting @lines.visible="true" // will draw the staff lines. // For anything other than German lute tablature the default is @lines.visible="true" - if (staff->IsTabLuteGerman() && staffDef->GetLinesVisible() != BOOLEAN_true) { + if (gltLines) { // German tablature has no staff, just a single base line // But internally we maintain the fiction of an invisible staff as a coordinate system SegmentedLine line(x1, x2); @@ -1317,7 +1326,8 @@ void View::DrawStaffLines(DeviceContext *dc, Staff *staff, StaffDef *staffDef, M y1 -= (m_doc->GetDrawingDoubleUnit(staff->m_drawingStaffSize) * staff->m_drawingLines) * 11 / 10; this->DrawHorizontalSegmentedLine(dc, y1, line, lineWidth / 2); } - else if (staffDef->GetLinesVisible() != BOOLEAN_false) { + // Normal staff lines + else { // draw staff lines for (j = 0; j < staff->m_drawingLines; ++j) { // Skewed lines - with Facs (neumes) only for now From 7f5ccbfae96e16e4f75d993dcbc7286b0af64b1e Mon Sep 17 00:00:00 2001 From: Laurent Pugin Date: Tue, 26 Nov 2024 13:20:35 +0100 Subject: [PATCH 15/15] Update src/view_page.cpp --- src/view_page.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/view_page.cpp b/src/view_page.cpp index e9d56bfbcba..304095385ab 100644 --- a/src/view_page.cpp +++ b/src/view_page.cpp @@ -1292,7 +1292,7 @@ void View::DrawStaffLines(DeviceContext *dc, Staff *staff, StaffDef *staffDef, M // If German lute tablature the default is @lines.visible="false", but setting @lines.visible="true" // will draw the staff lines. - bool gltLines = (staff->IsTabLuteGerman() && staffDef->GetLinesVisible() == BOOLEAN_true); + bool gltLines = (staff->IsTabLuteGerman() && staffDef->GetLinesVisible() != BOOLEAN_true); // For anything other than German lute tablature the default is @lines.visible="true" bool visibleLines = (staffDef->GetLinesVisible() != BOOLEAN_false);