777 lines
31 KiB
C++
777 lines
31 KiB
C++
//=============================================================================
|
|
// MuseScore
|
|
// Music Composition & Notation
|
|
//
|
|
// Copyright (C) 2018 Werner Schweer
|
|
//
|
|
// This program is free software; you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License version 2
|
|
// as published by the Free Software Foundation and appearing in
|
|
// the file LICENCE.GPL
|
|
//=============================================================================
|
|
|
|
#include "score.h"
|
|
#include "page.h"
|
|
#include "system.h"
|
|
#include "tremolo.h"
|
|
#include "measure.h"
|
|
#include "layout.h"
|
|
#include "bracket.h"
|
|
#include "spanner.h"
|
|
#include "barline.h"
|
|
#include "tie.h"
|
|
#include "chord.h"
|
|
#include "staff.h"
|
|
#include "box.h"
|
|
#include "spacer.h"
|
|
#include "sym.h"
|
|
#include "systemdivider.h"
|
|
#include "tuplet.h"
|
|
#include "dynamic.h"
|
|
#include "stafflines.h"
|
|
#include "tempotext.h"
|
|
#include "hairpin.h"
|
|
#include "part.h"
|
|
#include "keysig.h"
|
|
#include "sig.h"
|
|
#include "breath.h"
|
|
#include "tempo.h"
|
|
#include "fermata.h"
|
|
#include "lyrics.h"
|
|
|
|
namespace Ms {
|
|
|
|
extern void layoutTies(Chord* ch, System* system, const Fraction& stick);
|
|
extern void layoutDrumsetChord(Chord* c, const Drumset* drumset, const StaffType* st, qreal spatium);
|
|
|
|
//---------------------------------------------------------
|
|
// processLines
|
|
//---------------------------------------------------------
|
|
|
|
#if 0
|
|
static void processLines(System* system, std::vector<Spanner*> lines, bool align)
|
|
{
|
|
std::vector<SpannerSegment*> segments;
|
|
for (Spanner* sp : lines) {
|
|
SpannerSegment* ss = sp->layoutSystem(system); // create/layout spanner segment for this system
|
|
if (ss->autoplace())
|
|
segments.push_back(ss);
|
|
}
|
|
|
|
if (align && segments.size() > 1) {
|
|
qreal y = segments[0]->offset().y();
|
|
for (unsigned i = 1; i < segments.size(); ++i)
|
|
y = qMax(y, segments[i]->offset().y());
|
|
for (auto ss : segments)
|
|
ss->ryoffset() = y;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
//---------------------------------------------------------
|
|
// resetSystems
|
|
// in linear mode there is only one page
|
|
// which contains one system
|
|
//---------------------------------------------------------
|
|
|
|
void Score::resetSystems(bool layoutAll, LayoutContext& lc)
|
|
{
|
|
Page* page = 0;
|
|
if (layoutAll) {
|
|
for (System* s : _systems) {
|
|
for (SpannerSegment* ss : s->spannerSegments())
|
|
ss->setParent(0);
|
|
}
|
|
qDeleteAll(_systems);
|
|
_systems.clear();
|
|
qDeleteAll(pages());
|
|
pages().clear();
|
|
if (!firstMeasure()) {
|
|
qDebug("no measures");
|
|
return;
|
|
}
|
|
|
|
for (MeasureBase* mb = first(); mb; mb = mb->next())
|
|
mb->setSystem(0);
|
|
|
|
page = new Page(this);
|
|
pages().push_back(page);
|
|
page->bbox().setRect(0.0, 0.0, loWidth(), loHeight());
|
|
page->setNo(0);
|
|
|
|
System* system = new System(this);
|
|
_systems.push_back(system);
|
|
page->appendSystem(system);
|
|
for (int i = 0; i < nstaves(); ++i)
|
|
system->insertStaff(i);
|
|
}
|
|
else {
|
|
if (pages().isEmpty())
|
|
return;
|
|
page = pages().front();
|
|
System* system = systems().front();
|
|
system->clear();
|
|
}
|
|
lc.page = page;
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// collectLinearSystem
|
|
// Append all measures to System. VBox is not included to System
|
|
//---------------------------------------------------------
|
|
|
|
void Score::collectLinearSystem(LayoutContext& lc)
|
|
{
|
|
System* system = systems().front();
|
|
system->setInstrumentNames(/* longNames */ true);
|
|
// we need to reset tempo because fermata is setted
|
|
//inside getNextMeasure and it lead to twice timeStretch
|
|
resetTempo();
|
|
|
|
QPointF pos;
|
|
bool firstMeasure = true; //lc.startTick.isZero();
|
|
|
|
//set first measure to lc.nextMeasures for following
|
|
//utilizing in getNextMeasure()
|
|
lc.nextMeasure = _measures.first();
|
|
lc.tick = Fraction(0, 1);
|
|
getNextMeasure(lc);
|
|
|
|
while (lc.curMeasure) {
|
|
qreal ww = 0.0;
|
|
if (lc.curMeasure->isVBox() || lc.curMeasure->isTBox()) {
|
|
lc.curMeasure->setParent(nullptr);
|
|
getNextMeasure(lc);
|
|
continue;
|
|
}
|
|
system->appendMeasure(lc.curMeasure);
|
|
if (lc.curMeasure->isMeasure()) {
|
|
Measure* m = toMeasure(lc.curMeasure);
|
|
if (m->mmRest()) {
|
|
m->mmRest()->setSystem(nullptr);
|
|
}
|
|
if (firstMeasure) {
|
|
system->layoutSystem(0.0);
|
|
if (m->repeatStart()) {
|
|
Segment* s = m->findSegmentR(SegmentType::StartRepeatBarLine, Fraction(0,1));
|
|
if (!s->enabled())
|
|
s->setEnabled(true);
|
|
}
|
|
m->addSystemHeader(true);
|
|
pos.rx() += system->leftMargin();
|
|
firstMeasure = false;
|
|
}
|
|
else if (m->header())
|
|
m->removeSystemHeader();
|
|
if (m->trailer())
|
|
m->removeSystemTrailer();
|
|
if (m->tick() >= lc.startTick && m->tick() <= lc.endTick) {
|
|
// for measures in range, do full layout
|
|
m->createEndBarLines(true);
|
|
m->computeMinWidth();
|
|
ww = m->width();
|
|
m->stretchMeasure(ww);
|
|
}
|
|
else {
|
|
// for measures not in range, use existing layout
|
|
ww = m->width();
|
|
if (m->pos() != pos) {
|
|
// fix beam positions
|
|
// other elements with system as parent are processed in layoutSystemElements()
|
|
// but full beam processing is expensive and not needed if we adjust position here
|
|
QPointF p = pos - m->pos();
|
|
for (const Segment& s : m->segments()) {
|
|
if (!s.isChordRestType())
|
|
continue;
|
|
for (int track = 0; track < ntracks(); ++track) {
|
|
Element* e = s.element(track);
|
|
if (e) {
|
|
ChordRest* cr = toChordRest(e);
|
|
if (cr->beam() && cr->beam()->elements().front() == cr)
|
|
cr->beam()->rpos() += p;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
m->setPos(pos);
|
|
m->layoutStaffLines();
|
|
}
|
|
else if (lc.curMeasure->isHBox()) {
|
|
lc.curMeasure->setPos(pos + QPointF(toHBox(lc.curMeasure)->topGap(), 0.0));
|
|
lc.curMeasure->layout();
|
|
ww = lc.curMeasure->width();
|
|
}
|
|
pos.rx() += ww;
|
|
|
|
getNextMeasure(lc);
|
|
}
|
|
|
|
system->setWidth(pos.x());
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// layoutLinear
|
|
//---------------------------------------------------------
|
|
|
|
void Score::layoutLinear(bool layoutAll, LayoutContext& lc)
|
|
{
|
|
lc.score = this;
|
|
resetSystems(layoutAll, lc);
|
|
|
|
collectLinearSystem(lc);
|
|
// hideEmptyStaves(systems().front(), true); this does not make sense
|
|
|
|
lc.layoutLinear();
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// layoutLinear
|
|
//---------------------------------------------------------
|
|
|
|
void LayoutContext::layoutLinear()
|
|
{
|
|
System* system = score->systems().front();
|
|
|
|
#if 0 // replaced by layoutSystemElements()
|
|
//
|
|
// layout
|
|
// - beams
|
|
// - RehearsalMark, StaffText
|
|
// - Dynamic
|
|
// - update the segment shape + measure shape
|
|
//
|
|
//
|
|
int stick = system->measures().front()->tick();
|
|
int etick = system->measures().back()->endTick();
|
|
|
|
//
|
|
// layout slurs
|
|
//
|
|
if (etick > stick) { // ignore vbox
|
|
auto spanners = score->spannerMap().findOverlapping(stick, etick);
|
|
|
|
std::vector<Spanner*> spanner;
|
|
for (auto interval : spanners) {
|
|
Spanner* sp = interval.value;
|
|
if (sp->tick() < etick && sp->tick2() >= stick) {
|
|
if (sp->isSlur())
|
|
spanner.push_back(sp);
|
|
}
|
|
}
|
|
processLines(system, spanner, false);
|
|
}
|
|
|
|
std::vector<Dynamic*> dynamics;
|
|
for (MeasureBase* mb : system->measures()) {
|
|
if (!mb->isMeasure())
|
|
continue;
|
|
SegmentType st = SegmentType::ChordRest;
|
|
Measure* m = toMeasure(mb);
|
|
for (Segment* s = m->first(st); s; s = s->next(st)) {
|
|
for (Element* e : s->elist()) {
|
|
if (!e)
|
|
continue;
|
|
if (e->isChordRest()) {
|
|
ChordRest* cr = toChordRest(e);
|
|
if (isTopBeam(cr))
|
|
cr->beam()->layout();
|
|
if (e->isChord()) {
|
|
Chord* c = toChord(e);
|
|
for (Chord* ch : c->graceNotes())
|
|
layoutTies(ch, system, stick);
|
|
layoutTies(c, system, stick);
|
|
c->layoutArticulations2();
|
|
}
|
|
}
|
|
}
|
|
for (Element* e : s->annotations()) {
|
|
if (e->visible() && e->isDynamic()) {
|
|
Dynamic* d = toDynamic(e);
|
|
d->layout();
|
|
}
|
|
else if (e->isFiguredBass())
|
|
e->layout();
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// layout tuplet
|
|
//
|
|
|
|
for (MeasureBase* mb : system->measures()) {
|
|
if (!mb->isMeasure())
|
|
continue;
|
|
Measure* m = toMeasure(mb);
|
|
static const SegmentType st { SegmentType::ChordRest };
|
|
for (int track = 0; track < score->ntracks(); ++track) {
|
|
if (!score->staff(track / VOICES)->show()) {
|
|
track += VOICES-1;
|
|
continue;
|
|
}
|
|
for (Segment* s = m->first(st); s; s = s->next(st)) {
|
|
ChordRest* cr = s->cr(track);
|
|
if (!cr)
|
|
continue;
|
|
DurationElement* de = cr;
|
|
while (de->tuplet() && de->tuplet()->elements().front() == de) {
|
|
Tuplet* t = de->tuplet();
|
|
t->layout();
|
|
de = de->tuplet();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// layout SpannerSegments for current system
|
|
//
|
|
|
|
if (etick > stick) { // ignore vbox
|
|
auto spanners = score->spannerMap().findOverlapping(stick, etick);
|
|
|
|
std::vector<Spanner*> ottavas;
|
|
std::vector<Spanner*> spanner;
|
|
std::vector<Spanner*> pedal;
|
|
|
|
for (auto interval : spanners) {
|
|
Spanner* sp = interval.value;
|
|
if (sp->tick() < etick && sp->tick2() > stick) {
|
|
if (sp->isOttava())
|
|
ottavas.push_back(sp);
|
|
else if (sp->isPedal())
|
|
pedal.push_back(sp);
|
|
else if (!sp->isSlur() && !sp->isVolta()) // slurs are already, voltas will be later handled
|
|
spanner.push_back(sp);
|
|
}
|
|
}
|
|
processLines(system, ottavas, false);
|
|
processLines(system, pedal, true);
|
|
processLines(system, spanner, false);
|
|
}
|
|
|
|
//
|
|
// TempoText, Fermata
|
|
//
|
|
|
|
for (MeasureBase* mb : system->measures()) {
|
|
if (!mb->isMeasure())
|
|
continue;
|
|
SegmentType st = SegmentType::ChordRest;
|
|
Measure* m = toMeasure(mb);
|
|
for (Segment* s = m->first(st); s; s = s->next(st)) {
|
|
for (Element* e : s->annotations()) {
|
|
if (e->isTempoText()) {
|
|
TempoText* tt = toTempoText(e);
|
|
if (score->isMaster())
|
|
score->setTempo(tt->segment(), tt->tempo());
|
|
tt->layout();
|
|
}
|
|
else if (e->isFermata())
|
|
e->layout();
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Volta
|
|
//
|
|
|
|
if (etick > stick) { // ignore vbox
|
|
auto spanners = score->spannerMap().findOverlapping(stick, etick);
|
|
|
|
std::vector<Spanner*> voltas;
|
|
|
|
for (auto interval : spanners) {
|
|
Spanner* sp = interval.value;
|
|
if (sp->tick() < etick && sp->tick2() > stick) {
|
|
if (sp->isVolta())
|
|
voltas.push_back(sp);
|
|
}
|
|
}
|
|
processLines(system, voltas, false);
|
|
|
|
//
|
|
// vertical align volta segments
|
|
//
|
|
std::vector<SpannerSegment*> voltaSegments;
|
|
for (SpannerSegment* ss : system->spannerSegments()) {
|
|
if (ss->isVoltaSegment())
|
|
voltaSegments.push_back(ss);
|
|
}
|
|
if (voltaSegments.size() > 1) {
|
|
qreal y = 0;
|
|
for (SpannerSegment* ss : voltaSegments)
|
|
y = qMin(y, ss->offset().y());
|
|
for (SpannerSegment* ss : voltaSegments)
|
|
ss->ryoffset() = y;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Jump, Marker
|
|
//
|
|
|
|
for (MeasureBase* mb : system->measures()) {
|
|
if (!mb->isMeasure())
|
|
continue;
|
|
Measure* m = toMeasure(mb);
|
|
for (Element* e : m->el()) {
|
|
if (e->visible() && (e->isJump() || e->isMarker()))
|
|
e->layout();
|
|
}
|
|
}
|
|
|
|
//
|
|
// RehearsalMark, StaffText, FretDiagram
|
|
//
|
|
|
|
for (MeasureBase* mb : system->measures()) {
|
|
if (!mb->isMeasure())
|
|
continue;
|
|
SegmentType st = SegmentType::ChordRest;
|
|
Measure* m = toMeasure(mb);
|
|
for (Segment* s = m->first(st); s; s = s->next(st)) {
|
|
// layout in specific order
|
|
for (Element* e : s->annotations()) {
|
|
if (e->visible() && e->isFretDiagram())
|
|
e->layout();
|
|
}
|
|
for (Element* e : s->annotations()) {
|
|
if (e->visible() && (e->isStaffText() || e->isHarmony()))
|
|
e->layout();
|
|
}
|
|
for (Element* e : s->annotations()) {
|
|
if (e->visible() && e->isRehearsalMark())
|
|
e->layout();
|
|
}
|
|
}
|
|
}
|
|
|
|
score->layoutLyrics(system);
|
|
|
|
for (Spanner* sp : score->unmanagedSpanners()) {
|
|
if (sp->tick() >= etick || sp->tick2() <= stick)
|
|
continue;
|
|
sp->layoutSystem(system);
|
|
}
|
|
#endif
|
|
|
|
score->layoutSystemElements(system, *this);
|
|
|
|
system->layout2(); // compute staff distances
|
|
|
|
for (MeasureBase* mb : system->measures()) {
|
|
if (!mb->isMeasure())
|
|
continue;
|
|
Measure* m = toMeasure(mb);
|
|
|
|
for (int track = 0; track < score->ntracks(); ++track) {
|
|
for (Segment* segment = m->first(); segment; segment = segment->next()) {
|
|
Element* e = segment->element(track);
|
|
if (!e)
|
|
continue;
|
|
if (e->isChordRest()) {
|
|
if (m->tick() < startTick || m->tick() > endTick)
|
|
continue;
|
|
if (!score->staff(track2staff(track))->show())
|
|
continue;
|
|
ChordRest* cr = toChordRest(e);
|
|
if (notTopBeam(cr)) // layout cross staff beams
|
|
cr->beam()->layout();
|
|
if (notTopTuplet(cr)) {
|
|
// fix layout of tuplets
|
|
DurationElement* de = cr;
|
|
while (de->tuplet() && de->tuplet()->elements().front() == de) {
|
|
Tuplet* t = de->tuplet();
|
|
t->layout();
|
|
de = de->tuplet();
|
|
}
|
|
}
|
|
|
|
if (cr->isChord()) {
|
|
Chord* c = toChord(cr);
|
|
for (Chord* cc : c->graceNotes()) {
|
|
if (cc->beam() && cc->beam()->elements().front() == cc)
|
|
cc->beam()->layout();
|
|
for (Note* n : cc->notes()) {
|
|
Tie* tie = n->tieFor();
|
|
if (tie)
|
|
tie->layout();
|
|
for (Spanner* sp : n->spannerFor())
|
|
sp->layout();
|
|
}
|
|
for (Element* element : cc->el()) {
|
|
if (element->isSlur())
|
|
element->layout();
|
|
}
|
|
}
|
|
c->layoutArpeggio2();
|
|
for (Note* n : c->notes()) {
|
|
Tie* tie = n->tieFor();
|
|
if (tie)
|
|
tie->layout();
|
|
for (Spanner* sp : n->spannerFor())
|
|
sp->layout();
|
|
}
|
|
if (c->tremolo()) {
|
|
Tremolo* t = c->tremolo();
|
|
Chord* c1 = t->chord1();
|
|
Chord* c2 = t->chord2();
|
|
if (t->twoNotes() && c1 && c2 && (c1->staffMove() || c2->staffMove()))
|
|
t->layout();
|
|
}
|
|
}
|
|
}
|
|
else if (e->isBarLine())
|
|
toBarLine(e)->layout2();
|
|
}
|
|
}
|
|
m->layout2();
|
|
}
|
|
page->setPos(0, 0);
|
|
system->setPos(page->lm(), page->tm() + score->styleP(Sid::staffUpperBorder));
|
|
page->setWidth(system->width() + system->pos().x());
|
|
// Set buffer space after the last system to avoid problems with mouse input.
|
|
// Mouse input divides space between systems equally (see Score::searchSystem),
|
|
// hence the choice of the value.
|
|
const qreal buffer = 0.5 * score->styleS(Sid::maxSystemDistance).val() * score->spatium();
|
|
page->setHeight(system->height() + system->pos().y() + buffer);
|
|
page->rebuildBspTree();
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// layoutMeasureLinear
|
|
//---------------------------------------------------------
|
|
|
|
void LayoutContext::layoutMeasureLinear(MeasureBase* mb)
|
|
{
|
|
adjustMeasureNo(mb);
|
|
|
|
if (!mb->isMeasure()) {
|
|
mb->setTick(tick);
|
|
return;
|
|
}
|
|
|
|
//-----------------------------------------
|
|
// process one measure
|
|
//-----------------------------------------
|
|
|
|
Measure* measure = toMeasure(mb);
|
|
measure->moveTicks(tick - measure->tick());
|
|
|
|
//
|
|
// implement section break rest
|
|
//
|
|
if (measure->sectionBreak() && measure->pause() != 0.0)
|
|
score->setPause(measure->endTick(), measure->pause());
|
|
|
|
//
|
|
// calculate accidentals and note lines,
|
|
// create stem and set stem direction
|
|
//
|
|
for (int staffIdx = 0; staffIdx < score->nstaves(); ++staffIdx) {
|
|
Staff* staff = score->staff(staffIdx);
|
|
const Drumset* drumset = staff->part()->instrument()->useDrumset() ? staff->part()->instrument()->drumset() : 0;
|
|
AccidentalState as; // list of already set accidentals for this measure
|
|
as.init(staff->keySigEvent(measure->tick()), staff->clef(measure->tick()));
|
|
|
|
for (Segment& segment : measure->segments()) {
|
|
if (segment.isKeySigType()) {
|
|
KeySig* ks = toKeySig(segment.element(staffIdx * VOICES));
|
|
if (!ks)
|
|
continue;
|
|
Fraction t = segment.tick();
|
|
as.init(staff->keySigEvent(t), staff->clef(t));
|
|
ks->layout();
|
|
}
|
|
else if (segment.isChordRestType()) {
|
|
const StaffType* st = staff->staffType(segment.tick());
|
|
int track = staffIdx * VOICES;
|
|
int endTrack = track + VOICES;
|
|
|
|
for (int t = track; t < endTrack; ++t) {
|
|
ChordRest* cr = segment.cr(t);
|
|
if (!cr)
|
|
continue;
|
|
qreal m = staff->mag(segment.tick());
|
|
if (cr->small())
|
|
m *= score->styleD(Sid::smallNoteMag);
|
|
|
|
if (cr->isChord()) {
|
|
Chord* chord = toChord(cr);
|
|
chord->cmdUpdateNotes(&as);
|
|
for (Chord* c : chord->graceNotes()) {
|
|
c->setMag(m * score->styleD(Sid::graceNoteMag));
|
|
c->computeUp();
|
|
if (c->stemDirection() != Direction::AUTO)
|
|
c->setUp(c->stemDirection() == Direction::UP);
|
|
else
|
|
c->setUp(!(t % 2));
|
|
if (drumset)
|
|
layoutDrumsetChord(c, drumset, st, score->spatium());
|
|
c->layoutStem1();
|
|
}
|
|
if (drumset)
|
|
layoutDrumsetChord(chord, drumset, st, score->spatium());
|
|
chord->computeUp();
|
|
chord->layoutStem1(); // create stems needed to calculate spacing
|
|
// stem direction can change later during beam processing
|
|
}
|
|
cr->setMag(m);
|
|
}
|
|
}
|
|
else if (segment.isClefType()) {
|
|
Element* e = segment.element(staffIdx * VOICES);
|
|
if (e) {
|
|
toClef(e)->setSmall(true);
|
|
e->layout();
|
|
}
|
|
}
|
|
else if (segment.isType(SegmentType::TimeSig | SegmentType::Ambitus | SegmentType::HeaderClef)) {
|
|
Element* e = segment.element(staffIdx * VOICES);
|
|
if (e)
|
|
e->layout();
|
|
}
|
|
}
|
|
}
|
|
|
|
score->createBeams(measure);
|
|
|
|
for (int staffIdx = 0; staffIdx < score->nstaves(); ++staffIdx) {
|
|
for (Segment& segment : measure->segments()) {
|
|
if (segment.isChordRestType()) {
|
|
score->layoutChords1(&segment, staffIdx);
|
|
for (int voice = 0; voice < VOICES; ++voice) {
|
|
ChordRest* cr = segment.cr(staffIdx * VOICES + voice);
|
|
if (cr) {
|
|
for (Lyrics* l : cr->lyrics()) {
|
|
if (l)
|
|
l->layout();
|
|
}
|
|
if (cr->isChord())
|
|
toChord(cr)->layoutArticulations();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (Segment& segment : measure->segments()) {
|
|
if (segment.isBreathType()) {
|
|
qreal length = 0.0;
|
|
Fraction t = segment.tick();
|
|
// find longest pause
|
|
for (int i = 0, n = score->ntracks(); i < n; ++i) {
|
|
Element* e = segment.element(i);
|
|
if (e && e->isBreath()) {
|
|
Breath* b = toBreath(e);
|
|
b->layout();
|
|
length = qMax(length, b->pause());
|
|
}
|
|
}
|
|
if (length != 0.0)
|
|
score->setPause(t, length);
|
|
}
|
|
else if (segment.isTimeSigType()) {
|
|
for (int staffIdx = 0; staffIdx < score->nstaves(); ++staffIdx) {
|
|
TimeSig* ts = toTimeSig(segment.element(staffIdx * VOICES));
|
|
if (ts)
|
|
score->staff(staffIdx)->addTimeSig(ts);
|
|
}
|
|
}
|
|
else if (score->isMaster() && segment.isChordRestType()) {
|
|
for (Element* e : segment.annotations()) {
|
|
if (!(e->isTempoText()
|
|
|| e->isDynamic()
|
|
|| e->isFermata()
|
|
|| e->isRehearsalMark()
|
|
|| e->isFretDiagram()
|
|
|| e->isHarmony()
|
|
|| e->isStaffText() // ws: whats left?
|
|
|| e->isFiguredBass())) {
|
|
e->layout();
|
|
}
|
|
}
|
|
// TODO, this is not going to work, we just cleaned the tempomap
|
|
// it breaks the test midi/testBaroqueOrnaments.mscx where first note has stretch 2
|
|
// Also see fixTicks
|
|
qreal stretch = 0.0;
|
|
for (Element* e : segment.annotations()) {
|
|
if (e->isFermata())
|
|
stretch = qMax(stretch, toFermata(e)->timeStretch());
|
|
}
|
|
if (stretch != 0.0 && stretch != 1.0) {
|
|
qreal otempo = score->tempomap()->tempo(segment.tick().ticks());
|
|
qreal ntempo = otempo / stretch;
|
|
score->setTempo(segment.tick(), ntempo);
|
|
Fraction etick = segment.tick() + segment.ticks() - Fraction(1, 480*4);
|
|
auto e = score->tempomap()->find(etick.ticks());
|
|
if (e == score->tempomap()->end())
|
|
score->setTempo(etick, otempo);
|
|
}
|
|
}
|
|
else if (segment.isChordRestType()) {
|
|
// chord symbols need to be layouted in parts too
|
|
for (Element* e : segment.annotations()) {
|
|
if (e->isHarmony())
|
|
e->layout();
|
|
}
|
|
}
|
|
}
|
|
|
|
// update time signature map
|
|
// create event if measure len and time signature are different
|
|
// even if they are equivalent 4/4 vs 2/2
|
|
// also check if nominal time signature has changed
|
|
|
|
if (score->isMaster() && ((!measure->ticks().identical(sig) && measure->ticks() != sig * measure->mmRestCount())
|
|
|| (prevMeasure && prevMeasure->isMeasure()
|
|
&& !measure->timesig().identical(toMeasure(prevMeasure)->timesig()))))
|
|
{
|
|
if (measure->isMMRest())
|
|
sig = measure->mmRestFirst()->ticks();
|
|
else
|
|
sig = measure->ticks();
|
|
score->sigmap()->add(tick.ticks(), SigEvent(sig, measure->timesig(), measure->no()));
|
|
}
|
|
|
|
Segment* seg = measure->findSegmentR(SegmentType::StartRepeatBarLine, Fraction(0,1));
|
|
if (measure->repeatStart()) {
|
|
if (!seg)
|
|
seg = measure->getSegmentR(SegmentType::StartRepeatBarLine, Fraction(0,1));
|
|
measure->barLinesSetSpan(seg); // this also creates necessary barlines
|
|
for (int staffIdx = 0; staffIdx < score->nstaves(); ++staffIdx) {
|
|
BarLine* b = toBarLine(seg->element(staffIdx * VOICES));
|
|
if (b) {
|
|
b->setBarLineType(BarLineType::START_REPEAT);
|
|
b->layout();
|
|
}
|
|
}
|
|
}
|
|
else if (seg)
|
|
score->undoRemoveElement(seg);
|
|
|
|
for (Segment& s : measure->segments()) {
|
|
// DEBUG: relayout grace notes as beaming/flags may have changed
|
|
if (s.isChordRestType()) {
|
|
for (Element* e : s.elist()) {
|
|
if (e && e->isChord()) {
|
|
Chord* chord = toChord(e);
|
|
chord->layout();
|
|
if (chord->tremolo()) // debug
|
|
chord->tremolo()->layout();
|
|
}
|
|
}
|
|
}
|
|
else if (s.isEndBarLineType())
|
|
continue;
|
|
s.createShapes();
|
|
}
|
|
|
|
tick += measure->ticks();
|
|
}
|
|
|
|
} // namespace Ms
|
|
|