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