MuseScore/libmscore/check.cpp
2015-04-29 17:00:23 +02:00

287 lines
12 KiB
C++

//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2010-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 "score.h"
#include "slur.h"
#include "measure.h"
#include "tuplet.h"
#include "chordrest.h"
#include "rest.h"
#include "segment.h"
#include "staff.h"
#include "keysig.h"
#include "clef.h"
namespace Ms {
//---------------------------------------------------------
// checkSlurs
// helper routine to check for sanity slurs
//---------------------------------------------------------
void Score::checkSlurs()
{
#if 0 //TODO1
foreach(Element* e, _gel) {
if (e->type() != SLUR)
continue;
Slur* s = (Slur*)e;
Element* n1 = s->startElement();
Element* n2 = s->endElement();
if (n1 == 0 || n2 == 0 || n1 == n2) {
qDebug("unconnected slur: removing");
if (n1) {
((ChordRest*)n1)->removeSlurFor(s);
((ChordRest*)n1)->removeSlurBack(s);
}
if (n1 == 0)
qDebug(" start at %d(%d) not found", s->tick(), s->track());
if (n2 == 0)
qDebug(" end at %d(%d) not found", s->tick2(), s->track2());
if ((n1 || n2) && (n1==n2))
qDebug(" start == end");
int idx = _gel.indexOf(s);
_gel.removeAt(idx);
}
}
#endif
}
//---------------------------------------------------------
// checkScore
//---------------------------------------------------------
void Score::checkScore()
{
if (!firstMeasure())
return;
#if 0
for (Measure* m = firstMeasure(); m; m = m->nextMeasure())
m->segments()->check();
#endif
for (Segment* s = firstMeasure()->first(); s;) {
Segment* ns = s->next1();
if (s->segmentType() & (Segment::Type::ChordRest)) {
bool empty = true;
foreach(Element* e, s->elist()) {
if (e) {
empty = false;
break;
}
}
if (empty) {
// Measure* m = s->measure();
qDebug("checkScore: remove empty ChordRest segment");
// m->remove(s);
}
}
s = ns;
}
checkSlurs();
ChordRest* lcr = 0;
for (int staffIdx = 0; staffIdx < _staves.size(); ++staffIdx) {
int track = staffIdx * VOICES;
int tick = 0;
Staff* st = staff(staffIdx);
for (Segment* s = firstMeasure()->first(Segment::Type::ChordRest); s; s = s->next1(Segment::Type::ChordRest)) {
ChordRest* cr = static_cast<ChordRest*>(s->element(track));
if (!cr)
continue;
if (s->tick() != tick) {
if (lcr) {
Fraction timeStretch = st->timeStretch(lcr->tick());
Fraction f = cr->globalDuration() * timeStretch;
qDebug("Chord/Rest gap at tick %d(%s+%d)-%d(%s) staffIdx %d measure %d (len = %d)",
tick, lcr->name(), f.ticks(),
s->tick(), cr->name(), staffIdx, cr->measure()->no(),
cr->tick() - tick);
}
else {
qDebug("Chord/Rest gap at tick %d-%d(%s) staffIdx %d measure %d (len = %d)",
tick,
s->tick(), cr->name(), staffIdx, cr->measure()->no(),
cr->tick() - tick);
}
#if 0
if (cr->tick() > tick) {
int ttick = tick;
int ticks = cr->tick() - tick;
Fraction f = Fraction::fromTicks(ticks) / st->timeStretch(ttick);
qDebug(" insert %d/%d", f.numerator(), f.denominator());
while (ticks > 0) {
Measure* m = tick2measure(ttick);
int len = ticks;
// split notes on measure boundary
if ((ttick + len) > m->tick() + m->ticks())
len = m->tick() + m->ticks() - ttick;
Fraction timeStretch = st->timeStretch(ttick);
Fraction ff = Fraction::fromTicks(len);
qDebug(" - insert %d/%d", ff.numerator(), ff.denominator());
if (ff.numerator() == 0)
break;
Fraction fff = ff / timeStretch;
QList<Duration> dl = toDurationList(fff, true);
foreach(Duration d, dl) {
Rest* rest = new Rest(this);
rest->setDurationType(d);
rest->setDuration(d.fraction());
rest->setColor(Qt::red);
qDebug(" - Rest %d/%d", d.fraction().numerator(), d.fraction().denominator());
rest->setTrack(track);
Segment* s = m->getSegment(rest, ttick);
s->add(rest);
ttick += (d.fraction() * timeStretch).ticks();
}
ticks -= len;
}
}
#endif
tick = s->tick();
}
Fraction timeStretch = st->timeStretch(tick);
Fraction f = cr->globalDuration() * timeStretch;
// qDebug("%s %d + %d = %d", cr->name(), tick, f.ticks(), tick + f.ticks());
tick += f.ticks();
lcr = cr;
}
}
}
//---------------------------------------------------------
// sanityCheck - Simple check for score
/// Check that voice 1 is complete
/// Check that voices > 1 contains less than measure duration
//---------------------------------------------------------
bool Score::sanityCheck(const QString& name)
{
bool result = true;
int mNumber = 1;
QString error;
for (Measure* m = firstMeasure(); m; m = m->nextMeasure()) {
Fraction mLen = m->len();
int endStaff = staves().size();
for (int staffIdx = 0; staffIdx < endStaff; ++staffIdx) {
Fraction voices[VOICES] = {};
for (Segment* s = m->first(Segment::Type::ChordRest); s; s = s->next(Segment::Type::ChordRest)) {
for (int v = 0; v < VOICES; ++v) {
ChordRest* cr = static_cast<ChordRest*>(s->element(staffIdx* VOICES + v));
if (cr == 0)
continue;
voices[v] += cr->actualFraction();
}
}
if (voices[0] != mLen) {
QString msg = tr("Measure %1 Staff %2 incomplete. Expected: %3; Found: %4").arg(mNumber).arg( staffIdx+1).arg(mLen.print()).arg(voices[0].print());
qDebug() << msg;
error += QString("%1\n").arg(msg);
result = false;
}
for (int v = 1; v < VOICES; ++v) {
if (voices[v] > mLen) {
QString msg = tr("Measure %1, staff %2, voice %3 too long. Expected: %4; Found: %5").arg( mNumber).arg(staffIdx + 1).arg(v+1).arg(mLen.print()).arg(voices[v].print());
qDebug() << msg;
error += QString("%1\n").arg(msg);
result = false;
}
}
}
mNumber++;
}
if (!name.isEmpty()) {
QJsonObject json;
if (result) {
json["result"] = 0;
}
else {
json["result"] = 1;
json["error"] = error.trimmed().replace("\n", "\\n");
}
QJsonDocument jsonDoc(json);
QFile fp(name);
if (!fp.open(QIODevice::WriteOnly)) {
qDebug("Open <%s> failed", qPrintable(name));
return false;
}
fp.write(jsonDoc.toJson(QJsonDocument::Compact));
fp.close();
}
else {
MScore::lastError = error;
}
return result;
}
//---------------------------------------------------------
// checkKeys
/// check that key map is in sync with actual keys
//---------------------------------------------------------
bool Score::checkKeys()
{
bool rc = true;
for (int i = 0; i < nstaves(); ++i) {
Key k = staff(i)->key(0);
for (Measure* m = firstMeasure(); m; m = m->nextMeasure()) {
Segment* s = m->findSegment(Segment::Type::KeySig, m->tick());
if (s) {
Element* element = s->element(i * VOICES);
if (element)
k = static_cast<KeySig*>(element)->key();
}
if (staff(i)->key(m->tick()) != k) {
qDebug("measure %d (tick %d) : key %d, map %d", m->no(), m->tick(), k, staff(i)->key(m->tick()));
rc = false;
}
}
}
return rc;
}
//---------------------------------------------------------
// checkClefs
/// check that clef map is in sync with actual clefs
//---------------------------------------------------------
bool Score::checkClefs()
{
bool rc = true;
for (int i = 0; i < nstaves(); ++i) {
ClefType clef = staff(i)->clef(0);
Measure* cm = nullptr;
for (Measure* m = firstMeasure(); m; m = m->nextMeasure()) {
if (cm) {
Segment* s = cm->findSegment(Segment::Type::Clef, m->tick());
if (s) {
Element* element = s->element(i * VOICES);
if (element)
clef = static_cast<Clef*>(element)->clefType();
}
}
if (staff(i)->clef(m->tick()) != clef) {
qDebug("measure %d (tick %d) : clef %hhd, map %hhd", m->no(), m->tick(), clef, staff(i)->clef(m->tick()));
rc = false;
}
cm = m;
}
}
return rc;
}
}