1 #include "importmidi_tuplet_filter.h"
2 #include "importmidi_tuplet.h"
3 #include "importmidi_tuplet_voice.h"
4 #include "importmidi_chord.h"
5 #include "importmidi_quant.h"
6 #include "importmidi_inner.h"
7 #include "libmscore/mscore.h"
8
9 #include <set>
10
11
12 namespace Ms {
13 namespace MidiTuplet {
14
isMoreTupletVoicesAllowed(int voicesInUse,int availableVoices)15 bool isMoreTupletVoicesAllowed(int voicesInUse, int availableVoices)
16 {
17 return !(voicesInUse >= availableVoices || voicesInUse >= tupletVoiceLimit());
18 }
19
20 class TupletErrorResult
21 {
22 public:
TupletErrorResult(double t=0.0,double relPlaces=0.0,const ReducedFraction & r=ReducedFraction (0,1),size_t vc=0,size_t tc=0)23 TupletErrorResult(double t = 0.0,
24 double relPlaces = 0.0,
25 const ReducedFraction &r = ReducedFraction(0, 1),
26 size_t vc = 0,
27 size_t tc = 0)
28 : tupletAverageError(t)
29 , relativeUsedChordPlaces(relPlaces)
30 , sumLengthOfRests(r)
31 , voiceCount(vc)
32 , tupletCount(tc)
33 {}
34
isInitialized() const35 bool isInitialized() const { return tupletCount != 0; }
36
operator <(const TupletErrorResult & er) const37 bool operator<(const TupletErrorResult &er) const
38 {
39 double value = div(tupletAverageError, er.tupletAverageError)
40 - div(relativeUsedChordPlaces, er.relativeUsedChordPlaces)
41 + div(sumLengthOfRests.numerator() * 1.0 / sumLengthOfRests.denominator(),
42 er.sumLengthOfRests.numerator() * 1.0 / er.sumLengthOfRests.denominator());
43 if (value == 0) {
44 value = div(voiceCount, er.voiceCount)
45 + div(tupletCount, er.tupletCount);
46 }
47 return value < 0;
48 }
49
50 private:
div(double val1,double val2)51 static double div(double val1, double val2)
52 {
53 if (val1 == val2)
54 return 0;
55 return (val1 - val2) / qMax(val1, val2);
56 }
57
58 double tupletAverageError;
59 double relativeUsedChordPlaces;
60 ReducedFraction sumLengthOfRests;
61 size_t voiceCount;
62 size_t tupletCount;
63 };
64
haveCommonChords(int i,int j,const std::vector<TupletInfo> & tuplets)65 bool haveCommonChords(int i, int j, const std::vector<TupletInfo> &tuplets)
66 {
67 if (tuplets.empty())
68 return false;
69 std::set<std::pair<const ReducedFraction, MidiChord> *> usedChords;
70 for (const auto &chord: tuplets[i].chords)
71 usedChords.insert(&*chord.second);
72 for (const auto &chord: tuplets[j].chords)
73 if (usedChords.find(&*chord.second) != usedChords.end())
74 return true;
75 return false;
76 }
77
78 // remove overlapping tuplets with the same tuplet number
79 // when tuplet with bigger length contains the same notes
80
removeUselessTuplets(std::vector<TupletInfo> & tuplets)81 void removeUselessTuplets(std::vector<TupletInfo> &tuplets)
82 {
83 struct {
84 bool operator()(const TupletInfo &t1, const TupletInfo &t2) {
85 if (t1.tupletNumber != t2.tupletNumber)
86 return t1.tupletNumber < t2.tupletNumber;
87 return t1.len < t2.len;
88 }
89 } comparator;
90 std::sort(tuplets.begin(), tuplets.end(), comparator);
91
92 size_t beg = 0;
93 while (beg < tuplets.size()) {
94 size_t end = beg + 1;
95 while (tuplets.size() > end && tuplets[end].tupletNumber == tuplets[beg].tupletNumber)
96 ++end;
97 for (size_t i = beg; i < end - 1; ++i) {
98 const auto &t1 = tuplets[i];
99 for (size_t j = i + 1; j < end; ++j) {
100 const auto &t2 = tuplets[j];
101 if (t1.onTime >= t2.onTime && t1.onTime + t1.len <= t2.onTime + t2.len) {
102 // check onTimes
103 if (t2.chords.rbegin()->first < t1.onTime + t1.len
104 && t2.chords.begin()->first >= t1.onTime)
105 {
106 // remove larger tuplet
107 tuplets.erase(tuplets.begin() + j);
108 --j;
109 --end;
110 }
111 }
112 }
113 }
114 beg = end;
115 }
116 }
117
findLongestUncommonGroup(const std::vector<TupletInfo> & tuplets,const ReducedFraction & basicQuant)118 std::set<int> findLongestUncommonGroup(
119 const std::vector<TupletInfo> &tuplets,
120 const ReducedFraction &basicQuant)
121 {
122 struct TInfo
123 {
124 bool operator<(const TInfo &other) const
125 {
126 if (offTime < other.offTime)
127 return true;
128 else if (offTime > other.offTime)
129 return false;
130 else
131 return onTime > other.onTime;
132 }
133 bool operator==(const TInfo &other) const
134 {
135 return offTime == other.offTime;
136 }
137
138 ReducedFraction onTime;
139 ReducedFraction offTime;
140 int index;
141 };
142
143 std::vector<TInfo> info;
144 for (size_t i = 0; i != tuplets.size(); ++i) {
145 const auto &tuplet = tuplets[i];
146 const auto interval = tupletInterval(tuplet, basicQuant);
147 info.push_back({interval.first, interval.second, static_cast<int>(i)});
148 }
149
150 std::sort(info.begin(), info.end());
151 info.erase(std::unique(info.begin(), info.end()), info.end());
152
153 // check for overlapping tuplets
154 // and check for rare case: because of tol when detecting tuplets
155 // non-overlapping tuplets can have common chords
156
157 std::set<int> indexes;
158 size_t lastSelected = 0;
159 for (size_t i = 0; i != info.size(); ++i) {
160 if (i > 0 && info[i].onTime < info[lastSelected].offTime)
161 continue;
162 if (haveCommonChords(info[lastSelected].index, info[i].index, tuplets))
163 continue;
164 lastSelected = i;
165 indexes.insert(info[i].index);
166 }
167
168 return indexes;
169 }
170
171
172 struct TupletCommon
173 {
174 // indexes of tuplets that have common chords with the tuplet with tupletIndex
175 std::set<int> commonIndexes;
176 };
177
areInCommons(const TupletInfo & t1,const TupletInfo & t2)178 bool areInCommons(const TupletInfo &t1, const TupletInfo &t2)
179 {
180 for (auto it1 = t1.chords.begin(); it1 != t1.chords.end(); ++it1) {
181 for (auto it2 = t2.chords.begin(); it2 != t2.chords.end(); ++it2) {
182 if (&*it1->second != &*it2->second)
183 continue;
184 if (t1.firstChordIndex != 0 || t2.firstChordIndex != 0
185 || it1 != t1.chords.begin() || it2 != t2.chords.begin()
186 || !isMoreTupletVoicesAllowed(1, it1->second->second.notes.size())) {
187 return true;
188 }
189 }
190 }
191 return false;
192 }
193
findTupletCommons(const std::vector<TupletInfo> & tuplets)194 std::vector<TupletCommon> findTupletCommons(const std::vector<TupletInfo> &tuplets)
195 {
196 std::vector<TupletCommon> tupletCommons(tuplets.size());
197
198 for (size_t i = 0; i != tuplets.size() - 1; ++i) {
199 for (size_t j = i + 1; j != tuplets.size(); ++j) {
200 if (areInCommons(tuplets[i], tuplets[j]))
201 tupletCommons[i].commonIndexes.insert(int(j));
202 }
203 }
204 return tupletCommons;
205 }
206
isInCommonIndexes(int indexToCheck,const std::vector<int> & selectedTuplets,const std::vector<TupletCommon> & tupletCommons)207 bool isInCommonIndexes(
208 int indexToCheck,
209 const std::vector<int> &selectedTuplets,
210 const std::vector<TupletCommon> &tupletCommons)
211 {
212 for (size_t i = 0; i != selectedTuplets.size(); ++i) {
213 const int tupletIndex = selectedTuplets[i];
214
215 Q_ASSERT_X(indexToCheck != tupletIndex, "MidiTuplet::isInCommonIndexes",
216 "Checked indexes are the same but they should be different");
217
218 if (indexToCheck > tupletIndex) {
219 const auto &indexes = tupletCommons[tupletIndex].commonIndexes;
220 if (indexes.find(indexToCheck) != indexes.end())
221 return true;
222 }
223 else {
224 const auto &indexes = tupletCommons[indexToCheck].commonIndexes;
225 if (indexes.find(tupletIndex) != indexes.end())
226 return true;
227 }
228 }
229 return false;
230 }
231
findTupletError(const std::vector<int> & tupletIndexes,const std::vector<TupletInfo> & tuplets,size_t voiceCount,const ReducedFraction & basicQuant)232 TupletErrorResult findTupletError(
233 const std::vector<int> &tupletIndexes,
234 const std::vector<TupletInfo> &tuplets,
235 size_t voiceCount,
236 const ReducedFraction &basicQuant)
237 {
238 ReducedFraction sumError{0, 1};
239 ReducedFraction sumLengthOfRests{0, 1};
240 size_t sumChordCount = 0;
241 int sumChordPlaces = 0;
242 std::set<std::pair<const ReducedFraction, MidiChord> *> usedChords;
243 std::vector<char> usedIndexes(tuplets.size(), 0);
244
245 for (int i: tupletIndexes) {
246 const auto &tuplet = tuplets[i];
247
248 sumError += tuplet.tupletSumError;
249 sumLengthOfRests += tuplet.sumLengthOfRests;
250 sumChordCount += tuplet.chords.size();
251 sumChordPlaces += tuplet.tupletNumber;
252
253 usedIndexes[i] = 1;
254 for (const auto &chord: tuplet.chords)
255 usedChords.insert(&*chord.second);
256 }
257 // add quant error of all chords excluded from tuplets
258 for (size_t i = 0; i != tuplets.size(); ++i) {
259 if (usedIndexes[i])
260 continue;
261 const auto &tuplet = tuplets[i];
262 for (const auto &chord: tuplet.chords) {
263 if (usedChords.find(&*chord.second) != usedChords.end())
264 continue;
265 sumError += Quantize::findOnTimeQuantError(*chord.second, basicQuant);
266 }
267 }
268
269 return TupletErrorResult{
270 sumError.numerator() * 1.0 / (sumError.denominator() * sumChordCount),
271 sumChordCount * 1.0 / sumChordPlaces,
272 sumLengthOfRests,
273 voiceCount,
274 tupletIndexes.size()
275 };
276 }
277
278
279 #ifdef QT_DEBUG
280
areCommonsDifferent(const std::vector<int> & selectedCommons)281 bool areCommonsDifferent(const std::vector<int> &selectedCommons)
282 {
283 std::set<int> commons;
284 for (int i: selectedCommons) {
285 if (commons.find(i) != commons.end())
286 return false;
287 commons.insert(i);
288 }
289 return true;
290 }
291
areCommonsUncommon(const std::vector<int> & selectedCommons,const std::vector<TupletCommon> & tupletCommons)292 bool areCommonsUncommon(const std::vector<int> &selectedCommons,
293 const std::vector<TupletCommon> &tupletCommons)
294 {
295 std::set<int> commons;
296 for (int i: selectedCommons) {
297 for (int j: tupletCommons[i].commonIndexes)
298 commons.insert(j);
299 }
300 for (int i: selectedCommons) {
301 if (commons.find(i) != commons.end())
302 return false;
303 }
304 return true;
305 }
306
307 #endif
308
309
findAvailableVoice(size_t tupletIndex,const std::vector<std::pair<ReducedFraction,ReducedFraction>> & tupletIntervals,const std::map<int,std::vector<std::pair<ReducedFraction,ReducedFraction>>> & voiceIntervals)310 int findAvailableVoice(
311 size_t tupletIndex,
312 const std::vector<std::pair<ReducedFraction, ReducedFraction>> &tupletIntervals,
313 const std::map<int, std::vector<std::pair<ReducedFraction, ReducedFraction>>> &voiceIntervals)
314 {
315 int voice = 0;
316 while (true) {
317 const auto it = voiceIntervals.find(voice);
318 if (it != voiceIntervals.end()
319 && haveIntersection(tupletIntervals[tupletIndex], it->second)) {
320 ++voice;
321 continue;
322 }
323 break;
324 }
325
326 return voice;
327 }
328
329 std::map<std::pair<const ReducedFraction, MidiChord> *, int>
prepareUsedFirstChords(const std::vector<int> & selectedTuplets,const std::vector<TupletInfo> & tuplets)330 prepareUsedFirstChords(const std::vector<int> &selectedTuplets,
331 const std::vector<TupletInfo> &tuplets)
332 {
333 std::map<std::pair<const ReducedFraction, MidiChord> *, int> usedFirstChords;
334 for (int i: selectedTuplets) {
335 if (tuplets[i].firstChordIndex != 0)
336 continue;
337 const auto firstChord = tuplets[i].chords.begin();
338 const auto it = usedFirstChords.find(&*firstChord->second);
339 if (it != usedFirstChords.end())
340 ++(it->second);
341 else
342 usedFirstChords.insert({&*firstChord->second, 1});
343 }
344
345 return usedFirstChords;
346 }
347
findUnusedIndexes(const std::vector<int> & selectedTuplets)348 std::vector<int> findUnusedIndexes(const std::vector<int> &selectedTuplets)
349 {
350 std::vector<int> unusedIndexes;
351 int k = 0;
352 for (int i = 0; i != selectedTuplets.back(); ++i) {
353 if (i == selectedTuplets[k]) {
354 ++k;
355 continue;
356 }
357 unusedIndexes.push_back(i);
358 }
359 return unusedIndexes;
360 }
361
canUseIndex(int indexToCheck,const std::vector<TupletInfo> & tuplets,const std::vector<std::pair<ReducedFraction,ReducedFraction>> & tupletIntervals,const std::map<int,std::vector<std::pair<ReducedFraction,ReducedFraction>>> & voiceIntervals,const std::map<std::pair<const ReducedFraction,MidiChord> *,int> & usedFirstChords)362 bool canUseIndex(
363 int indexToCheck,
364 const std::vector<TupletInfo> &tuplets,
365 const std::vector<std::pair<ReducedFraction, ReducedFraction> > &tupletIntervals,
366 const std::map<int, std::vector<std::pair<ReducedFraction, ReducedFraction>>> &voiceIntervals,
367 const std::map<std::pair<const ReducedFraction, MidiChord> *, int> &usedFirstChords)
368 {
369 const auto &tuplet = tuplets[indexToCheck];
370 // check tuplets for common 1st chord
371 if (tuplet.firstChordIndex == 0) {
372 const auto firstChord = tuplet.chords.begin();
373 const auto it = usedFirstChords.find(&*firstChord->second);
374 if (it != usedFirstChords.end() && !isMoreTupletVoicesAllowed(
375 it->second, it->first->second.notes.size())) {
376 return false;
377 }
378 }
379 // check tuplets for resulting voice count
380 const int voice = findAvailableVoice(indexToCheck, tupletIntervals, voiceIntervals);
381 const int voiceCount = qMax((int)voiceIntervals.size(), voice + 1); // index + 1 = count
382 if (voiceCount > 1 && (int)tuplet.chords.size()
383 < tupletLimits(tuplet.tupletNumber).minNoteCountAddVoice) {
384 return false;
385 }
386 return true;
387 }
388
389
390 #ifdef QT_DEBUG
391
areTupletChordsEmpty(const std::vector<TupletInfo> & tuplets)392 bool areTupletChordsEmpty(const std::vector<TupletInfo> &tuplets)
393 {
394 for (const auto &tuplet: tuplets) {
395 if (tuplet.chords.empty())
396 return true;
397 }
398 return false;
399 }
400
401 template<typename Iter>
validateSelectedTuplets(Iter beginIt,Iter endIt,const std::vector<TupletInfo> & tuplets)402 bool validateSelectedTuplets(Iter beginIt,
403 Iter endIt,
404 const std::vector<TupletInfo> &tuplets)
405 {
406 // <chord address, used voices>
407 std::map<std::pair<const ReducedFraction, MidiChord> *, int> usedChords;
408 for (auto indexIt = beginIt; indexIt != endIt; ++indexIt) {
409 const auto &tuplet = tuplets[*indexIt];
410 const auto &chords = tuplet.chords;
411 for (auto it = chords.begin(); it != chords.end(); ++it) {
412 bool isFirstChord = (tuplet.firstChordIndex == 0 && it == tuplet.chords.begin());
413 const auto fit = usedChords.find(&*(it->second));
414 if (fit == usedChords.end()) {
415 usedChords.insert({&*(it->second), isFirstChord ? 1 : VOICES});
416 }
417 else {
418 if (!isFirstChord)
419 return false;
420 if (!isMoreTupletVoicesAllowed(fit->second, it->second->second.notes.size()))
421 return false;
422 ++(fit->second);
423 }
424 }
425 }
426 return true;
427 }
428
429 #endif
430
431
tryUpdateBestIndexes(std::vector<int> & bestTupletIndexes,TupletErrorResult & minCurrentError,const std::vector<int> & selectedTuplets,const std::vector<TupletInfo> & tuplets,const std::map<int,std::vector<std::pair<ReducedFraction,ReducedFraction>>> & voiceIntervals,const ReducedFraction & basicQuant)432 void tryUpdateBestIndexes(
433 std::vector<int> &bestTupletIndexes,
434 TupletErrorResult &minCurrentError,
435 const std::vector<int> &selectedTuplets,
436 const std::vector<TupletInfo> &tuplets,
437 const std::map<int, std::vector<std::pair<ReducedFraction, ReducedFraction>>> &voiceIntervals,
438 const ReducedFraction &basicQuant)
439 {
440 const size_t voiceCount = voiceIntervals.size();
441 const auto error = findTupletError(selectedTuplets, tuplets,
442 voiceCount, basicQuant);
443 if (!minCurrentError.isInitialized() || error < minCurrentError) {
444 minCurrentError = error;
445 bestTupletIndexes = selectedTuplets;
446 }
447 }
448
449 std::map<int, std::vector<std::pair<ReducedFraction, ReducedFraction> > >
prepareVoiceIntervals(const std::vector<int> & selectedTuplets,const std::vector<std::pair<ReducedFraction,ReducedFraction>> & tupletIntervals)450 prepareVoiceIntervals(
451 const std::vector<int> &selectedTuplets,
452 const std::vector<std::pair<ReducedFraction, ReducedFraction> > &tupletIntervals)
453 {
454 // <voice, intervals>
455 std::map<int, std::vector<std::pair<ReducedFraction, ReducedFraction>>> voiceIntervals;
456 for (int i: selectedTuplets) {
457 int voice = findAvailableVoice(i, tupletIntervals, voiceIntervals);
458 voiceIntervals[voice].push_back(tupletIntervals[i]);
459 }
460 return voiceIntervals;
461 }
462
463 class ValidTuplets
464 {
465 public:
ValidTuplets(int tupletsSize)466 ValidTuplets(int tupletsSize)
467 : indexes_(tupletsSize)
468 , first_(0)
469 {
470 for (int i = 0; i != (int)indexes_.size(); ++i)
471 indexes_[i] = {i - 1, i + 1};
472 }
473
first() const474 int first() const
475 {
476 return first_;
477 }
478
isValid(int index) const479 bool isValid(int index) const
480 {
481 return index >= first_ && index < (int)indexes_.size();
482 }
483
next(int index) const484 int next(int index) const
485 {
486 return indexes_[index].second;
487 }
488
empty() const489 bool empty() const
490 {
491 return first_ >= (int)indexes_.size();
492 }
493
exclude(int index)494 int exclude(int index)
495 {
496 if (index < first_)
497 return index;
498 int prev = indexes_[index].first;
499 int next = indexes_[index].second;
500 indexes_[index].first = -1;
501 indexes_[index].second = int(indexes_.size());
502 if (prev >= first_)
503 indexes_[prev].second = next;
504 if (next < (int)indexes_.size())
505 indexes_[next].first = prev;
506 if (index == first_)
507 first_ = next;
508 return next;
509 }
510
save()511 std::vector<std::pair<int, int>> save()
512 {
513 std::vector<std::pair<int, int>> indexes(indexes_.size() - first_);
514 for (size_t i = first_; i != indexes_.size(); ++i)
515 indexes[i - first_] = indexes_[i];
516 return indexes;
517 }
518
restore(const std::vector<std::pair<int,int>> & indexes)519 void restore(const std::vector<std::pair<int, int>> &indexes)
520 {
521 first_ = int(indexes_.size() - indexes.size());
522 for (size_t i = 0; i < indexes.size(); ++i)
523 indexes_[i + first_] = indexes[i];
524 }
525
526 private:
527 std::vector<std::pair<int, int>> indexes_; // pair<prev, next>
528 int first_;
529 };
530
531
findNextTuplet(std::vector<int> & selectedTuplets,ValidTuplets & validTuplets,std::vector<int> & bestTupletIndexes,TupletErrorResult & minCurrentError,const std::vector<TupletCommon> & tupletCommons,const std::vector<TupletInfo> & tuplets,const std::vector<std::pair<ReducedFraction,ReducedFraction>> & tupletIntervals,size_t commonsSize,const ReducedFraction & basicQuant)532 void findNextTuplet(
533 std::vector<int> &selectedTuplets,
534 ValidTuplets &validTuplets,
535 std::vector<int> &bestTupletIndexes,
536 TupletErrorResult &minCurrentError,
537 const std::vector<TupletCommon> &tupletCommons,
538 const std::vector<TupletInfo> &tuplets,
539 const std::vector<std::pair<ReducedFraction, ReducedFraction> > &tupletIntervals,
540 size_t commonsSize,
541 const ReducedFraction &basicQuant)
542 {
543 while (!validTuplets.empty()) {
544 size_t index = validTuplets.first();
545
546 bool isCommonGroupBegins = (selectedTuplets.empty() && index == commonsSize);
547 if (isCommonGroupBegins) { // first level
548 for (size_t i = index; i < tuplets.size(); ++i)
549 selectedTuplets.push_back(int(i));
550 }
551 else {
552 selectedTuplets.push_back(int(index));
553 }
554
555 Q_ASSERT_X(validateSelectedTuplets(selectedTuplets.begin(), selectedTuplets.end(), tuplets),
556 "MIDI tuplets::findNextTuplet", "Tuplets have common chords but they shouldn't");
557
558 const auto voiceIntervals = prepareVoiceIntervals(selectedTuplets, tupletIntervals);
559 const auto usedFirstChords = prepareUsedFirstChords(selectedTuplets, tuplets);
560
561 Q_ASSERT_X(areCommonsDifferent(selectedTuplets), "MidiTuplet::findNextTuplet",
562 "There are duplicates in selected commons");
563 Q_ASSERT_X(areCommonsUncommon(selectedTuplets, tupletCommons),
564 "MidiTuplet::findNextTuplet", "Incompatible selected commons");
565
566 if (isCommonGroupBegins) {
567 bool canAddMoreIndexes = false;
568 for (size_t i = 0; i != commonsSize; ++i) {
569 if (!isInCommonIndexes(int(i), selectedTuplets, tupletCommons)
570 && canUseIndex(int(i), tuplets, tupletIntervals,
571 voiceIntervals, usedFirstChords)) {
572 canAddMoreIndexes = true;
573 break;
574 }
575 }
576 if (!canAddMoreIndexes) {
577 tryUpdateBestIndexes(bestTupletIndexes, minCurrentError,
578 selectedTuplets, tuplets, voiceIntervals, basicQuant);
579 }
580 return;
581 }
582
583 validTuplets.exclude(int(index));
584 const auto savedTuplets = validTuplets.save();
585 // check tuplets for compatibility
586 if (!validTuplets.empty()) {
587 for (int i: tupletCommons[index].commonIndexes) {
588 validTuplets.exclude(i);
589 if (validTuplets.empty())
590 break;
591 }
592 }
593 for (int i = validTuplets.first(); validTuplets.isValid(i); ) {
594 if (!canUseIndex(i, tuplets, tupletIntervals,
595 voiceIntervals, usedFirstChords)) {
596 i = validTuplets.exclude(i);
597 continue;
598 }
599 i = validTuplets.next(i);
600 }
601 if (validTuplets.empty()) {
602 const auto unusedIndexes = findUnusedIndexes(selectedTuplets);
603 bool canAddMoreIndexes = false;
604 for (int i: unusedIndexes) {
605 if (!isInCommonIndexes(i, selectedTuplets, tupletCommons)
606 && canUseIndex(i, tuplets, tupletIntervals,
607 voiceIntervals, usedFirstChords)) {
608 canAddMoreIndexes = true;
609 break;
610 }
611 }
612 if (!canAddMoreIndexes) {
613 tryUpdateBestIndexes(bestTupletIndexes, minCurrentError,
614 selectedTuplets, tuplets, voiceIntervals, basicQuant);
615 }
616 }
617 else {
618 findNextTuplet(selectedTuplets, validTuplets, bestTupletIndexes, minCurrentError,
619 tupletCommons, tuplets, tupletIntervals, commonsSize, basicQuant);
620 }
621
622 selectedTuplets.pop_back();
623 validTuplets.restore(savedTuplets);
624 }
625 }
626
moveUncommonTupletsToEnd(std::vector<TupletInfo> & tuplets,std::set<int> & uncommons)627 void moveUncommonTupletsToEnd(std::vector<TupletInfo> &tuplets, std::set<int> &uncommons)
628 {
629 int swapWith = int(tuplets.size()) - 1;
630 for (int i = swapWith; i >= 0; --i) {
631 auto it = uncommons.find(i);
632 if (it != uncommons.end()) {
633 if (i != swapWith)
634 std::swap(tuplets[i], tuplets[swapWith]);
635 --swapWith;
636 uncommons.erase(it);
637 }
638 }
639
640 Q_ASSERT_X(uncommons.empty(), "MidiTuplet::moveUncommonTupletsToEnd",
641 "Untested uncommon tuplets remaining");
642 }
643
findBestTuplets(const std::vector<TupletCommon> & tupletCommons,const std::vector<TupletInfo> & tuplets,size_t commonsSize,const ReducedFraction & basicQuant)644 std::vector<int> findBestTuplets(
645 const std::vector<TupletCommon> &tupletCommons,
646 const std::vector<TupletInfo> &tuplets,
647 size_t commonsSize,
648 const ReducedFraction &basicQuant)
649 {
650 std::vector<int> bestTupletIndexes;
651 std::vector<int> selectedTuplets;
652 TupletErrorResult minCurrentError;
653 const auto tupletIntervals = findTupletIntervals(tuplets, basicQuant);
654
655 ValidTuplets validTuplets(int(tuplets.size()));
656
657 findNextTuplet(selectedTuplets, validTuplets, bestTupletIndexes, minCurrentError,
658 tupletCommons, tuplets, tupletIntervals, commonsSize, basicQuant);
659
660 return bestTupletIndexes;
661 }
662
removeExtraTuplets(std::vector<TupletInfo> & tuplets)663 void removeExtraTuplets(std::vector<TupletInfo> &tuplets)
664 {
665 const size_t MAX_TUPLETS = 17; // found empirically
666
667 if (tuplets.size() <= MAX_TUPLETS)
668 return;
669
670 std::map<TupletErrorResult, size_t> errors;
671 for (size_t i = 0; i != tuplets.size(); ++i) {
672 auto tupletError = TupletErrorResult{
673 tuplets[i].tupletSumError.numerator() * 1.0
674 / (tuplets[i].tupletSumError.denominator() * tuplets[i].chords.size()),
675 tuplets[i].chords.size() * 1.0 / tuplets[i].tupletNumber,
676 tuplets[i].sumLengthOfRests,
677 1,
678 1
679 };
680 errors.insert({tupletError, i});
681 }
682 std::vector<TupletInfo> newTuplets;
683 size_t count = 0;
684 for (const auto &e: errors) {
685 ++count;
686 newTuplets.push_back(tuplets[e.second]);
687 if (count == MAX_TUPLETS)
688 break;
689 }
690
691 std::swap(tuplets, newTuplets);
692 }
693
694 // first chord in tuplet may belong to other tuplet at the same time
695 // in the case if there are enough notes in this first chord
696 // to be split into different voices
697
filterTuplets(std::vector<TupletInfo> & tuplets,const ReducedFraction & basicQuant)698 void filterTuplets(std::vector<TupletInfo> &tuplets,
699 const ReducedFraction &basicQuant)
700 {
701 if (tuplets.empty())
702 return;
703
704 Q_ASSERT_X(!areTupletChordsEmpty(tuplets),
705 "MIDI tuplets: filterTuplets", "Tuplet has no chords but it should");
706
707 removeUselessTuplets(tuplets);
708 removeExtraTuplets(tuplets);
709
710 std::set<int> uncommons = findLongestUncommonGroup(tuplets, basicQuant);
711
712 Q_ASSERT_X(validateSelectedTuplets(uncommons.begin(), uncommons.end(), tuplets),
713 "MIDI tuplets: filterTuplets",
714 "Uncommon tuplets have common chords but they shouldn't");
715
716 size_t commonsSize = tuplets.size();
717 if (uncommons.size() > 1) {
718 commonsSize -= uncommons.size();
719 moveUncommonTupletsToEnd(tuplets, uncommons);
720 }
721 const auto tupletCommons = findTupletCommons(tuplets);
722
723 const std::vector<int> bestIndexes = findBestTuplets(tupletCommons, tuplets,
724 commonsSize, basicQuant);
725
726 Q_ASSERT_X(validateSelectedTuplets(bestIndexes.begin(), bestIndexes.end(), tuplets),
727 "MIDI tuplets: filterTuplets", "Tuplets have common chords but they shouldn't");
728
729 std::vector<TupletInfo> newTuplets;
730 for (int i: bestIndexes)
731 newTuplets.push_back(tuplets[i]);
732
733 std::swap(tuplets, newTuplets);
734 }
735
736 } // namespace MidiTuplet
737 } // namespace Ms
738