LCOV - code coverage report
Current view: top level - src/importexport/bb/internal - bb.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 48.9 % 679 332
Test Date: 2026-11-05 04:07:02

            Line data    Source code
       1              : /*
       2              :  * SPDX-License-Identifier: GPL-3.0-only
       3              :  * MuseScore-Studio-CLA-applies
       4              :  *
       5              :  * MuseScore Studio
       6              :  * Music Composition & Notation
       7              :  *
       8              :  * Copyright (C) 2021 MuseScore Limited and others
       9              :  *
      10              :  * This program is free software: you can redistribute it and/or modify
      11              :  * it under the terms of the GNU General Public License version 3 as
      12              :  * published by the Free Software Foundation.
      13              :  *
      14              :  * This program is distributed in the hope that it will be useful,
      15              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      16              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17              :  * GNU General Public License for more details.
      18              :  *
      19              :  * You should have received a copy of the GNU General Public License
      20              :  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
      21              :  */
      22              : 
      23              : #include <limits>
      24              : 
      25              : #include <QFile>
      26              : #include <QFileInfo>
      27              : 
      28              : #include "bb.h"
      29              : 
      30              : #include "engraving/dom/box.h"
      31              : #include "engraving/dom/chord.h"
      32              : #include "engraving/dom/chordlist.h"
      33              : #include "engraving/dom/drumset.h"
      34              : #include "engraving/dom/factory.h"
      35              : #include "engraving/dom/harmony.h"
      36              : #include "engraving/dom/key.h"
      37              : #include "engraving/dom/keysig.h"
      38              : #include "engraving/dom/layoutbreak.h"
      39              : #include "engraving/dom/masterscore.h"
      40              : #include "engraving/dom/measure.h"
      41              : #include "engraving/dom/mscore.h"
      42              : #include "engraving/dom/note.h"
      43              : #include "engraving/dom/part.h"
      44              : #include "engraving/dom/pitchspelling.h"
      45              : #include "engraving/dom/rest.h"
      46              : #include "engraving/dom/segment.h"
      47              : #include "engraving/dom/slur.h"
      48              : #include "engraving/dom/staff.h"
      49              : #include "engraving/dom/text.h"
      50              : #include "engraving/dom/tie.h"
      51              : #include "engraving/dom/utils.h"
      52              : #include "engraving/editing/editenharmonicspelling.h"
      53              : #include "engraving/editing/transpose.h"
      54              : #include "engraving/engravingerrors.h"
      55              : 
      56              : #include "log.h"
      57              : 
      58              : using namespace mu::engraving;
      59              : 
      60              : namespace mu::iex::bb {
      61              : //---------------------------------------------------------
      62              : //   BBTrack
      63              : //---------------------------------------------------------
      64              : 
      65            2 : BBTrack::BBTrack(BBFile* f)
      66              : {
      67            2 :     bb          = f;
      68            2 :     _outChannel = -1;
      69            2 :     _drumTrack  = false;
      70            2 : }
      71              : 
      72            0 : BBTrack::~BBTrack()
      73              : {
      74            0 : }
      75              : 
      76              : //---------------------------------------------------------
      77              : //   MNote
      78              : //    special Midi Note
      79              : //---------------------------------------------------------
      80              : 
      81              : struct MNote {
      82              :     Event mc;
      83              :     QList<Tie*> ties;
      84              : 
      85            0 :     MNote(const Event& _mc)
      86            0 :         : mc(_mc)
      87              :     {
      88            0 :         for (size_t i = 0; i < mc.notes().size(); ++i) {
      89            0 :             ties.append(0);
      90              :         }
      91            0 :     }
      92              : };
      93              : 
      94              : //---------------------------------------------------------
      95              : //   BBFile
      96              : //---------------------------------------------------------
      97              : 
      98         1983 : BBFile::BBFile()
      99              : {
     100       507648 :     for (int i = 0; i < MAX_BARS; ++i) {
     101       505665 :         _barType[i]  = 0;
     102              :     }
     103         1983 :     bbDivision = 120;
     104         1983 : }
     105              : 
     106              : //---------------------------------------------------------
     107              : //   BBFile
     108              : //---------------------------------------------------------
     109              : 
     110         1983 : BBFile::~BBFile()
     111              : {
     112         1983 : }
     113              : 
     114              : //---------------------------------------------------------
     115              : //   read
     116              : //    return false on error
     117              : //---------------------------------------------------------
     118              : 
     119         1983 : bool BBFile::read(const QString& name)
     120              : {
     121         1983 :     _siglist.clear();
     122         1983 :     _siglist.add(0, Fraction(4, 4));          // debug
     123              : 
     124         1983 :     _path = name;
     125         1983 :     QFile f(name);
     126         1983 :     if (!f.open(QIODevice::ReadOnly)) {
     127            0 :         return false;
     128              :     }
     129         1983 :     ba = f.readAll();
     130         1983 :     f.close();
     131              : 
     132         1983 :     a    = (const unsigned char*)ba.data();
     133         1983 :     size = ba.size();
     134              : 
     135              :     //---------------------------------------------------
     136              :     //    read version
     137              :     //---------------------------------------------------
     138              : 
     139         1983 :     int idx = 0;
     140         1983 :     if (!checkRange(idx, 1)) {
     141            1 :         LOGE() << "BB: truncated file (version)";
     142            1 :         return false;
     143              :     }
     144         1982 :     _version = a[idx++];
     145         1982 :     switch (_version) {
     146         1982 :     case 0x43:
     147              :     case 0x44:
     148              :     case 0x45:
     149              :     case 0x46:
     150              :     case 0x47:
     151              :     case 0x48:
     152              :     case 0x49:
     153         1982 :         break;
     154            0 :     default:
     155            0 :         LOGD("BB: unknown file version %02x", _version);
     156            0 :         return false;
     157              :     }
     158              : 
     159         1982 :     LOGD("read <%s> version 0x%02x", qPrintable(name), _version);
     160              : 
     161              :     //---------------------------------------------------
     162              :     //    read title
     163              :     //---------------------------------------------------
     164              : 
     165         1982 :     if (!checkRange(idx, 1)) {
     166            1 :         LOGE() << "BB: truncated file (title length)";
     167            1 :         return false;
     168              :     }
     169         1981 :     int len = a[idx++];
     170         1981 :     if (!checkRange(idx, len)) {
     171           11 :         LOGE() << "BB: truncated file (title)";
     172           11 :         return false;
     173              :     }
     174         1970 :     _title  = new char[len + 1];
     175        23640 :     for (int i = 0; i < len; ++i) {
     176        21670 :         _title[i] = a[idx++];
     177              :     }
     178         1970 :     _title[len] = 0;
     179              : 
     180              :     //---------------------------------------------------
     181              :     //    read style(timesig), key and bpm
     182              :     //---------------------------------------------------
     183              : 
     184         1970 :     if (!checkRange(idx, 3)) {
     185            3 :         LOGE() << "BB: truncated file (style)";
     186            3 :         return false;
     187              :     }
     188         1967 :     ++idx;
     189         1967 :     ++idx;
     190         1967 :     _style = a[idx++] - 1;
     191         1967 :     if (_style < 0 || _style >= int(sizeof(styles) / sizeof(*styles))) {
     192            0 :         LOGD("Import bb: unknown style %d", _style + 1);
     193            0 :         return false;
     194              :     }
     195         1967 :     if (!checkRange(idx, 1)) {
     196            1 :         LOGE() << "BB: truncated file (key)";
     197            1 :         return false;
     198              :     }
     199         1966 :     _key = a[idx++];
     200              : 
     201              :     // map D# G# A#   to Eb Ab Db
     202              :     // major   C, Db,  D, Eb,  E,  F, Gb,  G, Ab,  A, Bb,  B, C#, D#, F#, G#, A#
     203              :     // minor   C, Db,  D, Eb,  E,  F, Gb,  G, Ab,  A, Bb,  B, C#, D#, F#, G#, A#
     204              :     static int kt[] = {
     205              :         0,    0, -5,  2, -3,  4, -1, -6,  1, -4,  3, -2,  5,  7, -3,  6, -4, -2,
     206              :         -3, 4,  -1, -6,  1, -4,  3, -2,  5,  0, -5,  2,  4,  6,  3,  5, 7
     207              :     };
     208         1966 :     if (_key >= int(sizeof(kt) / sizeof(*kt))) {
     209            0 :         LOGD("bad key %d", _key);
     210            0 :         return false;
     211              :     }
     212         1966 :     _key = kt[_key];
     213              : 
     214         1966 :     if (!checkRange(idx, 2)) {
     215            2 :         LOGE() << "BB: truncated file (bpm)";
     216            2 :         return false;
     217              :     }
     218         1964 :     _bpm = a[idx] + (a[idx + 1] << 8);
     219         1964 :     idx += 2;
     220              : 
     221         1964 :     LOGD("Title <%s>", _title);
     222         1964 :     LOGD("style %d",   _style);
     223         1964 :     LOGD("key   %d",   _key);
     224         1964 :     LOGD("sig   %d/%d", timesigZ(), timesigN());
     225         1964 :     LOGD("bpm   %d", _bpm);
     226              : 
     227              :     //---------------------------------------------------
     228              :     //    read bar types
     229              :     //---------------------------------------------------
     230              : 
     231         1964 :     if (!checkRange(idx, 1)) {
     232            1 :         LOGE() << "BB: truncated file (bar start)";
     233            1 :         return false;
     234              :     }
     235         1963 :     int bar = a[idx++];             // starting bar number
     236        13711 :     while (bar < 255) {
     237        11757 :         if (!checkRange(idx, 1)) {
     238            6 :             LOGE() << "BB: truncated file (bar type)";
     239            6 :             return false;
     240              :         }
     241        11751 :         int val = a[idx++];
     242        11751 :         if (val == 0) {
     243         5874 :             if (!checkRange(idx, 1)) {
     244            3 :                 LOGE() << "BB: truncated file (bar run-length)";
     245            3 :                 return false;
     246              :             }
     247         5871 :             bar += a[idx++];
     248              :         } else {
     249         5877 :             LOGD("bar type: bar %d val %d", bar, val);
     250         5877 :             _barType[bar++] = val;
     251              :         }
     252              :     }
     253              : 
     254              :     //---------------------------------------------------
     255              :     //    read chord extensions
     256              :     //---------------------------------------------------
     257              : 
     258              :     int beat;
     259       187996 :     for (beat = 0; beat < MAX_BARS * 4;) {
     260       186192 :         if (!checkRange(idx, 1)) {
     261           99 :             LOGE() << "BB: truncated file (chord extension)";
     262           99 :             return false;
     263              :         }
     264       186093 :         int val = a[idx++];
     265       186093 :         if (val == 0) {
     266        95734 :             if (!checkRange(idx, 1)) {
     267           51 :                 LOGE() << "BB: truncated file (chord ext run-length)";
     268           51 :                 return false;
     269              :             }
     270        95683 :             beat += a[idx++];
     271              :         } else {
     272        90359 :             BBChord c;
     273        90359 :             c.extension = val;
     274        90359 :             c.beat      = beat * (timesigZ() / timesigN());
     275        90359 :             ++beat;
     276        90359 :             _chords.append(c);
     277              :         }
     278              :     }
     279              : 
     280              :     //---------------------------------------------------
     281              :     //    read chord root
     282              :     //---------------------------------------------------
     283              : 
     284         1804 :     int roots = 0;
     285         1804 :     int maxbeat = 0;
     286       172991 :     for (beat = 0; beat < MAX_BARS * 4;) {
     287       171338 :         if (!checkRange(idx, 1)) {
     288           99 :             LOGE() << "BB: truncated file (chord root)";
     289           99 :             return false;
     290              :         }
     291       171239 :         int val = a[idx++];
     292       171239 :         if (val == 0) {
     293        88079 :             if (!checkRange(idx, 1)) {
     294           51 :                 LOGE() << "BB: truncated file (chord root run-length)";
     295           51 :                 return false;
     296              :             }
     297        88028 :             beat += a[idx++];
     298              :         } else {
     299        83160 :             int root = val % 18;
     300        83160 :             int bass = (root - 1 + val / 18) % 18 + 1;
     301        83160 :             if (root == bass) {
     302        83158 :                 bass = 0;
     303              :             }
     304        83160 :             int ibeat = beat * (timesigZ() / timesigN());
     305        83160 :             if (roots >= _chords.size()) {
     306            1 :                 LOGE() << "BB: chord root/extension count mismatch";
     307            1 :                 return false;
     308              :             }
     309        83159 :             if (ibeat != _chords[roots].beat) {
     310            0 :                 LOGD("import bb: inconsistent chord type and root beat");
     311            0 :                 return false;
     312              :             }
     313        83159 :             _chords[roots].root = root;
     314        83159 :             _chords[roots].bass = bass;
     315        83159 :             if (maxbeat < beat) {
     316        81356 :                 maxbeat = beat;
     317              :             }
     318        83159 :             ++roots;
     319        83159 :             ++beat;
     320              :         }
     321              :     }
     322              : 
     323         1653 :     _measures = ((maxbeat + timesigZ() - 1) / timesigZ()) + 1;
     324              : 
     325         1653 :     if (roots != _chords.size()) {
     326            0 :         LOGD("import bb: roots %d != extensions %lld", roots, _chords.size());
     327            0 :         return false;
     328              :     }
     329         1653 :     LOGD("Measures %d", _measures);
     330              : 
     331         1653 :     if (!checkRange(idx, 1)) {
     332            1 :         LOGE() << "BB: truncated file (chorus marker)";
     333            1 :         return false;
     334              :     }
     335         1652 :     if (a[idx] == 1) {              //??
     336         1652 :         LOGD("Skip 0x%02x at 0x%04x", a[idx], idx);
     337         1652 :         ++idx;
     338              :     }
     339              : 
     340         1652 :     if (!checkRange(idx, 3)) {
     341            3 :         LOGE() << "BB: truncated file (chorus/repeats)";
     342            3 :         return false;
     343              :     }
     344         1649 :     _startChorus = a[idx++];
     345         1649 :     _endChorus   = a[idx++];
     346         1649 :     _repeats     = a[idx++];
     347              : 
     348         1649 :     LOGD("start chorus %d  end chorus %d repeats %d, pos now 0x%x",
     349              :          _startChorus, _endChorus, _repeats, idx);
     350              : 
     351         1649 :     if (_startChorus >= _endChorus) {
     352         1649 :         _startChorus = 0;
     353         1649 :         _endChorus = 0;
     354         1649 :         _repeats = 1;
     355              :     }
     356              : 
     357              :     //---------------------------------------------------
     358              :     //    read style file
     359              :     //---------------------------------------------------
     360              : 
     361         1649 :     bool found = false;
     362       103586 :     for (int i = idx; i < size; ++i) {
     363       103511 :         if (a[i] == 0x42) {
     364         1585 :             if (checkRange(i, 2) && a[i + 1] < 16) {
     365        11049 :                 for (int k = i + 2; k < (i + 18); ++k) {
     366        11049 :                     if (!checkRange(k, 4)) {
     367           10 :                         break;
     368              :                     }
     369        11039 :                     if (a[k] == '.' && a[k + 1] == 'S' && a[k + 2] == 'T' && a[k + 3] == 'Y') {
     370         1574 :                         found = true;
     371         1574 :                         break;
     372              :                     }
     373              :                 }
     374              :             }
     375         1585 :             if (found) {
     376         1574 :                 idx = i + 1;
     377         1574 :                 break;
     378              :             }
     379              :         }
     380              :     }
     381         1649 :     if (!found) {
     382           75 :         LOGD("import bb: style file not found");
     383           75 :         return false;
     384              :     }
     385              : 
     386         1574 :     LOGD("read styleName at 0x%x", idx);
     387         1574 :     if (!checkRange(idx, 1)) {
     388            0 :         LOGE() << "BB: truncated file (style name length)";
     389            0 :         return false;
     390              :     }
     391         1574 :     len = a[idx++];
     392         1574 :     if (!checkRange(idx, len)) {
     393            0 :         LOGE() << "BB: truncated file (style name)";
     394            0 :         return false;
     395              :     }
     396         1574 :     _styleName = new char[len + 1];
     397              : 
     398        17314 :     for (int i = 0; i < len; ++i) {
     399        15740 :         _styleName[i] = a[idx++];
     400              :     }
     401         1574 :     _styleName[len] = 0;
     402              : 
     403         1574 :     LOGD("style name <%s>", _styleName);
     404              : 
     405              :     // read midi events
     406         1574 :     if (!checkRange(size - 4, 4)) {
     407            0 :         LOGE() << "BB: truncated file (event header)";
     408            0 :         return false;
     409              :     }
     410         1574 :     int eventStart = a[size - 4] + a[size - 3] * 256;
     411         1574 :     int eventCount = a[size - 2] + a[size - 1] * 256;
     412              : 
     413         1574 :     const int EVENT_END_BOUNDARY = size - 4;
     414              : 
     415         1574 :     Fraction endTick = Fraction::fromTicks(_measures * bbDivision * 4 * timesigZ() / timesigN());
     416              : 
     417         1574 :     if (eventCount == 0) {
     418         1414 :         LOGD("no melody");
     419         1414 :         return true;
     420              :     } else {
     421          160 :         idx = eventStart;
     422          160 :         LOGD("melody found at 0x%x", idx);
     423          160 :         int i = 0;
     424          160 :         int lastLen = 0;
     425          164 :         for (i = 0; i < eventCount; ++i, idx+=12) {
     426          162 :             if (idx + 12 > EVENT_END_BOUNDARY) {
     427           76 :                 LOGE() << "Event out of range";
     428           76 :                 return false;
     429              :             }
     430           86 :             int type = a[idx + 4] & 0xf0;
     431           86 :             if (type == 0x90) {
     432            4 :                 int channel = a[idx + 7];
     433            4 :                 BBTrack* track = 0;
     434            4 :                 foreach (BBTrack* t, _tracks) {
     435            2 :                     if (t->outChannel() == channel) {
     436            2 :                         track = t;
     437            2 :                         break;
     438              :                     }
     439            4 :                 }
     440            4 :                 if (track == 0) {
     441            2 :                     track = new BBTrack(this);
     442            2 :                     track->setOutChannel(channel);
     443            2 :                     _tracks.append(track);
     444              :                 }
     445            4 :                 uint32_t rawTick = uint32_t(a[idx]) + (uint32_t(a[idx + 1]) << 8) + (uint32_t(a[idx + 2]) << 16)
     446            4 :                                    + (uint32_t(a[idx + 3]) << 24);
     447            4 :                 if (rawTick > uint32_t(std::numeric_limits<int>::max())) {
     448            1 :                     LOGD("note event tick out of range at idx %04x", idx);
     449            2 :                     continue;
     450              :                 }
     451            3 :                 Fraction tick = Fraction::fromTicks(int(rawTick));
     452            3 :                 tick -= Fraction::fromTicks(4 * bbDivision);
     453            3 :                 if (tick >= endTick) {
     454            0 :                     LOGD("event tick %d > %d", tick.ticks(), endTick.ticks());
     455            0 :                     continue;
     456              :                 }
     457            3 :                 Event note(ME_NOTE);
     458            3 :                 note.setOntime((tick.ticks() * Constants::DIVISION) / bbDivision);
     459            3 :                 note.setPitch(a[idx + 5]);
     460            3 :                 note.setVelo(a[idx + 6]);
     461            3 :                 note.setChannel(channel);
     462            3 :                 uint32_t rawLen = uint32_t(a[idx + 8]) + (uint32_t(a[idx + 9]) << 8) + (uint32_t(a[idx + 10]) << 16)
     463            3 :                                   + (uint32_t(a[idx + 11]) << 24);
     464            3 :                 if (rawLen > uint32_t(std::numeric_limits<int>::max())) {
     465            1 :                     LOGD("note event length out of range at idx %04x", idx);
     466            1 :                     continue;
     467              :                 }
     468            2 :                 int len1 = int(rawLen);
     469            2 :                 if (len1 == 0) {
     470            0 :                     if (lastLen == 0) {
     471            0 :                         LOGD("note event of len 0 at idx %04x", idx);
     472            0 :                         continue;
     473              :                     }
     474            0 :                     len1 = lastLen;
     475              :                 }
     476            2 :                 lastLen = len1;
     477            2 :                 note.setDuration(int((uint64_t(len1) * Constants::DIVISION) / bbDivision));
     478            2 :                 track->append(note);
     479           85 :             } else if (type == 0xb0 || type == 0xc0) {
     480              :                 // ignore controller
     481           82 :             } else if (type == 0) {
     482           74 :                 break;
     483              :             } else {
     484            8 :                 LOGD("unknown event type 0x%02x at x%04x", a[idx + 4], idx);
     485            8 :                 break;
     486              :             }
     487              :         }
     488           84 :         LOGD("Events found x%02x (%d)", i, i);
     489              :     }
     490           84 :     return true;
     491         1983 : }
     492              : 
     493              : //---------------------------------------------------------
     494              : //   importBB
     495              : //---------------------------------------------------------
     496              : 
     497            6 : Err importBB(MasterScore* score, const QString& name)
     498              : {
     499            6 :     BBFile bb;
     500            6 :     if (!QFileInfo::exists(name)) {
     501            0 :         return engraving::Err::FileNotFound;
     502              :     }
     503            6 :     if (!bb.read(name)) {
     504            2 :         LOGD("Cannot open file <%s>", qPrintable(name));
     505            2 :         return engraving::Err::FileOpenError;
     506              :     }
     507            4 :     score->style().set(Sid::chordsXmlFile, true);
     508            4 :     score->chordList()->read(u"chords.xml");
     509            4 :     *(score->sigmap()) = bb.siglist();
     510              : 
     511            4 :     QList<BBTrack*>* tracks = bb.tracks();
     512            4 :     int ntracks = tracks->size();
     513            4 :     if (ntracks == 0) {             // no events?
     514            4 :         ntracks = 1;
     515              :     }
     516            8 :     for (int i = 0; i < ntracks; ++i) {
     517            4 :         Part* part = new Part(score);
     518            4 :         Staff* s = Factory::createStaff(part);
     519            4 :         score->appendStaff(s);
     520            4 :         score->appendPart(part);
     521              :     }
     522              : 
     523              :     //---------------------------------------------------
     524              :     //  create measures
     525              :     //---------------------------------------------------
     526              : 
     527          196 :     for (int i = 0; i < bb.measures(); ++i) {
     528          192 :         Measure* measure  = Factory::createMeasure(score);
     529          192 :         Fraction tick = Fraction::fromTicks(score->sigmap()->bar2tick(i, 0));
     530          192 :         measure->setTick(tick);
     531          192 :         Fraction ts = score->sigmap()->timesig(tick.ticks()).timesig();
     532          192 :         measure->setTimesig(ts);
     533          192 :         measure->setTicks(ts);
     534          192 :         score->measures()->append(measure);
     535              :     }
     536              : 
     537              :     //---------------------------------------------------
     538              :     //  create notes
     539              :     //---------------------------------------------------
     540              : 
     541            4 :     foreach (BBTrack* track, * tracks) {
     542            0 :         track->cleanup();
     543            4 :     }
     544              : 
     545            4 :     if (tracks->isEmpty()) {
     546          196 :         for (MeasureBase* mb = score->first(); mb; mb = mb->next()) {
     547          192 :             if (!mb->isMeasure()) {
     548            0 :                 continue;
     549              :             }
     550          192 :             Measure* measure = (Measure*)mb;
     551          192 :             Segment* s = measure->getSegment(SegmentType::ChordRest, measure->tick());
     552          192 :             Rest* rest = Factory::createRest(s, TDuration(DurationType::V_MEASURE));
     553          192 :             rest->setTicks(measure->ticks());
     554          192 :             rest->setTrack(0);
     555          192 :             s->add(rest);
     556              :         }
     557              :     } else {
     558            0 :         int staffIdx = 0;
     559            0 :         foreach (BBTrack* track, * tracks) {
     560            0 :             bb.convertTrack(score, track, staffIdx++);
     561            0 :         }
     562              :     }
     563              : 
     564          196 :     for (MeasureBase* mb = score->first(); mb; mb = mb->next()) {
     565          192 :         if (!mb->isMeasure()) {
     566            0 :             continue;
     567              :         }
     568          192 :         Measure* measure = (Measure*)mb;
     569          192 :         Segment* s = measure->findSegment(SegmentType::ChordRest, measure->tick());
     570          192 :         if (s == 0) {
     571            0 :             Rest* rest = Factory::createRest(s, TDuration(DurationType::V_MEASURE));
     572            0 :             rest->setTicks(measure->ticks());
     573            0 :             rest->setTrack(0);
     574            0 :             Segment* s1 = measure->getSegment(SegmentType::ChordRest, measure->tick());
     575            0 :             s1->add(rest);
     576              :         }
     577              :     }
     578              : 
     579            4 :     EditEnharmonicSpelling::spell(score);
     580              : 
     581              :     //---------------------------------------------------
     582              :     //    create title
     583              :     //---------------------------------------------------
     584              : 
     585            4 :     MeasureBase* measureB = score->first();
     586            4 :     Text* text = Factory::createText(measureB, TextStyleType::TITLE);
     587            4 :     text->setPlainText(String::fromUtf8(bb.title()));
     588              : 
     589            4 :     if (!measureB->isVBox()) {
     590            4 :         measureB = Factory::createTitleVBox(score);
     591            4 :         measureB->setNext(score->first());
     592            4 :         score->measures()->add(measureB);
     593              :     }
     594            4 :     measureB->add(text);
     595              : 
     596              :     //---------------------------------------------------
     597              :     //    create chord symbols
     598              :     //---------------------------------------------------
     599              : 
     600              :     static const std::array<int, 17> table = {
     601              :         //C  Db, D,  Eb,  E, F, Gb, G,  Ab, A,  Bb, B,  C#, D#, F#  G#  A#
     602              :         14, 9, 16, 11, 18, 13, 8, 15, 10, 17, 12, 19, 21, 23, 20, 22, 24
     603              :     };
     604          196 :     foreach (const BBChord& c, bb.chords()) {
     605          192 :         Fraction tick = Fraction(c.beat, 4);          // c.beat  * Constants::division;
     606              : // LOGD("CHORD %d %d", c.beat, tick);
     607          192 :         Measure* m = score->tick2measure(tick);
     608          192 :         if (m == 0) {
     609            0 :             LOGD("import BB: measure for tick %d not found", tick.ticks());
     610            1 :             continue;
     611              :         }
     612          383 :         if (c.root <= 0 || c.root > table.size()) {
     613            1 :             LOGE() << "Chord root out of range";
     614            1 :             continue;
     615              :         }
     616          191 :         Segment* s = m->getSegment(SegmentType::ChordRest, tick);
     617          191 :         Harmony* h = Factory::createHarmony(s);
     618          191 :         h->setTrack(0);
     619          191 :         HarmonyInfo* info = new HarmonyInfo(score);
     620          191 :         info->setRootTpc(table[c.root - 1]);
     621          192 :         if (c.bass > 0 && c.bass <= table.size()) {
     622            0 :             info->setBassTpc(table[c.bass - 1]);
     623              :         } else {
     624          191 :             info->setBassTpc(Tpc::TPC_INVALID);
     625              :         }
     626          191 :         info->setId(c.extension);
     627          191 :         h->addChord(info);
     628              : 
     629          191 :         s->add(h);
     630            4 :     }
     631              : 
     632              :     //---------------------------------------------------
     633              :     //    insert layout breaks
     634              :     //    add chorus repeat
     635              :     //---------------------------------------------------
     636              : 
     637            4 :     int startChorus = bb.startChorus() - 1;
     638            4 :     int endChorus   = bb.endChorus() - 1;
     639              : 
     640            4 :     int n = 0;
     641          200 :     for (MeasureBase* mb = score->first(); mb; mb = mb->next()) {
     642          196 :         if (!mb->isMeasure()) {
     643            4 :             continue;
     644              :         }
     645          192 :         Measure* measure = (Measure*)mb;
     646          192 :         if (n && (n % 4) == 0) {
     647           44 :             LayoutBreak* lb = Factory::createLayoutBreak(measure);
     648           44 :             lb->setLayoutBreakType(LayoutBreakType::LINE);
     649           44 :             measure->add(lb);
     650              :         }
     651          192 :         if (startChorus == n) {
     652            0 :             measure->setRepeatStart(true);
     653          192 :         } else if (endChorus == n) {
     654            0 :             measure->setRepeatEnd(true);
     655            0 :             measure->setRepeatCount(bb.repeats());
     656              :         }
     657          192 :         ++n;
     658              :     }
     659              : 
     660            8 :     for (Staff* staff : score->staves()) {
     661            4 :         Fraction tick = Fraction(0, 1);
     662            4 :         KeySigEvent ke;
     663            4 :         Key key = Key(bb.key());
     664            4 :         Key cKey = key;
     665            4 :         Interval v = staff->part()->instrument(tick)->transpose();
     666            4 :         if (!v.isZero() && !score->style().styleB(Sid::concertPitch)) {
     667            0 :             cKey = Transpose::transposeKey(key, v);
     668              :             // if there are more than 6 accidentals in transposing key, it cannot be PreferSharpFlat::AUTO
     669            0 :             if ((key > 6 || key < -6) && staff->part()->preferSharpFlat() == PreferSharpFlat::AUTO) {
     670            0 :                 staff->part()->setPreferSharpFlat(PreferSharpFlat::NONE);
     671              :             }
     672              :         }
     673            4 :         ke.setConcertKey(cKey);
     674            4 :         ke.setKey(key);
     675            4 :         staff->setKey(tick, ke);
     676            4 :         Measure* mks = score->tick2measure(tick);
     677            4 :         Segment* sks = mks->getSegment(SegmentType::KeySig, tick);
     678            4 :         KeySig* keysig = Factory::createKeySig(sks);
     679            4 :         keysig->setTrack((static_cast<int>(score->staffIdx(staff->part())) + staff->rstaff()) * VOICES);
     680            4 :         keysig->setKey(cKey, key);
     681            4 :         sks->add(keysig);
     682            4 :     }
     683            4 :     score->updateTicksAndTimeSigMap();
     684            4 :     return engraving::Err::NoError;
     685            6 : }
     686              : 
     687              : //---------------------------------------------------------
     688              : //   processPendingNotes
     689              : //---------------------------------------------------------
     690              : 
     691            0 : Fraction BBFile::processPendingNotes(Score* score, QList<MNote*>* notes, const Fraction& l, int track)
     692              : {
     693            0 :     Fraction len(l);
     694            0 :     Staff* cstaff                = score->staff(track / VOICES);
     695            0 :     const Instrument* instrument = cstaff->part()->instrument();
     696            0 :     const Drumset* drumset       = instrument->drumset();
     697            0 :     bool useDrumset              = instrument->useDrumset();
     698            0 :     Fraction tick                = Fraction::fromTicks(notes->at(0)->mc.ontime());
     699              : 
     700              :     //
     701              :     // look for len of shortest note
     702              :     //
     703            0 :     for (const MNote* n : *notes) {
     704            0 :         if (n->mc.duration() < len.ticks()) {
     705            0 :             len = Fraction::fromTicks(n->mc.duration());
     706              :         }
     707              :     }
     708              : 
     709              :     //
     710              :     // split notes on measure boundary
     711              :     //
     712            0 :     Measure* measure = score->tick2measure(tick);
     713            0 :     if (measure == 0 || (tick >= measure->endTick())) {
     714            0 :         LOGD("no measure found for tick %d", tick.ticks());
     715            0 :         notes->clear();
     716            0 :         return len;
     717              :     }
     718            0 :     if ((tick + len) > measure->endTick()) {
     719            0 :         len = measure->endTick() - tick;
     720              :     }
     721              : 
     722            0 :     Segment* s = measure->getSegment(SegmentType::ChordRest, tick);
     723            0 :     Chord* chord = Factory::createChord(s);
     724            0 :     chord->setTrack(track);
     725            0 :     TDuration d;
     726            0 :     d.setVal(len.ticks());
     727            0 :     chord->setDurationType(d);
     728            0 :     chord->setTicks(d.fraction());
     729            0 :     s->add(chord);
     730              : 
     731            0 :     for (MNote* n : *notes) {
     732            0 :         std::vector<Event>& nl = n->mc.notes();
     733            0 :         for (size_t i = 0; i < nl.size(); ++i) {
     734            0 :             const Event& mn = nl[i];
     735            0 :             Note* note = Factory::createNote(chord);
     736            0 :             note->setPitch(mn.pitch(), mn.tpc(), mn.tpc());
     737            0 :             note->setTrack(track);
     738            0 :             chord->add(note);
     739              : 
     740            0 :             if (useDrumset) {
     741            0 :                 if (!drumset->isValid(mn.pitch())) {
     742            0 :                     LOGD("unmapped drum note 0x%02x %d", mn.pitch(), mn.pitch());
     743              :                 } else {
     744            0 :                     chord->setStemDirection(drumset->stemDirection(mn.pitch()));
     745              :                 }
     746              :             }
     747            0 :             if (n->ties[static_cast<int>(i)]) {
     748            0 :                 n->ties[static_cast<int>(i)]->setEndNote(note);
     749            0 :                 n->ties[static_cast<int>(i)]->setTrack(note->track());
     750            0 :                 note->setTieBack(n->ties[static_cast<int>(i)]);
     751              :             }
     752              :         }
     753            0 :         if (n->mc.duration() <= len.ticks()) {
     754            0 :             notes->removeAt(notes->indexOf(n));
     755            0 :             continue;
     756              :         }
     757            0 :         for (size_t i = 0; i < nl.size(); ++i) {
     758            0 :             const Event& mn = nl[i];
     759            0 :             Note* note = chord->findNote(mn.pitch());
     760            0 :             n->ties[static_cast<int>(i)] = Factory::createTie(note);
     761            0 :             n->ties[static_cast<int>(i)]->setStartNote(note);
     762            0 :             note->setTieFor(n->ties[static_cast<int>(i)]);
     763              :         }
     764            0 :         n->mc.setOntime(n->mc.ontime() + len.ticks());
     765            0 :         n->mc.setLen(n->mc.duration() - len.ticks());
     766              :     }
     767            0 :     return len;
     768              : }
     769              : 
     770              : //---------------------------------------------------------
     771              : //   collectNotes
     772              : //---------------------------------------------------------
     773              : 
     774            0 : static ciEvent collectNotes(const Fraction& tick, int voice, ciEvent i, const EventList* el, QList<MNote*>* notes)
     775              : {
     776            0 :     for (; i != el->end(); ++i) {
     777            0 :         const Event& e = *i;
     778            0 :         if (e.type() != ME_CHORD) {
     779            0 :             continue;
     780              :         }
     781            0 :         if (e.voice() != voice) {
     782            0 :             continue;
     783              :         }
     784            0 :         if (e.ontime() > tick.ticks()) {
     785            0 :             break;
     786              :         }
     787            0 :         if (e.ontime() < tick.ticks()) {
     788            0 :             continue;
     789              :         }
     790            0 :         MNote* n = new MNote(e);
     791            0 :         notes->append(n);
     792              :     }
     793            0 :     return i;
     794              : }
     795              : 
     796              : //---------------------------------------------------------
     797              : //   convertTrack
     798              : //---------------------------------------------------------
     799              : 
     800            0 : void BBFile::convertTrack(Score* score, BBTrack* track, int staffIdx)
     801              : {
     802            0 :     track->findChords();
     803            0 :     int voices         = track->separateVoices(2);
     804            0 :     const EventList el = track->events();
     805              : 
     806            0 :     for (int voice = 0; voice < voices; ++voice) {
     807            0 :         int tr = staffIdx * VOICES + voice;
     808            0 :         QList<MNote*> notes;
     809              : 
     810            0 :         Fraction ctick = Fraction(0, 1);
     811            0 :         ciEvent i = collectNotes(ctick, voice, el.begin(), &el, &notes);
     812              : 
     813            0 :         for (; i != el.end();) {
     814            0 :             const Event& e = *i;
     815            0 :             if (e.type() != ME_CHORD || e.voice() != voice) {
     816            0 :                 ++i;
     817            0 :                 continue;
     818              :             }
     819              :             //
     820              :             // process pending notes
     821              :             //
     822            0 :             Fraction restLen = Fraction::fromTicks(e.ontime()) - ctick;
     823              : // LOGD("ctick %d  rest %d ontick %d size %d", ctick, restLen, e.ontime(), notes.size());
     824              : 
     825            0 :             if (restLen <= Fraction(0, 1)) {
     826            0 :                 ASSERT_X(QString::asprintf("bad restlen ontime %d - ctick %d", e.ontime(), ctick.ticks()));
     827              :             }
     828              : 
     829            0 :             while (!notes.isEmpty()) {
     830            0 :                 Fraction len = processPendingNotes(score, &notes, restLen, tr);
     831            0 :                 if (len.isZero()) {
     832            0 :                     LOGD("processPendingNotes returns zero, restlen %d, track %d", restLen.ticks(), tr);
     833            0 :                     ctick += restLen;
     834            0 :                     restLen = Fraction(0, 1);
     835            0 :                     break;
     836              :                 }
     837            0 :                 ctick += len;
     838            0 :                 restLen -= len;
     839              :             }
     840              : // LOGD("  1.ctick %d  rest %d", ctick, restLen);
     841              :             //
     842              :             // check for gap and fill with rest
     843              :             //
     844            0 :             if (voice == 0) {
     845            0 :                 while (restLen > Fraction(0, 1)) {
     846            0 :                     Fraction len = restLen;
     847            0 :                     Measure* measure = score->tick2measure(ctick);
     848            0 :                     if (measure == 0 || (ctick >= measure->endTick())) {                 // at end?
     849            0 :                         ctick += len;
     850            0 :                         restLen -= len;
     851            0 :                         break;
     852              :                     }
     853              :                     // split rest on measure boundary
     854            0 :                     if ((ctick + len) > measure->endTick()) {
     855            0 :                         len = measure->endTick() - ctick;
     856            0 :                         if (len <= Fraction(0, 1)) {
     857            0 :                             LOGD("bad len %d", len.ticks());
     858            0 :                             break;
     859              :                         }
     860              :                     }
     861            0 :                     TDuration d;
     862            0 :                     d.setVal(len.ticks());
     863            0 :                     Segment* s = measure->getSegment(SegmentType::ChordRest, ctick);
     864            0 :                     Rest* rest = Factory::createRest(s, d);
     865            0 :                     rest->setTicks(d.fraction());
     866            0 :                     rest->setTrack(staffIdx * VOICES);
     867            0 :                     s->add(rest);
     868              : // LOGD("   add rest %d", len);
     869              : 
     870            0 :                     ctick   += len;
     871            0 :                     restLen -= len;
     872              :                 }
     873              :             } else {
     874            0 :                 ctick += restLen;
     875              :             }
     876              : 
     877              : // LOGD("  2.ctick %d  rest %d", ctick, restLen);
     878              :             //
     879              :             // collect all notes at ctick
     880              :             //
     881            0 :             i = collectNotes(ctick, voice, i, &el, &notes);
     882              :         }
     883              : 
     884              :         //
     885              :         // process pending notes
     886              :         //
     887            0 :         while (!notes.isEmpty()) {
     888            0 :             Fraction len = processPendingNotes(score, &notes, Fraction(0x7fffffff, 1), tr);
     889            0 :             ctick += len;
     890              :         }
     891            0 :         if (voice == 0) {
     892            0 :             Measure* measure = score->tick2measure(ctick);
     893            0 :             if (measure && (ctick < measure->endTick())) {             // at end?
     894            0 :                 TDuration d;
     895            0 :                 d.setVal((measure->endTick() - ctick).ticks());
     896            0 :                 Segment* s = measure->getSegment(SegmentType::ChordRest, ctick);
     897            0 :                 Rest* rest = Factory::createRest(s, d);
     898            0 :                 rest->setTicks(d.fraction());
     899            0 :                 rest->setTrack(staffIdx * VOICES);
     900            0 :                 s->add(rest);
     901              :             }
     902              :         }
     903            0 :     }
     904            0 : }
     905              : 
     906              : //---------------------------------------------------------
     907              : //   quantizeLen
     908              : //---------------------------------------------------------
     909              : 
     910            0 : int quantizeLen(int len, int raster)
     911              : {
     912            0 :     if (raster == 0) {
     913            0 :         return len;
     914              :     }
     915            0 :     return int(((float)len / raster) + 0.5) * raster;   //round to the closest multiple of raster
     916              : }
     917              : 
     918              : //---------------------------------------------------------
     919              : //   quantize
     920              : //    process one segment (measure)
     921              : //---------------------------------------------------------
     922              : 
     923            0 : void BBTrack::quantize(int startTick, int endTick, EventList* dst)
     924              : {
     925            0 :     int mintick = Constants::DIVISION * 64;
     926            0 :     iEvent i = _events.begin();
     927            0 :     for (; i != _events.end(); ++i) {
     928            0 :         if (i->ontime() >= startTick) {
     929            0 :             break;
     930              :         }
     931              :     }
     932            0 :     iEvent si = i;
     933            0 :     for (; i != _events.end(); ++i) {
     934            0 :         const Event& e = *i;
     935            0 :         if (e.ontime() >= endTick) {
     936            0 :             break;
     937              :         }
     938            0 :         if (e.type() == ME_NOTE && (e.duration() < mintick)) {
     939            0 :             mintick = e.duration();
     940              :         }
     941              :     }
     942            0 :     if (mintick <= Constants::DIVISION / 16) {        // minimum duration is 1/64
     943            0 :         mintick = Constants::DIVISION / 16;
     944            0 :     } else if (mintick <= Constants::DIVISION / 8) {
     945            0 :         mintick = Constants::DIVISION / 8;
     946            0 :     } else if (mintick <= Constants::DIVISION / 4) {
     947            0 :         mintick = Constants::DIVISION / 4;
     948            0 :     } else if (mintick <= Constants::DIVISION / 2) {
     949            0 :         mintick = Constants::DIVISION / 2;
     950            0 :     } else if (mintick <= Constants::DIVISION) {
     951            0 :         mintick = Constants::DIVISION;
     952            0 :     } else if (mintick <= Constants::DIVISION* 2) {
     953            0 :         mintick = Constants::DIVISION * 2;
     954            0 :     } else if (mintick <= Constants::DIVISION* 4) {
     955            0 :         mintick = Constants::DIVISION * 4;
     956            0 :     } else if (mintick <= Constants::DIVISION* 8) {
     957            0 :         mintick = Constants::DIVISION * 8;
     958              :     }
     959              :     int raster;
     960            0 :     if (mintick > Constants::DIVISION) {
     961            0 :         raster = Constants::DIVISION;
     962              :     } else {
     963            0 :         raster = mintick;
     964              :     }
     965              : 
     966              :     //
     967              :     //  quantize onset
     968              :     //
     969            0 :     for (i = si; i != _events.end(); ++i) {
     970            0 :         Event e = *i;
     971            0 :         if (e.ontime() >= endTick) {
     972            0 :             break;
     973              :         }
     974            0 :         if (e.type() == ME_NOTE) {
     975              :             // prefer moving note to the right
     976            0 :             int tick = ((e.ontime() + raster / 2) / raster) * raster;
     977            0 :             int diff = tick - e.ontime();
     978            0 :             int len  = e.duration() - diff;
     979            0 :             e.setOntime(tick);
     980            0 :             e.setLen(len);
     981              :         }
     982            0 :         dst->insert(e);
     983            0 :     }
     984              :     //
     985              :     //  quantize duration
     986              :     //
     987            0 :     for (i = dst->begin(); i != dst->end(); ++i) {
     988            0 :         Event& e = *i;
     989            0 :         if (e.type() != ME_NOTE) {
     990            0 :             continue;
     991              :         }
     992            0 :         int tick   = e.ontime();
     993            0 :         int len    = e.duration();
     994            0 :         int ntick  = tick + len;
     995            0 :         int nntick = -1;
     996            0 :         for (iEvent ii = (i + 1); ii != dst->end(); ++ii) {
     997            0 :             if (ii->type() == ME_NOTE) {
     998            0 :                 const Event& ee = *ii;
     999            0 :                 if (ee.ontime() == tick) {
    1000            0 :                     continue;
    1001              :                 }
    1002            0 :                 nntick = ee.ontime();
    1003            0 :                 break;
    1004              :             }
    1005              :         }
    1006            0 :         if (nntick == -1) {
    1007            0 :             len = quantizeLen(len, raster);
    1008              :         } else {
    1009            0 :             int diff = nntick - ntick;
    1010            0 :             if (diff > 0) {
    1011              :                 // insert rest?
    1012            0 :                 if (diff <= raster) {
    1013            0 :                     len = nntick - tick;
    1014              :                 } else {
    1015            0 :                     len = quantizeLen(len, raster);
    1016              :                 }
    1017              :             } else {
    1018            0 :                 if (diff > -raster) {
    1019            0 :                     len = nntick - tick;
    1020              :                 } else {
    1021            0 :                     len = quantizeLen(len, raster);
    1022              :                 }
    1023              :             }
    1024              :         }
    1025            0 :         e.setLen(len);
    1026              :     }
    1027            0 : }
    1028              : 
    1029              : //---------------------------------------------------------
    1030              : //   cleanup
    1031              : //    - quantize
    1032              : //    - remove overlaps
    1033              : //---------------------------------------------------------
    1034              : 
    1035            0 : void BBTrack::cleanup()
    1036              : {
    1037            0 :     EventList dl;
    1038              : 
    1039              :     //
    1040              :     // quantize
    1041              :     //
    1042            0 :     int lastTick = 0;
    1043            0 :     for (const Event& e : _events) {
    1044            0 :         if (e.type() != ME_NOTE) {
    1045            0 :             continue;
    1046              :         }
    1047            0 :         int offtime  = e.offtime();
    1048            0 :         if (offtime > lastTick) {
    1049            0 :             lastTick = offtime;
    1050              :         }
    1051              :     }
    1052            0 :     int startTick = 0;
    1053            0 :     for (int i = 1;; ++i) {
    1054            0 :         int endTick = bb->siglist().bar2tick(i, 0);
    1055            0 :         quantize(startTick, endTick, &dl);
    1056            0 :         if (endTick > lastTick) {
    1057            0 :             break;
    1058              :         }
    1059            0 :         startTick = endTick;
    1060            0 :     }
    1061              : 
    1062              :     //
    1063              :     //
    1064              :     //
    1065            0 :     _events.clear();
    1066              : 
    1067            0 :     for (iEvent i = dl.begin(); i != dl.end(); ++i) {
    1068            0 :         Event& e = *i;
    1069            0 :         if (e.type() == ME_NOTE) {
    1070            0 :             iEvent ii = i;
    1071            0 :             ++ii;
    1072            0 :             for (; ii != dl.end(); ++ii) {
    1073            0 :                 const Event& ee = *ii;
    1074            0 :                 if (ee.type() != ME_NOTE || ee.pitch() != e.pitch()) {
    1075            0 :                     continue;
    1076              :                 }
    1077            0 :                 if (ee.ontime() >= e.ontime() + e.duration()) {
    1078            0 :                     break;
    1079              :                 }
    1080            0 :                 e.setLen(ee.ontime() - e.ontime());
    1081            0 :                 break;
    1082              :             }
    1083            0 :             if (e.duration() <= 0) {
    1084            0 :                 continue;
    1085              :             }
    1086              :         }
    1087            0 :         _events.insert(e);
    1088              :     }
    1089            0 : }
    1090              : 
    1091              : //---------------------------------------------------------
    1092              : //   findChords
    1093              : //---------------------------------------------------------
    1094              : 
    1095            0 : void BBTrack::findChords()
    1096              : {
    1097            0 :     EventList dl;
    1098            0 :     size_t n = _events.size();
    1099              : 
    1100              :     Drumset* drumset;
    1101            0 :     if (_drumTrack) {
    1102            0 :         drumset = smDrumset;
    1103              :     } else {
    1104            0 :         drumset = 0;
    1105              :     }
    1106            0 :     int jitter = 3;     // tick tolerance for note on/off
    1107              : 
    1108            0 :     for (size_t i = 0; i < n; ++i) {
    1109            0 :         Event e = _events[i];
    1110            0 :         if (e.type() == ME_INVALID) {
    1111            0 :             continue;
    1112              :         }
    1113            0 :         if (e.type() != ME_NOTE) {
    1114            0 :             dl.push_back(e);
    1115            0 :             continue;
    1116              :         }
    1117              : 
    1118            0 :         Event note(e);
    1119            0 :         int ontime       = note.ontime();
    1120            0 :         int offtime      = note.offtime();
    1121            0 :         Event chord(ME_CHORD);
    1122            0 :         chord.setOntime(ontime);
    1123            0 :         chord.setLen(note.duration());
    1124            0 :         chord.notes().push_back(note);
    1125            0 :         int voice = 0;
    1126            0 :         chord.setVoice(voice);
    1127            0 :         dl.push_back(chord);
    1128            0 :         _events[i].setType(ME_INVALID);
    1129              : 
    1130            0 :         bool useDrumset = false;
    1131            0 :         if (drumset) {
    1132            0 :             int pitch = note.pitch();
    1133            0 :             if (drumset->isValid(pitch)) {
    1134            0 :                 useDrumset = true;
    1135            0 :                 voice = drumset->voice(pitch);
    1136            0 :                 chord.setVoice(voice);
    1137              :             }
    1138              :         }
    1139            0 :         for (size_t k = i + 1; k < n; ++k) {
    1140            0 :             if (_events[k].type() != ME_NOTE) {
    1141            0 :                 continue;
    1142              :             }
    1143            0 :             Event nn = _events[k];
    1144            0 :             if (nn.ontime() - jitter > ontime) {
    1145            0 :                 break;
    1146              :             }
    1147            0 :             if (qAbs(nn.ontime() - ontime) > jitter || qAbs(nn.offtime() - offtime) > jitter) {
    1148            0 :                 continue;
    1149              :             }
    1150            0 :             int pitch = nn.pitch();
    1151            0 :             if (useDrumset) {
    1152            0 :                 if (drumset->isValid(pitch) && drumset->voice(pitch) == voice) {
    1153            0 :                     chord.notes().push_back(nn);
    1154            0 :                     _events[k].setType(ME_INVALID);
    1155              :                 }
    1156              :             } else {
    1157            0 :                 chord.notes().push_back(nn);
    1158            0 :                 _events[k].setType(ME_INVALID);
    1159              :             }
    1160            0 :         }
    1161            0 :     }
    1162            0 :     _events = dl;
    1163            0 : }
    1164              : 
    1165              : //---------------------------------------------------------
    1166              : //   separateVoices
    1167              : //---------------------------------------------------------
    1168              : 
    1169            0 : int BBTrack::separateVoices(int /*maxVoices*/)
    1170              : {
    1171            0 :     return 1;
    1172              : }
    1173              : }
        

Generated by: LCOV version 2.0-1