You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
732 lines
25 KiB
732 lines
25 KiB
15 years ago
|
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
||
|
|
||
|
/*
|
||
|
Rosegarden
|
||
|
A MIDI and audio sequencer and musical notation editor.
|
||
|
|
||
|
This program is Copyright 2000-2008
|
||
|
Guillaume Laurent <glaurent@telegraph-road.org>,
|
||
|
Chris Cannam <cannam@all-day-breakfast.com>,
|
||
|
Richard Bown <richard.bown@ferventsoftware.com>
|
||
|
|
||
|
The moral rights of Guillaume Laurent, Chris Cannam, and Richard
|
||
|
Bown to claim authorship of this work have been asserted.
|
||
|
|
||
|
Other copyrights also apply to some parts of this work. Please
|
||
|
see the AUTHORS file and individual file headers for details.
|
||
|
|
||
|
This program is free software; you can redistribute it and/or
|
||
|
modify it under the terms of the GNU General Public License as
|
||
|
published by the Free Software Foundation; either version 2 of the
|
||
|
License, or (at your option) any later version. See the file
|
||
|
COPYING included with this distribution for more information.
|
||
|
*/
|
||
|
|
||
|
#include <cmath>
|
||
|
#include "NotationVLayout.h"
|
||
|
#include "misc/Debug.h"
|
||
|
|
||
|
#include <klocale.h>
|
||
|
#include "base/Composition.h"
|
||
|
#include "base/Event.h"
|
||
|
#include "base/LayoutEngine.h"
|
||
|
#include "base/NotationRules.h"
|
||
|
#include "base/NotationTypes.h"
|
||
|
#include "base/NotationQuantizer.h"
|
||
|
#include "base/Staff.h"
|
||
|
#include "gui/general/ProgressReporter.h"
|
||
|
#include "gui/editors/guitar/Chord.h"
|
||
|
#include "NotationChord.h"
|
||
|
#include "NotationElement.h"
|
||
|
#include "NotationProperties.h"
|
||
|
#include "NotationStaff.h"
|
||
|
#include "NotePixmapFactory.h"
|
||
|
#include <kmessagebox.h>
|
||
|
#include <qobject.h>
|
||
|
#include <qstring.h>
|
||
|
#include <qwidget.h>
|
||
|
|
||
|
|
||
|
namespace Rosegarden
|
||
|
{
|
||
|
|
||
|
using namespace BaseProperties;
|
||
|
|
||
|
|
||
|
NotationVLayout::NotationVLayout(Composition *c, NotePixmapFactory *npf,
|
||
|
const NotationProperties &properties,
|
||
|
QObject* parent, const char* name) :
|
||
|
ProgressReporter(parent, name),
|
||
|
m_composition(c),
|
||
|
m_npf(npf),
|
||
|
m_notationQuantizer(c->getNotationQuantizer()),
|
||
|
m_properties(properties)
|
||
|
{
|
||
|
// empty
|
||
|
}
|
||
|
|
||
|
NotationVLayout::~NotationVLayout()
|
||
|
{
|
||
|
// empty
|
||
|
}
|
||
|
|
||
|
NotationVLayout::SlurList &
|
||
|
|
||
|
NotationVLayout::getSlurList(Staff &staff)
|
||
|
{
|
||
|
SlurListMap::iterator i = m_slurs.find(&staff);
|
||
|
if (i == m_slurs.end()) {
|
||
|
m_slurs[&staff] = SlurList();
|
||
|
}
|
||
|
|
||
|
return m_slurs[&staff];
|
||
|
}
|
||
|
|
||
|
void
|
||
|
NotationVLayout::reset()
|
||
|
{
|
||
|
m_slurs.clear();
|
||
|
}
|
||
|
|
||
|
void
|
||
|
NotationVLayout::resetStaff(Staff &staff, timeT, timeT)
|
||
|
{
|
||
|
getSlurList(staff).clear();
|
||
|
}
|
||
|
|
||
|
void
|
||
|
NotationVLayout::scanStaff(Staff &staffBase, timeT, timeT)
|
||
|
{
|
||
|
START_TIMING;
|
||
|
|
||
|
NotationStaff &staff = dynamic_cast<NotationStaff &>(staffBase);
|
||
|
NotationElementList *notes = staff.getViewElementList();
|
||
|
|
||
|
NotationElementList::iterator from = notes->begin();
|
||
|
NotationElementList::iterator to = notes->end();
|
||
|
NotationElementList::iterator i;
|
||
|
|
||
|
for (i = from; i != to; ++i) {
|
||
|
|
||
|
NotationElement *el = static_cast<NotationElement*>(*i);
|
||
|
|
||
|
// Displaced Y will only be used for certain events -- in
|
||
|
// particular not for notes, whose y-coord is obviously kind
|
||
|
// of meaningful.
|
||
|
double displacedY = 0.0;
|
||
|
long dyRaw = 0;
|
||
|
el->event()->get<Int>(DISPLACED_Y, dyRaw);
|
||
|
displacedY = double(dyRaw * m_npf->getLineSpacing()) / 1000.0;
|
||
|
|
||
|
el->setLayoutY(staff.getLayoutYForHeight( -9) + displacedY);
|
||
|
|
||
|
if (el->isRest()) {
|
||
|
|
||
|
// rests for notes longer than the minim have hotspots
|
||
|
// aligned with the line above the middle line; the rest
|
||
|
// are aligned with the middle line
|
||
|
|
||
|
long noteType;
|
||
|
bool hasNoteType = el->event()->get
|
||
|
<Int>(NOTE_TYPE, noteType);
|
||
|
if (hasNoteType && noteType > Note::Minim) {
|
||
|
el->setLayoutY(staff.getLayoutYForHeight(6) + displacedY);
|
||
|
} else {
|
||
|
el->setLayoutY(staff.getLayoutYForHeight(4) + displacedY);
|
||
|
}
|
||
|
|
||
|
// Fix for bug 1090767 Rests outside staves have wrong glyphs
|
||
|
// by William <rosegarden4c AT orthoset.com>
|
||
|
// We use a "rest-outside-stave" glyph for any minim/semibreve/breve
|
||
|
// rest that has been displaced vertically e.g. by fine-positioning
|
||
|
// outside the stave. For small vertical displacements that keep
|
||
|
// the rest inside the stave, we use the "rest-inside-stave" glyph
|
||
|
// and also discretise the displacement into multiples of the
|
||
|
// stave-line spacing. The outside-stave glyphs match the character
|
||
|
// numbers 1D13A, 1D13B and 1D13C in the Unicode 4.0 standard.
|
||
|
|
||
|
if (hasNoteType && (displacedY > 0.1 || displacedY < -0.1)) {
|
||
|
|
||
|
// a fiddly check for transition from inside to outside:
|
||
|
|
||
|
int min = -1, max = 1;
|
||
|
|
||
|
switch (noteType) {
|
||
|
case Note::Breve:
|
||
|
min = -1;
|
||
|
max = 2;
|
||
|
break;
|
||
|
case Note::Semibreve:
|
||
|
min = -1;
|
||
|
max = 3;
|
||
|
break;
|
||
|
case Note::Minim:
|
||
|
min = -2;
|
||
|
max = 2;
|
||
|
break;
|
||
|
case Note::Crotchet:
|
||
|
min = -1;
|
||
|
max = 3;
|
||
|
break;
|
||
|
case Note::Quaver:
|
||
|
min = -2;
|
||
|
max = 3;
|
||
|
break;
|
||
|
case Note::Semiquaver:
|
||
|
min = -3;
|
||
|
max = 3;
|
||
|
break;
|
||
|
case Note::Demisemiquaver:
|
||
|
min = -3;
|
||
|
max = 4;
|
||
|
break;
|
||
|
case Note::Hemidemisemiquaver:
|
||
|
min = -4;
|
||
|
max = 4;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
bool outside = false;
|
||
|
|
||
|
if (noteType == Note::Breve) {
|
||
|
if (nearbyint(displacedY) < min * m_npf->getLineSpacing() ||
|
||
|
nearbyint(displacedY) > max * m_npf->getLineSpacing()) {
|
||
|
outside = true;
|
||
|
}
|
||
|
} else {
|
||
|
if ((int)displacedY < min * m_npf->getLineSpacing() ||
|
||
|
(int)displacedY > max * m_npf->getLineSpacing()) {
|
||
|
outside = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
el->event()->setMaybe<Bool>(m_properties.REST_OUTSIDE_STAVE,
|
||
|
outside);
|
||
|
|
||
|
if (!outside) {
|
||
|
displacedY = (double)m_npf->getLineSpacing() *
|
||
|
(int(nearbyint((double)displacedY /
|
||
|
m_npf->getLineSpacing())));
|
||
|
if (noteType > Note::Minim)
|
||
|
el->setLayoutY(staff.getLayoutYForHeight(6) + displacedY);
|
||
|
else
|
||
|
el->setLayoutY(staff.getLayoutYForHeight(4) + displacedY);
|
||
|
}
|
||
|
|
||
|
// if (displacedY != 0.0)
|
||
|
// NOTATION_DEBUG << "REST_OUTSIDE_STAVE AFTER "
|
||
|
// << " : displacedY : " << displacedY
|
||
|
// << " line-spacing : " << m_npf->getLineSpacing()
|
||
|
// << " time : " << (el->getViewAbsoluteTime())
|
||
|
// << endl;
|
||
|
} else {
|
||
|
el->event()->setMaybe<Bool>(m_properties.REST_OUTSIDE_STAVE,
|
||
|
false);
|
||
|
}
|
||
|
|
||
|
} else if (el->isNote()) {
|
||
|
|
||
|
NotationChord chord(*notes, i, m_notationQuantizer, m_properties);
|
||
|
if (chord.size() == 0)
|
||
|
continue;
|
||
|
|
||
|
std::vector<int> h;
|
||
|
for (unsigned int j = 0; j < chord.size(); ++j) {
|
||
|
long height = 0;
|
||
|
if (!(*chord[j])->event()->get
|
||
|
<Int>
|
||
|
(m_properties.HEIGHT_ON_STAFF, height)) {
|
||
|
std::cerr << QString("ERROR: Event in chord at %1 has no HEIGHT_ON_STAFF property!\nThis is a bug (the program would previously have crashed by now)").arg((*chord[j])->getViewAbsoluteTime()) << std::endl;
|
||
|
(*chord[j])->event()->dump(std::cerr);
|
||
|
}
|
||
|
h.push_back(height);
|
||
|
}
|
||
|
bool stemmed = chord.hasStem();
|
||
|
bool stemUp = chord.hasStemUp();
|
||
|
bool hasNoteHeadShifted = chord.hasNoteHeadShifted();
|
||
|
|
||
|
unsigned int flaggedNote = (stemUp ? chord.size() - 1 : 0);
|
||
|
|
||
|
bool hasShifted = chord.hasNoteHeadShifted();
|
||
|
|
||
|
double y0 = -1E50; // A very unlikely Y layout value
|
||
|
|
||
|
for (unsigned int j = 0; j < chord.size(); ++j) {
|
||
|
|
||
|
el = static_cast<NotationElement*>(*chord[j]);
|
||
|
el->setLayoutY(staff.getLayoutYForHeight(h[j]));
|
||
|
|
||
|
// Look for collision
|
||
|
const double eps = 0.001;
|
||
|
Event *eel = el->event();
|
||
|
double y = el->getLayoutY();
|
||
|
if (eel->has("pitch")) {
|
||
|
el->setIsColliding(fabs(y - y0) < eps);
|
||
|
y0 = y;
|
||
|
}
|
||
|
|
||
|
|
||
|
// These calculations and assignments are pretty much final
|
||
|
// if the chord is not in a beamed group, but if it is then
|
||
|
// they will be reworked by NotationGroup::applyBeam, which
|
||
|
// is called from NotationHLayout::layout, which is called
|
||
|
// after this. Any inaccuracies here for beamed groups
|
||
|
// should be stamped out there.
|
||
|
|
||
|
// el->event()->setMaybe<Bool>(STEM_UP, stemUp);
|
||
|
el->event()->setMaybe<Bool>(m_properties.VIEW_LOCAL_STEM_UP, stemUp);
|
||
|
|
||
|
bool primary =
|
||
|
((stemmed && stemUp) ? (j == 0) : (j == chord.size() - 1));
|
||
|
el->event()->setMaybe<Bool>
|
||
|
(m_properties.CHORD_PRIMARY_NOTE, primary);
|
||
|
|
||
|
if (primary) {
|
||
|
el->event()->setMaybe<Int>
|
||
|
(m_properties.CHORD_MARK_COUNT, chord.getMarkCountForChord());
|
||
|
}
|
||
|
|
||
|
bool shifted = chord.isNoteHeadShifted(chord[j]);
|
||
|
el->event()->setMaybe<Bool>
|
||
|
(m_properties.NOTE_HEAD_SHIFTED, shifted);
|
||
|
|
||
|
el->event()->setMaybe<Bool>
|
||
|
(m_properties.NOTE_DOT_SHIFTED, false);
|
||
|
if (hasShifted && stemUp) {
|
||
|
long dots = 0;
|
||
|
(void)el->event()->get
|
||
|
<Int>(NOTE_DOTS, dots);
|
||
|
if (dots > 0) {
|
||
|
el->event()->setMaybe<Bool>
|
||
|
(m_properties.NOTE_DOT_SHIFTED, true);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
el->event()->setMaybe<Bool>
|
||
|
(m_properties.NEEDS_EXTRA_SHIFT_SPACE,
|
||
|
hasNoteHeadShifted && !stemUp);
|
||
|
|
||
|
el->event()->setMaybe<Bool>
|
||
|
(m_properties.DRAW_FLAG, j == flaggedNote);
|
||
|
|
||
|
int stemLength = -1;
|
||
|
if (j != flaggedNote) {
|
||
|
stemLength = staff.getLayoutYForHeight(h[flaggedNote]) -
|
||
|
staff.getLayoutYForHeight(h[j]);
|
||
|
if (stemLength < 0)
|
||
|
stemLength = -stemLength;
|
||
|
// NOTATION_DEBUG << "Setting stem length to "
|
||
|
// << stemLength << endl;
|
||
|
} else {
|
||
|
int minStemLength = stemLength;
|
||
|
if (h[j] < -2 && stemUp) {
|
||
|
//!!! needs tuning, & applying for beamed stems too
|
||
|
minStemLength = staff.getLayoutYForHeight(h[j]) -
|
||
|
staff.getLayoutYForHeight(4);
|
||
|
} else if (h[j] > 10 && !stemUp) {
|
||
|
minStemLength = staff.getLayoutYForHeight(4) -
|
||
|
staff.getLayoutYForHeight(h[j]);
|
||
|
}
|
||
|
stemLength = std::max(minStemLength, stemLength);
|
||
|
}
|
||
|
|
||
|
el->event()->setMaybe<Int>
|
||
|
(m_properties.UNBEAMED_STEM_LENGTH, stemLength);
|
||
|
}
|
||
|
|
||
|
|
||
|
// #938545 (Broken notation: Duplicated note can float
|
||
|
// outside stave) -- Need to cope with the case where a
|
||
|
// note that's not a member of a chord (different stem
|
||
|
// direction &c) falls between notes that are members.
|
||
|
// Not optimal, as we can end up scanning the chord
|
||
|
// multiple times (we'll return to it after scanning the
|
||
|
// contained note). [We can't just iterate over all
|
||
|
// elements within the chord (as we can in hlayout)
|
||
|
// because we need them in height order.]
|
||
|
|
||
|
i = chord.getFirstElementNotInChord();
|
||
|
if (i == notes->end())
|
||
|
i = chord.getFinalElement();
|
||
|
else
|
||
|
--i;
|
||
|
|
||
|
} else {
|
||
|
|
||
|
if (el->event()->isa(Clef::EventType)) {
|
||
|
|
||
|
// clef pixmaps have the hotspot placed to coincide
|
||
|
// with the pitch of the clef -- so the alto clef
|
||
|
// should be "on" the middle line, the treble clef
|
||
|
// "on" the line below the middle, etc
|
||
|
|
||
|
el->setLayoutY(staff.getLayoutYForHeight
|
||
|
(Clef(*el->event()).getAxisHeight()));
|
||
|
|
||
|
} else if (el->event()->isa(Rosegarden::Key::EventType)) {
|
||
|
|
||
|
el->setLayoutY(staff.getLayoutYForHeight(12));
|
||
|
|
||
|
} else if (el->event()->isa(Text::EventType)) {
|
||
|
|
||
|
std::string type = Text::UnspecifiedType;
|
||
|
el->event()->get<String>(Text::TextTypePropertyName, type);
|
||
|
|
||
|
if (type == Text::Dynamic ||
|
||
|
type == Text::LocalDirection ||
|
||
|
type == Text::UnspecifiedType) {
|
||
|
el->setLayoutY(staff.getLayoutYForHeight(-7) + displacedY);
|
||
|
} else if (type == Text::Lyric) {
|
||
|
long verse = 0;
|
||
|
el->event()->get<Int>(Text::LyricVersePropertyName, verse);
|
||
|
el->setLayoutY(staff.getLayoutYForHeight(-10 - 3 * verse) + displacedY);
|
||
|
} else if (type == Text::Annotation) {
|
||
|
el->setLayoutY(staff.getLayoutYForHeight(-13) + displacedY);
|
||
|
} else {
|
||
|
el->setLayoutY(staff.getLayoutYForHeight(22) + displacedY);
|
||
|
}
|
||
|
|
||
|
} else if (el->event()->isa(Indication::EventType)) {
|
||
|
|
||
|
try {
|
||
|
std::string indicationType =
|
||
|
el->event()->get
|
||
|
<String>(Indication::IndicationTypePropertyName);
|
||
|
|
||
|
if (indicationType == Indication::Slur ||
|
||
|
indicationType == Indication::PhrasingSlur) {
|
||
|
getSlurList(staff).push_back(i);
|
||
|
}
|
||
|
|
||
|
if (indicationType == Indication::OttavaUp ||
|
||
|
indicationType == Indication::QuindicesimaUp) {
|
||
|
el->setLayoutY(staff.getLayoutYForHeight(15) + displacedY);
|
||
|
} else {
|
||
|
el->setLayoutY(staff.getLayoutYForHeight( -9) + displacedY);
|
||
|
}
|
||
|
} catch (...) {
|
||
|
el->setLayoutY(staff.getLayoutYForHeight( -9) + displacedY);
|
||
|
}
|
||
|
|
||
|
} else if (el->event()->isa(Guitar::Chord::EventType)) {
|
||
|
|
||
|
el->setLayoutY(staff.getLayoutYForHeight(22) + displacedY);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
PRINT_ELAPSED("NotationVLayout::scanStaff");
|
||
|
}
|
||
|
|
||
|
void
|
||
|
NotationVLayout::finishLayout(timeT, timeT)
|
||
|
{
|
||
|
START_TIMING;
|
||
|
|
||
|
for (SlurListMap::iterator mi = m_slurs.begin();
|
||
|
mi != m_slurs.end(); ++mi) {
|
||
|
|
||
|
for (SlurList::iterator si = mi->second.begin();
|
||
|
si != mi->second.end(); ++si) {
|
||
|
|
||
|
NotationElementList::iterator i = *si;
|
||
|
NotationStaff &staff = dynamic_cast<NotationStaff &>(*(mi->first));
|
||
|
|
||
|
positionSlur(staff, i);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
PRINT_ELAPSED("NotationVLayout::finishLayout");
|
||
|
}
|
||
|
|
||
|
void
|
||
|
NotationVLayout::positionSlur(NotationStaff &staff,
|
||
|
NotationElementList::iterator i)
|
||
|
{
|
||
|
NotationRules rules;
|
||
|
|
||
|
bool phrasing = ((*i)->event()->get
|
||
|
<String>(Indication::IndicationTypePropertyName)
|
||
|
== Indication::PhrasingSlur);
|
||
|
|
||
|
NotationElementList::iterator scooter = i;
|
||
|
|
||
|
timeT slurDuration = (*i)->event()->getDuration();
|
||
|
if (slurDuration == 0 && (*i)->event()->has("indicationduration")) {
|
||
|
slurDuration = (*i)->event()->get
|
||
|
<Int>("indicationduration"); // obs property
|
||
|
}
|
||
|
timeT endTime = (*i)->getViewAbsoluteTime() + slurDuration;
|
||
|
|
||
|
bool haveStart = false;
|
||
|
|
||
|
int startTopHeight = 4, endTopHeight = 4,
|
||
|
startBottomHeight = 4, endBottomHeight = 4,
|
||
|
maxTopHeight = 4, minBottomHeight = 4,
|
||
|
maxCount = 0, minCount = 0;
|
||
|
|
||
|
int startX = (int)(*i)->getLayoutX(), endX = startX + 10;
|
||
|
bool startStemUp = false, endStemUp = false;
|
||
|
long startMarks = 0, endMarks = 0;
|
||
|
bool startTied = false, endTied = false;
|
||
|
bool beamAbove = false, beamBelow = false;
|
||
|
bool dynamic = false;
|
||
|
|
||
|
std::vector<Event *> stemUpNotes, stemDownNotes;
|
||
|
|
||
|
// Scan the notes spanned by the slur, recording the top and
|
||
|
// bottom heights of the first and last chords, plus the presence
|
||
|
// of any troublesome beams and high or low notes in the body.
|
||
|
|
||
|
while (scooter != staff.getViewElementList()->end()) {
|
||
|
|
||
|
if ((*scooter)->getViewAbsoluteTime() >= endTime)
|
||
|
break;
|
||
|
Event *event = (*scooter)->event();
|
||
|
|
||
|
if (event->isa(Note::EventType)) {
|
||
|
|
||
|
long h = 0;
|
||
|
if (!event->get
|
||
|
<Int>(m_properties.HEIGHT_ON_STAFF, h)) {
|
||
|
KMessageBox::sorry
|
||
|
((QWidget *)parent(), i18n("Spanned note at %1 has no HEIGHT_ON_STAFF property!\nThis is a bug (the program would previously have crashed by now)").arg((*scooter)->getViewAbsoluteTime()));
|
||
|
event->dump(std::cerr);
|
||
|
}
|
||
|
|
||
|
bool stemUp = rules.isStemUp(h);
|
||
|
event->get
|
||
|
<Bool>(m_properties.VIEW_LOCAL_STEM_UP, stemUp);
|
||
|
|
||
|
bool beamed = false;
|
||
|
event->get
|
||
|
<Bool>(m_properties.BEAMED, beamed);
|
||
|
|
||
|
bool primary = false;
|
||
|
|
||
|
if (event->get
|
||
|
<Bool>
|
||
|
(m_properties.CHORD_PRIMARY_NOTE, primary) && primary) {
|
||
|
|
||
|
NotationChord chord(*(staff.getViewElementList()), scooter,
|
||
|
m_notationQuantizer, m_properties);
|
||
|
|
||
|
if (beamed) {
|
||
|
if (stemUp)
|
||
|
beamAbove = true;
|
||
|
else
|
||
|
beamBelow = true;
|
||
|
}
|
||
|
|
||
|
if (!haveStart) {
|
||
|
|
||
|
startBottomHeight = chord.getLowestNoteHeight();
|
||
|
startTopHeight = chord.getHighestNoteHeight();
|
||
|
minBottomHeight = startBottomHeight;
|
||
|
maxTopHeight = startTopHeight;
|
||
|
|
||
|
startX = (int)(*scooter)->getLayoutX();
|
||
|
startStemUp = stemUp;
|
||
|
startMarks = chord.getMarkCountForChord();
|
||
|
|
||
|
bool tied = false;
|
||
|
if ((event->get
|
||
|
<Bool>(TIED_FORWARD, tied) && tied) ||
|
||
|
(event->get<Bool>(TIED_BACKWARD, tied) && tied)) {
|
||
|
startTied = true;
|
||
|
}
|
||
|
|
||
|
haveStart = true;
|
||
|
|
||
|
} else {
|
||
|
if (chord.getLowestNoteHeight() < minBottomHeight) {
|
||
|
minBottomHeight = chord.getLowestNoteHeight();
|
||
|
++minCount;
|
||
|
}
|
||
|
if (chord.getHighestNoteHeight() > maxTopHeight) {
|
||
|
maxTopHeight = chord.getHighestNoteHeight();
|
||
|
++maxCount;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
endBottomHeight = chord.getLowestNoteHeight();
|
||
|
endTopHeight = chord.getHighestNoteHeight();
|
||
|
endX = (int)(*scooter)->getLayoutX();
|
||
|
endStemUp = stemUp;
|
||
|
endMarks = chord.getMarkCountForChord();
|
||
|
|
||
|
bool tied = false;
|
||
|
if ((event->get
|
||
|
<Bool>(TIED_FORWARD, tied) && tied) ||
|
||
|
(event->get<Bool>(TIED_BACKWARD, tied) && tied)) {
|
||
|
endTied = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!beamed) {
|
||
|
if (stemUp)
|
||
|
stemUpNotes.push_back(event);
|
||
|
else
|
||
|
stemDownNotes.push_back(event);
|
||
|
}
|
||
|
|
||
|
} else if (event->isa(Indication::EventType)) {
|
||
|
|
||
|
try {
|
||
|
std::string indicationType =
|
||
|
event->get
|
||
|
<String>(Indication::IndicationTypePropertyName);
|
||
|
|
||
|
if (indicationType == Indication::Crescendo ||
|
||
|
indicationType == Indication::Decrescendo)
|
||
|
dynamic = true;
|
||
|
} catch (...) { }
|
||
|
}
|
||
|
|
||
|
++scooter;
|
||
|
}
|
||
|
|
||
|
bool above = true;
|
||
|
|
||
|
if ((*i)->event()->has(NotationProperties::SLUR_ABOVE) &&
|
||
|
(*i)->event()->isPersistent<Bool>(NotationProperties::SLUR_ABOVE)) {
|
||
|
|
||
|
(*i)->event()->get
|
||
|
<Bool>(NotationProperties::SLUR_ABOVE, above);
|
||
|
|
||
|
} else if (phrasing) {
|
||
|
|
||
|
int score = 0; // for "above"
|
||
|
|
||
|
if (dynamic)
|
||
|
score += 2;
|
||
|
|
||
|
if (startStemUp == endStemUp) {
|
||
|
if (startStemUp)
|
||
|
score -= 2;
|
||
|
else
|
||
|
score += 2;
|
||
|
} else if (beamBelow != beamAbove) {
|
||
|
if (beamAbove)
|
||
|
score -= 2;
|
||
|
else
|
||
|
score += 2;
|
||
|
}
|
||
|
|
||
|
if (maxTopHeight < 6)
|
||
|
score += 1;
|
||
|
else if (minBottomHeight > 2)
|
||
|
score -= 1;
|
||
|
|
||
|
if (stemUpNotes.size() != stemDownNotes.size()) {
|
||
|
if (stemUpNotes.size() < stemDownNotes.size())
|
||
|
score += 1;
|
||
|
else
|
||
|
score -= 1;
|
||
|
}
|
||
|
|
||
|
above = (score >= 0);
|
||
|
|
||
|
} else {
|
||
|
|
||
|
if (startStemUp == endStemUp) {
|
||
|
above = !startStemUp;
|
||
|
} else if (beamBelow) {
|
||
|
above = true;
|
||
|
} else if (beamAbove) {
|
||
|
above = false;
|
||
|
} else if (stemUpNotes.size() != stemDownNotes.size()) {
|
||
|
above = (stemUpNotes.size() < stemDownNotes.size());
|
||
|
} else {
|
||
|
above = ((startTopHeight - 4) + (endTopHeight - 4) +
|
||
|
(4 - startBottomHeight) + (4 - endBottomHeight) <= 8);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// now choose the actual y-coord of the slur based on the side
|
||
|
// we've decided to put it on
|
||
|
|
||
|
int startHeight, endHeight;
|
||
|
int startOffset = 2, endOffset = 2;
|
||
|
|
||
|
if (above) {
|
||
|
|
||
|
if (!startStemUp)
|
||
|
startOffset += startMarks * 2;
|
||
|
else
|
||
|
startOffset += 5;
|
||
|
|
||
|
if (!endStemUp)
|
||
|
endOffset += startMarks * 2;
|
||
|
else
|
||
|
endOffset += 5;
|
||
|
|
||
|
startHeight = startTopHeight + startOffset;
|
||
|
endHeight = endTopHeight + endOffset;
|
||
|
|
||
|
bool maxRelevant = ((maxTopHeight != endTopHeight) || (maxCount > 1));
|
||
|
if (maxRelevant) {
|
||
|
int midHeight = (startHeight + endHeight) / 2;
|
||
|
if (maxTopHeight > midHeight - 1) {
|
||
|
startHeight += maxTopHeight - midHeight + 1;
|
||
|
endHeight += maxTopHeight - midHeight + 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
|
||
|
if (startStemUp)
|
||
|
startOffset += startMarks * 2;
|
||
|
else
|
||
|
startOffset += 5;
|
||
|
|
||
|
if (endStemUp)
|
||
|
endOffset += startMarks * 2;
|
||
|
else
|
||
|
endOffset += 5;
|
||
|
|
||
|
startHeight = startBottomHeight - startOffset;
|
||
|
endHeight = endBottomHeight - endOffset;
|
||
|
|
||
|
bool minRelevant = ((minBottomHeight != endBottomHeight) || (minCount > 1));
|
||
|
if (minRelevant) {
|
||
|
int midHeight = (startHeight + endHeight) / 2;
|
||
|
if (minBottomHeight < midHeight + 1) {
|
||
|
startHeight -= midHeight - minBottomHeight + 1;
|
||
|
endHeight -= midHeight - minBottomHeight + 1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int y0 = staff.getLayoutYForHeight(startHeight),
|
||
|
y1 = staff.getLayoutYForHeight(endHeight);
|
||
|
|
||
|
int dy = y1 - y0;
|
||
|
int length = endX - startX;
|
||
|
int diff = staff.getLayoutYForHeight(0) - staff.getLayoutYForHeight(3);
|
||
|
if (length < diff*10)
|
||
|
diff /= 2;
|
||
|
if (length > diff*3)
|
||
|
length -= diff / 2;
|
||
|
startX += diff;
|
||
|
|
||
|
(*i)->event()->setMaybe<Bool>(NotationProperties::SLUR_ABOVE, above);
|
||
|
(*i)->event()->setMaybe<Int>(m_properties.SLUR_Y_DELTA, dy);
|
||
|
(*i)->event()->setMaybe<Int>(m_properties.SLUR_LENGTH, length);
|
||
|
|
||
|
double displacedX = 0.0, displacedY = 0.0;
|
||
|
|
||
|
long dxRaw = 0;
|
||
|
(*i)->event()->get<Int>(DISPLACED_X, dxRaw);
|
||
|
displacedX = double(dxRaw * m_npf->getNoteBodyWidth()) / 1000.0;
|
||
|
|
||
|
long dyRaw = 0;
|
||
|
(*i)->event()->get<Int>(DISPLACED_Y, dyRaw);
|
||
|
displacedY = double(dyRaw * m_npf->getLineSpacing()) / 1000.0;
|
||
|
|
||
|
(*i)->setLayoutX(startX + displacedX);
|
||
|
(*i)->setLayoutY(y0 + displacedY);
|
||
|
}
|
||
|
|
||
|
}
|