Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
vpereverzev committed Oct 14, 2022
1 parent d826de6 commit 80c5abf
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 20 deletions.
79 changes: 70 additions & 9 deletions src/engraving/libmscore/spannermap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

#include "spannermap.h"
#include "spanner.h"
#include "part.h"

#include "log.h"

Expand All @@ -45,42 +46,102 @@ SpannerMap::SpannerMap()

void SpannerMap::update() const
{
std::vector<interval_tree::Interval<Spanner*> > intervals;
for (auto i : *this) {
intervals.push_back(interval_tree::Interval<Spanner*>(i.second->tick().ticks(), i.second->tick2().ticks(), i.second));
}
tree = interval_tree::IntervalTree<Spanner*>(intervals);
IntervalList regularIntervals;
IntervalList collisionFreeIntervals;

collectIntervals(regularIntervals, collisionFreeIntervals);

tree = interval_tree::IntervalTree<Spanner*>(regularIntervals);
collisionFreeTree = interval_tree::IntervalTree<Spanner*>(collisionFreeIntervals);
dirty = false;
}

//---------------------------------------------------------
// findContained
//---------------------------------------------------------

const std::vector<interval_tree::Interval<Spanner*> >& 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;
}

//---------------------------------------------------------
// findOverlapping
//---------------------------------------------------------

const std::vector<interval_tree::Interval<Spanner*> >& 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<ElementType, IntervalList>;
using IntervalsByPart = std::map<ID, IntervalsByType>;

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<Spanner*>(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
//---------------------------------------------------------
Expand Down
10 changes: 8 additions & 2 deletions src/engraving/libmscore/spannermap.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,17 +37,23 @@ class SpannerMap : std::multimap<int, Spanner*>
{
mutable bool dirty;
mutable interval_tree::IntervalTree<Spanner*> tree;
mutable interval_tree::IntervalTree<Spanner*> collisionFreeTree;
mutable std::vector<interval_tree::Interval<Spanner*> > results;

public:
typedef typename std::multimap<int, Spanner*>::const_reverse_iterator const_reverse_it;
typedef typename std::multimap<int, Spanner*>::const_iterator const_it;

using IntervalList = std::vector<interval_tree::Interval<Spanner*> >;

SpannerMap();

const std::vector<interval_tree::Interval<Spanner*> >& findContained(int start, int stop) const;
const std::vector<interval_tree::Interval<Spanner*> >& 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<int, Spanner*>& map() const { return *this; }

void collectIntervals(IntervalList& regularIntervals, IntervalList& collisionFreeIntervals) const;

const_reverse_it crbegin() const { return std::multimap<int, Spanner*>::crbegin(); }
const_reverse_it crend() const { return std::multimap<int, Spanner*>::crend(); }
const_it cbegin() const { return std::multimap<int, Spanner*>::cbegin(); }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
35 changes: 28 additions & 7 deletions src/engraving/playback/metaparsers/internal/spannersmetaparser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<ElementType> 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;
Expand All @@ -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: {
Expand All @@ -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;
Expand All @@ -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: {
Expand Down Expand Up @@ -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);
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,13 @@
namespace mu::engraving {
class SpannersMetaParser : public MetaParserBase<SpannersMetaParser>
{
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);
};
}

Expand Down

0 comments on commit 80c5abf

Please sign in to comment.