MuseScore/libmscore/dynamic.cpp

378 lines
13 KiB
C++
Raw Normal View History

2012-05-26 14:26:10 +02:00
//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2002-2011 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 "dynamic.h"
#include "xml.h"
#include "score.h"
#include "measure.h"
#include "system.h"
#include "segment.h"
#include "utils.h"
#include "style.h"
#include "mscore.h"
2013-08-12 14:57:17 +02:00
#include "chord.h"
2013-10-22 12:05:31 +02:00
#include "undo.h"
#include "sym.h"
2012-05-26 14:26:10 +02:00
2013-05-13 18:49:17 +02:00
namespace Ms {
//-----------------------------------------------------------------------------
// Dyn
// see: http://en.wikipedia.org/wiki/File:Dynamic's_Note_Velocity.svg
//-----------------------------------------------------------------------------
struct Dyn {
int velocity; ///< associated midi velocity (0-127, -1 = none)
bool accent; ///< if true add velocity to current chord velocity
const char* tag; // name of dynamics, eg. "fff"
const char* text; // utf8 text of dynamic
};
// variant with ligatures, works for both emmentaler and bravura:
static Dyn dynList[] = {
2012-05-26 14:26:10 +02:00
// dynamic:
{ -1, true, "other-dynamics", "" },
{ 1, false, "pppppp", "<sym>dynamicPiano</sym><sym>dynamicPiano</sym><sym>dynamicPiano</sym><sym>dynamicPiano</sym><sym>dynamicPiano</sym><sym>dynamicPiano</sym>" },
2014-02-21 10:12:30 +01:00
{ 5, false, "ppppp", "<sym>dynamicPiano</sym><sym>dynamicPiano</sym><sym>dynamicPiano</sym><sym>dynamicPiano</sym><sym>dynamicPiano</sym>" },
{ 10, false, "pppp", "<sym>dynamicPiano</sym><sym>dynamicPiano</sym><sym>dynamicPiano</sym><sym>dynamicPiano</sym>" },
{ 16, false, "ppp", "<sym>dynamicPiano</sym><sym>dynamicPiano</sym><sym>dynamicPiano</sym>" },
{ 33, false, "pp", "<sym>dynamicPiano</sym><sym>dynamicPiano</sym>" },
{ 49, false, "p", "<sym>dynamicPiano</sym>" },
{ 64, false, "mp", "<sym>dynamicMezzo</sym><sym>dynamicPiano</sym>" },
{ 80, false, "mf", "<sym>dynamicMezzo</sym><sym>dynamicForte</sym>" },
{ 96, false, "f", "<sym>dynamicForte</sym>" },
{ 112, false, "ff", "<sym>dynamicForte</sym><sym>dynamicForte</sym>" },
{ 126, false, "fff", "<sym>dynamicForte</sym><sym>dynamicForte</sym><sym>dynamicForte</sym>" },
{ 127, false, "ffff", "<sym>dynamicForte</sym><sym>dynamicForte</sym><sym>dynamicForte</sym><sym>dynamicForte</sym>" },
{ 127, false, "fffff", "<sym>dynamicForte</sym><sym>dynamicForte</sym><sym>dynamicForte</sym><sym>dynamicForte</sym><sym>dynamicForte</sym>" },
{ 127, false, "ffffff", "<sym>dynamicForte</sym><sym>dynamicForte</sym><sym>dynamicForte</sym><sym>dynamicForte</sym><sym>dynamicForte</sym><sym>dynamicForte</sym>" },
2012-05-26 14:26:10 +02:00
// accents:
{ 0, true, "fp", "<sym>dynamicForte</sym><sym>dynamicPiano</sym>"},
{ 0, true, "sf", "<sym>dynamicSforzando</sym><sym>dynamicForte</sym>"},
{ 0, true, "sfz", "<sym>dynamicSforzando</sym><sym>dynamicForte</sym><sym>dynamicZ</sym>"},
{ 0, true, "sff", "<sym>dynamicSforzando</sym><sym>dynamicForte</sym><sym>dynamicForte</sym>"},
{ 0, true, "sffz", "<sym>dynamicSforzando</sym><sym>dynamicForte</sym><sym>dynamicForte</sym><sym>dynamicZ</sym>"},
{ 0, true, "sfp", "<sym>dynamicSforzando</sym><sym>dynamicForte</sym><sym>dynamicPiano</sym>"},
{ 0, true, "sfpp", "<sym>dynamicSforzando</sym><sym>dynamicForte</sym><sym>dynamicPiano</sym><sym>dynamicPiano</sym>"},
{ 0, true, "rfz", "<sym>dynamicRinforzando</sym><sym>dynamicForte</sym><sym>dynamicZ</sym>"},
{ 0, true, "rf", "<sym>dynamicRinforzando</sym><sym>dynamicForte</sym>"},
{ 0, true, "fz", "<sym>dynamicForte</sym><sym>dynamicZ</sym>"},
{ 0, true, "m", "<sym>dynamicMezzo</sym>"},
{ 0, true, "r", "<sym>dynamicRinforzando</sym>"},
{ 0, true, "s", "<sym>dynamicSforzando</sym>"},
{ 0, true, "z", "<sym>dynamicZ</sym>"},
{ 0, true, "n", "<sym>dynamicNiente</sym>" }
2012-05-26 14:26:10 +02:00
};
2013-08-12 13:15:46 +02:00
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// Dynamic
//---------------------------------------------------------
Dynamic::Dynamic(Score* s)
: Text(s)
{
setFlags(ElementFlag::MOVABLE | ElementFlag::SELECTABLE | ElementFlag::ON_STAFF);
2012-05-26 14:26:10 +02:00
_velocity = -1;
_dynRange = Range::PART;
setTextStyleType(TextStyleType::DYNAMICS);
_dynamicType = Type::OTHER;
2012-05-26 14:26:10 +02:00
}
Dynamic::Dynamic(const Dynamic& d)
: Text(d)
{
2013-10-22 12:05:31 +02:00
_dynamicType = d._dynamicType;
_velocity = d._velocity;
_dynRange = d._dynRange;
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// velocity
//---------------------------------------------------------
int Dynamic::velocity() const
{
2014-05-21 15:44:16 +02:00
return _velocity <= 0 ? dynList[int(dynamicType())].velocity : _velocity;
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// write
//---------------------------------------------------------
void Dynamic::write(Xml& xml) const
{
2014-08-15 17:20:20 +02:00
if (!xml.canWrite(this))
return;
2012-05-26 14:26:10 +02:00
xml.stag("Dynamic");
xml.tag("subtype", dynamicTypeName());
2014-05-26 18:18:01 +02:00
writeProperty(xml, P_ID::VELOCITY);
writeProperty(xml, P_ID::DYNAMIC_RANGE);
Text::writeProperties(xml, dynamicType() == Type::OTHER);
2012-05-26 14:26:10 +02:00
xml.etag();
}
//---------------------------------------------------------
// read
//---------------------------------------------------------
2013-01-11 18:10:18 +01:00
void Dynamic::read(XmlReader& e)
2012-05-26 14:26:10 +02:00
{
2013-01-11 18:10:18 +01:00
while (e.readNextStartElement()) {
const QStringRef& tag = e.name();
if (tag == "subtype")
setDynamicType(e.readElementText());
else if (tag == "velocity")
2013-01-11 18:10:18 +01:00
_velocity = e.readInt();
else if (tag == "dynType")
_dynRange = Range(e.readInt());
2012-05-26 14:26:10 +02:00
else if (!Text::readProperties(e))
2013-01-11 18:10:18 +01:00
e.unknown();
2012-05-26 14:26:10 +02:00
}
if (textStyleType() == TextStyleType::DEFAULT)
setTextStyleType(TextStyleType::DYNAMICS);
2012-10-24 13:15:46 +02:00
}
//---------------------------------------------------------
// layout
//---------------------------------------------------------
void Dynamic::layout()
{
2016-06-14 16:00:16 +02:00
if (autoplace())
setUserOff(QPointF());
QPointF p(textStyle().offset(spatium()));
2016-08-26 12:43:43 +02:00
if (placeAbove())
2016-07-25 12:29:24 +02:00
p.ry() = staff()->height() - p.ry() + lineHeight();
setPos(p);
2014-03-14 19:10:48 +01:00
Text::layout1();
2013-08-12 14:57:17 +02:00
Segment* s = segment();
2016-06-14 16:00:16 +02:00
if (s) {
int t = track() & ~0x3;
for (int voice = 0; voice < VOICES; ++voice) {
Element* e = s->element(t + voice);
2016-06-27 08:48:58 +02:00
if (!e)
continue;
if (e->isChord()) {
2016-06-14 16:00:16 +02:00
Chord* c = toChord(e);
qreal noteHeadWidth = score()->noteHeadWidth() * c->mag();
if (c->stem() && !c->up()) // stem down
rxpos() += noteHeadWidth * .25; // center on stem + optical correction
else
rxpos() += noteHeadWidth * .5; // center on notehead
}
2013-08-12 14:57:17 +02:00
else
2016-06-14 16:00:16 +02:00
rxpos() += e->width() * .5;
break;
}
2016-07-19 14:36:09 +02:00
}
else
setPos(QPointF()); // for palette
adjustReadPos();
2016-07-19 14:36:09 +02:00
}
//-------------------------------------------------------------------
// doAutoplace
//
// Move Dynamic up or down to avoid collisions with other elements.
//-------------------------------------------------------------------
void Dynamic::doAutoplace()
{
Segment* s = segment();
if (!(s && autoplace()))
return;
2016-08-02 17:00:49 +02:00
qreal minDistance = score()->styleP(StyleIdx::dynamicsMinDistance);
2016-07-19 14:36:09 +02:00
Shape s1 = s->staffShape(staffIdx()).translated(s->pos());
Shape s2 = shape().translated(s->pos());
2016-08-26 12:43:43 +02:00
if (placeAbove()) {
2016-07-19 14:36:09 +02:00
qreal d = s2.minVerticalDistance(s1);
if (d > -minDistance)
rUserYoffset() = -d - minDistance;
}
else {
qreal d = s1.minVerticalDistance(s2);
if (d > -minDistance)
rUserYoffset() = d + minDistance;
2013-08-12 14:57:17 +02:00
}
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// setDynamicType
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
void Dynamic::setDynamicType(const QString& tag)
2012-05-26 14:26:10 +02:00
{
int n = sizeof(dynList)/sizeof(*dynList);
2013-02-14 13:50:59 +01:00
for (int i = 0; i < n; ++i) {
2014-04-14 13:16:46 +02:00
if (dynList[i].tag == tag || dynList[i].text == tag) {
setDynamicType(Type(i));
setXmlText(QString::fromUtf8(dynList[i].text));
2012-05-26 14:26:10 +02:00
return;
}
}
2013-08-12 13:15:46 +02:00
qDebug("setDynamicType: other <%s>", qPrintable(tag));
setDynamicType(Type::OTHER);
setXmlText(tag);
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// dynamicTypeName
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
QString Dynamic::dynamicTypeName() const
2012-05-26 14:26:10 +02:00
{
2014-05-21 15:44:16 +02:00
return dynList[int(dynamicType())].tag;
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// startEdit
//---------------------------------------------------------
void Dynamic::startEdit(MuseScoreView* v, const QPointF& p)
{
Text::startEdit(v, p);
}
2013-10-22 12:05:31 +02:00
//---------------------------------------------------------
// endEdit
//---------------------------------------------------------
void Dynamic::endEdit()
{
Text::endEdit();
if (xmlText() != QString::fromUtf8(dynList[int(_dynamicType)].text))
_dynamicType = Type::OTHER;
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
2012-11-19 10:08:15 +01:00
// reset
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
2012-11-19 10:08:15 +01:00
void Dynamic::reset()
2012-05-26 14:26:10 +02:00
{
2012-11-19 10:08:15 +01:00
Text::reset();
2012-05-26 14:26:10 +02:00
}
2013-10-22 12:05:31 +02:00
//---------------------------------------------------------
// drag
//---------------------------------------------------------
QRectF Dynamic::drag(EditData* ed)
{
QRectF f = Element::drag(ed);
//
// move anchor
//
Qt::KeyboardModifiers km = qApp->keyboardModifiers();
if (km != (Qt::ShiftModifier | Qt::ControlModifier)) {
int si = staffIdx();
Segment* seg = segment();
score()->dragPosition(ed->pos, &si, &seg);
if (seg != segment() || staffIdx() != si) {
2014-03-24 13:23:54 +01:00
QPointF pos1(canvasPos());
2013-10-22 12:05:31 +02:00
score()->undo(new ChangeParent(this, seg, si));
setUserOff(QPointF());
layout();
2014-03-24 13:23:54 +01:00
QPointF pos2(canvasPos());
2013-10-22 12:05:31 +02:00
setUserOff(pos1 - pos2);
ed->startMove = pos2;
}
}
return f;
}
//---------------------------------------------------------
// undoSetDynRange
//---------------------------------------------------------
void Dynamic::undoSetDynRange(Range v)
{
2016-08-02 17:00:49 +02:00
undoChangeProperty(P_ID::DYNAMIC_RANGE, int(v));
}
//---------------------------------------------------------
// getProperty
//---------------------------------------------------------
QVariant Dynamic::getProperty(P_ID propertyId) const
{
2016-08-02 17:00:49 +02:00
switch (propertyId) {
2014-05-26 18:18:01 +02:00
case P_ID::DYNAMIC_RANGE: return int(_dynRange);
case P_ID::VELOCITY: return velocity();
case P_ID::SUBTYPE: return int(_dynamicType);
default:
return Text::getProperty(propertyId);
}
}
//---------------------------------------------------------
// setProperty
//---------------------------------------------------------
bool Dynamic::setProperty(P_ID propertyId, const QVariant& v)
{
switch (propertyId) {
2014-05-26 18:18:01 +02:00
case P_ID::DYNAMIC_RANGE:
_dynRange = Range(v.toInt());
break;
2014-05-26 18:18:01 +02:00
case P_ID::VELOCITY:
_velocity = v.toInt();
break;
2014-05-26 18:18:01 +02:00
case P_ID::SUBTYPE:
_dynamicType = Type(v.toInt());
break;
default:
if (!Text::setProperty(propertyId, v))
return false;
break;
}
triggerLayout();
return true;
}
//---------------------------------------------------------
// propertyDefault
//---------------------------------------------------------
QVariant Dynamic::propertyDefault(P_ID id) const
{
switch(id) {
case P_ID::TEXT_STYLE_TYPE:
return int(TextStyleType::DYNAMICS);
case P_ID::DYNAMIC_RANGE:
return int(Range::PART);
case P_ID::VELOCITY:
return -1;
default:
return Text::propertyDefault(id);
}
}
//---------------------------------------------------------
// accessibleInfo
//---------------------------------------------------------
2016-02-04 17:06:32 +01:00
QString Dynamic::accessibleInfo() const
{
return QString("%1: %2").arg(Element::accessibleInfo()).arg(this->dynamicTypeName());
}
2013-05-13 18:49:17 +02:00
}