Skip to content

Commit

Permalink
Merge pull request #3640 from anatoly-os/CustomNoteheads23
Browse files Browse the repository at this point in the history
fix #271198: Add a more flexible way to choose noteheads in drumset for percussion instrument (2.3)
  • Loading branch information
anatoly-os committed May 12, 2018
2 parents 3182aa5 + 60283b0 commit 6257956
Show file tree
Hide file tree
Showing 23 changed files with 833 additions and 266 deletions.
2 changes: 1 addition & 1 deletion libmscore/beam.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1911,7 +1911,7 @@ qDebug("create stem in layout beam, track %d", c->track());
if (c->hook())
score()->undoRemoveElement(c->hook());

QPointF stemPos(c->stemPos());
QPointF stemPos(c->stemPosX() + c->pagePos().x(), c->stemPos().y());
qreal x2 = stemPos.x() - _pagePos.x();
qreal y1 = (x2 - x1) * slope + py1 + _pagePos.y();
qreal y2 = stemPos.y();
Expand Down
16 changes: 8 additions & 8 deletions libmscore/chord.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -734,7 +734,8 @@ void Chord::addLedgerLines()

// check if note horiz. pos. is outside current range
// if more length on the right, increase range
x = note->pos().x();
// ledger lines need the leftmost point of the notehead with a respect of bbox
x = note->pos().x() + note->bboxXShift();
if (x-extraLen < minX) {
minX = x - extraLen;
// minXr = minX - extraLen;
Expand Down Expand Up @@ -1154,10 +1155,9 @@ qreal Chord::centerX() const
return staff()->staffType()->chordStemPosX(this) * spatium();

const Note* note = up() ? upNote() : downNote();
qreal x = note->pos().x();
x += note->headWidth() * .5;
qreal x = note->pos().x() + note->noteheadCenterX();
if (note->mirror()) {
x += note->headWidth() * (up() ? -1.0 : 1.0);
x += note->headBodyWidth() * (up() ? -1.0 : 1.0);
}
return x;
}
Expand Down Expand Up @@ -1872,10 +1872,10 @@ void Chord::layoutPitched()
if (sc->notes().size() > 1) {
// some notes may be further to the right than start note
// allow overlap with those notes to count toward the minimum
qreal snEnd = sn->x() + sn->headWidth();
qreal snEnd = sn->x() + sn->bboxRightPos();
qreal scEnd = snEnd;
for (Note* n : sc->notes())
scEnd = qMax(scEnd, n->x() + n->headWidth());
scEnd = qMax(scEnd, n->x() + n->bboxRightPos());
overlap += scEnd - snEnd;
}
}
Expand Down Expand Up @@ -2798,9 +2798,9 @@ QPointF Chord::layoutArticulation(Articulation* a)
}
if (!staff()->isTabStaff() && !alignToStem) {
if (up())
pos.rx() -= upNote()->headWidth() * .5; // move half-a-note-head to left
pos.rx() -= upNote()->headBodyWidth() * .5; // move half-a-note-head to left
else
pos.rx() += upNote()->headWidth() * .5; // move half-a-note-head to right
pos.rx() += upNote()->headBodyWidth() * .5; // move half-a-note-head to right
}
a->setPos(pos);
a->adjustReadPos();
Expand Down
6 changes: 3 additions & 3 deletions libmscore/chordline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,12 +117,12 @@ void ChordLine::layout()
QPointF p(note->pos());
// chordlines to the right of the note
if (_chordLineType == ChordLineType::FALL || _chordLineType == ChordLineType::DOIT)
setPos(p.x() + note->headWidth() + _spatium * .2, p.y());
setPos(p.x() + note->bboxRightPos() + _spatium * .2, p.y());
// chordlines to the left of the note
if (_chordLineType == ChordLineType::PLOP)
setPos(p.x() + note->headWidth() * .25, p.y() - note->headHeight() * .75);
setPos(p.x() + note->bboxRightPos() * .25, p.y() - note->headHeight() * .75);
if (_chordLineType == ChordLineType::SCOOP) {
qreal x = p.x() + (chord()->up() ? note->headWidth() * .25 : _spatium * -.2);
qreal x = p.x() + (chord()->up() ? note->bboxRightPos() * .25 : _spatium * -.2);
setPos(x, p.y() + note->headHeight() * .75);
}
}
Expand Down
23 changes: 22 additions & 1 deletion libmscore/drumset.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,17 @@ void Drumset::save(Xml& xml) const
if (!isValid(i))
continue;
xml.stag(QString("Drum pitch=\"%1\"").arg(i));
xml.tag("head", int(noteHead(i)));
const NoteHead::Group nh = noteHead(i);
//write custom as Normal notehead group + noteheads tag to keep compatibility with 2.X versions
int saveValue = (nh == NoteHead::Group::HEAD_CUSTOM) ? int(NoteHead::Group::HEAD_NORMAL) : int(nh);
xml.tag("head", saveValue);
if (nh == NoteHead::Group::HEAD_CUSTOM) {
xml.stag("noteheads");
for (int j = 0; j < int(NoteHead::Type::HEAD_TYPES); j++) {
xml.tag(NoteHead::type2name(NoteHead::Type(j)), Sym::id2name(noteHeads(i, NoteHead::Type(j))));
}
xml.etag();
}
xml.tag("line", line(i));
xml.tag("voice", voice(i));
xml.tag("name", name(i));
Expand Down Expand Up @@ -75,6 +85,17 @@ void Drumset::load(XmlReader& e)

if (tag == "head")
_drum[pitch].notehead = NoteHead::Group(e.readInt());
else if (tag == "noteheads") {
_drum[pitch].notehead = NoteHead::Group::HEAD_CUSTOM;
while (e.readNextStartElement()) {
const QStringRef& nhTag(e.name());
int noteType = int(NoteHead::name2type(nhTag.toString()));
if (noteType > int(NoteHead::Type::HEAD_TYPES) - 1 || noteType < 0)
return;

_drum[pitch].noteheads[noteType] = Sym::name2id(e.readElementText());
}
}
else if (tag == "line")
_drum[pitch].line = e.readInt();
else if (tag == "voice")
Expand Down
11 changes: 9 additions & 2 deletions libmscore/drumset.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

#include "mscore.h"
#include "note.h"
#include "sym.h"

namespace Ms {

Expand All @@ -26,7 +27,11 @@ class Xml;

struct DrumInstrument {
QString name;

// if @notehead == HEAD_CUSTOM, use @noteheads to extract custom noteheads
NoteHead::Group notehead; ///< notehead symbol set
SymId noteheads[int(NoteHead::Type::HEAD_TYPES)] = { SymId::noteheadWhole, SymId::noteheadHalf, SymId::noteheadBlack, SymId::noteheadDoubleWhole };

int line; ///< place notehead onto this line
MScore::Direction stemDirection;
int voice;
Expand All @@ -48,10 +53,12 @@ static const int DRUM_INSTRUMENTS = 128;

class Drumset {
DrumInstrument _drum[DRUM_INSTRUMENTS];

public:
bool isValid(int pitch) const { return _drum[pitch].notehead != NoteHead::Group::HEAD_INVALID; }
bool isValid(int pitch) const { return !_drum[pitch].name.isEmpty(); }
NoteHead::Group noteHead(int pitch) const { return _drum[pitch].notehead; }
SymId noteHeads(int pitch, NoteHead::Type t) const { return _drum[pitch].noteheads[int(t)]; }

int line(int pitch) const { return _drum[pitch].line; }
int voice(int pitch) const { return _drum[pitch].voice; }
MScore::Direction stemDirection(int pitch) const { return _drum[pitch].stemDirection; }
Expand Down
30 changes: 13 additions & 17 deletions libmscore/layout.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -609,7 +609,7 @@ qreal Score::layoutChords2(QList<Note*>& notes, bool up)

// accumulate return value
if (!mirror)
maxWidth = qMax(maxWidth, note->headWidth());
maxWidth = qMax(maxWidth, note->bboxRightPos());

// prepare for next iteration
lvisible = note->visible();
Expand Down Expand Up @@ -827,36 +827,29 @@ void Score::layoutChords3(QList<Note*>& notes, Staff* staff, Segment* segment)
++nAcc;
}

qreal hw = note->headWidth(); // actual head width, including note & chord mag
Chord* chord = note->chord();
bool _up = chord->up();
qreal stemX = chord->stemPosX(); // stem position for nominal notehead, but allowing for mag

qreal overlapMirror;
if (chord->stem()) {
qreal stemWidth = chord->stem()->lineWidth();
qreal stemWidth5 = stemWidth * 0.5;
chord->stem()->rxpos() = _up ? stemX - stemWidth5 : stemWidth5;
chord->stem()->rxpos() = _up ? chord->stemPosX() - stemWidth5 : stemWidth5;
overlapMirror = stemWidth;
}
else if (chord->durationType().headType() == NoteHead::Type::HEAD_WHOLE)
overlapMirror = styleD(StyleIdx::stemWidth) * chord->mag() * sp;
else
overlapMirror = 0.0;

qreal x;
qreal x = 0.0;
if (note->mirror()) {
if (_up)
x = stemX - overlapMirror;
else
x = stemX - hw + overlapMirror;
}
else {
if (_up)
x = stemX - hw;
x = chord->stemPosX() - overlapMirror;
else
x = 0.0;
x = -note->headBodyWidth() + overlapMirror;
}
else if (_up)
x = chord->stemPosX() - note->headBodyWidth();

note->rypos() = (note->line() + stepOffset) * stepDistance;
note->rxpos() = x;
Expand All @@ -877,13 +870,16 @@ void Score::layoutChords3(QList<Note*>& notes, Staff* staff, Segment* segment)
//if (chord->stem())
// chord->stem()->rxpos() = _up ? x + hw - stemWidth5 : x + stemWidth5;

qreal xx = x + hw + chord->pos().x();
qreal xx = x + chord->stemPosX() + chord->pos().x();

if (chord->dots()) {
if (chord->up())
upDotPosX = qMax(upDotPosX, xx);
else
downDotPosX = qMax(downDotPosX, xx);
else {
qreal noteheadShift = note->headBodyWidth();
downDotPosX = qMax(downDotPosX, xx + noteheadShift);
}

MScore::Direction dotPosition = note->userDotPosition();

if (dotPosition == MScore::Direction::AUTO && nNotes > 1 && note->visible() && !note->dotsHidden()) {
Expand Down
2 changes: 1 addition & 1 deletion libmscore/line.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -614,7 +614,7 @@ QPointF SLine::linePos(Grip grip, System** sys) const
// chord bbox() is unreliable, look at notes
// this also allows us to more easily ignore ledger lines
for (Note* n : static_cast<Chord*>(cr)->notes())
maxRight = qMax(maxRight, cr->x() + n->x() + n->headWidth());
maxRight = qMax(maxRight, cr->x() + n->x() + n->bboxRightPos());
}
else {
// rest - won't normally happen
Expand Down
87 changes: 85 additions & 2 deletions libmscore/note.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ namespace Ms {
// notehead groups
//---------------------------------------------------------

static const SymId noteHeads[2][int(NoteHead::Group::HEAD_GROUPS)][int(NoteHead::Type::HEAD_TYPES)] = {
//int(NoteHead::Group::HEAD_GROUPS) - 1: "-1" is needed to prevent building CUSTOM_GROUP noteheads set, since it is built by users and keep a specific set of existing noteheads
static const SymId noteHeads[2][int(NoteHead::Group::HEAD_GROUPS) - 1][int(NoteHead::Type::HEAD_TYPES)] = {
// previous non-SMUFL data kept in comments for future reference
{ // down stem
{ SymId::noteheadWhole, SymId::noteheadHalf, SymId::noteheadBlack, SymId::noteheadDoubleWhole },
Expand Down Expand Up @@ -137,6 +138,14 @@ static const char* noteHeadNames[] = {
QT_TRANSLATE_NOOP("noteheadnames", "Alt. Brevis")
};

// same order as NoteHead::Type, partially extracted from master code to support custom noteheads in drumset file
static const char* noteHeadTypeNames[] = {
"auto",
"whole",
"half",
"quarter",
"breve"
};
//---------------------------------------------------------
// noteHead
//---------------------------------------------------------
Expand Down Expand Up @@ -450,6 +459,16 @@ SymId Note::noteHead() const
if (_headType != NoteHead::Type::HEAD_AUTO)
ht = _headType;

if (_headGroup == NoteHead::Group::HEAD_CUSTOM) {
if (chord() && chord()->staff()) {
if (chord()->staff()->isDrumStaff())
return chord()->staff()->part()->instrument(chord()->tick())->drumset()->noteHeads(_pitch, ht);
}
else {
return _cachedNoteheadSym;
}
}

SymId t = noteHead(up, _headGroup, ht);
if (t == SymId::noSym) {
qDebug("invalid notehead %d/%d", int(_headGroup), int(ht));
Expand All @@ -458,10 +477,33 @@ SymId Note::noteHead() const
return t;
}

//---------------------------------------------------------
// bboxRightPos
//
// returns the x of the symbol bbox. It is different from headWidth() because zero point could be different from leftmost bbox position.
//---------------------------------------------------------

qreal Note::bboxRightPos() const
{
const auto& bbox = score()->scoreFont()->bbox(noteHead(), magS());
return bbox.right();
}

//---------------------------------------------------------
// headBodyWidth
//
// returns the width of the notehead "body". It is actual for slashed noteheads like -O-, where O is body.
//---------------------------------------------------------

qreal Note::headBodyWidth() const
{
return headWidth() + 2 * bboxXShift();
}

//---------------------------------------------------------
// headWidth
//
// returns the width of the notehead symbol
// returns the width of the symbol bbox
// or the width of the string representation of the fret mark
//---------------------------------------------------------

Expand All @@ -470,6 +512,27 @@ qreal Note::headWidth() const
return symWidth(noteHead());
}

//---------------------------------------------------------
// bboxXShift
//
// returns the x shift of the notehead bounding box
//---------------------------------------------------------
qreal Note::bboxXShift() const
{
const auto& bbox = score()->scoreFont()->bbox(noteHead(), magS());
return bbox.bottomLeft().x();
}

//---------------------------------------------------------
// noteheadCenterX
//
// returns the x coordinate of the notehead center related to the basepoint of the notehead bbox
//---------------------------------------------------------
qreal Note::noteheadCenterX() const
{
return score()->scoreFont()->width(noteHead(), magS()) / 2 + bboxXShift();
}

//---------------------------------------------------------
// tabHeadWidth
//---------------------------------------------------------
Expand Down Expand Up @@ -2679,6 +2742,26 @@ const char* NoteHead::groupToGroupName(NoteHead::Group group)
}

//---------------------------------------------------------
// type2name
//---------------------------------------------------------
const char* NoteHead::type2name(Type type)
{
return noteHeadTypeNames[int(type) + 1];
}

//---------------------------------------------------------
// name2type
//---------------------------------------------------------

NoteHead::Type NoteHead::name2type(const QString& s)
{
for (int i = 0; i <= int(NoteHead::Type::HEAD_TYPES); ++i) {
if (s.compare(QString(noteHeadTypeNames[i])) == 0)
return NoteHead::Type(i - 1);
}
return NoteHead::Type::HEAD_AUTO;
}
//---------------------------------------------------------
// subtypeName
//---------------------------------------------------------

Expand Down
Loading

0 comments on commit 6257956

Please sign in to comment.