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 }