Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Tree: ba582c232f
Fetching contributors…

Cannot retrieve contributors at this time

651 lines (603 sloc) 25.662 kB
//=============================================================================
// MuseScore
// Music Composition & Notation
// $Id:$
//
// 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 "note.h"
#include "tuplet.h"
#include "utils.h"
//---------------------------------------------------------
// cleanupTuplet
//---------------------------------------------------------
static void cleanupTuplet(Tuplet* t)
{
foreach(DurationElement* e, t->elements()) {
if (e->type() == Element::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->isChordRest()) {
ChordRest* cr = static_cast<ChordRest*>(e);
foreach(Spanner* sp, cr->spannerFor())
delete sp;
foreach(Element* el, cr->annotations())
delete el;
delete e;
}
else if (e->type() == Element::TUPLET) {
Tuplet* t = static_cast<Tuplet*>(e);
cleanupTuplet(t);
}
else
delete e;
}
}
//---------------------------------------------------------
// readSpanner
//---------------------------------------------------------
void TrackList::readSpanner(int track, const QList<Spanner*>& spannerFor,
const QList<Spanner*>& spannerBack, ChordRest* dst,
QHash<Spanner*,Spanner*>* map)
{
foreach (Spanner* oldSpanner, spannerFor) {
if (track != -1 && oldSpanner->track() != track)
continue;
Spanner* newSpanner = static_cast<Spanner*>(oldSpanner->clone());
map->insert(oldSpanner, newSpanner);
dst->addSpannerFor(newSpanner);
newSpanner->setStartElement(dst);
int etick = oldSpanner->endTick();
if (etick >= range()->last()->tick()) {
newSpanner->setEndElement(oldSpanner->endElement());
oldSpanner->removeSpannerBack();
map->remove(oldSpanner);
}
}
foreach (Spanner* oldSpanner, spannerBack) {
if (track != -1 && oldSpanner->track() != track)
continue;
Spanner* newSpanner = map->value(oldSpanner);
if (newSpanner) {
dst->addSpannerBack(newSpanner);
newSpanner->setEndElement(dst);
map->remove(oldSpanner);
}
else {
int stick = oldSpanner->startTick();
if (stick < range()->first()->tick()) {
dst->addSpannerBack(oldSpanner);
}
else {
qDebug("readSpanner: %s not found", oldSpanner->name());
// TODO: handle failure case:
// - remove start spanner
// - delete spanner
}
}
}
}
//---------------------------------------------------------
// writeSpanner
//---------------------------------------------------------
void TrackList::writeSpanner(int track, ChordRest* src, ChordRest* dst,
Segment* segment, QHash<Spanner*, Spanner*>* map) const
{
foreach(Spanner* oldSpanner, src->spannerFor()) {
Spanner* newSpanner = static_cast<Spanner*>(oldSpanner->clone());
newSpanner->setTrack(track);
map->insert(oldSpanner, newSpanner);
if (newSpanner->type() == Element::SLUR) {
dst->addSpannerFor(newSpanner);
newSpanner->setStartElement(dst);
}
else {
segment->addSpannerFor(newSpanner);
newSpanner->setStartElement(segment);
}
int etick = oldSpanner->endTick();
if (etick >= range()->last()->tick()) {
newSpanner->setEndElement(oldSpanner->endElement());
newSpanner->addSpannerBack();
map->remove(oldSpanner);
}
}
foreach(Spanner* oldSpanner, src->spannerBack()) {
Spanner* newSpanner = map->value(oldSpanner);
if (newSpanner) {
if (newSpanner->type() == Element::SLUR) {
dst->addSpannerBack(newSpanner);
newSpanner->setEndElement(dst);
}
else {
segment->addSpannerBack(newSpanner);
newSpanner->setEndElement(segment);
}
map->remove(oldSpanner);
}
else {
int stick = oldSpanner->startTick();
if (stick < range()->first()->tick()) {
if (oldSpanner->type() == Element::SLUR) {
dst->addSpannerBack(oldSpanner);
oldSpanner->setEndElement(dst);
}
else {
segment->addSpannerBack(oldSpanner);
oldSpanner->setEndElement(segment);
}
}
else {
qDebug("writeSpanner: %s not found", oldSpanner->name());
// TODO: handle failure case:
// - remove start slur from chord/rest
// - delete slur
}
}
}
foreach(Element* e, src->annotations()) {
Element* element = e->clone();
element->setTrack(track);
segment->add(element);
}
}
//---------------------------------------------------------
// append
//---------------------------------------------------------
void TrackList::append(Element* e, QHash<Spanner*,Spanner*>* map)
{
Q_ASSERT(map);
if (e->isDurationElement()) {
Fraction d = static_cast<DurationElement*>(e)->duration();
_duration += d;
bool accumulateRest = e->type() == Element::REST && !isEmpty()
&& back()->type() == Element::REST;
Segment* s = accumulateRest ? static_cast<Rest*>(e)->segment() : 0;
if (s && s->spannerFor().isEmpty() && s->spannerBack().isEmpty()) {
// 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::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);
ChordRest* dst = static_cast<ChordRest*>(element);
readSpanner(-1, src->spannerFor(), src->spannerBack(), dst, map);
Segment* s = src->segment();
readSpanner(src->track(), s->spannerFor(), s->spannerBack(), dst, map);
foreach(Element* ee, src->segment()->annotations()) {
if (ee->track() == e->track())
dst->annotations().append(ee->clone());
}
}
}
}
else
QList<Element*>::append(e->clone());
}
//---------------------------------------------------------
// appendGap
//---------------------------------------------------------
void TrackList::appendGap(const Fraction& d)
{
if (!isEmpty() && (back()->type() == Element::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);
}
}
//---------------------------------------------------------
// read
//---------------------------------------------------------
void TrackList::read(int track, const Segment* fs, const Segment* es, QHash<Spanner*,Spanner*>* map)
{
int tick = fs->tick();
int gap = 0;
const Segment* s;
for (s = fs; s; s = s->next1()) {
Element* e = s->element(track);
if (e && !e->generated()) {
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) {
qDebug("TrackList::read: cannot start in middle of tuplet");
abort();
}
de = tuplet;
// find last chord/rest in (possibly nested) tuplet:
DurationElement* nde = tuplet;
while (nde) {
nde = tuplet->elements().back();
if (nde->type() != Element::TUPLET)
break;
}
s = static_cast<ChordRest*>(nde)->segment();
// continue with first chord/rest after tuplet
}
if (gap)
appendGap(Fraction::fromTicks(gap));
append(de, map);
// add duration to ticks if not grace note
if (!(de->type() == Element::CHORD && static_cast<Chord*>(de)->isGrace()))
tick += de->duration().ticks();;
}
else if (e->type() == Element::BAR_LINE)
;
else
append(e, map);
}
if (s == es)
break;
}
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::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::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)
printf("Tied note not found\n");
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::SegmentType st;
if (ne->type() == Element::CHORD && static_cast<Chord*>(ne)->noteType() != NOTE_NORMAL)
st = Segment::SegGrace;
else
st = Segment::SegChordRest;
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::TUPLET)
return false;
while (!duration.isZero()) {
if (e->type() == Element::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;
}
//---------------------------------------------------------
// write
// rewrite notes into measure list m
//---------------------------------------------------------
bool TrackList::write(int track, Measure* measure, QHash<Spanner*, Spanner*>* map) 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 (e->type() == Element::CHORD && static_cast<Chord*>(e)->isGrace()) {
segment = m->getSegment(Segment::SegGrace, m->tick() + pos.ticks());
Element* element = e->clone();
element->setTrack(track);
segment->add(element);
writeSpanner(track + i, static_cast<Chord*>(e),
static_cast<Chord*>(element), segment, map);
continue;
}
if (duration > rest && e->type() == Element::TUPLET) {
// cannot split tuplet
qDebug("TrackList::write: cannot split tuplet\n");
return false;
}
//
// split note/rest
//
bool firstCRinSplit = true;
while (duration.numerator() > 0) {
// handle full measure rest
if (e->type() == Element::REST
&& (duration >= rest || e == back())
&& rest == m->len())
{
segment = m->getSegment(e, m->tick() + pos.ticks());
//
// handle full measure rest
//
if ((track % VOICES) == 0) {
// write only for voice 1
Rest* r = new Rest(score, TDuration::V_MEASURE);
r->setDuration(m->len());
r->setTrack(track);
segment->add(r);
if (e->isChordRest() && firstCRinSplit) {
ChordRest* src = static_cast<ChordRest*>(e);
writeSpanner(track, src, r, segment, map);
}
}
duration -= m->len();
pos += m->len();
rest.set(0, 1);
}
else {
Fraction d = qMin(rest, duration);
ChordRest* dst;
if (e->type() == Element::REST) {
segment = m->getSegment(Segment::SegChordRest, m->tick() + pos.ticks());
Rest* r = new Rest(score, TDuration(d));
dst = r;
r->setTrack(track);
segment->add(r);
duration -= d;
rest -= d;
pos += d;
}
else if (e->type() == Element::CHORD) {
segment = m->getSegment(e, m->tick() + pos.ticks());
Chord* c = static_cast<Chord*>(e)->clone();
dst = c;
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::TUPLET) {
writeTuplet(static_cast<Tuplet*>(e), m, m->tick() + pos.ticks());
duration -= d;
rest -= d;
pos += d;
}
if (e->isChordRest() && firstCRinSplit) {
ChordRest* src = static_cast<ChordRest*>(e);
writeSpanner(track, src, dst, segment, map);
}
}
if (pos == m->len()) {
if (m->nextMeasure()) {
m = m->nextMeasure();
rest = m->len();
pos = Fraction();
}
else
rest = Fraction();
}
firstCRinSplit = false;
}
}
else if (e->type() == Element::KEYSIG) {
// keysig has to be at start of measure
}
else if (e->type() == Element::BAR_LINE)
;
else {
if (m == 0)
break;
segment = m->getSegment(e, m->tick() + 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()) {
Element* el = s->element(track);
if (el == 0 || el->type() != Element::CHORD)
continue;
foreach(Note* n, static_cast<Chord*>(el)->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, int startTrack, int endTrack)
{
_first = first;
_last = last;
spannerMap.clear();
for (int track = startTrack; track < endTrack; ++track) {
TrackList* dl = new TrackList(this);
dl->read(track, first, last, &spannerMap);
tracks.append(dl);
}
if (!spannerMap.isEmpty()) {
printf("ScoreRange::read(): dangling Spanner\n");
foreach(Spanner* s, spannerMap) {
printf(" <%s> end %p\n", s->name(), s->endElement());
}
}
}
//---------------------------------------------------------
// write
//---------------------------------------------------------
bool ScoreRange::write(int track, Measure* m) const
{
spannerMap.clear();
int n = tracks.size();
for (int i = 0; i < n; ++i) {
const TrackList* dl = tracks[i];
if (!dl->write(track + i, m, &spannerMap))
return false;
}
if (!spannerMap.isEmpty()) {
printf("ScoreRange::write(): dangling Spanner\n");
foreach(Spanner* s, spannerMap) {
printf(" <%s>\n", s->name());
}
}
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();
}
Jump to Line
Something went wrong with that request. Please try again.