2013-09-05 16:37:49 +02:00
|
|
|
//=============================================================================
|
|
|
|
// MuseScore
|
|
|
|
// Music Composition & Notation
|
|
|
|
//
|
|
|
|
// Copyright (C) 2013 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
|
|
|
|
//=============================================================================
|
|
|
|
|
2013-08-23 20:53:42 +02:00
|
|
|
#include "importmidi_clef.h"
|
|
|
|
#include "libmscore/score.h"
|
|
|
|
#include "libmscore/staff.h"
|
|
|
|
#include "libmscore/measure.h"
|
|
|
|
#include "libmscore/segment.h"
|
|
|
|
#include "libmscore/clef.h"
|
|
|
|
#include "libmscore/chordrest.h"
|
|
|
|
#include "libmscore/chord.h"
|
|
|
|
#include "libmscore/note.h"
|
|
|
|
#include "libmscore/slur.h"
|
2014-03-15 09:29:08 +01:00
|
|
|
#include "libmscore/element.h"
|
2014-03-01 18:00:30 +01:00
|
|
|
#include "importmidi_tie.h"
|
2014-03-15 09:29:08 +01:00
|
|
|
#include "importmidi_meter.h"
|
2013-08-23 20:53:42 +02:00
|
|
|
#include "preferences.h"
|
|
|
|
|
|
|
|
#include <set>
|
|
|
|
|
|
|
|
|
|
|
|
namespace Ms {
|
|
|
|
|
|
|
|
extern Preferences preferences;
|
|
|
|
|
|
|
|
namespace MidiClef {
|
|
|
|
|
|
|
|
|
|
|
|
class AveragePitch
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
AveragePitch() : sumPitch_(0), count_(0) {}
|
|
|
|
AveragePitch(int sumPitch, int count) : sumPitch_(sumPitch), count_(count) {}
|
|
|
|
|
2014-03-02 12:23:27 +01:00
|
|
|
int pitch() const { return qRound(sumPitch_ * 1.0 / count_); }
|
2013-08-23 20:53:42 +02:00
|
|
|
void addPitch(int pitch)
|
|
|
|
{
|
|
|
|
sumPitch_ += pitch;
|
|
|
|
++count_;
|
|
|
|
}
|
|
|
|
AveragePitch& operator+=(const AveragePitch &other)
|
|
|
|
{
|
2014-03-14 18:50:02 +01:00
|
|
|
sumPitch_ += other.sumPitch_;
|
|
|
|
count_ += other.count_;
|
2013-08-23 20:53:42 +02:00
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
private:
|
|
|
|
int sumPitch_;
|
|
|
|
int count_;
|
|
|
|
};
|
|
|
|
|
2014-03-14 18:50:02 +01:00
|
|
|
class MinMaxPitch
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
MinMaxPitch() : minPitch_(std::numeric_limits<int>::max()), maxPitch_(-1) {}
|
|
|
|
MinMaxPitch(int minPitch, int maxPitch) : minPitch_(minPitch), maxPitch_(maxPitch) {}
|
|
|
|
|
|
|
|
int minPitch() const { return minPitch_; }
|
|
|
|
int maxPitch() const { return maxPitch_; }
|
|
|
|
int empty() const { return minPitch_ == std::numeric_limits<int>::max() || maxPitch_ == -1; }
|
|
|
|
void addPitch(int pitch)
|
|
|
|
{
|
|
|
|
if (pitch < minPitch_)
|
|
|
|
minPitch_ = pitch;
|
|
|
|
if (pitch > maxPitch_)
|
|
|
|
maxPitch_ = pitch;
|
|
|
|
}
|
|
|
|
private:
|
|
|
|
int minPitch_;
|
|
|
|
int maxPitch_;
|
|
|
|
};
|
|
|
|
|
2013-08-23 20:53:42 +02:00
|
|
|
|
2014-03-14 14:38:16 +01:00
|
|
|
int clefMidPitch()
|
2014-03-01 18:00:30 +01:00
|
|
|
{
|
2014-03-14 14:38:16 +01:00
|
|
|
static const int midPitch = 60;
|
|
|
|
return midPitch;
|
2014-03-01 18:00:30 +01:00
|
|
|
}
|
2013-08-23 20:53:42 +02:00
|
|
|
|
|
|
|
ClefType clefTypeFromAveragePitch(int averagePitch)
|
|
|
|
{
|
2014-03-14 14:38:16 +01:00
|
|
|
return averagePitch < clefMidPitch() ? ClefType::F : ClefType::G;
|
2013-08-23 20:53:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void createClef(ClefType clefType, Staff* staff, int tick, bool isSmall = false)
|
|
|
|
{
|
|
|
|
Clef* clef = new Clef(staff->score());
|
|
|
|
clef->setClefType(clefType);
|
|
|
|
const int track = staff->idx() * VOICES;
|
|
|
|
clef->setTrack(track);
|
|
|
|
clef->setGenerated(false);
|
|
|
|
clef->setMag(staff->mag());
|
|
|
|
clef->setSmall(isSmall);
|
|
|
|
Measure* m = staff->score()->tick2measure(tick);
|
|
|
|
Segment* seg = m->getSegment(clef, tick);
|
|
|
|
seg->add(clef);
|
|
|
|
}
|
|
|
|
|
|
|
|
void createSmallClef(ClefType clefType, Segment *chordRestSeg, Staff *staff)
|
|
|
|
{
|
|
|
|
const int strack = staff->idx() * VOICES;
|
|
|
|
const int tick = chordRestSeg->tick();
|
|
|
|
Segment *clefSeg = chordRestSeg->measure()->findSegment(Segment::SegClef, tick);
|
|
|
|
// remove clef if it is not the staff clef
|
|
|
|
if (clefSeg && clefSeg != chordRestSeg->score()->firstSegment(Segment::SegClef)) {
|
|
|
|
Clef *c = static_cast<Clef *>(clefSeg->element(strack)); // voice == 0 for clefs
|
|
|
|
if (c) {
|
|
|
|
clefSeg->remove(c);
|
|
|
|
delete c;
|
|
|
|
if (clefSeg->isEmpty()) {
|
|
|
|
chordRestSeg->measure()->remove(clefSeg);
|
|
|
|
delete clefSeg;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
createClef(clefType, staff, tick, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
AveragePitch findAverageSegPitch(const Segment *seg, int strack)
|
|
|
|
{
|
2014-03-14 18:50:02 +01:00
|
|
|
AveragePitch averagePitch;
|
|
|
|
for (int voice = 0; voice < VOICES; ++voice) {
|
|
|
|
ChordRest *cr = static_cast<ChordRest *>(seg->element(strack + voice));
|
2014-05-22 19:49:51 +02:00
|
|
|
if (cr && cr->type() == Element::ElementType::CHORD) {
|
2014-03-14 18:50:02 +01:00
|
|
|
Chord *chord = static_cast<Chord *>(cr);
|
|
|
|
const auto ¬es = chord->notes();
|
|
|
|
for (const Note *note: notes)
|
|
|
|
averagePitch.addPitch(note->pitch());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return averagePitch;
|
|
|
|
}
|
|
|
|
|
|
|
|
MinMaxPitch findMinMaxSegPitch(const Segment *seg, int strack)
|
|
|
|
{
|
|
|
|
MinMaxPitch minMaxPitch;
|
2013-08-23 20:53:42 +02:00
|
|
|
for (int voice = 0; voice < VOICES; ++voice) {
|
|
|
|
ChordRest *cr = static_cast<ChordRest *>(seg->element(strack + voice));
|
2014-05-22 19:49:51 +02:00
|
|
|
if (cr && cr->type() == Element::ElementType::CHORD) {
|
2013-08-23 20:53:42 +02:00
|
|
|
Chord *chord = static_cast<Chord *>(cr);
|
|
|
|
const auto ¬es = chord->notes();
|
|
|
|
for (const Note *note: notes)
|
2014-03-14 18:50:02 +01:00
|
|
|
minMaxPitch.addPitch(note->pitch());
|
2013-08-23 20:53:42 +02:00
|
|
|
}
|
|
|
|
}
|
2014-03-14 18:50:02 +01:00
|
|
|
return minMaxPitch;
|
2013-08-23 20:53:42 +02:00
|
|
|
}
|
|
|
|
|
2014-03-01 16:25:28 +01:00
|
|
|
|
|
|
|
#ifdef QT_DEBUG
|
|
|
|
|
|
|
|
bool doesClefBreakTie(const Staff *staff)
|
|
|
|
{
|
|
|
|
const int strack = staff->idx() * VOICES;
|
|
|
|
|
|
|
|
for (int voice = 0; voice < VOICES; ++voice) {
|
2014-03-01 18:05:35 +01:00
|
|
|
bool currentTie = false;
|
2014-03-01 16:25:28 +01:00
|
|
|
for (Segment *seg = staff->score()->firstSegment(); seg; seg = seg->next1()) {
|
|
|
|
if (seg->segmentType() == Segment::SegChordRest) {
|
2014-03-01 18:00:30 +01:00
|
|
|
if (MidiTie::isTiedBack(seg, strack, voice))
|
2014-03-01 18:05:35 +01:00
|
|
|
currentTie = false;
|
|
|
|
if (MidiTie::isTiedFor(seg, strack, voice))
|
|
|
|
currentTie = true;
|
2014-03-01 16:25:28 +01:00
|
|
|
}
|
|
|
|
else if (seg->segmentType() == Segment::SegClef && seg->element(strack)) {
|
2014-03-01 18:05:35 +01:00
|
|
|
if (currentTie) {
|
2014-03-01 16:25:28 +01:00
|
|
|
qDebug() << "Clef breaks tie; measure number (from 1):"
|
|
|
|
<< seg->measure()->no() + 1
|
|
|
|
<< ", staff index (from 0):" << staff->idx();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2014-03-14 18:50:02 +01:00
|
|
|
|
|
|
|
// clef index: 0 - treble, 1 - bass
|
2014-03-01 16:25:28 +01:00
|
|
|
|
2014-03-15 10:07:48 +01:00
|
|
|
int findPitchPenaltyForClef(int pitch, int clefIndex)
|
2013-08-23 20:53:42 +02:00
|
|
|
{
|
2014-03-15 10:07:48 +01:00
|
|
|
static const int farPitchPenalty = 10000;
|
|
|
|
static const int approxPitchPenalty = 1;
|
2014-03-14 14:38:16 +01:00
|
|
|
static const int dx = 5;
|
|
|
|
|
|
|
|
static const int midPitch = clefMidPitch(); // all notes equal or upper - better in G clef
|
|
|
|
static const int highPitch = midPitch + dx; // all notes equal or upper - in G clef
|
|
|
|
static const int lowPitch = midPitch - dx; // all notes lower - in F clef
|
|
|
|
|
|
|
|
switch (clefIndex) {
|
|
|
|
case 0:
|
|
|
|
if (pitch < lowPitch)
|
|
|
|
return farPitchPenalty;
|
|
|
|
else if (pitch < midPitch)
|
|
|
|
return approxPitchPenalty;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
if (pitch >= highPitch)
|
|
|
|
return farPitchPenalty;
|
|
|
|
else if (pitch >= midPitch)
|
|
|
|
return approxPitchPenalty;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
Q_ASSERT_X(false, "MidiClef::pitchPenalty", "Unknown clef type");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2013-08-23 20:53:42 +02:00
|
|
|
|
2014-03-15 10:57:20 +01:00
|
|
|
std::pair<Element::ElementType, ReducedFraction>
|
|
|
|
findChordRest(const Segment *seg, int strack)
|
|
|
|
{
|
2014-05-22 19:49:51 +02:00
|
|
|
Element::ElementType elType = Element::ElementType::INVALID;
|
2014-03-15 10:57:20 +01:00
|
|
|
ReducedFraction newRestLen(0, 1);
|
|
|
|
for (int voice = 0; voice < VOICES; ++voice) {
|
|
|
|
ChordRest *cr = static_cast<ChordRest *>(seg->element(strack + voice));
|
|
|
|
if (!cr)
|
|
|
|
continue;
|
2014-05-22 19:49:51 +02:00
|
|
|
if (cr->type() == Element::ElementType::CHORD) {
|
|
|
|
elType = Element::ElementType::CHORD;
|
2014-03-15 10:57:20 +01:00
|
|
|
break;
|
|
|
|
}
|
2014-05-22 19:49:51 +02:00
|
|
|
else if (cr->type() == Element::ElementType::REST) {
|
|
|
|
elType = Element::ElementType::REST;
|
2014-03-15 10:57:20 +01:00
|
|
|
newRestLen = qMax(newRestLen, ReducedFraction(cr->globalDuration()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return {elType, newRestLen};
|
|
|
|
}
|
|
|
|
|
2014-03-15 10:07:48 +01:00
|
|
|
int findClefChangePenalty(
|
2014-03-14 18:50:02 +01:00
|
|
|
int pos,
|
|
|
|
int clefIndex,
|
2014-03-15 09:29:08 +01:00
|
|
|
const std::vector<std::vector<int>> &trebleBassPath,
|
|
|
|
const Segment *segment,
|
|
|
|
const Staff *staff)
|
2014-03-14 14:38:16 +01:00
|
|
|
{
|
2014-03-15 10:57:20 +01:00
|
|
|
if (pos == 0)
|
|
|
|
return 0;
|
|
|
|
|
2014-03-15 10:07:48 +01:00
|
|
|
static const int clefChangePenalty = 1000;
|
2014-03-15 10:29:45 +01:00
|
|
|
static const int orphanChordPenalty = 2;
|
2014-03-14 14:38:16 +01:00
|
|
|
static const int notesBetweenClefs = 5; // should be >= 2
|
2013-08-23 20:53:42 +02:00
|
|
|
|
2014-03-15 09:29:08 +01:00
|
|
|
int j = pos;
|
|
|
|
ReducedFraction totalRestLen(0, 1);
|
2014-03-15 10:07:48 +01:00
|
|
|
int penalty = 0;
|
2014-03-15 09:29:08 +01:00
|
|
|
const int strack = staff->idx() * VOICES;
|
|
|
|
const auto barFraction = ReducedFraction(
|
|
|
|
staff->score()->sigmap()->timesig(segment->tick()).timesig());
|
|
|
|
const ReducedFraction beatLen = Meter::beatLength(barFraction);
|
|
|
|
|
2014-03-15 10:57:20 +01:00
|
|
|
// find backward penalty
|
2014-03-15 09:29:08 +01:00
|
|
|
for (const Segment *segPrev = segment->prev1(Segment::SegChordRest); ;
|
|
|
|
segPrev = segPrev->prev1(Segment::SegChordRest)) {
|
|
|
|
if (!segPrev) {
|
|
|
|
penalty += clefChangePenalty;
|
|
|
|
break;
|
|
|
|
}
|
2014-03-15 10:57:20 +01:00
|
|
|
const auto el = findChordRest(segPrev, strack);
|
2014-05-22 19:49:51 +02:00
|
|
|
if (el.first == Element::ElementType::CHORD) {
|
2014-03-15 09:29:08 +01:00
|
|
|
--j;
|
|
|
|
if (j == pos - notesBetweenClefs)
|
|
|
|
break;
|
|
|
|
if (j == 0 || trebleBassPath[clefIndex][j] != clefIndex) {
|
|
|
|
penalty += clefChangePenalty;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
totalRestLen = {0, 1};
|
|
|
|
}
|
2014-05-22 19:49:51 +02:00
|
|
|
else if (el.first == Element::ElementType::REST) {
|
2014-03-15 10:57:20 +01:00
|
|
|
totalRestLen += el.second;
|
2014-03-15 10:29:45 +01:00
|
|
|
if (totalRestLen >= beatLen) {
|
2014-03-15 10:46:47 +01:00
|
|
|
if (j != pos)
|
2014-03-15 10:29:45 +01:00
|
|
|
penalty += orphanChordPenalty;
|
2014-03-15 09:29:08 +01:00
|
|
|
break;
|
2014-03-15 10:29:45 +01:00
|
|
|
}
|
2014-03-15 09:29:08 +01:00
|
|
|
}
|
2014-03-14 14:38:16 +01:00
|
|
|
}
|
2014-03-15 10:57:20 +01:00
|
|
|
// find forward penalty
|
2014-03-15 09:29:08 +01:00
|
|
|
int chordCounter = 0;
|
|
|
|
for (const Segment *seg = segment; ; seg = seg->next1(Segment::SegChordRest)) {
|
|
|
|
if (!seg) {
|
|
|
|
penalty += clefChangePenalty;
|
|
|
|
break;
|
|
|
|
}
|
2014-03-15 10:57:20 +01:00
|
|
|
const auto el = findChordRest(seg, strack);
|
2014-05-22 19:49:51 +02:00
|
|
|
if (el.first == Element::ElementType::CHORD) {
|
2014-03-15 09:29:08 +01:00
|
|
|
++chordCounter;
|
|
|
|
if (chordCounter == notesBetweenClefs)
|
|
|
|
break;
|
2014-03-15 10:46:47 +01:00
|
|
|
totalRestLen = {0, 1};
|
|
|
|
}
|
2014-05-22 19:49:51 +02:00
|
|
|
else if (el.first == Element::ElementType::REST) {
|
2014-03-15 10:57:20 +01:00
|
|
|
totalRestLen += el.second;
|
2014-03-15 10:46:47 +01:00
|
|
|
if (totalRestLen >= beatLen) {
|
|
|
|
penalty += orphanChordPenalty;
|
|
|
|
break;
|
|
|
|
}
|
2014-03-15 09:29:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return penalty;
|
2014-03-14 14:38:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
ClefType clefFromIndex(int index)
|
|
|
|
{
|
|
|
|
return (index == 0) ? ClefType::G : ClefType::F;
|
|
|
|
}
|
|
|
|
|
2014-03-15 10:07:48 +01:00
|
|
|
void makeDynamicProgrammingStep(std::vector<std::vector<int>> &penalties,
|
2014-03-14 14:38:16 +01:00
|
|
|
std::vector<std::vector<int>> &optimalPaths,
|
|
|
|
int pos,
|
|
|
|
MidiTie::TieStateMachine::State tieState,
|
2014-03-15 09:29:08 +01:00
|
|
|
const MinMaxPitch &minMaxPitch,
|
|
|
|
const Segment *seg,
|
|
|
|
const Staff *staff)
|
2014-03-14 14:38:16 +01:00
|
|
|
{
|
2014-03-22 15:30:10 +01:00
|
|
|
for (int clefIndex = 0; clefIndex != 2; ++clefIndex)
|
2014-03-14 14:38:16 +01:00
|
|
|
optimalPaths[clefIndex].resize(pos + 1);
|
|
|
|
|
2014-03-20 05:41:51 +01:00
|
|
|
for (int curClef = 0; curClef != 2; ++curClef) {
|
|
|
|
const int significantPitch = (curClef == 0)
|
|
|
|
? minMaxPitch.minPitch() : minMaxPitch.maxPitch();
|
|
|
|
const int pitchPenalty = findPitchPenaltyForClef(significantPitch, curClef);
|
|
|
|
int minPenalty = std::numeric_limits<int>::max();
|
|
|
|
int minIndex;
|
|
|
|
for (int prevClef = 0; prevClef != 2; ++prevClef) {
|
|
|
|
int penalty = pitchPenalty;
|
|
|
|
if (prevClef != curClef) {
|
|
|
|
if (tieState == MidiTie::TieStateMachine::State::TIED_BACK
|
|
|
|
|| tieState == MidiTie::TieStateMachine::State::TIED_BOTH) {
|
|
|
|
continue; // there is a tie breakage that is incorrect
|
|
|
|
}
|
|
|
|
penalty += findClefChangePenalty(pos, prevClef, optimalPaths, seg, staff);
|
|
|
|
}
|
2014-03-22 15:30:10 +01:00
|
|
|
penalty += (pos > 0) ? penalties[prevClef][(pos + 1) % 2] : 0;
|
2014-03-20 05:41:51 +01:00
|
|
|
if ((prevClef != curClef && penalty < minPenalty)
|
|
|
|
|| (prevClef == curClef && penalty <= minPenalty)) {
|
|
|
|
minPenalty = penalty;
|
|
|
|
minIndex = prevClef;
|
|
|
|
}
|
2014-03-14 14:38:16 +01:00
|
|
|
}
|
2014-03-20 05:41:51 +01:00
|
|
|
|
2014-03-22 15:30:10 +01:00
|
|
|
penalties[curClef][pos % 2] = minPenalty;
|
2014-03-20 05:41:51 +01:00
|
|
|
if (pos > 0)
|
|
|
|
optimalPaths[curClef][pos] = minIndex;
|
2014-03-14 14:38:16 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void createClefs(Staff *staff,
|
|
|
|
const std::vector<std::vector<int>> &optimalPaths,
|
2014-03-22 15:08:22 +01:00
|
|
|
int lastClef,
|
2014-03-14 14:38:16 +01:00
|
|
|
std::vector<Segment *> segments,
|
|
|
|
ClefType *mainClef)
|
|
|
|
{
|
2014-03-22 15:08:22 +01:00
|
|
|
int currentClef = lastClef;
|
|
|
|
for (size_t i = optimalPaths[0].size() - 1; i; --i) {
|
|
|
|
const int prevClef = optimalPaths[currentClef][i];
|
|
|
|
if (prevClef != currentClef) {
|
|
|
|
createSmallClef(clefFromIndex(currentClef), segments[i], staff);
|
|
|
|
currentClef = prevClef;
|
2013-08-23 20:53:42 +02:00
|
|
|
}
|
2014-03-14 14:38:16 +01:00
|
|
|
}
|
2014-03-22 15:08:22 +01:00
|
|
|
*mainClef = clefFromIndex(currentClef);
|
2014-03-14 14:38:16 +01:00
|
|
|
}
|
2013-08-23 20:53:42 +02:00
|
|
|
|
2014-03-14 14:38:16 +01:00
|
|
|
void createClefs(Staff *staff, int indexOfOperation, bool isDrumTrack)
|
|
|
|
{
|
|
|
|
if (isDrumTrack) {
|
|
|
|
createClef(ClefType::PERC, staff, 0);
|
|
|
|
return;
|
|
|
|
}
|
2013-08-23 20:53:42 +02:00
|
|
|
|
2014-03-14 14:38:16 +01:00
|
|
|
ClefType mainClef = staff->clefTypeList(0)._concertClef;
|
|
|
|
const int strack = staff->idx() * VOICES;
|
|
|
|
const auto trackOpers = preferences.midiImportOperations.trackOperations(indexOfOperation);
|
|
|
|
|
|
|
|
if (trackOpers.changeClef) {
|
|
|
|
MidiTie::TieStateMachine tieTracker;
|
|
|
|
|
2014-03-22 15:08:22 +01:00
|
|
|
// find optimal clef changes via dynamic programming
|
2014-03-15 10:07:48 +01:00
|
|
|
std::vector<std::vector<int>> penalties(2); // 0 - treble, 1 - bass
|
2014-03-22 15:30:10 +01:00
|
|
|
for (size_t i = 0; i != penalties.size(); ++i)
|
|
|
|
penalties[i].resize(2); // 2 = current + prev
|
2014-03-15 10:07:48 +01:00
|
|
|
std::vector<std::vector<int>> optimalPaths(2); // first col is unused
|
2014-03-14 14:38:16 +01:00
|
|
|
std::vector<Segment *> segments;
|
|
|
|
|
|
|
|
int pos = 0;
|
|
|
|
for (Segment *seg = staff->score()->firstSegment(Segment::SegChordRest); seg;
|
|
|
|
seg = seg->next1(Segment::SegChordRest)) {
|
|
|
|
|
2014-03-14 18:50:02 +01:00
|
|
|
const auto minMaxPitch = findMinMaxSegPitch(seg, strack);
|
2014-03-15 10:07:48 +01:00
|
|
|
if (minMaxPitch.empty()) // no chords
|
2014-03-14 14:38:16 +01:00
|
|
|
continue;
|
|
|
|
tieTracker.addSeg(seg, strack);
|
|
|
|
segments.push_back(seg);
|
|
|
|
|
|
|
|
makeDynamicProgrammingStep(penalties, optimalPaths, pos,
|
2014-03-15 09:29:08 +01:00
|
|
|
tieTracker.state(), minMaxPitch, seg, staff);
|
2014-03-14 14:38:16 +01:00
|
|
|
++pos;
|
2013-08-23 20:53:42 +02:00
|
|
|
}
|
2014-03-22 15:08:22 +01:00
|
|
|
|
|
|
|
if (!optimalPaths[0].empty()) {
|
2014-03-22 15:30:10 +01:00
|
|
|
int lastClef = (penalties[1][(pos - 1) % 2] < penalties[0][(pos - 1) % 2])
|
2014-03-22 15:08:22 +01:00
|
|
|
? 1 : 0;
|
|
|
|
// get the optimal clef changes found via dynamic programming
|
|
|
|
createClefs(staff, optimalPaths, lastClef, segments, &mainClef);
|
|
|
|
}
|
2014-03-14 14:38:16 +01:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
AveragePitch allAveragePitch;
|
|
|
|
for (Segment *seg = staff->score()->firstSegment(Segment::SegChordRest); seg;
|
|
|
|
seg = seg->next1(Segment::SegChordRest)) {
|
|
|
|
allAveragePitch += findAverageSegPitch(seg, strack);
|
2013-08-23 20:53:42 +02:00
|
|
|
}
|
2014-03-14 14:38:16 +01:00
|
|
|
mainClef = MidiClef::clefTypeFromAveragePitch(allAveragePitch.pitch());
|
2013-08-23 20:53:42 +02:00
|
|
|
}
|
2014-03-01 16:25:28 +01:00
|
|
|
|
2014-03-14 14:38:16 +01:00
|
|
|
staff->setClef(0, mainClef); // set main clef
|
|
|
|
createClef(mainClef, staff, 0);
|
|
|
|
|
2014-03-01 16:25:28 +01:00
|
|
|
Q_ASSERT_X(!doesClefBreakTie(staff), "MidiClef::createClefs", "Clef breaks the tie");
|
2013-08-23 20:53:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace MidiClef
|
|
|
|
} // namespace Ms
|
|
|
|
|