From 0f72a893f601fbd73f1220b39d349bf464f954a1 Mon Sep 17 00:00:00 2001 From: pereverzev_v Date: Fri, 14 Oct 2022 22:44:54 +0300 Subject: [PATCH] Fixed #12869 --- src/engraving/libmscore/spannermap.cpp | 79 ++++++++++++++++--- src/engraving/libmscore/spannermap.h | 10 ++- .../metaparsers/chordarticulationsparser.cpp | 8 +- .../internal/spannersmetaparser.cpp | 35 ++++++-- .../metaparsers/internal/spannersmetaparser.h | 5 +- 5 files changed, 117 insertions(+), 20 deletions(-) diff --git a/src/engraving/libmscore/spannermap.cpp b/src/engraving/libmscore/spannermap.cpp index a2e31e3b82bd9..a23c035b641fc 100644 --- a/src/engraving/libmscore/spannermap.cpp +++ b/src/engraving/libmscore/spannermap.cpp @@ -22,6 +22,7 @@ #include "spannermap.h" #include "spanner.h" +#include "part.h" #include "log.h" @@ -45,11 +46,13 @@ SpannerMap::SpannerMap() void SpannerMap::update() const { - std::vector > intervals; - for (auto i : *this) { - intervals.push_back(interval_tree::Interval(i.second->tick().ticks(), i.second->tick2().ticks(), i.second)); - } - tree = interval_tree::IntervalTree(intervals); + IntervalList regularIntervals; + IntervalList collisionFreeIntervals; + + collectIntervals(regularIntervals, collisionFreeIntervals); + + tree = interval_tree::IntervalTree(regularIntervals); + collisionFreeTree = interval_tree::IntervalTree(collisionFreeIntervals); dirty = false; } @@ -57,13 +60,20 @@ void SpannerMap::update() const // findContained //--------------------------------------------------------- -const std::vector >& SpannerMap::findContained(int start, int stop) const +const SpannerMap::IntervalList& SpannerMap::findContained(int start, int stop, bool excludeCollisions) const { if (dirty) { update(); } + results.clear(); - tree.findContained(start, stop, results); + + if (excludeCollisions) { + collisionFreeTree.findContained(start, stop, results); + } else { + tree.findContained(start, stop, results); + } + return results; } @@ -71,16 +81,67 @@ const std::vector >& SpannerMap::findContained // findOverlapping //--------------------------------------------------------- -const std::vector >& SpannerMap::findOverlapping(int start, int stop) const +const SpannerMap::IntervalList& SpannerMap::findOverlapping(int start, int stop, bool excludeCollisions) const { if (dirty) { update(); } + results.clear(); - tree.findOverlapping(start, stop, results); + + if (excludeCollisions) { + collisionFreeTree.findOverlapping(start, stop, results); + } else { + tree.findOverlapping(start, stop, results); + } + return results; } +void SpannerMap::collectIntervals(IntervalList& regularIntervals, IntervalList& collisionFreeIntervals) const +{ + using IntervalsByType = std::map; + using IntervalsByPart = std::map; + + IntervalsByPart intervalsByPart; + + //!Note Because of the current UX of spanners adjustments spanners collision is a regular thing, + //! so we have to manage those cases when two similar spanners (e.g. Pedal line) are overlapping + //! with each other. + constexpr int collidingSpannersPadding = 1; + + for (const auto& pair : *this) { + int newSpannerStartTick = pair.second->tick().ticks(); + int newSpannerEndTick = pair.second->tick2().ticks(); + + IntervalsByType& intervalsByType = intervalsByPart[pair.second->part()->id()]; + IntervalList& intervalList = intervalsByType[pair.second->type()]; + + if (!intervalList.empty()) { + auto lastIntervalIt = intervalList.rbegin(); + if (lastIntervalIt->stop >= newSpannerStartTick) { + lastIntervalIt->stop = newSpannerStartTick - collidingSpannersPadding; + } + } + + intervalList.emplace_back(interval_tree::Interval(newSpannerStartTick, + newSpannerEndTick, + pair.second)); + + regularIntervals.push_back(interval_tree::Interval(newSpannerStartTick, + newSpannerEndTick, + pair.second)); + } + + for (const auto& pair : intervalsByPart) { + for (const auto& intervals : pair.second) { + collisionFreeIntervals.insert(collisionFreeIntervals.end(), + intervals.second.begin(), + intervals.second.end()); + } + } +} + //--------------------------------------------------------- // addSpanner //--------------------------------------------------------- diff --git a/src/engraving/libmscore/spannermap.h b/src/engraving/libmscore/spannermap.h index 013b3bc5d611f..8a4c1c11439a6 100644 --- a/src/engraving/libmscore/spannermap.h +++ b/src/engraving/libmscore/spannermap.h @@ -37,17 +37,23 @@ class SpannerMap : std::multimap { mutable bool dirty; mutable interval_tree::IntervalTree tree; + mutable interval_tree::IntervalTree collisionFreeTree; mutable std::vector > results; public: typedef typename std::multimap::const_reverse_iterator const_reverse_it; typedef typename std::multimap::const_iterator const_it; + using IntervalList = std::vector >; + SpannerMap(); - const std::vector >& findContained(int start, int stop) const; - const std::vector >& findOverlapping(int start, int stop) const; + const IntervalList& findContained(int start, int stop, bool excludeCollisions = false) const; + const IntervalList& findOverlapping(int start, int stop, bool excludeCollisions = false) const; const std::multimap& map() const { return *this; } + + void collectIntervals(IntervalList& regularIntervals, IntervalList& collisionFreeIntervals) const; + const_reverse_it crbegin() const { return std::multimap::crbegin(); } const_reverse_it crend() const { return std::multimap::crend(); } const_it cbegin() const { return std::multimap::cbegin(); } diff --git a/src/engraving/playback/metaparsers/chordarticulationsparser.cpp b/src/engraving/playback/metaparsers/chordarticulationsparser.cpp index ddcafb0d4027c..585b4d9e6109d 100644 --- a/src/engraving/playback/metaparsers/chordarticulationsparser.cpp +++ b/src/engraving/playback/metaparsers/chordarticulationsparser.cpp @@ -96,11 +96,17 @@ void ChordArticulationsParser::parseSpanners(const Chord* chord, const Rendering return; } - auto intervals = spannerMap.findOverlapping(ctx.nominalPositionStartTick, ctx.nominalPositionEndTick); + auto intervals = spannerMap.findOverlapping(ctx.nominalPositionStartTick, + ctx.nominalPositionEndTick, + /*excludeCollisions*/ true); for (const auto& interval : intervals) { Spanner* spanner = interval.value; + if (!SpannersMetaParser::isAbleToParse(spanner)) { + continue; + } + if (spanner->part() != chord->part()) { continue; } diff --git a/src/engraving/playback/metaparsers/internal/spannersmetaparser.cpp b/src/engraving/playback/metaparsers/internal/spannersmetaparser.cpp index 9bd7c853353c6..a5f32347b6609 100644 --- a/src/engraving/playback/metaparsers/internal/spannersmetaparser.cpp +++ b/src/engraving/playback/metaparsers/internal/spannersmetaparser.cpp @@ -26,12 +26,27 @@ #include "libmscore/note.h" #include "libmscore/spanner.h" #include "libmscore/trill.h" +#include "libmscore/pedal.h" #include "playback/utils/pitchutils.h" using namespace mu::engraving; -void SpannersMetaParser::doParse(const EngravingItem* item, const RenderingContext& ctx, mpe::ArticulationMap& result) +bool SpannersMetaParser::isAbleToParse(const EngravingItem* spannerItem) +{ + static const std::unordered_set SUPPORTED_TYPES = { + ElementType::SLUR, + ElementType::PEDAL, + ElementType::LET_RING, + ElementType::PALM_MUTE, + ElementType::TRILL, + ElementType::GLISSANDO, + }; + + return SUPPORTED_TYPES.find(spannerItem->type()) != SUPPORTED_TYPES.cend(); +} + +void SpannersMetaParser::doParse(const EngravingItem* item, const RenderingContext& spannerCtx, mpe::ArticulationMap& result) { IF_ASSERT_FAILED(item->isSpanner()) { return; @@ -43,7 +58,7 @@ void SpannersMetaParser::doParse(const EngravingItem* item, const RenderingConte mpe::pitch_level_t overallPitchRange = 0; mpe::dynamic_level_t overallDynamicRange = 0; - int overallDurationTicks = spanner->ticks().ticks(); + int overallDurationTicks = spannerCtx.nominalDurationTicks; switch (spanner->type()) { case ElementType::SLUR: { @@ -64,9 +79,15 @@ void SpannersMetaParser::doParse(const EngravingItem* item, const RenderingConte break; } - case ElementType::PEDAL: + case ElementType::PEDAL: { type = mpe::ArticulationType::Pedal; + const Pedal* pedal = toPedal(spanner); + if (pedal->endHookType() == HookType::HOOK_45) { + overallDurationTicks -= Constants::division / 4; + } + break; + } case ElementType::LET_RING: type = mpe::ArticulationType::LaissezVibrer; break; @@ -90,7 +111,7 @@ void SpannersMetaParser::doParse(const EngravingItem* item, const RenderingConte } else if (trill->trillType() == TrillType::PRALLPRALL_LINE) { type = mpe::ArticulationType::LinePrall; } - overallDurationTicks = ctx.nominalDurationTicks; + overallDurationTicks = spannerCtx.nominalDurationTicks; break; } case ElementType::GLISSANDO: { @@ -135,11 +156,11 @@ void SpannersMetaParser::doParse(const EngravingItem* item, const RenderingConte mpe::ArticulationMeta articulationMeta; articulationMeta.type = type; - articulationMeta.pattern = ctx.profile->pattern(type); - articulationMeta.timestamp = ctx.nominalTimestamp; + articulationMeta.pattern = spannerCtx.profile->pattern(type); + articulationMeta.timestamp = spannerCtx.nominalTimestamp; articulationMeta.overallPitchChangesRange = overallPitchRange; articulationMeta.overallDynamicChangesRange = overallDynamicRange; - articulationMeta.overallDuration = durationFromTicks(ctx.beatsPerSecond.val, overallDurationTicks); + articulationMeta.overallDuration = durationFromTicks(spannerCtx.beatsPerSecond.val, overallDurationTicks); appendArticulationData(std::move(articulationMeta), result); } diff --git a/src/engraving/playback/metaparsers/internal/spannersmetaparser.h b/src/engraving/playback/metaparsers/internal/spannersmetaparser.h index 35d5219114e1b..d25eb0caec9e2 100644 --- a/src/engraving/playback/metaparsers/internal/spannersmetaparser.h +++ b/src/engraving/playback/metaparsers/internal/spannersmetaparser.h @@ -28,10 +28,13 @@ namespace mu::engraving { class SpannersMetaParser : public MetaParserBase { +public: + static bool isAbleToParse(const EngravingItem* spannerItem); + protected: friend MetaParserBase; - static void doParse(const EngravingItem* item, const RenderingContext& ctx, mpe::ArticulationMap& result); + static void doParse(const EngravingItem* item, const RenderingContext& spannerCtx, mpe::ArticulationMap& result); }; }