File indexing completed on 2024-05-12 16:34:15

0001 /* This file is part of the KDE project
0002  * Copyright (C) 2007 Marijn Kruisselbrink <mkruisselbrink@kde.org>
0003  *
0004  * This library is free software; you can redistribute it and/or
0005  * modify it under the terms of the GNU Library General Public
0006  * License as published by the Free Software Foundation; either
0007  * version 2 of the License, or (at your option) any later version.
0008  *
0009  * This library is distributed in the hope that it will be useful,
0010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012  * Library General Public License for more details.
0013  *
0014  * You should have received a copy of the GNU Library General Public License
0015  * along with this library; see the file COPYING.LIB.  If not, write to
0016  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0017  * Boston, MA 02110-1301, USA.
0018  */
0019 #include "Engraver.h"
0020 #include "core/Bar.h"
0021 #include "core/Sheet.h"
0022 #include "core/Voice.h"
0023 #include "core/Part.h"
0024 #include "core/VoiceBar.h"
0025 #include "core/VoiceElement.h"
0026 #include "core/Clef.h"
0027 #include "core/Staff.h"
0028 #include "core/StaffSystem.h"
0029 #include "core/KeySignature.h"
0030 #include "core/TimeSignature.h"
0031 #include "core/Chord.h"
0032 #include "core/Note.h"
0033 
0034 #include <limits.h>
0035 #include <math.h>
0036 
0037 #include <QList>
0038 #include <QVector>
0039 #include <QVarLengthArray>
0040 #include <QMultiMap>
0041 
0042 #ifndef log2
0043 # define log2(x) (log(x) / M_LN2)
0044 #endif
0045 
0046 using namespace MusicCore;
0047 
0048 Engraver::Engraver()
0049 {
0050 }
0051 
0052 void Engraver::engraveSheet(Sheet* sheet, int firstSystem, QSizeF size, bool doEngraveBars, int* lastSystem)
0053 {
0054     *lastSystem = -1;
0055     int firstBar = 0;
0056     if (firstSystem != 0) {
0057         firstBar = sheet->staffSystem(firstSystem)->firstBar();
0058     }
0059 
0060     //debugMusic << "Engraving from firstSystem:" << firstSystem << "firstBar:" << firstBar;
0061 
0062     if (doEngraveBars || true) {
0063         // engrave all bars in the sheet
0064         for (int i = firstBar; i < sheet->barCount(); i++) {
0065             engraveBar(sheet->bar(i));
0066         }
0067     }
0068 
0069     // now layout bars in staff systems
0070     int curSystem = firstSystem;
0071     qreal deltay = sheet->staffSystem(firstSystem)->top() - sheet->staffSystem(0)->top();
0072     QPointF p(0, sheet->staffSystem(curSystem)->top() - deltay);
0073     int lastStart = firstBar;
0074     qreal lineWidth = size.width();
0075     qreal indent = sheet->staffSystem(curSystem)->indent();
0076     lineWidth -= indent;
0077     if (firstBar > 0) {
0078         p.setX(indent - sheet->bar(firstBar-1)->prefix());
0079     }
0080     bool prevPrefixPlaced = firstBar != 0;
0081     for (int i = firstBar; i < sheet->barCount(); i++) {
0082         Bar* bar = sheet->bar(i);
0083         bool prefixPlaced = false;
0084         if (i > 0 && p.x() + bar->naturalSize() + bar->prefix() - indent > lineWidth) {
0085             // scale all sizes
0086             // first calculate the total scalable width and total fixed width of all preceding bars
0087             qreal scalable = 0, fixed = 0;
0088             for (int j = lastStart; j < i; j++) {
0089                 scalable += sheet->bar(j)->size();
0090                 fixed += sheet->bar(j)->prefix();
0091             }
0092             fixed += bar->prefix();
0093             if (prevPrefixPlaced) {
0094                 fixed -= sheet->bar(lastStart)->prefix();
0095             }
0096 
0097             // if line is too width, half sizefactor until it is small enough
0098             qreal minFactor = 1.0;
0099             if (scalable + fixed > lineWidth) {
0100                 for (int lim = 0; lim < 32; lim++) {
0101                     minFactor /= 2;
0102                     qreal newSize = engraveBars(sheet, lastStart, i-1, minFactor);
0103                     if (newSize <= lineWidth) break;
0104                 }
0105             }
0106             // double sizefactor until line becomes too width
0107             qreal maxFactor = 2.0;
0108             for (int lim = 0; lim < 32; lim++) {
0109                 qreal newSize = engraveBars(sheet, lastStart, i-1, maxFactor);
0110                 if (newSize >= lineWidth) break;
0111                 maxFactor *= 2;
0112             }
0113             // now binary search between min and max factor for ideal factor
0114             while (minFactor < maxFactor - 1e-4) {
0115                 qreal middle = (minFactor + maxFactor) / 2;
0116                 qreal newSize = engraveBars(sheet, lastStart, i-1, middle);
0117                 if (newSize > lineWidth) {
0118                     maxFactor = middle;
0119                 } else {
0120                     minFactor = middle;
0121                 }
0122             }
0123 
0124             QPointF sp = sheet->bar(lastStart)->position() - QPointF(sheet->bar(lastStart)->prefix(), 0);
0125             for (int j = lastStart; j < i; j++) {
0126                 sheet->bar(j)->setPosition(sp + QPointF(sheet->bar(j)->prefix(), 0));
0127                 sp.setX(sp.x() + sheet->bar(j)->size() + sheet->bar(j)->prefix());
0128             }
0129             lastStart = i;
0130             if (bar->prefix() > 0) {
0131                 bar->setPrefixPosition(sp);
0132                 prefixPlaced = true;
0133             }
0134             prevPrefixPlaced = prefixPlaced;
0135 
0136             curSystem++;
0137             p.setY(sheet->staffSystem(curSystem)->top() - deltay);
0138             sheet->staffSystem(curSystem)->setFirstBar(i);
0139 
0140             indent = 0;
0141             QList<Clef*> clefs;
0142             // Extra space for clef/key signature repeating
0143             for (int partIdx = 0; partIdx < sheet->partCount(); partIdx++) {
0144                 Part* part = sheet->part(partIdx);
0145                 for (int staffIdx = 0; staffIdx < part->staffCount(); staffIdx++) {
0146                     Staff* staff = part->staff(staffIdx);
0147                     qreal w = 0;
0148                     Clef* clef = staff->lastClefChange(i, 0);
0149                     if (clef) {
0150                         w += clef->width() + 15;
0151                         clefs.append(clef);
0152                     }
0153                     KeySignature* ks = staff->lastKeySignatureChange(i);
0154                     if (ks) w += ks->width() + 15;
0155                     if (w > indent) indent = w;
0156                 }
0157             }
0158             sheet->staffSystem(curSystem)->setIndent(indent);
0159             sheet->staffSystem(curSystem)->setLineWidth(lineWidth);
0160             sheet->staffSystem(curSystem)->setClefs(clefs);
0161             lineWidth = size.width() - indent;
0162             p.setX(indent - bar->prefix());
0163 
0164             if (p.y() + sheet->staffSystem(curSystem)->height() >= size.height()) {
0165                 *lastSystem = curSystem-1;
0166 
0167                 // some code depends on having the position of the next bar
0168                 sheet->bar(i)->setPosition(p + QPointF(bar->prefix(), 0), !prefixPlaced);
0169                 sheet->bar(i)->setSize(sheet->bar(i)->naturalSize());
0170 
0171                 break;
0172             }
0173         }
0174         sheet->bar(i)->setPosition(p + QPointF(bar->prefix(), 0), !prefixPlaced);
0175         sheet->bar(i)->setSize(sheet->bar(i)->naturalSize());
0176         p.setX(p.x() + sheet->bar(i)->size() + bar->prefix());
0177     }
0178     if (*lastSystem == -1) *lastSystem = curSystem;
0179     // potentially scale last staff system if it is too wide
0180     if (p.x() - indent > lineWidth) {
0181         qreal scalable = 0, fixed = 0;
0182         for (int j = lastStart; j < sheet->barCount(); j++) {
0183             scalable += sheet->bar(j)->size();
0184             fixed += sheet->bar(j)->prefix();
0185         }
0186         // now scale factor is (available width - fixed) / scalable width;
0187         qreal factor = (lineWidth - fixed) / scalable;
0188         Q_UNUSED(factor);
0189         QPointF sp = sheet->bar(lastStart)->position() - QPointF(sheet->bar(lastStart)->prefix(), 0);
0190         for (int j = lastStart; j < sheet->barCount(); j++) {
0191             //sheet->bar(j)->setPosition(sp + QPointF(sheet->bar(j)->prefix(), 0));
0192             //sheet->bar(j)->setSize(sheet->bar(j)->desiredSize() * factor);
0193             sp.setX(sp.x() + sheet->bar(j)->size() + sheet->bar(j)->prefix());
0194         }
0195     }
0196 
0197     sheet->setStaffSystemCount(curSystem+1);
0198 }
0199 
0200 qreal Engraver::engraveBars(Sheet* sheet, int firstBar, int lastBar, qreal sizeFactor)
0201 {
0202     qreal size = 0;
0203     for (int i = firstBar; i <= lastBar; i++) {
0204         engraveBar(sheet->bar(i), sizeFactor);
0205         size += sheet->bar(i)->size() + sheet->bar(i)->prefix();
0206     }
0207     return size;
0208 }
0209 
0210 struct Simultanity {
0211     int startTime;
0212     int duration; ///< the duration of this simultanity (as in the startTime of the next one minus start time of this one)
0213     int minChordDuration; ///< the duration of the shortest note not yet finished at this time
0214     qreal space;
0215     QList<VoiceElement*> voiceElements;
0216     Simultanity(int time) : startTime(time), duration(0), minChordDuration(0), space(0) {}
0217 };
0218 
0219 static void collectSimultanities(Sheet* sheet, int barIdx, QList<Simultanity>& simultanities, int& shortestNote)
0220 {
0221     Bar* bar = sheet->bar(barIdx);
0222 
0223     // collect all voices in all parts
0224     QList<VoiceBar*> voices;
0225     QList<int> voiceIds;
0226     for (int p = 0; p < sheet->partCount(); p++) {
0227         Part* part = sheet->part(p);
0228         for (int v = 0; v < part->voiceCount(); v++) {
0229             voices.append(bar->voice(part->voice(v)));
0230             voiceIds.append(v);
0231         }
0232     }
0233 
0234     QVarLengthArray<int> nextTime(voices.size());
0235     QVarLengthArray<int> nextIndex(voices.size());
0236     // initialize stuff to 0
0237     for (int i = 0; i < voices.size(); i++) {
0238         nextTime[i] = 0;
0239         nextIndex[i] = 0;
0240     }
0241 
0242     QMultiMap<Staff*, VoiceBar*> staffVoices;
0243     foreach (VoiceBar* vb, voices) {
0244         for (int e = 0; e < vb->elementCount(); e++) {
0245             Staff* s = vb->element(e)->staff();
0246             if (!staffVoices.contains(s, vb)) staffVoices.insert(s, vb);
0247         }
0248     }
0249 
0250     shortestNote = INT_MAX;
0251     // loop until all elements are placed
0252     for (;;) {
0253         // find earliest start time
0254         int time = INT_MAX;
0255         for (int i = 0; i < voices.size(); i++) {
0256             if (nextIndex[i] < voices[i]->elementCount()) {
0257                 if (nextTime[i] < time) time = nextTime[i];
0258             }
0259         }
0260 
0261         // none found, break
0262         if (time == INT_MAX) break;
0263 
0264         // now add the correct items to a new simultanity
0265         Simultanity sim(time);
0266         for (int i = 0; i < voices.size(); i++) {
0267             if (nextTime[i] == time && nextIndex[i] < voices[i]->elementCount()) {
0268                 sim.voiceElements.append(voices[i]->element(nextIndex[i]));
0269                 nextTime[i] += voices[i]->element(nextIndex[i])->length();
0270                 shortestNote = qMin(shortestNote, voices[i]->element(nextIndex[i])->length());
0271                 nextIndex[i]++;
0272             }
0273         }
0274 
0275         // figure out the length of the shortest note with index = nextIndex[i]-1
0276         int minLength = INT_MAX;
0277         for (int i = 0; i < voices.size(); i++) {
0278             if (nextIndex[i] && nextTime[i] > time) {
0279                 minLength = qMin(minLength, voices[i]->element(nextIndex[i]-1)->length());
0280             }
0281         }
0282         sim.minChordDuration = minLength;
0283 
0284         simultanities.append(sim);
0285     }
0286 
0287     // now fill in the duration of the simultanities
0288     for (int i = 0; i < simultanities.size() - 1; i++) {
0289         simultanities[i].duration = simultanities[i+1].startTime - simultanities[i].startTime;
0290     }
0291     if (simultanities.size()) {
0292         Simultanity& sim = simultanities[simultanities.size() - 1];
0293         sim.duration = 0;
0294         for (int i = 0; i < sim.voiceElements.size(); i++) {
0295             sim.duration = qMax(sim.duration, sim.voiceElements[i]->length());
0296         }
0297     }
0298 }
0299 
0300 void Engraver::engraveBar(Bar* bar, qreal sizeFactor)
0301 {
0302 
0303     Sheet* sheet = bar->sheet();
0304     int barIdx = sheet->indexOfBar(bar);
0305 
0306     QList<Simultanity> simultanities;
0307     int shortestNoteLength; // 'm' in the formula
0308     // collect simultanities
0309     collectSimultanities(sheet, barIdx, simultanities, shortestNoteLength);
0310 
0311     // 'T' in the formula
0312     qreal baseFactor = bar->sizeFactor() * sizeFactor - log2((qreal) qMin(shortestNoteLength, (int) Note8Length) / WholeLength);
0313 
0314     // assign space to simultanities according to durations
0315     for (int i = 0; i < simultanities.size(); i++) {
0316         Simultanity& sim = simultanities[i];
0317 
0318         qreal scaleFactor = (qreal) sim.duration / sim.minChordDuration; // 'e' in the formula
0319         if (scaleFactor > 1) scaleFactor = 1;
0320         qreal duration = (qreal) sim.duration / WholeLength;
0321         sim.space = scaleFactor * ( log2(duration) + baseFactor );
0322     }
0323 
0324     // give voice elements positions according to space assigned
0325     qreal noteHeadSize = 7.0;
0326 
0327     qreal curx = 15.0;
0328     for (int s = 0; s < simultanities.size(); s++) {
0329         Simultanity& sim = simultanities[s];
0330         foreach (VoiceElement* ve, sim.voiceElements) {
0331             ve->setX(curx - ve->beatline());
0332         }
0333         curx += sim.space * noteHeadSize;
0334     }
0335 
0336     // collect all voices in all parts
0337     QList<VoiceBar*> voices;
0338     QList<int> voiceIds;
0339     for (int p = 0; p < sheet->partCount(); p++) {
0340         Part* part = sheet->part(p);
0341         for (int v = 0; v < part->voiceCount(); v++) {
0342             voices.append(bar->voice(part->voice(v)));
0343             rebeamBar(part, bar->voice(part->voice(v)));
0344             voiceIds.append(v);
0345         }
0346     }
0347 
0348     QVarLengthArray<int> nextTime(voices.size());
0349     QVarLengthArray<int> nextIndex(voices.size());
0350     // initialize stuff to 0
0351     for (int i = 0; i < voices.size(); i++) {
0352         nextTime[i] = 0;
0353         nextIndex[i] = 0;
0354     }
0355 
0356     // collect staff elements in all staffs
0357     int staffCount = 0;
0358     for (int p = 0; p < sheet->partCount(); p++) {
0359         staffCount += sheet->part(p)->staffCount();
0360     }
0361 
0362     QVarLengthArray<QList<StaffElement*> > staffElements(staffCount);
0363 
0364     for (int st = 0, p = 0; p < sheet->partCount(); ++p) {
0365         Part* part = sheet->part(p);
0366         for (int s = 0; s < part->staffCount(); s++, st++) {
0367             Staff* staff = part->staff(s);
0368             for (int i = 0; i < bar->staffElementCount(staff); i++) {
0369                 staffElements[st].append(bar->staffElement(staff, i));
0370             }
0371         }
0372     }
0373 
0374     QMultiMap<Staff*, VoiceBar*> staffVoices;
0375     foreach (VoiceBar* vb, voices) {
0376         for (int e = 0; e < vb->elementCount(); e++) {
0377             Staff* s = vb->element(e)->staff();
0378             if (!staffVoices.contains(s, vb)) staffVoices.insert(s, vb);
0379         }
0380     }
0381 
0382     qreal x = 0; // this is the end position of the last placed elements
0383     bool endOfPrefix = false;
0384     QList<StaffElement*> prefix;
0385     // loop until all elements are placed
0386     for (;;) {
0387         // find earliest start time
0388         int time = INT_MAX;
0389         for (int i = 0; i < voices.size(); i++) {
0390             if (nextIndex[i] < voices[i]->elementCount()) {
0391                 if (nextTime[i] < time) time = nextTime[i];
0392             }
0393         }
0394 
0395         bool staffElement = false;
0396         int priority = INT_MIN;
0397         for (int s = 0; s < staffCount; s++) {
0398             if (staffElements[s].size() > 0) {
0399                 if (staffElements[s][0]->startTime() <= time) {
0400                     time = staffElements[s][0]->startTime();
0401                     staffElement = true;
0402                     if (staffElements[s][0]->priority() > priority) {
0403                         priority = staffElements[s][0]->priority();
0404                     }
0405                 }
0406             }
0407         }
0408 
0409         if ((!staffElement || time > 0) && !endOfPrefix) {
0410             // we've reached the end of the prefix; now update all already placed staff elements to have correct
0411             // (negative) x coordinates, and set the size of the prefix.
0412             if (prefix.size() > 0) {
0413                 qreal prefixSize = x + 5;
0414                 bar->setPrefix(prefixSize);
0415                 foreach (StaffElement* se, prefix) {
0416                     se->setX(se->x() - prefixSize);
0417                 }
0418                 x = 0;
0419             } else {
0420                 bar->setPrefix(0.0);
0421             }
0422             endOfPrefix = true;
0423         }
0424 
0425         // none found, break
0426         if (time == INT_MAX) break;
0427 
0428         qreal maxEnd = x;
0429         // now update all items with correct start time
0430         if (staffElement) {
0431             for (int s = 0; s < staffCount; s++) {
0432                 if (staffElements[s].size() > 0 && staffElements[s][0]->startTime() == time && staffElements[s][0]->priority() == priority) {
0433                     StaffElement* se = staffElements[s].takeAt(0);
0434                     qreal xpos = x + 15;
0435                     se->setX(xpos);
0436                     qreal xend = se->width() + xpos;
0437                     if (xend > maxEnd) maxEnd = xend;
0438                     if (!endOfPrefix) prefix.append(se);
0439                 }
0440             }
0441         } else {
0442             for (int i = 0; i < voices.size(); i++) {
0443                 if (nextTime[i] == time && nextIndex[i] < voices[i]->elementCount()) {
0444                     // If it is a chord, also figure out correct stem direction for the chord; right now
0445                     // direction is only based on position of the notes, but in the future this should
0446                     // also depend on other chord in other voices in the same staff
0447                     Chord* c = dynamic_cast<Chord*>(voices[i]->element(nextIndex[i]));
0448                     if (c) {
0449                         // if this is the continuation or end of a beam, the first chord in the beam has the
0450                         // correct stem direction already
0451                         if (c->beamType(0) == BeamContinue || c->beamType(0) == BeamEnd) {
0452                             c->setStemDirection(c->beamStart(0)->stemDirection());
0453                         } else if (c->beamType(0) == BeamStart) {
0454                             // for the start of a beam, check all the other chords in the beam to determine
0455                             // the correct direction
0456                             if (staffVoices.count(c->staff()) > 1) {
0457                                 int voiceIdx = voiceIds[i];
0458                                 c->setStemDirection(voiceIdx & 1 ? StemDown : StemUp);
0459                             } else {
0460                                 int numUp = 0;
0461                                 int numDown = 0;
0462                                 const Chord* endChord = c->beamEnd(0);
0463                                 for (int j = nextIndex[i]; j < voices[i]->elementCount(); j++) {
0464                                     Chord* chord = dynamic_cast<Chord*>(voices[i]->element(j));
0465                                     if (!chord) continue;
0466                                     if (chord->desiredStemDirection() == StemUp) {
0467                                         numUp++;
0468                                     } else {
0469                                         numDown++;
0470                                     }
0471                                     if (chord == endChord) break;
0472                                 }
0473                                 if (numUp > numDown) {
0474                                     c->setStemDirection(StemUp);
0475                                 } else if (numUp < numDown) {
0476                                     c->setStemDirection(StemDown);
0477                                 } else {
0478                                     c->setStemDirection(c->desiredStemDirection());
0479                                 }
0480                             }
0481                         } else {
0482                             Staff* staff = c->staff();
0483                             if (staffVoices.count(staff) > 1) {
0484                                 int voiceIdx = voiceIds[i];
0485                                 c->setStemDirection(voiceIdx & 1 ? StemDown : StemUp);
0486                             } else {
0487                                 c->setStemDirection(c->desiredStemDirection());
0488                             }
0489                         }
0490                     }
0491 
0492                     qreal xpos = x + 15;
0493                     //voices[i]->element(nextIndex[i])->setX(xpos);
0494                     qreal xend = voices[i]->element(nextIndex[i])->width() + xpos;
0495                     if (xend > maxEnd) maxEnd = xend;
0496                     nextTime[i] += voices[i]->element(nextIndex[i])->length();
0497                     nextIndex[i]++;
0498                 }
0499             }
0500         }
0501 
0502         x = maxEnd;
0503     }
0504     if (curx < 50) curx = 50;
0505     bar->setSize(curx);
0506 
0507     // finally calculate correct stem lengths for all beamed groups of notes
0508     foreach (VoiceBar* vb, voices) {
0509         for (int i = 0; i < vb->elementCount(); i++) {
0510             Chord* c = dynamic_cast<Chord*>(vb->element(i));
0511             if (!c) continue;
0512             if (c->beamType(0) == BeamStart) {
0513                 // fetch all chords in the beam
0514                 QList<Chord*> chords;
0515                 QVector<QPointF> stemEnds;
0516                 for (int j = i; j < vb->elementCount(); j++) {
0517                     Chord* chord = dynamic_cast<Chord*>(vb->element(j));
0518                     if (!chord) continue;
0519                     if (chord->beamStart(0) == c) {
0520                         chord->setStemLength(chord->desiredStemLength());
0521                         chords.append(chord);
0522                         stemEnds.append(QPointF(chord->stemX(), chord->stemEndY(false)));
0523                     }
0524                     if (chord == c->beamEnd(0)) {
0525                         break;
0526                     }
0527                 }
0528 
0529                 for (int j = stemEnds.size()-1; j >= 0; j--) {
0530                     stemEnds[j] -= stemEnds[0];
0531                 }
0532                 if (c->stemDirection() == StemUp) {
0533                     for (int j = 0; j < stemEnds.size(); j++) {
0534                         stemEnds[j].setY(-stemEnds[j].y());
0535                     }
0536                 }
0537 
0538                 // now somehow fit a line through all those points...
0539                 qreal bestError = 1e99;
0540                 qreal bestK = 0, bestL = 0;
0541                 for (int a = 0; a < stemEnds.size(); a++) {
0542                     for (int b = a+1; b < stemEnds.size(); b++) {
0543                         // assume a line that passes through stemEnds[a] and stemEnds[b]
0544                         // line is in form of k*x + l
0545                         qreal k = (stemEnds[b].y() - stemEnds[a].y()) / (stemEnds[b].x() - stemEnds[a].x());
0546                         qreal l = stemEnds[a].y() - (stemEnds[a].x() * k);
0547 
0548                         //debugMusic << "a:" << stemEnds[a] << ", b:" << stemEnds[b] << ", k:" << k << ", l:" << l;
0549 
0550                         //for (int j = 0; j < stemEnds.size(); j++) {
0551                         //    debugMusic << "    " << stemEnds[j] << "; " << (k * stemEnds[j].x() + l);
0552                         //}
0553                         // check if it is entirely above all stemEnds, and calculate sum of distances to stemEnds
0554                         bool validLine = true;
0555                         qreal error = 0;
0556                         for (int j = 0; j < stemEnds.size(); j++) {
0557                             qreal y = k * stemEnds[j].x() + l;
0558                             if (y < stemEnds[j].y() - 1e-9) {
0559                                 validLine = false;
0560                                 break;
0561                             } else {
0562                                 error += y - stemEnds[j].y();
0563                             }
0564                         }
0565                         if (validLine) {
0566                             if (error < bestError) {
0567                                 bestError = error;
0568                                 bestK = k;
0569                                 bestL = l;
0570                             }
0571                         }
0572                     }
0573                 }
0574 
0575                 //debugMusic << "bestError:" << bestError << "bestK:" << bestK << "bestL:" << bestL;
0576 
0577                 c->setStemLength(c->desiredStemLength() + bestL / c->staff()->lineSpacing());
0578                 Chord* endChord = c->beamEnd(0);
0579                 qreal endY = stemEnds[stemEnds.size()-1].x() * bestK + bestL;
0580                 //debugMusic << "old y:" << stemEnds[stemEnds.size()-1].y() << "new y:" << endY;
0581                 qreal extra = endY - stemEnds[stemEnds.size()-1].y();
0582                 endChord->setStemLength(endChord->desiredStemLength() + extra / endChord->staff()->lineSpacing());
0583             }
0584         }
0585     }
0586 }
0587 
0588 void Engraver::rebeamBar(Part* part, VoiceBar* vb)
0589 {
0590     Bar* bar = vb->bar();
0591     TimeSignature* ts = part->staff(0)->lastTimeSignatureChange(bar);
0592     if (!ts) return;
0593 
0594     QList<int> beats = ts->beatLengths();
0595     int nextBeat = 0;
0596     int passedBeats = 0;
0597 
0598     int curTime = 0;
0599     int beamStartTime = 0;
0600     for (int i = 0, beamStart = -1; i < vb->elementCount(); i++) {
0601         VoiceElement* ve = vb->element(i);
0602         Chord* c = dynamic_cast<Chord*>(ve);
0603         if (!c) continue;
0604         curTime += ve->length();
0605 
0606         if (c->duration() <= EighthNote && beamStart < 0) {
0607             beamStart = i;
0608             beamStartTime = curTime - ve->length();
0609             for (int b = 0; b < c->beamCount(); b++) {
0610                 c->setBeam(b, c, c, BeamFlag);
0611             }
0612         }
0613 
0614         int beatEnd = beats[nextBeat] + passedBeats;
0615         if (curTime >= beatEnd || c->noteCount() == 0 || c->duration() > EighthNote || i == vb->elementCount()-1) {
0616             int beamEnd = i;
0617             if (c->duration() > EighthNote || c->noteCount() == 0) {
0618                 beamEnd--;
0619             }
0620 
0621             if (beamEnd > beamStart && beamStart >= 0) {
0622                 Chord* sChord = dynamic_cast<Chord*>(vb->element(beamStart));
0623                 Chord* eChord = dynamic_cast<Chord*>(vb->element(beamEnd));
0624 
0625                 int start[6] = {-1, -1, -1, -1, -1, -1};
0626                 int startTime[6];
0627 
0628                 for (int j = beamStart, beamTime = beamStartTime; j <= beamEnd; j++) {
0629                     Chord* chord = dynamic_cast<Chord*>(vb->element(j));
0630                     if (chord) {
0631                         int factor = Note8Length;
0632                         for (int b = 1; b < chord->beamCount(); b++) {
0633                             if (start[b] == -1) {
0634                                 start[b] = j;
0635                                 startTime[b] = beamTime;
0636                             }
0637                             factor /= 2;
0638                         }
0639                         for (int b = chord->beamCount(); b < 6; b++) {
0640                             if (start[b] != -1) {
0641                                 Chord* sc = static_cast<Chord*>(vb->element(start[b]));
0642                                 Chord* ec = static_cast<Chord*>(vb->element(j-1));
0643                                 if (sc == ec) {
0644                                     int sTime = startTime[b];
0645                                     int eTime = sTime + sc->length();
0646                                     int preSTime = (sTime / factor) * factor; // largest multiple of factor <= sTime
0647                                     int postETime = ((eTime + factor - 1) / factor) * factor; // smallest multiple of factor >= eTime
0648                                     if (sTime - preSTime < postETime - eTime) {
0649                                         sc->setBeam(b, sc, ec, BeamForwardHook);
0650                                     } else {
0651                                         sc->setBeam(b, sc, ec, BeamBackwardHook);
0652                                     }
0653                                 } else {
0654                                     for (int k = start[b]; k < j; k++) {
0655                                         Chord* chord = dynamic_cast<Chord*>(vb->element(k));
0656                                         if (chord) chord->setBeam(b, sc, ec);
0657                                     }
0658                                 }
0659                                 start[b] = -1;
0660                             }
0661                             factor /= 2;
0662                         }
0663 
0664                         chord->setBeam(0, sChord, eChord);
0665                         beamTime += chord->length();
0666                     }
0667                 }
0668                 int factor = Note8Length;
0669                 for (int b = 1; b < 6; b++) {
0670                     if (start[b] != -1) {
0671                         Chord* sc = static_cast<Chord*>(vb->element(start[b]));
0672                         Chord* ec = static_cast<Chord*>(vb->element(beamEnd));
0673                         if (sc == ec) {
0674                             int sTime = startTime[b];
0675                             int eTime = sTime + sc->length();
0676                             int preSTime = (sTime / factor) * factor; // largest multiple of factor <= sTime
0677                             int postETime = ((eTime + factor - 1) / factor) * factor; // smallest multiple of factor >= eTime
0678                             if (sTime - preSTime < postETime - eTime) {
0679                                 sc->setBeam(b, sc, ec, BeamForwardHook);
0680                             } else {
0681                                 sc->setBeam(b, sc, ec, BeamBackwardHook);
0682                             }
0683                         } else {
0684                             for (int k = start[b]; k <= beamEnd; k++) {
0685                                 Chord* chord = dynamic_cast<Chord*>(vb->element(k));
0686                                 if (chord) chord->setBeam(b, sc, ec);
0687                             }
0688                         }
0689                         start[b] = -1;
0690                     }
0691                     factor /= 2;
0692                 }
0693             }
0694 
0695             beamStart = -1;
0696             while (curTime >= beatEnd) {
0697                 passedBeats += beats[nextBeat];
0698                 nextBeat++;
0699                 if (nextBeat >= beats.size()) nextBeat = 0;
0700                 beatEnd = passedBeats + beats[nextBeat];
0701             }
0702         }
0703     }
0704 }