From a30a66819301dbda320bb38b8046e79cfb211a6c Mon Sep 17 00:00:00 2001 From: vpereverzev Date: Mon, 24 Jan 2022 17:09:54 +0200 Subject: [PATCH] Added predefined, strongly typed elements for playing techniques --- src/engraving/layout/layoutmeasure.cpp | 8 +- src/engraving/layout/layoutsystem.cpp | 2 +- src/engraving/libmscore/chordrest.cpp | 1 + src/engraving/libmscore/edit.cpp | 5 + src/engraving/libmscore/engravingobject.cpp | 1 + src/engraving/libmscore/engravingobject.h | 5 +- src/engraving/libmscore/excerpt.cpp | 2 + src/engraving/libmscore/factory.cpp | 3 + src/engraving/libmscore/libmscore.cmake | 2 + src/engraving/libmscore/note.cpp | 1 + src/engraving/libmscore/paste.cpp | 3 +- .../libmscore/playtechannotation.cpp | 120 ++++++++++++++++++ src/engraving/libmscore/playtechannotation.h | 54 ++++++++ src/engraving/libmscore/property.cpp | 7 +- src/engraving/libmscore/property.h | 1 + src/engraving/libmscore/rest.cpp | 1 + src/engraving/libmscore/scorefile.cpp | 1 + src/engraving/libmscore/segment.cpp | 8 +- src/engraving/libmscore/select.cpp | 4 +- src/engraving/libmscore/types.h | 1 + src/engraving/property/propertyvalue.cpp | 2 + src/engraving/property/propertyvalue.h | 4 + src/engraving/rw/measurerw.cpp | 2 + src/engraving/rw/xmlwriter.cpp | 6 + src/engraving/types/types.h | 40 ++++++ src/engraving/types/typesconv.cpp | 26 ++++ src/engraving/types/typesconv.h | 3 + src/engraving/utests/element_tests.cpp | 1 + .../musicxml/internal/musicxml/exportxml.cpp | 4 +- src/notation/internal/notationinteraction.cpp | 2 + src/palette/internal/palettecreator.cpp | 72 +++++++---- 31 files changed, 356 insertions(+), 36 deletions(-) create mode 100644 src/engraving/libmscore/playtechannotation.cpp create mode 100644 src/engraving/libmscore/playtechannotation.h diff --git a/src/engraving/layout/layoutmeasure.cpp b/src/engraving/layout/layoutmeasure.cpp index 8537dcccb6..d473ea9c8a 100644 --- a/src/engraving/layout/layoutmeasure.cpp +++ b/src/engraving/layout/layoutmeasure.cpp @@ -337,7 +337,7 @@ void LayoutMeasure::createMMRest(const LayoutOptions& options, Score* score, Mea for (EngravingItem* e : underlyingSeg->annotations()) { // look at elements in underlying measure if (!(e->isRehearsalMark() || e->isTempoText() || e->isHarmony() || e->isStaffText() || e->isSystemText() - || e->isInstrumentChange())) { + || e->isPlayTechAnnotation() || e->isInstrumentChange())) { continue; } // try to find a match in mmr @@ -361,7 +361,7 @@ void LayoutMeasure::createMMRest(const LayoutOptions& options, Score* score, Mea for (EngravingItem* e : s->annotations()) { // look at elements in mmr if (!(e->isRehearsalMark() || e->isTempoText() || e->isHarmony() || e->isStaffText() || e->isSystemText() - || e->isInstrumentChange())) { + || e->isPlayTechAnnotation() || e->isInstrumentChange())) { continue; } // try to find a match in underlying measure @@ -400,7 +400,7 @@ static bool validMMRestMeasure(const LayoutContext& ctx, Measure* m) for (Segment* s = m->first(); s; s = s->next()) { for (EngravingItem* e : s->annotations()) { if (!(e->isRehearsalMark() || e->isTempoText() || e->isHarmony() || e->isStaffText() || e->isSystemText() - || e->isInstrumentChange())) { + || e->isPlayTechAnnotation() || e->isInstrumentChange())) { return false; } } @@ -505,7 +505,7 @@ static bool breakMultiMeasureRest(const LayoutContext& ctx, Measure* m) } if (e->isRehearsalMark() || e->isTempoText() - || ((e->isHarmony() || e->isStaffText() || e->isSystemText() || e->isInstrumentChange()) + || ((e->isHarmony() || e->isStaffText() || e->isSystemText() || e->isPlayTechAnnotation() || e->isInstrumentChange()) && (e->systemFlag() || ctx.score()->staff(e->staffIdx())->show()))) { return true; } diff --git a/src/engraving/layout/layoutsystem.cpp b/src/engraving/layout/layoutsystem.cpp index 7b46c573fe..eb2f1adcb8 100644 --- a/src/engraving/layout/layoutsystem.cpp +++ b/src/engraving/layout/layoutsystem.cpp @@ -1008,7 +1008,7 @@ void LayoutSystem::layoutSystemElements(const LayoutOptions& options, LayoutCont for (const Segment* s : sl) { for (EngravingItem* e : s->annotations()) { - if (e->isStaffText() || e->isSystemText() || e->isInstrumentChange()) { + if (e->isPlayTechAnnotation() || e->isStaffText() || e->isSystemText() || e->isInstrumentChange()) { e->layout(); } } diff --git a/src/engraving/libmscore/chordrest.cpp b/src/engraving/libmscore/chordrest.cpp index 4bf8d1992a..51ac45296f 100644 --- a/src/engraving/libmscore/chordrest.cpp +++ b/src/engraving/libmscore/chordrest.cpp @@ -507,6 +507,7 @@ EngravingItem* ChordRest::drop(EditData& data) case ElementType::TEXT: case ElementType::STAFF_TEXT: case ElementType::SYSTEM_TEXT: + case ElementType::PLAYTECH_ANNOTATION: case ElementType::STICKING: case ElementType::STAFF_STATE: // fall through diff --git a/src/engraving/libmscore/edit.cpp b/src/engraving/libmscore/edit.cpp index 911084e79d..df6dfa1704 100644 --- a/src/engraving/libmscore/edit.cpp +++ b/src/engraving/libmscore/edit.cpp @@ -2138,6 +2138,7 @@ void Score::cmdFlip() || e->isJump() || e->isMarker() || e->isStaffText() + || e->isPlayTechAnnotation() || e->isSticking() || e->isFingering() || e->isDynamic() @@ -5270,6 +5271,7 @@ void Score::undoAddElement(EngravingItem* element, bool ctrlModifier) && et != ElementType::DYNAMIC && et != ElementType::STAFF_TEXT && et != ElementType::SYSTEM_TEXT + && et != ElementType::PLAYTECH_ANNOTATION && et != ElementType::STICKING && et != ElementType::TREMOLO && et != ElementType::ARPEGGIO @@ -5330,6 +5332,7 @@ void Score::undoAddElement(EngravingItem* element, bool ctrlModifier) || element->isTremoloBar() || element->isDynamic() || element->isStaffText() + || element->isPlayTechAnnotation() || element->isSticking() || element->isFretDiagram() || element->isHarmony() @@ -5355,6 +5358,7 @@ void Score::undoAddElement(EngravingItem* element, bool ctrlModifier) // this should be same list excluded in cloneStaff() case ElementType::STAFF_TEXT: case ElementType::SYSTEM_TEXT: + case ElementType::PLAYTECH_ANNOTATION: case ElementType::FRET_DIAGRAM: case ElementType::HARMONY: case ElementType::FIGURED_BASS: @@ -5446,6 +5450,7 @@ void Score::undoAddElement(EngravingItem* element, bool ctrlModifier) || element->isTremoloBar() || element->isDynamic() || element->isStaffText() + || element->isPlayTechAnnotation() || element->isSticking() || element->isFretDiagram() || element->isFermata() diff --git a/src/engraving/libmscore/engravingobject.cpp b/src/engraving/libmscore/engravingobject.cpp index 1768358322..44d72f63ce 100644 --- a/src/engraving/libmscore/engravingobject.cpp +++ b/src/engraving/libmscore/engravingobject.cpp @@ -832,6 +832,7 @@ bool EngravingObject::isTextBase() const || type() == ElementType::JUMP || type() == ElementType::STAFF_TEXT || type() == ElementType::SYSTEM_TEXT + || type() == ElementType::PLAYTECH_ANNOTATION || type() == ElementType::REHEARSAL_MARK || type() == ElementType::INSTRUMENT_CHANGE || type() == ElementType::FIGURED_BASS diff --git a/src/engraving/libmscore/engravingobject.h b/src/engraving/libmscore/engravingobject.h index 15c4b24418..47134bdede 100644 --- a/src/engraving/libmscore/engravingobject.h +++ b/src/engraving/libmscore/engravingobject.h @@ -135,6 +135,7 @@ class StaffTypeChange; class MeasureBase; class Page; class SystemText; +class PlayTechAnnotation; class BracketItem; class Spanner; class SpannerSegment; @@ -383,6 +384,7 @@ public: CONVERT(MMRestRange, MMREST_RANGE) CONVERT(StaffText, STAFF_TEXT) CONVERT(SystemText, SYSTEM_TEXT) + CONVERT(PlayTechAnnotation, PLAYTECH_ANNOTATION) CONVERT(BracketItem, BRACKET_ITEM) CONVERT(Score, SCORE) CONVERT(Staff, STAFF) @@ -461,7 +463,7 @@ public: bool isStaffTextBase() const { - return isStaffText() || isSystemText(); + return isStaffText() || isSystemText() || isPlayTechAnnotation(); } }; @@ -625,6 +627,7 @@ CONVERT(Harmony) CONVERT(Volta) CONVERT(Jump) CONVERT(StaffText) +CONVERT(PlayTechAnnotation) CONVERT(Ottava) CONVERT(LayoutBreak) CONVERT(Segment) diff --git a/src/engraving/libmscore/excerpt.cpp b/src/engraving/libmscore/excerpt.cpp index aef529cd39..b3ed7f145f 100644 --- a/src/engraving/libmscore/excerpt.cpp +++ b/src/engraving/libmscore/excerpt.cpp @@ -1134,6 +1134,7 @@ void Excerpt::cloneStaff(Staff* srcStaff, Staff* dstStaff) // this should be same list excluded in Score::undoAddElement() case ElementType::STAFF_TEXT: case ElementType::SYSTEM_TEXT: + case ElementType::PLAYTECH_ANNOTATION: case ElementType::FRET_DIAGRAM: case ElementType::HARMONY: case ElementType::FIGURED_BASS: @@ -1378,6 +1379,7 @@ void Excerpt::cloneStaff2(Staff* srcStaff, Staff* dstStaff, const Fraction& star // this should be same list excluded in Score::undoAddElement() case ElementType::STAFF_TEXT: case ElementType::SYSTEM_TEXT: + case ElementType::PLAYTECH_ANNOTATION: case ElementType::FRET_DIAGRAM: case ElementType::HARMONY: case ElementType::FIGURED_BASS: diff --git a/src/engraving/libmscore/factory.cpp b/src/engraving/libmscore/factory.cpp index e7409f4664..1837bb7cac 100644 --- a/src/engraving/libmscore/factory.cpp +++ b/src/engraving/libmscore/factory.cpp @@ -60,6 +60,7 @@ #include "instrchange.h" #include "stafftext.h" #include "systemtext.h" +#include "playtechannotation.h" #include "rehearsalmark.h" #include "stafftypechange.h" #include "tremolo.h" @@ -148,6 +149,7 @@ static const ElementName elementNames[] = { { ElementType::TEMPO_TEXT, "Tempo", QT_TRANSLATE_NOOP("elementName", "Tempo") }, { ElementType::STAFF_TEXT, "StaffText", QT_TRANSLATE_NOOP("elementName", "Staff text") }, { ElementType::SYSTEM_TEXT, "SystemText", QT_TRANSLATE_NOOP("elementName", "System text") }, + { ElementType::PLAYTECH_ANNOTATION, "PlayTechAnnotation", QT_TRANSLATE_NOOP("elementName", "Playing technique annotation") }, { ElementType::REHEARSAL_MARK, "RehearsalMark", QT_TRANSLATE_NOOP("elementName", "Rehearsal mark") }, { ElementType::INSTRUMENT_CHANGE, "InstrumentChange", QT_TRANSLATE_NOOP("elementName", "Instrument change") }, { ElementType::STAFFTYPE_CHANGE, "StaffTypeChange", QT_TRANSLATE_NOOP("elementName", "Staff type change") }, @@ -259,6 +261,7 @@ EngravingItem* Factory::doCreateItem(ElementType type, EngravingItem* parent) case ElementType::MMREST_RANGE: return new MMRestRange(parent->isMeasure() ? toMeasure(parent) : dummy->measure()); case ElementType::INSTRUMENT_NAME: return new InstrumentName(parent->isSystem() ? toSystem(parent) : dummy->system()); case ElementType::STAFF_TEXT: return new StaffText(parent->isSegment() ? toSegment(parent) : dummy->segment()); + case ElementType::PLAYTECH_ANNOTATION: return new PlayTechAnnotation(parent->isSegment() ? toSegment(parent) : dummy->segment()); case ElementType::SYSTEM_TEXT: return new SystemText(parent->isSegment() ? toSegment(parent) : dummy->segment()); case ElementType::REHEARSAL_MARK: return new RehearsalMark(parent->isSegment() ? toSegment(parent) : dummy->segment()); case ElementType::INSTRUMENT_CHANGE: return new InstrumentChange(parent); diff --git a/src/engraving/libmscore/libmscore.cmake b/src/engraving/libmscore/libmscore.cmake index ca7b4d33a3..4360a2f102 100644 --- a/src/engraving/libmscore/libmscore.cmake +++ b/src/engraving/libmscore/libmscore.cmake @@ -339,4 +339,6 @@ set(LIBMSCORE_SRC ${CMAKE_CURRENT_LIST_DIR}/vibrato.h ${CMAKE_CURRENT_LIST_DIR}/volta.cpp ${CMAKE_CURRENT_LIST_DIR}/volta.h + ${CMAKE_CURRENT_LIST_DIR}/playtechannotation.cpp + ${CMAKE_CURRENT_LIST_DIR}/playtechannotation.h ) diff --git a/src/engraving/libmscore/note.cpp b/src/engraving/libmscore/note.cpp index 71a143df88..922691756e 100644 --- a/src/engraving/libmscore/note.cpp +++ b/src/engraving/libmscore/note.cpp @@ -1717,6 +1717,7 @@ bool Note::acceptDrop(EditData& data) const || (type == ElementType::TIMESIG) || (type == ElementType::BAR_LINE) || (type == ElementType::STAFF_TEXT) + || (type == ElementType::PLAYTECH_ANNOTATION) || (type == ElementType::SYSTEM_TEXT) || (type == ElementType::STICKING) || (type == ElementType::TEMPO_TEXT) diff --git a/src/engraving/libmscore/paste.cpp b/src/engraving/libmscore/paste.cpp index 20b0561003..dbc6d7d3e3 100644 --- a/src/engraving/libmscore/paste.cpp +++ b/src/engraving/libmscore/paste.cpp @@ -392,6 +392,7 @@ bool Score::pasteStaff(XmlReader& e, Segment* dst, int dstStaff, Fraction scale) || tag == "Image" || tag == "Text" || tag == "StaffText" + || tag == "PlayTechAnnotation" || tag == "TempoText" || tag == "FiguredBass" || tag == "Sticking" @@ -909,7 +910,7 @@ void Score::pasteSymbols(XmlReader& e, ChordRest* dst) } else { undoAddElement(el); } - } else if (tag == "StaffText" || tag == "Sticking") { + } else if (tag == "StaffText" || tag == "PlayTechAnnotation" || tag == "Sticking") { EngravingItem* el = Factory::createItemByName(tag, this->dummy()); el->read(e); el->setTrack(destTrack); diff --git a/src/engraving/libmscore/playtechannotation.cpp b/src/engraving/libmscore/playtechannotation.cpp new file mode 100644 index 0000000000..33c2c058a1 --- /dev/null +++ b/src/engraving/libmscore/playtechannotation.cpp @@ -0,0 +1,120 @@ +/* + * SPDX-License-Identifier: GPL-3.0-only + * MuseScore-CLA-applies + * + * MuseScore + * Music Composition & Notation + * + * Copyright (C) 2021 MuseScore BVBA and others + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "playtechannotation.h" + +#include "rw/xml.h" +#include "types/typesconv.h" +#include "segment.h" + +using namespace mu::engraving; +using namespace Ms; + +static const ElementStyle annotationStyle { + { Sid::staffTextPlacement, Pid::PLACEMENT }, + { Sid::staffTextMinDistance, Pid::MIN_DISTANCE }, +}; + +PlayTechAnnotation::PlayTechAnnotation(Segment* parent, PlayingTechniqueType techniqueType, TextStyleType tid) + : StaffTextBase(ElementType::PLAYTECH_ANNOTATION, parent, tid, ElementFlag::MOVABLE | ElementFlag::ON_STAFF), + m_techniqueType(techniqueType) +{ + initElementStyle(&annotationStyle); +} + +void PlayTechAnnotation::setTechniqueType(const PlayingTechniqueType techniqueType) +{ + m_techniqueType = techniqueType; +} + +PlayTechAnnotation* PlayTechAnnotation::clone() const +{ + return new PlayTechAnnotation(*this); +} + +void PlayTechAnnotation::layout() +{ + StaffTextBase::layout(); + autoplaceSegmentElement(); +} + +void PlayTechAnnotation::write(XmlWriter& writer) const +{ + writer.startObject(this); + writeProperty(writer, Pid::PLAY_TECH_TYPE); + StaffTextBase::writeProperties(writer); + writer.endObject(); +} + +void PlayTechAnnotation::read(XmlReader& reader) +{ + while (reader.readNextStartElement()) { + const QStringRef& tag(reader.name()); + + if (readProperty(tag, reader, Pid::PLAY_TECH_TYPE)) { + continue; + } + + if (!StaffTextBase::readProperties(reader)) { + reader.unknown(); + } + } +} + +PropertyValue PlayTechAnnotation::getProperty(Pid id) const +{ + switch (id) { + case Pid::PLAY_TECH_TYPE: + return m_techniqueType; + default: + return StaffTextBase::getProperty(id); + } +} + +bool PlayTechAnnotation::setProperty(Pid propertyId, const mu::engraving::PropertyValue& val) +{ + switch (propertyId) { + case Pid::PLAY_TECH_TYPE: + m_techniqueType = PlayingTechniqueType(val.toInt()); + break; + default: + if (!StaffTextBase::setProperty(propertyId, val)) { + return false; + } + break; + } + + triggerLayout(); + return true; +} + +PropertyValue PlayTechAnnotation::propertyDefault(Pid id) const +{ + switch (id) { + case Pid::TEXT_STYLE: + return TextStyleType::STAFF; + case Pid::PLAY_TECH_TYPE: + return PlayingTechniqueType::Natural; + default: + return StaffTextBase::propertyDefault(id); + } +} diff --git a/src/engraving/libmscore/playtechannotation.h b/src/engraving/libmscore/playtechannotation.h new file mode 100644 index 0000000000..a8874fd318 --- /dev/null +++ b/src/engraving/libmscore/playtechannotation.h @@ -0,0 +1,54 @@ +/* + * SPDX-License-Identifier: GPL-3.0-only + * MuseScore-CLA-applies + * + * MuseScore + * Music Composition & Notation + * + * Copyright (C) 2021 MuseScore BVBA and others + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef MU_ENGRAVING_PLAYINGTECHNIQUEANNOTATION_H +#define MU_ENGRAVING_PLAYINGTECHNIQUEANNOTATION_H + +#include "stafftextbase.h" +#include "types/types.h" + +namespace Ms { +class PlayTechAnnotation final : public StaffTextBase +{ +public: + PlayTechAnnotation(Segment* parent = nullptr, PlayingTechniqueType techniqueType = PlayingTechniqueType::Natural, + TextStyleType tid = TextStyleType::STAFF); + + PlayingTechniqueType techniqueType() const; + void setTechniqueType(const PlayingTechniqueType techniqueType); + + PlayTechAnnotation* clone() const override; + void layout() override; + +private: + void write(XmlWriter& writer) const override; + void read(XmlReader& reader) override; + + mu::engraving::PropertyValue getProperty(Pid id) const override; + bool setProperty(Pid propertyId, const mu::engraving::PropertyValue& val) override; + mu::engraving::PropertyValue propertyDefault(Pid id) const override; + + PlayingTechniqueType m_techniqueType = PlayingTechniqueType::Undefined; +}; +} + +#endif // MU_ENGRAVING_PLAYINGTECHNIQUEANNOTATION_H diff --git a/src/engraving/libmscore/property.cpp b/src/engraving/libmscore/property.cpp index 5b25d20c5b..529835cf84 100644 --- a/src/engraving/libmscore/property.cpp +++ b/src/engraving/libmscore/property.cpp @@ -383,10 +383,12 @@ static constexpr PropertyMetaData propertyList[] = { { Pid::START_WITH_MEASURE_ONE, true, "startWithMeasureOne", P_TYPE::BOOL, DUMMY_QT_TR_NOOP("propertyName", "start with measure one") }, { Pid::FIRST_SYSTEM_INDENTATION,true, "firstSystemIndentation",P_TYPE::BOOL, DUMMY_QT_TR_NOOP("propertyName", "first system indentation") }, - { Pid::PATH, false, "path", P_TYPE::DRAW_PATH, DUMMY_QT_TR_NOOP("propertyName", "path") }, + { Pid::PATH, false, "path", P_TYPE::DRAW_PATH, DUMMY_QT_TR_NOOP("propertyName", "path") }, { Pid::PREFER_SHARP_FLAT, true, "preferSharpFlat", P_TYPE::INT, DUMMY_QT_TR_NOOP("propertyName", "prefer sharps or flats") }, + { Pid::PLAY_TECH_TYPE, true, "playTechType", P_TYPE::PLAYTECH_TYPE, DUMMY_QT_TR_NOOP("propertyName", "playing technique type") }, + { Pid::END, false, "++end++", P_TYPE::INT, DUMMY_QT_TR_NOOP("propertyName", "") } }; /* *INDENT-ON* */ @@ -569,6 +571,9 @@ PropertyValue readProperty(Pid id, XmlReader& e) case P_TYPE::DURATION_TYPE_WITH_DOTS: case P_TYPE::INT_LIST: return PropertyValue(); + + case P_TYPE::PLAYTECH_TYPE: + return PropertyValue(TConv::fromXml(e.readElementText(), PlayingTechniqueType::Natural)); default: qFatal("unhandled PID type"); break; diff --git a/src/engraving/libmscore/property.h b/src/engraving/libmscore/property.h index fcdc46d0f5..a12693407e 100644 --- a/src/engraving/libmscore/property.h +++ b/src/engraving/libmscore/property.h @@ -388,6 +388,7 @@ enum class Pid { PATH, // for ChordLine to make its shape changes undoable PREFER_SHARP_FLAT, + PLAY_TECH_TYPE, END }; diff --git a/src/engraving/libmscore/rest.cpp b/src/engraving/libmscore/rest.cpp index 4d6532a2ae..0dcd894d51 100644 --- a/src/engraving/libmscore/rest.cpp +++ b/src/engraving/libmscore/rest.cpp @@ -186,6 +186,7 @@ bool Rest::acceptDrop(EditData& data) const || (type == ElementType::TIMESIG) || (type == ElementType::SYSTEM_TEXT) || (type == ElementType::STAFF_TEXT) + || (type == ElementType::PLAYTECH_ANNOTATION) || (type == ElementType::BAR_LINE) || (type == ElementType::BREATH) || (type == ElementType::CHORD) diff --git a/src/engraving/libmscore/scorefile.cpp b/src/engraving/libmscore/scorefile.cpp index 458c8a62ed..c15e26331e 100644 --- a/src/engraving/libmscore/scorefile.cpp +++ b/src/engraving/libmscore/scorefile.cpp @@ -565,6 +565,7 @@ void Score::writeSegments(XmlWriter& xml, int strack, int etrack, ElementType et = e1->type(); if ((et == ElementType::REHEARSAL_MARK) || (et == ElementType::SYSTEM_TEXT) + || (et == ElementType::PLAYTECH_ANNOTATION) || (et == ElementType::JUMP) || (et == ElementType::MARKER) || (et == ElementType::TEMPO_TEXT) diff --git a/src/engraving/libmscore/segment.cpp b/src/engraving/libmscore/segment.cpp index 7e1a9955af..001ee68943 100644 --- a/src/engraving/libmscore/segment.cpp +++ b/src/engraving/libmscore/segment.cpp @@ -576,6 +576,7 @@ void Segment::add(EngravingItem* el) case ElementType::FRET_DIAGRAM: case ElementType::STAFF_TEXT: case ElementType::SYSTEM_TEXT: + case ElementType::PLAYTECH_ANNOTATION: case ElementType::REHEARSAL_MARK: case ElementType::MARKER: case ElementType::IMAGE: @@ -740,6 +741,7 @@ void Segment::remove(EngravingItem* el) case ElementType::REHEARSAL_MARK: case ElementType::STAFF_TEXT: case ElementType::SYSTEM_TEXT: + case ElementType::PLAYTECH_ANNOTATION: case ElementType::SYMBOL: case ElementType::TAB_DURATION_SYMBOL: case ElementType::TEMPO_TEXT: @@ -860,6 +862,7 @@ void Segment::sortStaves(QList& dst) if (!e->systemFlag() || (et == ElementType::REHEARSAL_MARK) || (et == ElementType::SYSTEM_TEXT) + || (et == ElementType::PLAYTECH_ANNOTATION) || (et == ElementType::JUMP) || (et == ElementType::MARKER) || (et == ElementType::TEMPO_TEXT) @@ -1757,6 +1760,7 @@ EngravingItem* Segment::nextElement(int activeStaff) case ElementType::TEMPO_TEXT: case ElementType::STAFF_TEXT: case ElementType::SYSTEM_TEXT: + case ElementType::PLAYTECH_ANNOTATION: case ElementType::REHEARSAL_MARK: case ElementType::MARKER: case ElementType::IMAGE: @@ -1898,6 +1902,7 @@ EngravingItem* Segment::prevElement(int activeStaff) case ElementType::TEMPO_TEXT: case ElementType::STAFF_TEXT: case ElementType::SYSTEM_TEXT: + case ElementType::PLAYTECH_ANNOTATION: case ElementType::REHEARSAL_MARK: case ElementType::MARKER: case ElementType::IMAGE: @@ -2264,7 +2269,8 @@ void Segment::createShape(int staffIdx) && !e->isInstrumentChange() && !e->isArticulation() && !e->isFermata() - && !e->isStaffText()) { + && !e->isStaffText() + && !e->isPlayTechAnnotation()) { // annotations added here are candidates for collision detection // lyrics, ... s.add(e->shape().translated(e->pos())); diff --git a/src/engraving/libmscore/select.cpp b/src/engraving/libmscore/select.cpp index fa3a39f08a..cde56f7776 100644 --- a/src/engraving/libmscore/select.cpp +++ b/src/engraving/libmscore/select.cpp @@ -70,6 +70,7 @@ #include "stafftext.h" #include "sticking.h" #include "linkedobjects.h" +#include "playtechannotation.h" #include "log.h" @@ -1003,8 +1004,9 @@ QByteArray Selection::symbolListMimeData() const break; } continue; + case ElementType::PLAYTECH_ANNOTATION: case ElementType::STAFF_TEXT: - seg = toStaffText(e)->segment(); + seg = toStaffTextBase(e)->segment(); break; case ElementType::STICKING: seg = toSticking(e)->segment(); diff --git a/src/engraving/libmscore/types.h b/src/engraving/libmscore/types.h index fc202713d7..ceef079105 100644 --- a/src/engraving/libmscore/types.h +++ b/src/engraving/libmscore/types.h @@ -91,6 +91,7 @@ enum class ElementType { TEMPO_TEXT, STAFF_TEXT, SYSTEM_TEXT, + PLAYTECH_ANNOTATION, REHEARSAL_MARK, INSTRUMENT_CHANGE, STAFFTYPE_CHANGE, diff --git a/src/engraving/property/propertyvalue.cpp b/src/engraving/property/propertyvalue.cpp index 176cfc5871..ba1003343b 100644 --- a/src/engraving/property/propertyvalue.cpp +++ b/src/engraving/property/propertyvalue.cpp @@ -152,6 +152,7 @@ QVariant PropertyValue::toQVariant() const case P_TYPE::HOOK_TYPE: return static_cast(value()); case P_TYPE::KEY_MODE: return static_cast(value()); case P_TYPE::TEXT_STYLE: return static_cast(value()); + case P_TYPE::PLAYTECH_TYPE: return static_cast(value()); // Other case P_TYPE::GROUPS: { @@ -238,6 +239,7 @@ PropertyValue PropertyValue::fromQVariant(const QVariant& v, P_TYPE type) case P_TYPE::HOOK_TYPE: return PropertyValue(HookType(v.toInt())); case P_TYPE::KEY_MODE: return PropertyValue(KeyMode(v.toInt())); case P_TYPE::TEXT_STYLE: return PropertyValue(TextStyleType(v.toInt())); + case P_TYPE::PLAYTECH_TYPE: return PropertyValue(PlayingTechniqueType(v.toInt())); // Other case P_TYPE::GROUPS: { diff --git a/src/engraving/property/propertyvalue.h b/src/engraving/property/propertyvalue.h index bc41adf20d..a0b811fab1 100644 --- a/src/engraving/property/propertyvalue.h +++ b/src/engraving/property/propertyvalue.h @@ -96,6 +96,7 @@ enum class P_TYPE { HOOK_TYPE, KEY_MODE, TEXT_STYLE, + PLAYTECH_TYPE, // Other GROUPS, @@ -234,6 +235,9 @@ public: PropertyValue(TextStyleType v) : m_type(P_TYPE::TEXT_STYLE), m_data(make_data(v)) {} + PropertyValue(PlayingTechniqueType v) + : m_type(P_TYPE::PLAYTECH_TYPE), m_data(make_data(v)) {} + // Other PropertyValue(const GroupNodes& v) : m_type(P_TYPE::GROUPS), m_data(make_data(v)) {} diff --git a/src/engraving/rw/measurerw.cpp b/src/engraving/rw/measurerw.cpp index f20e4fd8ca..d3a50ca046 100644 --- a/src/engraving/rw/measurerw.cpp +++ b/src/engraving/rw/measurerw.cpp @@ -436,6 +436,7 @@ void MeasureRW::readVoice(Measure* measure, XmlReader& e, ReadContext& ctx, int || tag == "StaffText" || tag == "Sticking" || tag == "SystemText" + || tag == "PlayTechAnnotation" || tag == "RehearsalMark" || tag == "InstrumentChange" || tag == "StaffState" @@ -610,6 +611,7 @@ void MeasureRW::writeMeasure(const Ms::Measure* measure, XmlWriter& xml, int sta ElementType et = e->type(); if ((et == ElementType::REHEARSAL_MARK) || (et == ElementType::SYSTEM_TEXT) + || (et == ElementType::PLAYTECH_ANNOTATION) || (et == ElementType::JUMP) || (et == ElementType::MARKER) || (et == ElementType::TEMPO_TEXT) diff --git a/src/engraving/rw/xmlwriter.cpp b/src/engraving/rw/xmlwriter.cpp index 13e1aac6d7..8718233fca 100644 --- a/src/engraving/rw/xmlwriter.cpp +++ b/src/engraving/rw/xmlwriter.cpp @@ -445,6 +445,12 @@ void XmlWriter::tagProperty(const char* name, P_TYPE type, const PropertyValue& *this << TConv::toXml(data.value()); *this << "\n"; } break; + case P_TYPE::PLAYTECH_TYPE: { + putLevel(); + *this << "<" << name << ">"; + *this << TConv::toXml(data.value()); + *this << "\n"; + } break; default: { UNREACHABLE; //! TODO } diff --git a/src/engraving/types/types.h b/src/engraving/types/types.h index acfda269f9..da1239a4ee 100644 --- a/src/engraving/types/types.h +++ b/src/engraving/types/types.h @@ -489,6 +489,44 @@ enum class TextStyleType { IGNORED_TYPES // used for types no longer relevant (mainly Figured bass text type) }; +enum class AnnotationCategory { + Undefined = -1, + TempoAnnotation, + PlayingAnnotation, + Other, +}; + +enum class PlayingTechniqueType { + Undefined = -1, + Natural, + Pizzicato, + Open, + Mute, + Tremolo, + Detache, + Martele, + ColLegno, + SulPonticello, + SulTasto, + Vibrato, + Legato +}; + +enum class TempoTechniqueType { + Undefined = -1, + Accelerando, + Allargando, + Calando, + Lentando, + Morendo, + Precipitando, + Rallentando, + Ritardando, + Smorzando, + Sostenuto, + Stringendo +}; + // P_TYPE::CHANGE_METHOD enum class ChangeMethod : signed char { NORMAL, @@ -542,6 +580,8 @@ using ChangeDirection = mu::engraving::ChangeDirection; using AccidentalRole = mu::engraving::AccidentalRole; using DurationType = mu::engraving::DurationType; using DurationTypeWithDots = mu::engraving::DurationTypeWithDots; +using PlayingTechniqueType = mu::engraving::PlayingTechniqueType; +using TempoTechniqueType = mu::engraving::TempoTechniqueType; } #endif // MU_ENGRAVING_TYPES_H diff --git a/src/engraving/types/typesconv.cpp b/src/engraving/types/typesconv.cpp index 6d9fc7f7dd..a8b3368e59 100644 --- a/src/engraving/types/typesconv.cpp +++ b/src/engraving/types/typesconv.cpp @@ -807,3 +807,29 @@ DurationType TConv::fromXml(const QString& tag, DurationType def) { return findTypeByXmlTag(DURATION_TYPES, tag, def); } + +static const std::vector > PLAY_TECH_TYPES = { + { PlayingTechniqueType::Undefined, "undefined" }, + { PlayingTechniqueType::Natural, "natural" }, + { PlayingTechniqueType::Pizzicato, "pizzicato" }, + { PlayingTechniqueType::Open, "open" }, + { PlayingTechniqueType::Mute, "mute" }, + { PlayingTechniqueType::Tremolo, "tremolo" }, + { PlayingTechniqueType::Detache, "detache" }, + { PlayingTechniqueType::Martele, "martele" }, + { PlayingTechniqueType::ColLegno, "col_legno" }, + { PlayingTechniqueType::SulPonticello, "sul_ponticello" }, + { PlayingTechniqueType::SulTasto, "sul_tasto" }, + { PlayingTechniqueType::Vibrato, "vibrato" }, + { PlayingTechniqueType::Legato, "legato" } +}; + +QString TConv::toXml(PlayingTechniqueType v) +{ + return findXmlTagByType(PLAY_TECH_TYPES, v); +} + +PlayingTechniqueType TConv::fromXml(const QString& tag, PlayingTechniqueType def) +{ + return findTypeByXmlTag(PLAY_TECH_TYPES, tag, def); +} diff --git a/src/engraving/types/typesconv.h b/src/engraving/types/typesconv.h index 91f8625ff8..b9c8918d0c 100644 --- a/src/engraving/types/typesconv.h +++ b/src/engraving/types/typesconv.h @@ -100,6 +100,9 @@ public: static QString toUserName(DurationType v); static QString toXml(DurationType v); static DurationType fromXml(const QString& tag, DurationType def); + + static QString toXml(PlayingTechniqueType v); + static PlayingTechniqueType fromXml(const QString& tag, PlayingTechniqueType def); }; } diff --git a/src/engraving/utests/element_tests.cpp b/src/engraving/utests/element_tests.cpp index 4c39890cb1..8f1c7bde0a 100644 --- a/src/engraving/utests/element_tests.cpp +++ b/src/engraving/utests/element_tests.cpp @@ -60,6 +60,7 @@ TEST_F(ElementTests, DISABLED_testIds) ElementType::TEXT, ElementType::INSTRUMENT_NAME, ElementType::STAFF_TEXT, + ElementType::PLAYTECH_ANNOTATION, ElementType::REHEARSAL_MARK, ElementType::INSTRUMENT_CHANGE, ElementType::NOTEHEAD, diff --git a/src/importexport/musicxml/internal/musicxml/exportxml.cpp b/src/importexport/musicxml/internal/musicxml/exportxml.cpp index 909770ff43..a2310caa12 100644 --- a/src/importexport/musicxml/internal/musicxml/exportxml.cpp +++ b/src/importexport/musicxml/internal/musicxml/exportxml.cpp @@ -3978,6 +3978,7 @@ static void directionTag(XmlWriter& xml, Attributes& attr, EngravingItem const* || el->type() == ElementType::INSTRUMENT_CHANGE || el->type() == ElementType::REHEARSAL_MARK || el->type() == ElementType::STAFF_TEXT + || el->type() == ElementType::PLAYTECH_ANNOTATION || el->type() == ElementType::SYMBOL || el->type() == ElementType::TEXT) { // handle other elements attached (e.g. via Segment / Measure) to a system @@ -5562,7 +5563,8 @@ static bool commonAnnotations(ExportMusicXml* exp, const EngravingItem* e, int s exp->symbol(toSymbol(e), sstaff); } else if (e->isTempoText()) { exp->tempoText(toTempoText(e), sstaff); - } else if (e->isStaffText() || e->isSystemText() || e->isText() || (e->isInstrumentChange() && e->visible())) { + } else if (e->isPlayTechAnnotation() || e->isStaffText() || e->isSystemText() || e->isText() + || (e->isInstrumentChange() && e->visible())) { exp->words(toTextBase(e), sstaff); } else if (e->isDynamic()) { exp->dynamic(toDynamic(e), sstaff); diff --git a/src/notation/internal/notationinteraction.cpp b/src/notation/internal/notationinteraction.cpp index 9b347df0e4..f511dd5cc6 100644 --- a/src/notation/internal/notationinteraction.cpp +++ b/src/notation/internal/notationinteraction.cpp @@ -983,6 +983,7 @@ bool NotationInteraction::isDropAccepted(const PointF& pos, Qt::KeyboardModifier case ElementType::TEMPO_TEXT: case ElementType::STAFF_TEXT: case ElementType::SYSTEM_TEXT: + case ElementType::PLAYTECH_ANNOTATION: case ElementType::NOTEHEAD: case ElementType::TREMOLO: case ElementType::LAYOUT_BREAK: @@ -1136,6 +1137,7 @@ bool NotationInteraction::drop(const PointF& pos, Qt::KeyboardModifiers modifier case ElementType::TEMPO_TEXT: case ElementType::STAFF_TEXT: case ElementType::SYSTEM_TEXT: + case ElementType::PLAYTECH_ANNOTATION: case ElementType::NOTEHEAD: case ElementType::TREMOLO: case ElementType::LAYOUT_BREAK: diff --git a/src/palette/internal/palettecreator.cpp b/src/palette/internal/palettecreator.cpp index 7d019c055a..e24b681fca 100644 --- a/src/palette/internal/palettecreator.cpp +++ b/src/palette/internal/palettecreator.cpp @@ -71,6 +71,7 @@ #include "libmscore/spacer.h" #include "libmscore/staffstate.h" #include "libmscore/stafftext.h" +#include "libmscore/playtechannotation.h" #include "libmscore/stafftypechange.h" #include "libmscore/systemtext.h" #include "libmscore/tempo.h" @@ -103,6 +104,7 @@ MAKE_ELEMENT(Hairpin, score->dummy()->segment()) MAKE_ELEMENT(SystemText, score->dummy()->segment()) MAKE_ELEMENT(TempoText, score->dummy()->segment()) MAKE_ELEMENT(StaffText, score->dummy()->segment()) +MAKE_ELEMENT(PlayTechAnnotation, score->dummy()->segment()) MAKE_ELEMENT(RehearsalMark, score->dummy()->segment()) MAKE_ELEMENT(Jump, score->dummy()->measure()) @@ -1293,48 +1295,68 @@ PalettePtr PaletteCreator::newTextPalette(bool defaultPalette) sp->appendElement(meaNum, QT_TRANSLATE_NOOP("palette", "Measure number"))->setElementTranslated(true); if (!defaultPalette) { - auto pz = makeElement(gpaletteScore); + auto pz = makeElement(gpaletteScore); pz->setXmlText(QT_TRANSLATE_NOOP("palette", "pizz.")); - pz->setChannelName(0, "pizzicato"); - pz->setChannelName(1, "pizzicato"); - pz->setChannelName(2, "pizzicato"); - pz->setChannelName(3, "pizzicato"); + pz->setTechniqueType(PlayingTechniqueType::Pizzicato); sp->appendElement(pz, QT_TRANSLATE_NOOP("palette", "Pizzicato"))->setElementTranslated(true); - auto ar = makeElement(gpaletteScore); + auto ar = makeElement(gpaletteScore); ar->setXmlText(QT_TRANSLATE_NOOP("palette", "arco")); - ar->setChannelName(0, "arco"); - ar->setChannelName(1, "arco"); - ar->setChannelName(2, "arco"); - ar->setChannelName(3, "arco"); + ar->setTechniqueType(PlayingTechniqueType::Natural); sp->appendElement(ar, QT_TRANSLATE_NOOP("palette", "Arco"))->setElementTranslated(true); - auto tm = makeElement(gpaletteScore); + auto detache = makeElement(gpaletteScore); + detache->setXmlText(QT_TRANSLATE_NOOP("palette", "detache")); + detache->setTechniqueType(PlayingTechniqueType::Detache); + sp->appendElement(detache, QT_TRANSLATE_NOOP("palette", "Detache"))->setElementTranslated(true); + + auto martele = makeElement(gpaletteScore); + martele->setXmlText(QT_TRANSLATE_NOOP("palette", "martele")); + martele->setTechniqueType(PlayingTechniqueType::Martele); + sp->appendElement(martele, QT_TRANSLATE_NOOP("palette", "Martele"))->setElementTranslated(true); + + auto colLegno = makeElement(gpaletteScore); + colLegno->setXmlText(QT_TRANSLATE_NOOP("palette", "col legno")); + colLegno->setTechniqueType(PlayingTechniqueType::ColLegno); + sp->appendElement(colLegno, QT_TRANSLATE_NOOP("palette", "Martele"))->setElementTranslated(true); + + auto sulPont = makeElement(gpaletteScore); + sulPont->setXmlText(QT_TRANSLATE_NOOP("palette", "sul pont.")); + sulPont->setTechniqueType(PlayingTechniqueType::SulPonticello); + sp->appendElement(sulPont, QT_TRANSLATE_NOOP("palette", "Sul Ponticello"))->setElementTranslated(true); + + auto sulTasto = makeElement(gpaletteScore); + sulTasto->setXmlText(QT_TRANSLATE_NOOP("palette", "sul tasto")); + sulTasto->setTechniqueType(PlayingTechniqueType::SulTasto); + sp->appendElement(sulTasto, QT_TRANSLATE_NOOP("palette", "Sul Tasto"))->setElementTranslated(true); + + auto vibrato = makeElement(gpaletteScore); + vibrato->setXmlText(QT_TRANSLATE_NOOP("palette", "vibrato")); + vibrato->setTechniqueType(PlayingTechniqueType::Vibrato); + sp->appendElement(vibrato, QT_TRANSLATE_NOOP("palette", "Vibrato"))->setElementTranslated(true); + + auto legato = makeElement(gpaletteScore); + legato->setXmlText(QT_TRANSLATE_NOOP("palette", "legato")); + legato->setTechniqueType(PlayingTechniqueType::Legato); + sp->appendElement(legato, QT_TRANSLATE_NOOP("palette", "Legato"))->setElementTranslated(true); + + auto tm = makeElement(gpaletteScore); tm->setTextStyleType(TextStyleType::EXPRESSION); tm->setXmlText(QT_TRANSLATE_NOOP("palette", "tremolo")); - tm->setChannelName(0, "tremolo"); - tm->setChannelName(1, "tremolo"); - tm->setChannelName(2, "tremolo"); - tm->setChannelName(3, "tremolo"); + tm->setTechniqueType(PlayingTechniqueType::Tremolo); sp->appendElement(tm, QT_TRANSLATE_NOOP("palette", "Tremolo"))->setElementTranslated(true); - auto mu = makeElement(gpaletteScore); + auto mu = makeElement(gpaletteScore); /*: For brass and plucked string instruments: staff text that prescribes to use mute while playing, see https://en.wikipedia.org/wiki/Mute_(music) */ mu->setXmlText(QT_TRANSLATE_NOOP("palette", "mute")); - mu->setChannelName(0, "mute"); - mu->setChannelName(1, "mute"); - mu->setChannelName(2, "mute"); - mu->setChannelName(3, "mute"); + mu->setTechniqueType(PlayingTechniqueType::Mute); /*: For brass and plucked string instruments: staff text that prescribes to use mute while playing, see https://en.wikipedia.org/wiki/Mute_(music) */ sp->appendElement(mu, QT_TRANSLATE_NOOP("palette", "Mute"))->setElementTranslated(true); - auto no = makeElement(gpaletteScore); + auto no = makeElement(gpaletteScore); /*: For brass and plucked string instruments: staff text that prescribes to play without mute, see https://en.wikipedia.org/wiki/Mute_(music) */ no->setXmlText(QT_TRANSLATE_NOOP("palette", "open")); - no->setChannelName(0, "open"); - no->setChannelName(1, "open"); - no->setChannelName(2, "open"); - no->setChannelName(3, "open"); + no->setTechniqueType(PlayingTechniqueType::Open); /*: For brass and plucked string instruments: staff text that prescribes to play without mute, see https://en.wikipedia.org/wiki/Mute_(music) */ sp->appendElement(no, QT_TRANSLATE_NOOP("palette", "Open"))->setElementTranslated(true);