MuseScore/libmscore/range.cpp
2014-08-11 21:17:55 +02:00

604 lines
23 KiB
C++

//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2012 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 "range.h"
#include "measure.h"
#include "segment.h"
#include "rest.h"
#include "chord.h"
#include "score.h"
#include "slur.h"
#include "tie.h"
#include "note.h"
#include "tuplet.h"
#include "barline.h"
#include "utils.h"
#include "staff.h"
#include "excerpt.h"
namespace Ms {
//---------------------------------------------------------
// cleanupTuplet
//---------------------------------------------------------
static void cleanupTuplet(Tuplet* t)
{
foreach(DurationElement* e, t->elements()) {
if (e->type() == Element::Type::TUPLET)
cleanupTuplet(static_cast<Tuplet*>(e));
delete e;
}
}
//---------------------------------------------------------
// TrackList
//---------------------------------------------------------
TrackList::~TrackList()
{
int n = size();
for (int i = 0; i < n; ++i) {
Element* e = at(i);
if (e->type() == Element::Type::TUPLET) {
Tuplet* t = static_cast<Tuplet*>(e);
cleanupTuplet(t);
}
else
delete e;
}
}
//---------------------------------------------------------
// append
//---------------------------------------------------------
void TrackList::append(Element* e)
{
if (e->isDurationElement()) {
Fraction d = static_cast<DurationElement*>(e)->duration();
_duration += d;
bool accumulateRest = e->type() == Element::Type::REST && !isEmpty()
&& back()->type() == Element::Type::REST;
Segment* s = accumulateRest ? static_cast<Rest*>(e)->segment() : 0;
if (s && !s->score()->isSpannerStartEnd(s->tick(), e->track()) && !s->annotations().size()) {
// akkumulate rests
Rest* rest = static_cast<Rest*>(back());
Fraction d = rest->duration();
d += static_cast<Rest*>(e)->duration();
rest->setDuration(d);
}
else
{
Element* element = e->clone();
QList<Element*>::append(element);
if (e->type() == Element::Type::TUPLET) {
Tuplet* srcTuplet = static_cast<Tuplet*>(e);
Tuplet* dstTuplet = static_cast<Tuplet*>(element);
foreach(const DurationElement* de, srcTuplet->elements())
dstTuplet->add(de->clone());
}
else {
ChordRest* src = static_cast<ChordRest*>(e);
Segment* s = src->segment();
foreach(Element* ee, s->annotations()) {
if (ee->track() == e->track())
_range->annotations.push_back({ s->tick(), ee->clone() });
}
}
}
}
else
QList<Element*>::append(e->clone());
}
//---------------------------------------------------------
// appendGap
//---------------------------------------------------------
void TrackList::appendGap(const Fraction& d)
{
if (d.isZero())
return;
Element* e = isEmpty() ? 0 : back();
if (e && (e->type() == Element::Type::REST)) {
Rest* rest = static_cast<Rest*>(back());
Fraction dd = rest->duration();
dd += d;
_duration += d;
rest->setDuration(dd);
}
else {
Rest* rest = new Rest(0);
rest->setDuration(d);
QList<Element*>::append(rest);
_duration += d;
}
}
//---------------------------------------------------------
// read
//---------------------------------------------------------
void TrackList::read(int track, const Segment* fs, const Segment* es)
{
int tick = fs->tick();
int gap = 0;
const Segment* s;
for (s = fs; s && (s != es); s = s->next1()) {
Element* e = s->element(track);
if (!e || e->generated()) {
foreach(Element* ee, s->annotations()) {
if (ee->track() == track)
_range->annotations.push_back({ s->tick(), ee->clone() });
}
continue;
}
if (e->isChordRest()) {
DurationElement* de = static_cast<DurationElement*>(e);
gap = s->tick() - tick;
if (de->tuplet()) {
Tuplet* tuplet = de->tuplet();
if (tuplet->elements().front() != de) {
qFatal("TrackList::read: cannot start in middle of tuplet");
}
de = tuplet;
// find last chord/rest in (possibly nested) tuplet:
DurationElement* nde = tuplet;
while (nde) {
nde = tuplet->elements().back();
if (nde->type() != Element::Type::TUPLET)
break;
}
s = static_cast<ChordRest*>(nde)->segment();
// continue with first chord/rest after tuplet
}
if (gap) {
appendGap(Fraction::fromTicks(gap));
tick += gap;
}
append(de);
tick += de->duration().ticks();;
}
else if (e->type() == Element::Type::BAR_LINE) {
BarLine* bl = static_cast<BarLine*>(e);
if (bl->barLineType() != BarLineType::NORMAL)
append(e);
}
// else if (e->type() == Element::REPEAT_MEASURE) {
// // TODO: copy previous measure contents?
// }
else
append(e);
}
gap = es->tick() - tick;
if (gap)
appendGap(Fraction::fromTicks(gap));
//
// connect ties
//
int n = size();
for (int i = 0; i < n; ++i) {
Element* e = at(i);
if (e->type() != Element::Type::CHORD)
continue;
Chord* chord = static_cast<Chord*>(e);
foreach(Note* n1, chord->notes()) {
Tie* tie = n1->tieFor();
if (!tie)
continue;
for (int k = i+1; k < n; ++k) {
Element* ee = at(k);
if (ee->type() != Element::Type::CHORD)
continue;
Chord* c2 = static_cast<Chord*>(ee);
bool found = false;
foreach(Note* n2, c2->notes()) {
if (n1->pitch() == n2->pitch()) {
tie->setEndNote(n2);
n2->setTieBack(tie);
found = true;
break;
}
}
if (!found)
qDebug("Tied note not found");
break;
}
}
}
}
//---------------------------------------------------------
// writeTuplet
//---------------------------------------------------------
Tuplet* TrackList::writeTuplet(Tuplet* tuplet, Measure* measure, int tick) const
{
Tuplet* dt = tuplet->clone();
dt->setParent(measure);
foreach (DurationElement* e, tuplet->elements()) {
if (e->isChordRest()) {
Element* ne = e->clone();
Segment::Type st = Segment::Type::ChordRest;
Segment* segment = measure->getSegment(st, tick);
segment->add(ne);
dt->add(ne);
}
else {
Tuplet* nt = writeTuplet(static_cast<Tuplet*>(e), measure, tick);
dt->add(nt);
}
tick += e->globalDuration().ticks();
}
return dt;
}
//---------------------------------------------------------
// canWrite
// check if list can be written to measure list m
// check for tuplets crossing barlines
//---------------------------------------------------------
bool TrackList::canWrite(const Fraction& measureLen) const
{
Fraction pos;
Fraction rest = measureLen;
int n = size();
for (int i = 0; i < n; ++i) {
Element* e = at(i);
if (e->isDurationElement()) {
Fraction duration = static_cast<DurationElement*>(e)->duration();
if (duration > rest && e->type() == Element::Type::TUPLET)
return false;
while (!duration.isZero()) {
if (e->type() == Element::Type::REST && duration >= rest && rest == measureLen) {
duration -= rest;
pos = measureLen;
}
else {
Fraction d = qMin(rest, duration);
duration -= d;
rest -= d;
pos += d;
}
if (pos == measureLen) {
pos = Fraction();
rest = measureLen;
}
}
}
}
return true;
}
//---------------------------------------------------------
// dump
//---------------------------------------------------------
void TrackList::dump() const
{
qDebug("TrackList: elements %d, duration %d/%d", size(), _duration.numerator(), _duration.denominator());
for (int i = 0; i < size(); ++i) {
Element* e = at(i);
qDebug(" %s", e->name());
if (e->isDurationElement()) {
Fraction d = static_cast<DurationElement*>(e)->duration();
qDebug(" duration %d/%d", d.numerator(), d.denominator());
}
}
}
//---------------------------------------------------------
// write
// rewrite notes into measure list m
//---------------------------------------------------------
bool TrackList::write(int track, Measure* measure) const
{
Fraction pos;
Measure* m = measure;
Score* score = m->score();
Fraction rest = m->len();
Segment* segment = 0;
int n = size();
for (int i = 0; i < n; ++i) {
Element* e = at(i);
if (e->isDurationElement()) {
Fraction duration = static_cast<DurationElement*>(e)->duration();
if (duration > rest && e->type() == Element::Type::TUPLET) {
// cannot split tuplet
qDebug("TrackList::write: cannot split tuplet");
return false;
}
//
// split note/rest
//
while (duration.numerator() > 0) {
if ((e->type() == Element::Type::REST || e->type() == Element::Type::REPEAT_MEASURE)
&& (duration >= rest || e == back())
&& (rest == m->len()))
{
//
// handle full measure rest
//
segment = m->getSegment(e, m->tick() + pos.ticks());
if ((track % VOICES) == 0) {
// write only for voice 1
Rest* r = new Rest(score, TDuration::DurationType::V_MEASURE);
r->setDuration(m->len());
r->setTrack(track);
segment->add(r);
}
duration -= m->len();
pos += m->len();
rest.set(0, 1);
}
else {
Fraction d = qMin(rest, duration);
if (e->type() == Element::Type::REST || e->type() == Element::Type::REPEAT_MEASURE) {
segment = m->getSegment(Segment::Type::ChordRest, m->tick() + pos.ticks());
Rest* r = new Rest(score, TDuration(d));
r->setTrack(track);
segment->add(r);
duration -= d;
rest -= d;
pos += d;
}
else if (e->type() == Element::Type::CHORD) {
segment = m->getSegment(e, m->tick() + pos.ticks());
Chord* c = static_cast<Chord*>(e)->clone();
c->setScore(score);
c->setTrack(track);
c->setDuration(d);
c->setDurationType(TDuration(d));
segment->add(c);
duration -= d;
rest -= d;
pos += d;
foreach(Note* note, c->notes()) {
if (!duration.isZero() || note->tieFor()) {
Tie* tie = new Tie(score);
note->add(tie);
}
else
note->setTieFor(0);
note->setTieBack(0);
}
}
else if (e->type() == Element::Type::TUPLET) {
writeTuplet(static_cast<Tuplet*>(e), m, m->tick() + pos.ticks());
duration -= d;
rest -= d;
pos += d;
}
}
if (pos == m->len()) {
if (m->nextMeasure()) {
m = m->nextMeasure();
rest = m->len();
pos = Fraction();
}
else {
if (!duration.isZero()) {
qDebug("Tracklist::write: premature end of measure list in track %d, rest %d/%d",
track, duration.numerator(), duration.denominator());
++i;
qDebug("%d elements missing", n-i);
for (; i < n; ++i) {
Element* e = at(i);
qDebug(" <%s>", e->name());
if (e->isChordRest()) {
ChordRest* cr = static_cast<ChordRest*>(e);
qDebug(" %d/%d",
cr->duration().numerator(),
cr->duration().denominator());
}
}
rest = Fraction();
duration = Fraction();
Q_ASSERT(false);
}
}
}
}
}
else if (e->type() == Element::Type::BAR_LINE) {
if (pos.numerator() == 0 && m) {
BarLineType t = static_cast<BarLine*>(e)->barLineType();
Measure* pm = m->prevMeasure();
if (pm)
pm->setEndBarLineType(t,0);
}
}
else {
if (m == nullptr)
break;
// add the element in its own segment;
// but KeySig has to be at start of (current) measure
segment = m->getSegment(e, m->tick() + ((e->type() == Element::Type::KEYSIG) ? 0 : pos.ticks()));
Element* ne = e->clone();
ne->setScore(score);
ne->setTrack(track);
segment->add(ne);
}
}
//
// connect ties
//
for (Segment* s = measure->first(); s; s = s->next1()) {
Chord* chord = static_cast<Chord*>(s->element(track));
if (chord == 0 || chord->type() != Element::Type::CHORD)
continue;
foreach (Note* n, chord->notes()) {
Tie* tie = n->tieFor();
if (!tie)
continue;
Note* nn = searchTieNote(n);
if (nn) {
tie->setEndNote(nn);
nn->setTieBack(tie);
}
}
if (s == segment)
break;
}
return true;
}
//---------------------------------------------------------
// ScoreRange
//---------------------------------------------------------
ScoreRange::~ScoreRange()
{
qDeleteAll(tracks);
}
//---------------------------------------------------------
// canWrite
//---------------------------------------------------------
bool ScoreRange::canWrite(const Fraction& f) const
{
int n = tracks.size();
for (int i = 0; i < n; ++i) {
TrackList* dl = tracks[i];
if (!dl->canWrite(f))
return false;
}
return true;
}
//---------------------------------------------------------
// read
//---------------------------------------------------------
void ScoreRange::read(Segment* first, Segment* last)
{
_first = first;
_last = last;
int startTrack = 0;
int endTrack = first->score()->nstaves() * VOICES;
spanner.clear();
for (auto i : first->score()->spanner()) {
Spanner* s = i.second;
if (s->tick() >= first->tick() && s->tick() < last->tick() &&
s->track() >= startTrack && s->track() < endTrack) {
Spanner* ns = static_cast<Spanner*>(s->clone());
ns->setTick(ns->tick() - first->tick());
ns->setTick2(ns->tick2() - first->tick());
spanner.push_back(ns);
}
}
for (int track = startTrack; track < endTrack; ++track) {
TrackList* dl = new TrackList(this);
dl->read(track, first, last);
tracks.append(dl);
}
}
//---------------------------------------------------------
// write
//---------------------------------------------------------
bool ScoreRange::write(Score* score, int tick) const
{
int track = 0;
for (TrackList* dl : tracks) {
if (!dl->write(track, score->tick2measure(tick)))
return false;
if ((track % VOICES) == 0) {
int staffIdx = track / VOICES;
Staff* ostaff = score->staff(staffIdx);
LinkedStaves* linkedStaves = ostaff->linkedStaves();
if (linkedStaves) {
for (Staff* nstaff : linkedStaves->staves()) {
if (nstaff == ostaff)
continue;
cloneStaff2(ostaff, nstaff, tick, tick + dl->duration().ticks());
}
}
}
++track;
}
for (Spanner* s : spanner) {
s->setTick(s->tick() + first()->tick());
s->setTick2(s->tick2() + first()->tick());
if (s->type() == Element::Type::SLUR) {
Slur* slur = static_cast<Slur*>(s);
if (slur->startCR()->isGrace()) {
Chord* sc = slur->startChord();
int idx = sc->graceIndex();
Chord* dc = static_cast<Chord*>(score->findCR(s->tick(), s->track()));
s->setStartElement(dc->graceNotes()[idx]);
}
else
s->setStartElement(0);
if (slur->endCR()->isGrace()) {
Chord* sc = slur->endChord();
int idx = sc->graceIndex();
Chord* dc = static_cast<Chord*>(score->findCR(s->tick2(), s->track2()));
s->setEndElement(dc->graceNotes()[idx]);
}
else
s->setEndElement(0);
}
score->undoAddElement(s);
}
for (const Annotation& a : annotations) {
Measure* tm = score->tick2measure(a.tick);
Segment *op = static_cast<Segment*>(a.e->parent());
Segment* s = tm->undoGetSegment(op->segmentType(), a.tick);
if (s) {
a.e->setParent(s);
score->undoAddElement(a.e);
}
}
return true;
}
//---------------------------------------------------------
// fill
//---------------------------------------------------------
void ScoreRange::fill(const Fraction& f)
{
int n = tracks.size();
for (int i = 0; i < n; ++i)
tracks[i]->appendGap(f);
}
//---------------------------------------------------------
// duration
//---------------------------------------------------------
Fraction ScoreRange::duration() const
{
return tracks.isEmpty() ? Fraction() : tracks[0]->duration();
}
}