MuseScore/libmscore/staff.cpp
2013-06-26 14:38:23 +02:00

864 lines
28 KiB
C++

//=============================================================================
// 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 "mscore.h"
#include "staff.h"
#include "part.h"
#include "clef.h"
#include "xml.h"
#include "score.h"
#include "bracket.h"
#include "keysig.h"
#include "segment.h"
#include "style.h"
#include "measure.h"
#include "tablature.h"
#include "stafftype.h"
#include "undo.h"
#include "cleflist.h"
#include "timesig.h"
#include "instrtemplate.h"
#include "barline.h"
namespace Ms {
//---------------------------------------------------------
// idx
//---------------------------------------------------------
int Staff::idx() const
{
return _score->staffIdx(this);
}
//---------------------------------------------------------
// bracket
//---------------------------------------------------------
BracketType Staff::bracket(int idx) const
{
if (idx < _brackets.size())
return _brackets[idx]._bracket;
return NO_BRACKET;
}
//---------------------------------------------------------
// bracketSpan
//---------------------------------------------------------
int Staff::bracketSpan(int idx) const
{
if (idx < _brackets.size())
return _brackets[idx]._bracketSpan;
return 0;
}
//---------------------------------------------------------
// setBracket
//---------------------------------------------------------
void Staff::setBracket(int idx, BracketType val)
{
for (int i = _brackets.size(); i <= idx; ++i)
_brackets.append(BracketItem());
_brackets[idx]._bracket = val;
while (!_brackets.isEmpty() && (_brackets.last()._bracket == NO_BRACKET))
_brackets.removeLast();
}
//---------------------------------------------------------
// setBracketSpan
//---------------------------------------------------------
void Staff::setBracketSpan(int idx, int val)
{
Q_ASSERT(idx >= 0);
Q_ASSERT(val >= 0);
for (int i = _brackets.size(); i <= idx; ++i)
_brackets.append(BracketItem());
_brackets[idx]._bracketSpan = val;
}
//---------------------------------------------------------
// addBracket
//---------------------------------------------------------
void Staff::addBracket(BracketItem b)
{
if (!_brackets.isEmpty() && _brackets[0]._bracket == NO_BRACKET) {
_brackets[0] = b;
}
else {
//
// create new bracket level
//
foreach(Staff* s, _score->staves()) {
if (s == this)
s->_brackets.append(b);
else
s->_brackets.append(BracketItem());
}
}
}
//---------------------------------------------------------
// cleanupBrackets
//---------------------------------------------------------
void Staff::cleanupBrackets()
{
int index = idx();
int n = _score->nstaves();
for (int i = 0; i < _brackets.size(); ++i) {
if (_brackets[i]._bracket == NO_BRACKET)
continue;
int span = _brackets[i]._bracketSpan;
if (span > (n - index)) {
span = n - index;
_brackets[i]._bracketSpan = span;
}
}
for (int i = 0; i < _brackets.size(); ++i) {
if (_brackets[i]._bracket == NO_BRACKET)
continue;
int span = _brackets[i]._bracketSpan;
if (span <= 1)
_brackets[i] = BracketItem();
else {
// delete all other brackets with same span
for (int k = i + 1; k < _brackets.size(); ++k) {
if (span == _brackets[k]._bracketSpan)
_brackets[k] = BracketItem();
}
}
}
}
//---------------------------------------------------------
// partName
//---------------------------------------------------------
QString Staff::partName() const
{
return _part->partName();
}
//---------------------------------------------------------
// Staff
//---------------------------------------------------------
Staff::Staff(Score* s)
{
_score = s;
_rstaff = 0;
_part = 0;
_keymap[0] = KeySigEvent(0); // default to C major
_staffType = _score->staffType(PITCHED_STAFF_TYPE);
_small = false;
_invisible = false;
_userDist = .0;
_barLineSpan = 1;
_barLineFrom = 0;
_barLineTo = (lines()-1)*2;
_updateKeymap = true;
_linkedStaves = 0;
_initialClef = ClefTypeList(CLEF_G, CLEF_G);
}
Staff::Staff(Score* s, Part* p, int rs)
{
_score = s;
_rstaff = rs;
_part = p;
_keymap[0] = KeySigEvent(0); // default to C major
_staffType = _score->staffType(PITCHED_STAFF_TYPE);
_small = false;
_invisible = false;
_userDist = .0;
_barLineSpan = 1;
_barLineFrom = 0;
_barLineTo = (lines()-1)*2;
_updateKeymap = true;
_linkedStaves = 0;
_initialClef = ClefTypeList(CLEF_G, CLEF_G);
}
//---------------------------------------------------------
// ~Staff
//---------------------------------------------------------
Staff::~Staff()
{
if (_linkedStaves) {
_linkedStaves->remove(this);
if (_linkedStaves->isEmpty())
delete _linkedStaves;
}
}
//---------------------------------------------------------
// Staff::clefTypeList
//---------------------------------------------------------
ClefTypeList Staff::clefTypeList(int tick) const
{
ClefTypeList ctl = _initialClef;
int track = idx() * VOICES;
for (Segment* s = score()->firstSegment(); s; s = s->next1()) {
if (s->tick() > tick)
break;
if (s->segmentType() != Segment::SegClef)
continue;
if (s->element(track) && !s->element(track)->generated())
ctl = static_cast<Clef*>(s->element(track))->clefTypeList();
}
return ctl;
}
//---------------------------------------------------------
// Staff::clef
//---------------------------------------------------------
ClefType Staff::clef(int tick) const
{
auto i = clefs.upper_bound(tick);
if (i != clefs.begin())
--i;
if (i == clefs.end())
return score()->concertPitch() ? _initialClef._concertClef : _initialClef._transposingClef;
else
return i->second->clefType();
}
ClefType Staff::clef(Segment* segment) const
{
ClefType ct = score()->concertPitch() ? _initialClef._concertClef : _initialClef._transposingClef;
int track = idx() * VOICES;
for (;;) {
segment = segment->prev1(Segment::SegClef);
if (segment == 0)
break;
if (segment->element(track)) {
ct = static_cast<Clef*>(segment->element(track))->clefType();
break;
}
}
return ct;
}
//---------------------------------------------------------
// timeStretch
//---------------------------------------------------------
Fraction Staff::timeStretch(int tick) const
{
TimeSig* timesig = timeSig(tick);
return timesig == 0 ? Fraction(1,1) : timesig->stretch();
}
//---------------------------------------------------------
// timeSig
// lookup time signature before or at tick
//---------------------------------------------------------
TimeSig* Staff::timeSig(int tick) const
{
auto i = timesigs.upper_bound(tick);
if (i != timesigs.begin())
--i;
return (i == timesigs.end()) ? 0 : i->second;
}
//---------------------------------------------------------
// group
//---------------------------------------------------------
const Groups& Staff::group(int tick) const
{
TimeSig* ts = timeSig(tick);
if (ts) {
if (!ts->groups().empty())
return ts->groups();
return Groups::endings(ts->sig());
}
Measure* m = score()->tick2measure(tick);
return Groups::endings(m->timesig());
}
//---------------------------------------------------------
// addClef
//---------------------------------------------------------
void Staff::addClef(Clef* clef)
{
if (clef->generated()) {
if (clef->segment()->tick() == 0)
_initialClef = clef->clefTypeList();
return;
}
if (clef->segment()->measure() == 0)
abort();
int tick = clef->segment()->tick();
clefs.insert(std::pair<int,Clef*>(tick, clef));
}
//---------------------------------------------------------
// addTimeSig
//---------------------------------------------------------
void Staff::addTimeSig(TimeSig* timesig)
{
timesigs[timesig->segment()->tick()] = timesig;
}
//---------------------------------------------------------
// removeClef
//---------------------------------------------------------
void Staff::removeClef(Clef* clef)
{
if (clef->generated())
return;
int tick = clef->segment()->tick();
for (auto i = clefs.lower_bound(tick); i != clefs.upper_bound(tick); ++i) {
if (i->second == clef) {
clefs.erase(i);
return;
}
}
qDebug("Staff::removeClef: Clef at %d not found", tick);
// abort();
}
//---------------------------------------------------------
// removeTimeSig
//---------------------------------------------------------
void Staff::removeTimeSig(TimeSig* timesig)
{
int tick = timesig->segment()->tick();
timesigs.erase(tick);
}
//---------------------------------------------------------
// Staff::key
//
// locates the key sig currently in effect at tick
//---------------------------------------------------------
KeySigEvent Staff::key(int tick) const
{
return _keymap.key(tick);
}
//---------------------------------------------------------
// Staff::nextKeyTick
//
// return the tick at which the key sig after tick is located
// return 0, if no such a key sig
//---------------------------------------------------------
int Staff::nextKeyTick(int tick) const
{
return _keymap.nextKeyTick(tick);
}
//---------------------------------------------------------
// write
//---------------------------------------------------------
void Staff::write(Xml& xml) const
{
int idx = score()->staffIdx(this);
xml.stag(QString("Staff id=\"%1\"").arg(idx + 1));
if (linkedStaves()) {
Score* s = score();
if (s->parentScore())
s = s->parentScore();
foreach(Staff* staff, linkedStaves()->staves()) {
if ((staff->score() == s) && (staff != this))
xml.tag("linkedTo", s->staffIdx(staff) + 1);
}
}
xml.tag("type", score()->staffTypeIdx(_staffType));
if (small() && !xml.excerptmode) // switch small staves to normal ones when extracting part
xml.tag("small", small());
if (invisible())
xml.tag("invisible", invisible());
foreach(const BracketItem& i, _brackets)
xml.tagE("bracket type=\"%d\" span=\"%d\"", i._bracket, i._bracketSpan);
// for economy and consistency, only output "from" and "to" attributes if different from default
int defaultLineFrom = (lines() == 1 ? BARLINE_SPAN_1LINESTAFF_FROM : 0);
int defaultLineTo;
if (_barLineSpan == 0) // if no bar line at all
defaultLineTo = _barLineTo; // whatever the current spanTo is, use as default
else { // if some bar line, default is the default for span target staff
int targetStaffIdx = idx + _barLineSpan - 1;
if (targetStaffIdx >= score()->nstaves()) {
qFatal("bad _barLineSpan %d for staff %d (nstaves %d)",
_barLineSpan, idx, score()->nstaves());
}
int targetStaffLines = score()->staff(targetStaffIdx)->lines();
defaultLineTo = (targetStaffLines == 1 ? BARLINE_SPAN_1LINESTAFF_TO : (targetStaffLines-1) * 2);
}
if (_barLineSpan != 1 || _barLineFrom != defaultLineFrom || _barLineTo != defaultLineTo) {
if(_barLineFrom != defaultLineFrom || _barLineTo != defaultLineTo)
xml.tag(QString("barLineSpan from=\"%1\" to=\"%2\"").arg(_barLineFrom).arg(_barLineTo), _barLineSpan);
else
xml.tag("barLineSpan", _barLineSpan);
}
if (_userDist != 0.0)
xml.tag("distOffset", _userDist / spatium());
xml.etag();
}
//---------------------------------------------------------
// read
//---------------------------------------------------------
void Staff::read(XmlReader& e)
{
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
if (tag == "type") {
StaffType* st = score()->staffType(e.readInt());
if (st) {
_staffType = st;
// set default barLineTo according to staff type (1-line staff bar lines are special)
_barLineFrom = (lines() == 1 ? BARLINE_SPAN_1LINESTAFF_FROM : 0);
_barLineTo = (lines() == 1 ? BARLINE_SPAN_1LINESTAFF_TO : (lines() - 1) * 2);
}
}
else if (tag == "small")
setSmall(e.readInt());
else if (tag == "invisible")
setInvisible(e.readInt());
else if (tag == "keylist")
_keymap.read(e, _score);
else if (tag == "bracket") {
BracketItem b;
b._bracket = BracketType(e.intAttribute("type", -1));
b._bracketSpan = e.intAttribute("span", 0);
_brackets.append(b);
e.readNext();
}
else if (tag == "barLineSpan") {
// WARNING: following statement assumes number of staff lines to be correctly set
// must read attributes before reading the main value
int defaultSpan = (lines() == 1 ? BARLINE_SPAN_1LINESTAFF_FROM : 0);
_barLineFrom = e.attribute("from", QString::number(defaultSpan)).toInt();
// the proper default SpanTo depends upon the barLineSpan
// as we do not know it yet, set a generic (UNKNOWN) default
defaultSpan = UNKNOWN_BARLINE_TO;
_barLineTo = e.intAttribute("to", defaultSpan);
// ready to read the main value...
_barLineSpan = e.readInt();
//...and to adjust the SpanTo value if the source did not provide an explicit value
// if no bar line or single staff span, set _barLineTo to this staff height
// if span to another staff (yet to be read), leave as unknown
// (Score::read() will retrieve the correct height of the target staff)
if (_barLineTo == UNKNOWN_BARLINE_TO && _barLineSpan <= 1)
_barLineTo = lines() == 1 ? BARLINE_SPAN_1LINESTAFF_TO : (lines() - 1) * 2;
}
else if (tag == "distOffset")
_userDist = e.readDouble() * spatium();
else if (tag == "linkedTo") {
int v = e.readInt() - 1;
//
// if this is an excerpt, link staff to parentScore()
//
if (score()->parentScore()) {
Staff* st = score()->parentScore()->staff(v);
if (st)
linkTo(st);
else {
qDebug("staff %d not found in parent", v);
}
}
else {
int idx = score()->staffIdx(this);
if (v < idx)
linkTo(score()->staff(v));
}
}
else
e.unknown();
}
}
//---------------------------------------------------------
// height
//---------------------------------------------------------
qreal Staff::height() const
{
return (lines()-1) * spatium() * _staffType->lineDistance().val();
}
//---------------------------------------------------------
// spatium
//---------------------------------------------------------
qreal Staff::spatium() const
{
return _score->spatium() * mag();
}
//---------------------------------------------------------
// mag
//---------------------------------------------------------
qreal Staff::mag() const
{
return _small ? score()->styleD(ST_smallStaffMag) : 1.0;
}
//---------------------------------------------------------
// setKey
//---------------------------------------------------------
void Staff::setKey(int tick, int st)
{
KeySigEvent ke;
ke.setAccidentalType(st);
setKey(tick, ke);
}
void Staff::setKey(int tick, const KeySigEvent& st)
{
_keymap[tick] = st;
}
//---------------------------------------------------------
// removeKey
//---------------------------------------------------------
void Staff::removeKey(int tick)
{
_keymap.erase(tick);
}
//---------------------------------------------------------
// channel
//---------------------------------------------------------
int Staff::channel(int tick, int voice) const
{
if (_channelList[voice].isEmpty())
return 0;
QMap<int, int>::const_iterator i = _channelList[voice].lowerBound(tick);
if (i == _channelList[voice].begin())
return _channelList[voice].begin().value();
--i;
return i.value();
}
//---------------------------------------------------------
// lines
//---------------------------------------------------------
int Staff::lines() const
{
return _staffType->lines();
}
//---------------------------------------------------------
// setLines
//---------------------------------------------------------
void Staff::setLines(int val)
{
if (val == lines())
return;
//
// create new staff type
//
StaffType* st = _staffType->clone();
if(part() && !partName().isEmpty())
st->setName(partName());
st->setLines(val);
setStaffType(st);
score()->addStaffType(st);
}
//---------------------------------------------------------
// line distance
//---------------------------------------------------------
qreal Staff::lineDistance() const
{
return _staffType->lineDistance().val();
}
//---------------------------------------------------------
// slashStyle
//---------------------------------------------------------
bool Staff::slashStyle() const
{
return _staffType->slashStyle();
}
//---------------------------------------------------------
// setSlashStyle
//---------------------------------------------------------
void Staff::setSlashStyle(bool val)
{
_staffType->setSlashStyle(val);
}
//---------------------------------------------------------
// linkTo
//---------------------------------------------------------
void Staff::linkTo(Staff* staff)
{
if (!_linkedStaves) {
if (staff->linkedStaves()) {
_linkedStaves = staff->linkedStaves();
}
else {
_linkedStaves = new LinkedStaves;
_linkedStaves->add(staff);
staff->setLinkedStaves(_linkedStaves);
}
_linkedStaves->add(this);
}
else {
qDebug("Staff::linkTo: staff already linked\n");
abort();
}
}
//---------------------------------------------------------
// add
//---------------------------------------------------------
void LinkedStaves::add(Staff* staff)
{
_staves.append(staff);
}
//---------------------------------------------------------
// remove
//---------------------------------------------------------
void LinkedStaves::remove(Staff* staff)
{
_staves.removeOne(staff);
}
//---------------------------------------------------------
// primaryStaff
/// if there are linked staves, the primary staff is
/// the one who is played back
//---------------------------------------------------------
bool Staff::primaryStaff() const
{
QList<Staff*> s;
if (!_linkedStaves)
return true;
foreach(Staff* staff, _linkedStaves->staves()) {
if (staff->score() == score())
s.append(staff);
}
return s.front() == this;
}
//---------------------------------------------------------
// setStaffType
//---------------------------------------------------------
void Staff::setStaffType(StaffType* st)
{
if (_staffType == st)
return;
int linesOld = lines();
int linesNew = st->lines();
_staffType = st;
if (linesNew != linesOld) {
int sIdx = score()->staffIdx(this);
if (sIdx < 0) { // staff does not belong to score (yet?)
if (linesNew == 1) { // 1-line staves have special bar lines
_barLineFrom = BARLINE_SPAN_1LINESTAFF_FROM;
_barLineTo = BARLINE_SPAN_1LINESTAFF_TO;
}
else { // set default barLineFrom/to (from first to last staff line)
_barLineFrom = 0;
_barLineTo = (linesNew-1)*2;
}
}
else // update barLineFrom/To in whole score context
score()->updateBarLineSpans(sIdx, linesOld, linesNew /*, true*/);
}
//
// check for right clef-type and fix
// if necessary
//
ClefType ct = clef(0);
StaffGroup csg = clefTable[ct].staffGroup;
if (_staffType->group() != csg) {
switch(_staffType->group()) {
case TAB_STAFF: ct = ClefType(score()->styleI(ST_tabClef)); break;
case PITCHED_STAFF: ct = CLEF_G; break; // TODO: use preferred clef for instrument
case PERCUSSION_STAFF: ct = CLEF_PERC; break;
}
setInitialClef(ct);
}
}
//---------------------------------------------------------
// init
//---------------------------------------------------------
void Staff::init(const InstrumentTemplate* t, const StaffType* staffType, int cidx)
{
// set staff-type-independent parameters
if (cidx > MAX_STAVES) {
setSmall(false);
setInitialClef(t->clefTypes[0]);
}
else {
setSmall(t->smallStaff[cidx]);
setInitialClef(t->clefTypes[cidx]); // initial clef will be fixed to staff-type clef by setStaffType()
setBracket(0, t->bracket[cidx]);
setBracketSpan(0, t->bracketSpan[cidx]);
setBarLineSpan(t->barlineSpan[cidx]);
}
// determine staff type and set number of lines accordingly
// set lines AFTER setting the staff type, so if lines are different, the right staff type is cloned
StaffType* st = 0;
// get staff type if given or from instrument staff type, if not given
// (if none, get default for staff group)
const StaffType* presetStaffType = (staffType ? staffType : StaffType::preset(t->staffTypePreset) );
if (!presetStaffType)
presetStaffType = StaffType::getDefaultPreset(t->staffGroup, 0);
// look for a staff type with same structure among staff types already defined in the score
bool found = false;
foreach (StaffType** scoreStaffType, score()->staffTypes()) {
if ( (*scoreStaffType)->isSameStructure(*presetStaffType) ) {
st = *scoreStaffType; // staff type found in score: use for instrument staff
found = true;
break;
}
}
// if staff type not found in score, use from preset (for staff and adding to score staff types)
if (!found) {
st = presetStaffType->clone();
score()->addStaffType(st);
}
// use selected staff type
setStaffType(st);
// if (st->group() == PITCHED_STAFF) // if PITCHED (in other staff groups num of lines is determined by style)
// setLines(t->staffLines[cidx]); // use number of lines from instr. template
}
//---------------------------------------------------------
// initFromStaffType
//---------------------------------------------------------
void Staff::initFromStaffType(const StaffType* staffType)
{
// get staff type if given (if none, get default preset for default staff group)
const StaffType* presetStaffType = staffType;
if (!presetStaffType)
presetStaffType = StaffType::getDefaultPreset(PITCHED_STAFF, 0);
// look for a staff type with same structure among staff types already defined in the score
StaffType* st = 0;
foreach (StaffType** scoreStaffType, score()->staffTypes()) {
if ( (*scoreStaffType)->isSameStructure(*presetStaffType) ) {
st = *scoreStaffType; // staff type found in score: use for instrument staff
break;
}
}
// if staff type not found in score, use from preset (for staff and adding to score staff types)
if (!st) {
st = presetStaffType->clone();
score()->addStaffType(st);
}
// use selected staff type
setStaffType(st);
}
//---------------------------------------------------------
// spatiumChanged
//---------------------------------------------------------
void Staff::spatiumChanged(qreal oldValue, qreal newValue)
{
_userDist = (_userDist / oldValue) * newValue;
}
//---------------------------------------------------------
// show
//---------------------------------------------------------
bool Staff::show() const
{
return _part->show();
}
//---------------------------------------------------------
// setInitialClef
//---------------------------------------------------------
void Staff::setInitialClef(const ClefTypeList& cl)
{
_initialClef = cl;
}
void Staff::setInitialClef(ClefType ct)
{
_initialClef = ClefTypeList(ct, ct);
}
bool Staff::genKeySig()
{
switch(_staffType->group()) {
case TAB_STAFF:
return false;
case PITCHED_STAFF:
return static_cast<StaffTypePitched*>(_staffType)->genKeysig();
case PERCUSSION_STAFF:
return static_cast<StaffTypePercussion*>(_staffType)->genKeysig();
default:
return true;
}
}
bool Staff::showLedgerLines()
{
switch(_staffType->group()) {
case TAB_STAFF:
return false;
case PITCHED_STAFF:
return static_cast<StaffTypePitched*>(_staffType)->showLedgerLines();
case PERCUSSION_STAFF:
return static_cast<StaffTypePercussion*>(_staffType)->showLedgerLines();
default:
return true;
}
}
}