void SwingDetector::add(ChordRest *cr)
      {
      if (elements.empty()) {
            if (ReducedFraction(cr->globalDuration()) >= FULL_LEN)
                  return;
            const int tickInBar = cr->tick() - cr->measure()->tick();
            if (tickInBar % MScore::division == 0)
                  append(cr);
            }
      else {
            if (sumLen + ReducedFraction(cr->globalDuration()) > FULL_LEN) {
                  reset();
                  return;
                  }
            append(cr);
            if (sumLen == FULL_LEN) {
                              // check for swing patterns
                  switch (swingType) {
                        case MidiOperation::Swing::SWING:
                              checkNormalSwing();
                              break;
                        case MidiOperation::Swing::SHUFFLE:
                              checkShuffle();
                              break;
                        default:
                              break;
                        }
                  reset();
                  }
            }
      }
void addTitleIfAny(const std::multimap<ReducedFraction, std::string> &lyricTrack, Score *score)
      {
      int textCounter = 0;
      for (const auto &lyric: lyricTrack) {
            if (lyric.first == ReducedFraction(0, 1)) {
                  QString text = MidiCharset::convertToCharset(lyric.second);
                  if (isTitlePrefix(text)) {
                        ++textCounter;
                        addTitleToScore(score, text, textCounter);
                        }
                  }
            else if (lyric.first > ReducedFraction(0, 1)) {
                  break;
                  }
            }
      }
void SwingDetector::append(ChordRest *cr)
      {
      if (cr->type() == Element::CHORD || cr->type() == Element::REST) {
            elements.push_back(cr);
            sumLen += ReducedFraction(cr->globalDuration());
            }
      }
void SwingDetector::append(ChordRest *cr)
      {
      if (cr->isChord() || cr->isRest()) {
            elements.push_back(cr);
            sumLen += ReducedFraction(cr->globalTicks());
            }
      }
Beispiel #5
0
void removeOverlappingNotes(std::multimap<int, MTrack> &tracks)
      {
      for (auto &track: tracks) {
            auto &chords = track.second.chords;
            for (auto i1 = chords.begin(); i1 != chords.end(); ++i1) {
                  auto &chord1 = i1->second;
                  const auto &onTime1 = i1->first;
                  for (auto &note1: chord1.notes) {
                        for (auto i2 = std::next(i1); i2 != chords.end(); ++i2) {
                              const auto &onTime2 = i2->first;
                              if (onTime2 >= note1.offTime)
                                    break;
                              auto &chord2 = i2->second;
                              if (chord1.voice != chord2.voice)
                                    continue;
                              for (auto &note2: chord2.notes) {
                                    if (note2.pitch != note1.pitch)
                                          continue;
                                    qDebug("Midi import: overlapping events: %d+%d %d+%d",
                                           onTime1.ticks(), note1.offTime.ticks(),
                                           onTime2.ticks(), note2.offTime.ticks());
                                    note1.offTime = onTime2;
                                    i2 = std::prev(chords.end());
                                    break;
                                    }
                              }
                        if (note1.offTime <= ReducedFraction(0, 1)) {
                              qDebug("removeOverlappingNotes: duration <= 0: drop note at %d",
                                     onTime1.ticks());
                              continue;
                              }
                        }
                  } // for note1
            }
      }
ReducedFraction ReducedFraction::reduced() const
      {
      const int tmp = gcd(numerator_, denominator_);
      checkDivisionOverflow(numerator_, tmp);
      checkDivisionOverflow(denominator_, tmp);
      return ReducedFraction(numerator_ / tmp, denominator_ / tmp);
      }
Beispiel #7
0
void quantizeChords(std::multimap<ReducedFraction, MidiChord> &chords,
                    const std::multimap<ReducedFraction, MidiTuplet::TupletData> &tupletEvents,
                    const TimeSigMap *sigmap)
      {
      std::multimap<ReducedFraction, MidiChord> quantizedChords;
      for (auto &chordEvent: chords) {
            MidiChord chord = chordEvent.second;     // copy chord
            auto onTime = chordEvent.first;
            const auto barStart = findBarStart(onTime, sigmap);
            if (chord.quantizedOnTime == ReducedFraction(-1, 1)) {
                  const auto raster = findQuantRaster(onTime, chord.voice, tupletEvents, chords, sigmap);
                  onTime = barStart + Quantize::quantizeValue(onTime - barStart, raster);
                  }
            else {
                  onTime = chord.quantizedOnTime;
                  chord.quantizedOnTime = {-1, 1};
                  }

            for (auto it = chord.notes.begin(); it != chord.notes.end(); ) {
                  MidiNote &note = *it;
                  if (note.quantizedOffTime == ReducedFraction(-1, 1)) {
                        auto offTime = note.offTime;
                        auto raster = findQuantRaster(offTime, chord.voice, tupletEvents, chords, sigmap);
                        if (Meter::isSimpleNoteDuration(raster))    // offTime is not inside tuplet
                              raster = reduceRasterIfDottedNote(note.offTime - chordEvent.first, raster);

                        offTime = barStart + Quantize::quantizeValue(offTime - barStart, raster);
                        note.offTime = offTime;
                        }
                  else {
                        note.offTime = note.quantizedOffTime;
                        note.quantizedOffTime = {-1, 1};
                        }

                  if (note.offTime - onTime < MChord::minAllowedDuration()) {
                        it = chord.notes.erase(it);
                        qDebug() << "quantizeChords: note was removed due to its short length";
                        continue;
                        }
                  ++it;
                  }
            if (!chord.notes.isEmpty())
                  quantizedChords.insert({onTime, chord});
            }

      std::swap(chords, quantizedChords);
      }
bool areDurationsEqual(
            const QList<std::pair<ReducedFraction, TDuration> > &durations,
            const ReducedFraction &desiredLen)
      {
      ReducedFraction sum(0, 1);
      for (const auto &d: durations)
            sum += ReducedFraction(d.second.fraction()) / d.first;

      return desiredLen == desiredLen;
      }
ReducedFraction ReducedFraction::reduced() const
      {
      const int tmp = gcd(numerator_, denominator_);

      Q_ASSERT_X(!isDivisionOverflow(numerator_, tmp),
                 "ReducedFraction::reduced", "Division overflow");
      Q_ASSERT_X(!isDivisionOverflow(denominator_, tmp),
                 "ReducedFraction::reduced", "Division overflow");

      return ReducedFraction(numerator_ / tmp, denominator_ / tmp);
      }
Beispiel #10
0
ReducedFraction quantizeValue(const ReducedFraction &value,
                              const ReducedFraction &raster)
      {
      const auto valueReduced = value.reduced();
      const auto rasterReduced = raster.reduced();
      int valNum = valueReduced.numerator() * rasterReduced.denominator();
      const int rastNum = rasterReduced.numerator() * valueReduced.denominator();
      const int commonDen = valueReduced.denominator() * rasterReduced.denominator();
      valNum = ((valNum + rastNum / 2) / rastNum) * rastNum;
      return ReducedFraction(valNum, commonDen).reduced();
      }
void minimizeNumberOfRests(
            std::multimap<ReducedFraction, MidiChord> &chords,
            const TimeSigMap *sigmap,
            const std::multimap<ReducedFraction, MidiTuplet::TupletData> &tuplets)
      {
      for (auto it = chords.begin(); it != chords.end(); ++it) {
            for (MidiNote &note: it->second.notes) {
                  const auto barStart = MidiBar::findBarStart(note.offTime, sigmap);
                  const auto barFraction = ReducedFraction(
                                                sigmap->timesig(barStart.ticks()).timesig());
                  auto durationStart = (it->first > barStart) ? it->first : barStart;
                  if (it->second.isInTuplet) {
                        const auto &tuplet = it->second.tuplet->second;
                        if (note.offTime >= tuplet.onTime + tuplet.len)
                              durationStart = tuplet.onTime + tuplet.len;
                        }
                  auto endTime = (barStart == note.offTime)
                                    ? barStart : barStart + barFraction;
                  if (note.isInTuplet) {
                        const auto &tuplet = note.tuplet->second;
                        if (note.offTime == tuplet.onTime + tuplet.len)
                              continue;
                        endTime = barStart + Quantize::quantizeToLarge(
                                    note.offTime - barStart, tuplet.len / tuplet.tupletNumber);
                        }

                  const auto beatLen = Meter::beatLength(barFraction);
                  const auto beatTime = barStart + Quantize::quantizeToLarge(
                                                      note.offTime - barStart, beatLen);
                  if (endTime > beatTime)
                        endTime = beatTime;

                  auto next = std::next(it);
                  while (next != chords.end()
                              && (next->second.voice != it->second.voice
                                  || next->first < note.offTime)) {
                        ++next;
                        }
                  if (next != chords.end()) {
                        if (next->first < endTime)
                              endTime = next->first;
                        if (next->second.isInTuplet && !note.isInTuplet) {
                              const auto &tuplet = next->second.tuplet->second;
                              if (tuplet.onTime < endTime)
                                    endTime = tuplet.onTime;
                              }
                        }

                  lengthenNote(note, it->second.voice, it->first, durationStart, endTime,
                               barStart, barFraction, tuplets);
                  }
            }
      }
Beispiel #12
0
ReducedFraction findMinDuration(const ReducedFraction &onTime,
                                const QList<MidiChord> &midiChords,
                                const ReducedFraction &length)
      {
      ReducedFraction len = length;
      for (const auto &chord: midiChords) {
            for (const auto &note: chord.notes) {
                  if ((note.offTime - onTime < len)
                              && (note.offTime - onTime != ReducedFraction(0, 1)))
                        len = note.offTime - onTime;
                  }
            }
      return len;
      }
void addLyricsToScore(const std::multimap<ReducedFraction, std::string> &lyricTrack,
                      const Staff *staffAddTo)
      {
      Score *score = staffAddTo->score();
      int textCounter = 0;

      for (const auto &e: lyricTrack) {
            const auto &tick = e.first;
            QString str = MidiCharset::convertToCharset(e.second);
            if (tick == ReducedFraction(0, 1)) {
                  addTitle(score, str, &textCounter);
                  }
            else {
                  QString text = removeSlashes(str);
                  score->addLyrics(tick.ticks(), staffAddTo->idx(), text);
                  }
            }
      }
Beispiel #14
0
ReducedFraction findQuantRaster(
            const ReducedFraction &time,
            int voice,
            const std::multimap<ReducedFraction, MidiTuplet::TupletData> &tupletEvents,
            const std::multimap<ReducedFraction, MidiChord> &chords,
            const TimeSigMap *sigmap)
      {
      ReducedFraction raster;
      const auto tupletIt = MidiTuplet::findTupletContainsTime(voice, time, tupletEvents);

      if (tupletIt != tupletEvents.end() && time > tupletIt->first)
            raster = tupletIt->second.tupletQuant;   // quantize onTime with tuplet quant
      else {
                        // quantize onTime with regular quant
            const auto startBarTick = findBarStart(time, sigmap);
            const auto endBarTick = startBarTick
                        + ReducedFraction(sigmap->timesig(startBarTick.ticks()).timesig());
            const auto startBarChordIt = MChord::findFirstChordInRange(
                                                chords, startBarTick, endBarTick);
            raster = findRegularQuantRaster(startBarChordIt, chords.end(), endBarTick);
            }
      return raster;
      }
void addLyricsToScore(
            const std::multimap<ReducedFraction, std::string> &lyricTrack,
            const std::vector<std::pair<ReducedFraction, ReducedFraction>> &matchedLyricTimes,
            const Staff *staffAddTo)
      {
      Score *score = staffAddTo->score();

      addTitleIfAny(lyricTrack, score);

      for (const auto &timePair: matchedLyricTimes) {
            const auto quantizedTime = timePair.first;
            const auto originalTime = timePair.second;
            const auto it = lyricTrack.find(originalTime);

            Q_ASSERT_X(it != lyricTrack.end(),
                       "MidiLyrics::addLyricsToScore", "Lyric time not found");

            QString text = MidiCharset::convertToCharset(it->second);
            if (originalTime != ReducedFraction(0, 1) || !isTitlePrefix(text)) { // not title
                  score->addLyrics(quantizedTime.ticks(), staffAddTo->idx(),
                                   removeSlashes(text).toHtmlEscaped());
                  }
            }
      }
ReducedFraction ReducedFraction::fromTicks(int ticks)
      {
      return ReducedFraction(ticks, MScore::division * 4).reduced();
      }
Beispiel #17
0
void setToNegative(ReducedFraction &v1, ReducedFraction &v2, ReducedFraction &v3)
      {
      v1 = ReducedFraction(-1, 1);
      v2 = ReducedFraction(-1, 1);
      v3 = ReducedFraction(-1, 1);
      }
void SwingDetector::reset()
      {
      elements.clear();
      sumLen = ReducedFraction(0);
      }
void createInstruments(Score *score, QList<MTrack> &tracks)
      {
      const auto& opers = preferences.midiImportOperations;
      const auto &instrListOption = opers.data()->trackOpers.msInstrList;

      const int ntracks = tracks.size();
      for (int idx = 0; idx < ntracks; ++idx) {
            MTrack& track = tracks[idx];
            Part* part = new Part(score);

            const auto &instrList = instrListOption.value(track.indexOfOperation);
            const InstrumentTemplate *instr = nullptr;
            if (!instrList.empty()) {
                  const int instrIndex = opers.data()->trackOpers.msInstrIndex.value(
                                                                    track.indexOfOperation);
                  instr = instrList[instrIndex];
                  if (instr)
                        part->initFromInstrTemplate(instr);
                  }

            if (areNext3OrganStaff(idx, tracks))
                  part->setStaves(3);
            else if (areNext2GrandStaff(idx, tracks))
                  part->setStaves(2);
            else
                  part->setStaves(1);

            if (part->nstaves() == 1) {
                  if (track.mtrack->drumTrack()) {
                        part->staff(0)->setStaffType(0, StaffType::preset(StaffTypes::PERC_DEFAULT));
                        if (!instr) {
                              part->instrument()->setDrumset(smDrumset);
                              }
                        }
                  }
            else {
                  if (!instr) {
                        part->staff(0)->setBarLineSpan(2);
                        part->staff(0)->setBracket(0, BracketType::BRACE);
                        }
                  else {
                        part->staff(0)->setBarLineSpan(instr->barlineSpan[0]);
                        part->staff(0)->setBracket(0, instr->bracket[0]);
                        }
                  part->staff(0)->setBracketSpan(0, 2);
                  }

            if (instr) {
                  for (int i = 0; i != part->nstaves(); ++i) {
                        if (instr->staffTypePreset)
                              part->staff(i)->setStaffType(0, instr->staffTypePreset);
                        part->staff(i)->setLines(0, instr->staffLines[i]);
                        part->staff(i)->setSmall(0, instr->smallStaff[i]);
                        part->staff(i)->setDefaultClefType(instr->clefTypes[i]);
                        }
                  }

            for (int i = 0; i != part->nstaves(); ++i) {
                  if (i > 0)
                        ++idx;
                  tracks[idx].staff = part->staff(i);
                  }

            // only importing a single volume per track here, skip when multiple volumes
            // are defined, or the single volume is not defined on tick 0.
            if (track.volumes.size() == 1) {
                  for (auto &i: track.volumes) {
                        if (i.first == ReducedFraction(0, 1)) {
                              part->instrument()->channel(0)->volume = i.second;
                              }
                        }
                  }

            score->appendPart(part);
            }
      }
void lengthenNote(
            MidiNote &note,
            int voice,
            const ReducedFraction &noteOnTime,
            const ReducedFraction &durationStart,
            const ReducedFraction &endTime,
            const ReducedFraction &barStart,
            const ReducedFraction &barFraction,
            const std::multimap<ReducedFraction, MidiTuplet::TupletData> &tuplets)
      {
      if (endTime <= note.offTime)
            return;

      const auto &opers = preferences.midiImportOperations.data()->trackOpers;
      const int currentTrack = preferences.midiImportOperations.currentTrack();

      const bool useDots = opers.useDots.value(currentTrack);
      const auto tupletsForDuration = MidiTuplet::findTupletsInBarForDuration(
                              voice, barStart, note.offTime, endTime - note.offTime, tuplets);

      Q_ASSERT_X(note.quant != ReducedFraction(-1, 1),
                 "Simplify::lengthenNote", "Note quant value was not set");

      const auto origNoteDurations = Meter::toDurationList(
                              durationStart - barStart, note.offTime - barStart, barFraction,
                              tupletsForDuration, Meter::DurationType::NOTE, useDots, false);

      const auto origRestDurations = Meter::toDurationList(
                              note.offTime - barStart, endTime - barStart, barFraction,
                              tupletsForDuration, Meter::DurationType::REST, useDots, false);

      Q_ASSERT_X(areDurationsEqual(origNoteDurations, note.offTime - durationStart),
                 "Simplify::lengthenNote", "Too short note durations remaining");
      Q_ASSERT_X(areDurationsEqual(origRestDurations, endTime - note.offTime),
                 "Simplify::lengthenNote", "Too short rest durations remaining");

                  // double - because can be + 0.5 for dots
      double minNoteDurationCount = MidiDuration::durationCount(origNoteDurations);
      double minRestDurationCount = MidiDuration::durationCount(origRestDurations);

      ReducedFraction bestOffTime(-1, 1);

      for (ReducedFraction offTime = note.offTime + note.quant;
                           offTime <= endTime; offTime += note.quant) {
            double noteDurationCount = 0;
            double restDurationCount = 0;

            const auto noteDurations = Meter::toDurationList(
                              durationStart - barStart, offTime - barStart, barFraction,
                              tupletsForDuration, Meter::DurationType::NOTE, useDots, false);

            Q_ASSERT_X(areDurationsEqual(noteDurations, offTime - durationStart),
                       "Simplify::lengthenNote", "Too short note durations remaining");

            noteDurationCount += MidiDuration::durationCount(noteDurations);

            if (offTime < endTime) {
                  const auto restDurations = Meter::toDurationList(
                              offTime - barStart, endTime - barStart, barFraction,
                              tupletsForDuration, Meter::DurationType::REST, useDots, false);

                  Q_ASSERT_X(areDurationsEqual(restDurations, endTime - offTime),
                             "Simplify::lengthenNote", "Too short rest durations remaining");

                  restDurationCount += MidiDuration::durationCount(restDurations);
                  }

            if (noteDurationCount + restDurationCount
                              < minNoteDurationCount + minRestDurationCount) {
                  if (opers.isHumanPerformance.value() || noteDurationCount <= 1.5) {
                        minNoteDurationCount = noteDurationCount;
                        minRestDurationCount = restDurationCount;
                        bestOffTime = offTime;
                        }
                  }
            }

      if (bestOffTime == ReducedFraction(-1, 1))
            return;

      // check for staccato:
      //    don't apply staccato if note is tied
      //    (case noteOnTime != durationStart - another bar, for example)

      bool hasLossOfAccuracy = false;
      const double addedPart = ((bestOffTime - note.offTime)
                                / (bestOffTime - durationStart)).toDouble();
      const double STACCATO_TOL = 0.3;

      if (addedPart >= STACCATO_TOL) {
            if (noteOnTime == durationStart && minNoteDurationCount <= 1.5)
                  note.staccato = true;
            else
                  hasLossOfAccuracy = true;
            }

      // if the difference is only in one note/rest and there is some loss of accuracy -
      // discard change because it silently reduces duration accuracy
      // without significant improvement of readability

      if (!opers.isHumanPerformance.value()
                  && (origNoteDurations.size() + origRestDurations.size())
                       - (minNoteDurationCount + minRestDurationCount) <= 1
                  && !hasComplexBeamedDurations(origNoteDurations)
                  && !hasComplexBeamedDurations(origRestDurations)
                  && hasLossOfAccuracy) {
            return;
            }

      note.offTime = bestOffTime;
      }
ReducedFraction ReducedFraction::absValue() const
      {
      return ReducedFraction(qAbs(numerator_), qAbs(denominator_));
      }
ReducedFraction userTimeSigToFraction(
            MidiOperations::TimeSigNumerator timeSigNumerator,
            MidiOperations::TimeSigDenominator timeSigDenominator)
      {
      int numerator = 4;
      int denominator = 4;

      switch (timeSigNumerator) {
            case MidiOperations::TimeSigNumerator::_2:
                  numerator = 2;
                  break;
            case MidiOperations::TimeSigNumerator::_3:
                  numerator = 3;
                  break;
            case MidiOperations::TimeSigNumerator::_4:
                  numerator = 4;
                  break;
            case MidiOperations::TimeSigNumerator::_5:
                  numerator = 5;
                  break;
            case MidiOperations::TimeSigNumerator::_6:
                  numerator = 6;
                  break;
            case MidiOperations::TimeSigNumerator::_7:
                  numerator = 7;
                  break;
            case MidiOperations::TimeSigNumerator::_9:
                  numerator = 9;
                  break;
            case MidiOperations::TimeSigNumerator::_12:
                  numerator = 12;
                  break;
            case MidiOperations::TimeSigNumerator::_15:
                  numerator = 15;
                  break;
            case MidiOperations::TimeSigNumerator::_21:
                  numerator = 21;
                  break;
            default:
                  break;
            }

      switch (timeSigDenominator) {
            case MidiOperations::TimeSigDenominator::_2:
                  denominator = 2;
                  break;
            case MidiOperations::TimeSigDenominator::_4:
                  denominator = 4;
                  break;
            case MidiOperations::TimeSigDenominator::_8:
                  denominator = 8;
                  break;
            case MidiOperations::TimeSigDenominator::_16:
                  denominator = 16;
                  break;
            case MidiOperations::TimeSigDenominator::_32:
                  denominator = 32;
                  break;
            default:
                  break;
            }

      return ReducedFraction(numerator, denominator);
      }
TupletInfo findTupletApproximation(
            const ReducedFraction &tupletLen,
            int tupletNumber,
            const ReducedFraction &basicQuant,
            const ReducedFraction &startTupletTime,
            const std::multimap<ReducedFraction, MidiChord>::iterator &startChordIt,
            const std::multimap<ReducedFraction, MidiChord>::iterator &endChordIt)
      {
      TupletInfo tupletInfo;
      tupletInfo.tupletNumber = tupletNumber;
      tupletInfo.onTime = startTupletTime;
      tupletInfo.len = tupletLen;
      const auto tupletNoteLen = tupletLen / tupletNumber;

      struct Error
            {
            bool operator<(const Error &e) const
                  {
                  if (tupletError < e.tupletError)
                        return true;
                  if (tupletError > e.tupletError)
                        return false;
                  return (tupletRegularDiff < e.tupletRegularDiff);
                  }

            ReducedFraction tupletError;
            ReducedFraction tupletRegularDiff;
            };

      struct Candidate
            {
            int posIndex;
            std::multimap<ReducedFraction, MidiChord>::iterator chord;
            ReducedFraction regularError;
            };

      std::multimap<Error, Candidate> chordCandidates;

      for (int posIndex = 0; posIndex != tupletNumber; ++posIndex) {
            const auto tupletNotePos = startTupletTime + tupletNoteLen * posIndex;
            for (auto it = startChordIt; it != endChordIt; ++it) {
                  if (it->first < tupletNotePos - tupletNoteLen / 2)
                        continue;
                  if (it->first > tupletNotePos + tupletNoteLen / 2)
                        break;

                  const auto tupletError = (it->first - tupletNotePos).absValue();
                  const auto regularError = Quantize::findOnTimeQuantError(*it, basicQuant);
                  const auto diff = tupletError - regularError;
                  chordCandidates.insert({{tupletError, diff}, {posIndex, it, regularError}});
                  }
            }

      std::set<std::pair<const ReducedFraction, MidiChord> *> usedChords;
      std::set<int> usedPosIndexes;
      ReducedFraction diffSum;
      int firstChordIndex = 0;

      for (const auto &candidate: chordCandidates) {
            if (diffSum + candidate.first.tupletRegularDiff > ReducedFraction(0, 1))
                  break;
            const Candidate &c = candidate.second;
            if (usedPosIndexes.find(c.posIndex) != usedPosIndexes.end())
                  continue;
            if (usedChords.find(&*c.chord) != usedChords.end())
                  continue;

            usedChords.insert(&*c.chord);
            usedPosIndexes.insert(c.posIndex);
            diffSum += candidate.first.tupletRegularDiff;
            tupletInfo.chords.insert({c.chord->first, c.chord});
            tupletInfo.tupletSumError += candidate.first.tupletError;
            tupletInfo.regularSumError += c.regularError;
                        // if chord was inserted to the beginning - remember its pos index
            if (c.chord->first == tupletInfo.chords.begin()->first)
                  firstChordIndex = c.posIndex;
            }

      tupletInfo.firstChordIndex = firstChordIndex;

      return tupletInfo;
      }