LCOV - code coverage report
Current view: top level - src/importexport/midi/internal/midishared - midifile.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 82.9 % 490 406
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 "midifile.h"
      24              : 
      25              : #include "containers.h"
      26              : 
      27              : #include "log.h"
      28              : 
      29              : using namespace mu::engraving;
      30              : 
      31              : namespace mu::iex::midi {
      32          212 : MidiFile::MidiFile()
      33              : {
      34          212 :     fp               = 0;
      35          212 :     _division        = 0;
      36          212 :     _isDivisionInTps = false;
      37          212 :     _format          = 1;
      38          212 :     _noRunningStatus = false;
      39              : 
      40          212 :     status           = 0;
      41          212 :     sstatus          = 0;
      42          212 :     click            = 0;
      43          212 :     curPos           = 0;
      44          212 : }
      45              : 
      46              : //---------------------------------------------------------
      47              : //   write
      48              : //    returns true on error
      49              : //---------------------------------------------------------
      50              : 
      51           12 : bool MidiFile::write(QIODevice* out)
      52              : {
      53           12 :     fp = out;
      54           12 :     write("MThd", 4);
      55           12 :     writeLong(6);                   // header len
      56           12 :     writeShort(_format);            // format
      57           12 :     writeShort(static_cast<int>(_tracks.size()));
      58           12 :     writeShort(_division);
      59          173 :     for (const auto& t: _tracks) {
      60          161 :         if (writeTrack(t)) {
      61            0 :             return true;
      62              :         }
      63              :     }
      64           12 :     return false;
      65              : }
      66              : 
      67              : //---------------------------------------------------------
      68              : //   write
      69              : //---------------------------------------------------------
      70              : 
      71         2079 : void MidiFile::writeEvent(const MidiEvent& event)
      72              : {
      73         2079 :     switch (event.type()) {
      74          414 :     case ME_NOTEON:
      75          414 :         writeStatus(ME_NOTEON, event.channel());
      76          414 :         put(event.pitch());
      77          414 :         put(event.velo());
      78          414 :         break;
      79              : 
      80            0 :     case ME_NOTEOFF:
      81            0 :         writeStatus(ME_NOTEOFF, event.channel());
      82            0 :         put(event.pitch());
      83            0 :         put(event.velo());
      84            0 :         break;
      85              : 
      86            0 :     case ME_PITCHBEND:
      87            0 :         writeStatus(ME_PITCHBEND, event.channel());
      88            0 :         put(event.dataA());
      89            0 :         put(event.dataB());
      90            0 :         break;
      91              : 
      92         1151 :     case ME_CONTROLLER:
      93         1151 :         switch (event.controller()) {
      94          164 :         case CTRL_PROGRAM:
      95          164 :             writeStatus(ME_PROGRAM, event.channel());
      96          164 :             put(event.value() & 0x7f);
      97          164 :             break;
      98            0 :         case CTRL_PRESS:
      99            0 :             writeStatus(ME_AFTERTOUCH, event.channel());
     100            0 :             put(event.value() & 0x7f);
     101            0 :             break;
     102          987 :         default:
     103          987 :             writeStatus(ME_CONTROLLER, event.channel());
     104          987 :             put(event.controller());
     105          987 :             put(event.value() & 0x7f);
     106          987 :             break;
     107              :         }
     108         1151 :         break;
     109              : 
     110          514 :     case ME_META:
     111          514 :         put(ME_META);
     112          514 :         put(event.metaType());
     113              :         // Don't null terminate text meta events
     114          514 :         if (event.metaType() >= 0x1 && event.metaType() <= 0x14) {
     115          161 :             putvl(event.len() - 1);
     116          161 :             write(event.edata(), event.len() - 1);
     117              :         } else {
     118          353 :             putvl(event.len());
     119          353 :             write(event.edata(), event.len());
     120              :         }
     121          514 :         resetRunningStatus();               // really ?!
     122          514 :         break;
     123              : 
     124            0 :     case ME_SYSEX:
     125            0 :         put(ME_SYSEX);
     126            0 :         putvl(event.len() + 1);            // including 0xf7
     127            0 :         write(event.edata(), event.len());
     128            0 :         put(ME_ENDSYSEX);
     129            0 :         resetRunningStatus();
     130            0 :         break;
     131              :     }
     132         2079 : }
     133              : 
     134              : //---------------------------------------------------------
     135              : //   writeTrack
     136              : //---------------------------------------------------------
     137              : 
     138          161 : bool MidiFile::writeTrack(const MidiTrack& t)
     139              : {
     140          161 :     write("MTrk", 4);
     141          161 :     qint64 lenpos = fp->pos();
     142          161 :     writeLong(0);                   // dummy len
     143              : 
     144          161 :     status   = -1;
     145          161 :     int tick = 0;
     146         2240 :     for (const auto& i : t.events()) {
     147         2079 :         int ntick = i.first;
     148         2079 :         putvl(ntick - tick);        // write tick delta
     149              :         //
     150              :         // if track channel != -1, then use this
     151              :         //    channel for all events in this track
     152              :         //
     153         2079 :         if (t.outChannel() != -1) {
     154         2079 :             writeEvent(i.second);
     155              :         }
     156         2079 :         tick = ntick;
     157              :     }
     158              : 
     159              :     //---------------------------------------------------
     160              :     //    write "End Of Track" Meta
     161              :     //    write Track Len
     162              :     //
     163              : 
     164          161 :     putvl(1);
     165          161 :     put(0xff);          // Meta
     166          161 :     put(0x2f);          // EOT
     167          161 :     putvl(0);           // len 0
     168          161 :     qint64 endpos = fp->pos();
     169          161 :     fp->seek(lenpos);
     170          161 :     writeLong(endpos - lenpos - 4); // tracklen
     171          161 :     fp->seek(endpos);
     172          161 :     return false;
     173              : }
     174              : 
     175              : //---------------------------------------------------------
     176              : //   writeStatus
     177              : //---------------------------------------------------------
     178              : 
     179         1565 : void MidiFile::writeStatus(int nstat, int c)
     180              : {
     181         1565 :     nstat |= (c & 0xf);
     182              :     //
     183              :     //  running status; except for Sysex- and Meta Events
     184              :     //
     185         1565 :     if (_noRunningStatus || (((nstat & 0xf0) != 0xf0) && (nstat != status))) {
     186          706 :         status = nstat;
     187          706 :         put(nstat);
     188              :     }
     189         1565 : }
     190              : 
     191              : //---------------------------------------------------------
     192              : //   readMidi
     193              : //    return false on error
     194              : //---------------------------------------------------------
     195              : 
     196           99 : bool MidiFile::read(QIODevice* in)
     197              : {
     198           99 :     fp = in;
     199           99 :     _tracks.clear();
     200           99 :     curPos    = 0;
     201              : 
     202              :     // === Read header_chunk = "MThd" + <header_length> + <format> + <n> + <division>
     203              :     //
     204              :     // "MThd" 4 bytes
     205              :     //    the literal string MThd, or in hexadecimal notation: 0x4d546864.
     206              :     //    These four characters at the start of the MIDI file
     207              :     //    indicate that this is a MIDI file.
     208              :     // <header_length> 4 bytes
     209              :     //    length of the header chunk (always =6 bytes long - the size of the next
     210              :     //    three fields which are considered the header chunk).
     211              :     //    Although the header chunk currently always contains 6 bytes of data,
     212              :     //    this should not be assumed, this value should always be read and acted upon,
     213              :     //    to allow for possible future extension to the standard.
     214              :     // <format> 2 bytes
     215              :     //    0 = single track file format
     216              :     //    1 = multiple track file format
     217              :     //    2 = multiple song file format (i.e., a series of type 0 files)
     218              :     // <n> 2 bytes
     219              :     //    number of track chunks that follow the header chunk
     220              :     // <division> 2 bytes
     221              :     //    unit of time for delta timing. If the value is positive, then it represents
     222              :     //    the units per beat. For example, +96 would mean 96 ticks per beat.
     223              :     //    If the value is negative, delta times are in SMPTE compatible units.
     224              : 
     225              :     char tmp[4];
     226              : 
     227           99 :     read(tmp, 4);
     228           99 :     int len = readLong();
     229           99 :     if (memcmp(tmp, "MThd", 4) || len < 6) {
     230            0 :         throw(QString("bad midifile: MThd expected"));
     231              :     }
     232              : 
     233           99 :     if (len > 6) {
     234            0 :         throw(QString("unsupported MIDI header data size: %1 instead of 6").arg(len));
     235              :     }
     236              : 
     237           99 :     _format     = readShort();
     238           99 :     int ntracks = readShort();
     239              : 
     240              :     // ================ Read MIDI division =================
     241              :     //
     242              :     //                        2 bytes
     243              :     //  +-------+---+-------------------+-----------------+
     244              :     //  |  bit  |15 | 14              8 | 7             0 |
     245              :     //  +-------+---+-------------------------------------+
     246              :     //  |       | 0 |       ticks per quarter note        |
     247              :     //  | value +---+-------------------------------------+
     248              :     //  |       | 1 |  -frames/second   |   ticks/frame   |
     249              :     //  +-------+---+-------------------+-----------------+
     250              : 
     251              :     char firstByte;
     252           99 :     fp->getChar(&firstByte);
     253              :     char secondByte;
     254           99 :     fp->getChar(&secondByte);
     255           99 :     const char topBit = (firstByte & 0x80) >> 7;
     256              : 
     257           99 :     if (topBit == 0) {              // ticks per beat
     258           98 :         _isDivisionInTps = false;
     259           98 :         _division = (firstByte << 8) | (secondByte & 0xff);
     260              :     } else {                        // ticks per second = fps * ticks per frame
     261            1 :         _isDivisionInTps = true;
     262            1 :         const int framesPerSecond = -((signed char)firstByte);
     263            1 :         const int ticksPerFrame = secondByte;
     264            1 :         if (framesPerSecond == 29) {
     265            0 :             _division = qRound(29.97 * ticksPerFrame);
     266              :         } else {
     267            1 :             _division = framesPerSecond * ticksPerFrame;
     268              :         }
     269              :     }
     270              : 
     271              :     // =====================================================
     272              : 
     273           99 :     switch (_format) {
     274            6 :     case 0:
     275            6 :         if (readTrack()) {
     276            0 :             return false;
     277              :         }
     278            6 :         break;
     279           93 :     case 1:
     280          259 :         for (int i = 0; i < ntracks; i++) {
     281          168 :             if (readTrack()) {
     282            2 :                 return false;
     283              :             }
     284              :         }
     285           91 :         break;
     286            0 :     default:
     287            0 :         throw(QString("midi file format %1 not implemented").arg(_format));
     288              : 
     289              :         // Prevent "unreachable code" warning
     290              :         // return false;
     291              :     }
     292           97 :     return true;
     293              : }
     294              : 
     295              : //---------------------------------------------------------
     296              : //   readTrack
     297              : //    return true on error
     298              : //---------------------------------------------------------
     299              : 
     300          174 : bool MidiFile::readTrack()
     301              : {
     302              :     char tmp[4];
     303          174 :     read(tmp, 4);
     304          174 :     if (memcmp(tmp, "MTrk", 4)) {
     305            0 :         throw(QString("bad midifile: MTrk expected"));
     306              :     }
     307          174 :     int len       = readLong();         // len
     308          174 :     qint64 endPos = curPos + len;
     309          174 :     status        = -1;
     310          174 :     sstatus       = -1;    // running status, will not be reset on meta or sysex
     311          174 :     click         =  0;
     312          174 :     _tracks.push_back(MidiTrack());
     313              : 
     314          174 :     int port = 0;
     315          174 :     _tracks.back().setOutPort(port);
     316          174 :     _tracks.back().setOutChannel(-1);
     317              : 
     318              :     for (;;) {
     319         3698 :         MidiEvent event;
     320         3698 :         if (!readEvent(&event)) {
     321            2 :             return true;
     322              :         }
     323              : 
     324              :         // check for end of track:
     325         3696 :         if ((event.type() == ME_META) && (event.metaType() == META_EOT)) {
     326          172 :             break;
     327              :         }
     328         3524 :         _tracks.back().insert(click, event);
     329         7222 :     }
     330          172 :     if (curPos != endPos) {
     331            0 :         LOGW("bad track len: %lld != %lld, %lld bytes too much\n", endPos, curPos, endPos - curPos);
     332            0 :         if (curPos < endPos) {
     333            0 :             LOGW("  skip %lld\n", endPos - curPos);
     334            0 :             fp->skip(endPos - curPos);
     335              :         }
     336              :     }
     337          172 :     return false;
     338              : }
     339              : 
     340              : /*---------------------------------------------------------
     341              :  *    read
     342              :  *    return false on error
     343              :  *---------------------------------------------------------*/
     344              : 
     345        13572 : void MidiFile::read(void* p, qint64 len)
     346              : {
     347        13572 :     curPos += len;
     348        13572 :     qint64 rv = fp->read((char*)p, len);
     349        13572 :     if (rv != len) {
     350            0 :         throw(QString("bad midifile: unexpected EOF"));
     351              :     }
     352        13572 : }
     353              : 
     354              : //---------------------------------------------------------
     355              : //   write
     356              : //---------------------------------------------------------
     357              : 
     358         8806 : bool MidiFile::write(const void* p, qint64 len)
     359              : {
     360         8806 :     qint64 rv = fp->write((char*)p, len);
     361         8806 :     if (rv == len) {
     362         8806 :         return false;
     363              :     }
     364            0 :     LOGD("write midifile failed: %s", fp->errorString().toLatin1().data());
     365            0 :     return true;
     366              : }
     367              : 
     368              : //---------------------------------------------------------
     369              : //   readShort
     370              : //---------------------------------------------------------
     371              : 
     372          198 : int MidiFile::readShort()
     373              : {
     374              :     char c;
     375          198 :     int val = 0;
     376          594 :     for (int i = 0; i < 2; ++i) {
     377          396 :         fp->getChar(&c);
     378          396 :         val <<= 8;
     379          396 :         val += (c & 0xff);
     380              :     }
     381          198 :     return val;
     382              : }
     383              : 
     384              : //---------------------------------------------------------
     385              : //   writeShort
     386              : //---------------------------------------------------------
     387              : 
     388           36 : void MidiFile::writeShort(int i)
     389              : {
     390           36 :     fp->putChar(i >> 8);
     391           36 :     fp->putChar(i);
     392           36 : }
     393              : 
     394              : //---------------------------------------------------------
     395              : //   readLong
     396              : //   writeLong
     397              : //---------------------------------------------------------
     398              : 
     399          273 : int MidiFile::readLong()
     400              : {
     401              :     char c;
     402          273 :     int val = 0;
     403         1365 :     for (int i = 0; i < 4; ++i) {
     404         1092 :         fp->getChar(&c);
     405         1092 :         val <<= 8;
     406         1092 :         val += (c & 0xff);
     407              :     }
     408          273 :     return val;
     409              : }
     410              : 
     411              : //---------------------------------------------------------
     412              : //   writeLong
     413              : //---------------------------------------------------------
     414              : 
     415          334 : void MidiFile::writeLong(int i)
     416              : {
     417          334 :     fp->putChar(i >> 24);
     418          334 :     fp->putChar(i >> 16);
     419          334 :     fp->putChar(i >> 8);
     420          334 :     fp->putChar(i);
     421          334 : }
     422              : 
     423              : /*---------------------------------------------------------
     424              :  *    getvl
     425              :  *    Read variable-length number (7 bits per byte, MSB first)
     426              :  *---------------------------------------------------------*/
     427              : 
     428         4306 : int MidiFile::getvl()
     429              : {
     430         4306 :     int l = 0;
     431         4823 :     for (int i = 0; i < 16; i++) {
     432              :         uchar c;
     433         4823 :         read(&c, 1);
     434         4823 :         l += (c & 0x7f);
     435         4823 :         if (!(c & 0x80)) {
     436         4306 :             return l;
     437              :         }
     438          517 :         l <<= 7;
     439              :     }
     440            0 :     return -1;
     441              : }
     442              : 
     443              : /*---------------------------------------------------------
     444              :  *    putvl
     445              :  *    Write variable-length number (7 bits per byte, MSB first)
     446              :  *---------------------------------------------------------*/
     447              : 
     448         2915 : void MidiFile::putvl(unsigned val)
     449              : {
     450         2915 :     unsigned long buf = val & 0x7f;
     451         3097 :     while ((val >>= 7) > 0) {
     452          182 :         buf <<= 8;
     453          182 :         buf |= 0x80;
     454          182 :         buf += (val & 0x7f);
     455              :     }
     456              :     for (;;) {
     457         3097 :         put(buf);
     458         3097 :         if (buf & 0x80) {
     459          182 :             buf >>= 8;
     460              :         } else {
     461         2915 :             break;
     462              :         }
     463              :     }
     464         2915 : }
     465              : 
     466              : //---------------------------------------------------------
     467              : //   MidiTrack
     468              : //---------------------------------------------------------
     469              : 
     470          346 : MidiTrack::MidiTrack()
     471              : {
     472          346 :     _outChannel = -1;
     473          346 :     _outPort    = -1;
     474          346 :     _drumTrack  = false;
     475          346 : }
     476              : 
     477         1265 : MidiTrack::~MidiTrack()
     478              : {
     479         1265 : }
     480              : 
     481              : //---------------------------------------------------------
     482              : //   insert
     483              : //---------------------------------------------------------
     484              : 
     485         5746 : void MidiTrack::insert(int tick, const MidiEvent& event)
     486              : {
     487         5746 :     _events.insert({ tick, event });
     488         5746 : }
     489              : 
     490              : //---------------------------------------------------------
     491              : //   readEvent
     492              : //    return true on success
     493              : //---------------------------------------------------------
     494              : 
     495         3698 : bool MidiFile::readEvent(MidiEvent* event)
     496              : {
     497              :     uchar me, a, b;
     498              : 
     499         3698 :     int nclick = getvl();
     500         3698 :     if (nclick == -1) {
     501            0 :         LOGD("readEvent: error 1(getvl)");
     502            0 :         return false;
     503              :     }
     504         3698 :     click += nclick;
     505              :     for (;;) {
     506         3698 :         read(&me, 1);
     507         3698 :         if (me >= 0xf1 && me <= 0xfe && me != 0xf7) {
     508            0 :             LOGD("MIDI: Unknown Message 0x%02x", me & 0xff);
     509              :         } else {
     510              :             break;
     511              :         }
     512              :     }
     513              : 
     514              :     int dataLen;
     515              : 
     516         3698 :     if (me == 0xf0 || me == 0xf7) {
     517           41 :         status  = -1;                      // no running status
     518           41 :         int len = getvl();
     519           41 :         if (len < 1) {
     520            2 :             LOGD("readEvent: error 3");
     521            2 :             return false;
     522              :         }
     523           39 :         dataLen = len;
     524           39 :         std::vector<unsigned char> data(len + 1);
     525           39 :         read(data.data(), len);
     526           39 :         if (data[len - 1] != 0xf7) {
     527            0 :             LOGD("SYSEX does not end with 0xf7!");
     528              :             // more to come?
     529              :         } else {
     530           39 :             dataLen--;            // don't count 0xf7
     531              :         }
     532           39 :         event->setType(ME_SYSEX);
     533           39 :         event->setEData(std::move(data));
     534           39 :         event->setLen(dataLen);
     535           39 :         return true;
     536           39 :     }
     537              : 
     538         3657 :     if (me == ME_META) {
     539          567 :         status = -1;                      // no running status
     540              :         uchar type;
     541          567 :         read(&type, 1);
     542          567 :         dataLen = getvl();                    // read len
     543          567 :         if (dataLen == -1) {
     544            0 :             LOGD("readEvent: error 6");
     545            0 :             return false;
     546              :         }
     547          567 :         std::vector<unsigned char> data(dataLen + 1);
     548          567 :         if (dataLen) {
     549          377 :             read(data.data(), dataLen);
     550              :         }
     551              : 
     552          567 :         event->setType(ME_META);
     553          567 :         event->setMetaType(type);
     554          567 :         event->setLen(dataLen);
     555          567 :         event->setEData(std::move(data));
     556          567 :         return true;
     557          567 :     }
     558              : 
     559         3090 :     if (me & 0x80) {                       // status byte
     560          832 :         status   = me;
     561          832 :         sstatus  = status;
     562          832 :         read(&a, 1);
     563              :     } else {
     564         2258 :         if (status == -1) {
     565            0 :             LOGD("readEvent: no running status, read 0x%02x", me);
     566            0 :             LOGD("sstatus ist 0x%02x", sstatus);
     567            0 :             if (sstatus == -1) {
     568            0 :                 return 0;
     569              :             }
     570            0 :             status = sstatus;
     571              :         }
     572         2258 :         a = me;
     573              :     }
     574         3090 :     int channel = status & 0x0f;
     575         3090 :     b           = 0;
     576         3090 :     switch (status & 0xf0) {
     577         2963 :     case ME_NOTEOFF:
     578              :     case ME_NOTEON:
     579              :     case ME_POLYAFTER:
     580              :     case ME_CONTROLLER:                // controller
     581              :     case ME_PITCHBEND:                // pitch bend
     582         2963 :         read(&b, 1);
     583         2963 :         break;
     584              :     }
     585         3090 :     event->setType(status & 0xf0);
     586         3090 :     event->setChannel(channel);
     587         3090 :     switch (status & 0xf0) {
     588          123 :     case ME_NOTEOFF:
     589          123 :         event->setDataA(a & 0x7f);
     590          123 :         event->setDataB(b & 0x7f);
     591          123 :         break;
     592         1622 :     case ME_NOTEON:
     593         1622 :         event->setDataA(a & 0x7f);
     594         1622 :         event->setDataB(b & 0x7f);
     595         1622 :         break;
     596            0 :     case ME_POLYAFTER:
     597            0 :         event->setType(ME_CONTROLLER);
     598            0 :         event->setController(CTRL_POLYAFTER);
     599            0 :         event->setValue(((a & 0x7f) << 8) + (b & 0x7f));
     600            0 :         break;
     601         1217 :     case ME_CONTROLLER:                // controller
     602         1217 :         event->setController(a & 0x7f);
     603         1217 :         event->setValue(b & 0x7f);
     604         1217 :         break;
     605            1 :     case ME_PITCHBEND:                // pitch bend
     606            1 :         event->setDataA(a & 0x7f);
     607            1 :         event->setDataB(b & 0x7f);
     608            1 :         break;
     609          127 :     case ME_PROGRAM:
     610          127 :         event->setValue(a & 0x7f);
     611          127 :         break;
     612            0 :     case ME_AFTERTOUCH:
     613            0 :         event->setType(ME_CONTROLLER);
     614            0 :         event->setController(CTRL_PRESS);
     615            0 :         event->setValue(a & 0x7f);
     616            0 :         break;
     617            0 :     default:                  // f1 f2 f3 f4 f5 f6 f7 f8 f9
     618            0 :         LOGD("BAD STATUS 0x%02x, me 0x%02x", status, me);
     619            0 :         return false;
     620              :     }
     621              : 
     622         3090 :     if ((a & 0x80) || (b & 0x80)) {
     623            0 :         LOGD("8't bit in data set(%02x %02x): tick %d read 0x%02x  status:0x%02x",
     624              :              a & 0xff, b & 0xff, click, me, status);
     625            0 :         LOGD("readEvent: error 16");
     626            0 :         if (b & 0x80) {
     627              :             // Try to fix: interpret as channel byte
     628            0 :             status   = b;
     629            0 :             sstatus  = status;
     630            0 :             return true;
     631              :         }
     632            0 :         return false;
     633              :     }
     634         3090 :     return true;
     635              : }
     636              : 
     637              : //---------------------------------------------------------
     638              : //   setOutChannel
     639              : //---------------------------------------------------------
     640              : 
     641          520 : void MidiTrack::setOutChannel(int n)
     642              : {
     643          520 :     _outChannel = n;
     644          520 :     if (_outChannel == 9) {
     645          160 :         _drumTrack = true;
     646              :     }
     647          520 : }
     648              : 
     649              : //---------------------------------------------------------
     650              : //   mergeNoteOnOffAndFindMidiType
     651              : //    - find matching note on / note off events and merge
     652              : //      into a note event with tick duration
     653              : //---------------------------------------------------------
     654              : 
     655          185 : void MidiTrack::mergeNoteOnOff()
     656              : {
     657          185 :     std::multimap<int, MidiEvent> el;
     658              : 
     659          185 :     int hbank = 0xff;
     660          185 :     int lbank = 0xff;
     661          185 :     int rpnh  = -1;
     662          185 :     int rpnl  = -1;
     663          185 :     int datah = 0;
     664          185 :     int datal = 0;
     665          185 :     int dataType = 0;   // 0 : disabled, 0x20000 : rpn, 0x30000 : nrpn;
     666              : 
     667         3709 :     for (auto i = _events.begin(); i != _events.end(); ++i) {
     668         3524 :         MidiEvent& ev = i->second;
     669         3524 :         if (ev.type() == ME_INVALID) {
     670         2642 :             continue;
     671              :         }
     672         2661 :         if ((ev.type() != ME_NOTEON) && (ev.type() != ME_NOTEOFF)) {
     673         1779 :             if (ev.type() == ME_CONTROLLER) {
     674         1217 :                 int val  = ev.value();
     675         1217 :                 int cn   = ev.controller();
     676         1217 :                 if (cn == CTRL_HBANK) {
     677           20 :                     hbank = val;
     678           20 :                     ev.setType(ME_INVALID);
     679           20 :                     continue;
     680         1197 :                 } else if (cn == CTRL_LBANK) {
     681           20 :                     lbank = val;
     682           20 :                     ev.setType(ME_INVALID);
     683           20 :                     continue;
     684         1177 :                 } else if (cn == CTRL_HDATA) {
     685          166 :                     datah = val;
     686              : 
     687              :                     // check if a CTRL_LDATA follows
     688              :                     // e.g. wie have a 14 bit controller:
     689          166 :                     auto ii = i;
     690          166 :                     ++ii;
     691          166 :                     bool found = false;
     692          544 :                     for (; ii != _events.end(); ++ii) {
     693          515 :                         MidiEvent& ev1 = ii->second;
     694          515 :                         if (ev1.type() == ME_CONTROLLER) {
     695          137 :                             if (ev1.controller() == CTRL_LDATA) {
     696              :                                 // handle later
     697            3 :                                 found = true;
     698              :                             }
     699          137 :                             break;
     700              :                         }
     701              :                     }
     702          166 :                     if (!found) {
     703          163 :                         if (rpnh == -1 || rpnl == -1) {
     704            0 :                             LOGD("parameter number not defined, data 0x%x", datah);
     705            0 :                             ev.setType(ME_INVALID);
     706            3 :                             continue;
     707              :                         } else {
     708          163 :                             ev.setController(dataType | (rpnh << 8) | rpnl);
     709          163 :                             ev.setValue(datah);
     710              :                         }
     711              :                     } else {
     712            3 :                         ev.setType(ME_INVALID);
     713            3 :                         continue;
     714              :                     }
     715         1011 :                 } else if (cn == CTRL_LDATA) {
     716            3 :                     datal = val;
     717              : 
     718            3 :                     if (rpnh == -1 || rpnl == -1) {
     719            0 :                         LOGD("parameter number not defined, data 0x%x 0x%x, tick %d, channel %d",
     720            0 :                              datah, datal, i->first, ev.channel());
     721            0 :                         continue;
     722              :                     }
     723              :                     // assume that the sequence is always
     724              :                     //    CTRL_HDATA - CTRL_LDATA
     725              :                     // eg. that LDATA is always send last
     726              : 
     727              :                     // 14 Bit RPN/NRPN
     728            3 :                     ev.setController((dataType + 0x30000) | (rpnh << 8) | rpnl);
     729            3 :                     ev.setValue((datah << 7) | datal);
     730         1008 :                 } else if (cn == CTRL_HRPN) {
     731          210 :                     rpnh = val;
     732          210 :                     dataType = 0x20000;
     733          210 :                     ev.setType(ME_INVALID);
     734          210 :                     continue;
     735          798 :                 } else if (cn == CTRL_LRPN) {
     736          210 :                     rpnl = val;
     737          210 :                     dataType = 0x20000;
     738          210 :                     ev.setType(ME_INVALID);
     739          210 :                     continue;
     740          588 :                 } else if (cn == CTRL_HNRPN) {
     741            6 :                     rpnh = val;
     742            6 :                     dataType = 0x30000;
     743            6 :                     ev.setType(ME_INVALID);
     744            6 :                     continue;
     745          582 :                 } else if (cn == CTRL_LNRPN) {
     746            6 :                     rpnl = val;
     747            6 :                     dataType = 0x30000;
     748            6 :                     ev.setType(ME_INVALID);
     749            6 :                     continue;
     750          576 :                 } else if (cn == CTRL_PROGRAM) {
     751            0 :                     ev.setValue((hbank << 16) | (lbank << 8) | ev.value());
     752              :                     // TODO el.insert(ev);
     753            0 :                     ev.setType(ME_INVALID);
     754            0 :                     continue;
     755              :                 }
     756          562 :             } else if (ev.type() == ME_SYSEX) {
     757           39 :                 int len = ev.len();
     758           39 :                 const uchar* buffer = ev.edata();
     759              :                 // Yamaha
     760           39 :                 if (buffer[0] == 0x43) {
     761            1 :                     int type   = buffer[1] & 0xf0;
     762            1 :                     if (type == 0x10) {
     763            1 :                         if (len == 7 && buffer[2] == 0x4c && buffer[3] == 0x08 && buffer[5] == 7) {
     764              :                             // part mode
     765              :                             // 0 - normal
     766              :                             // 1 - DRUM
     767              :                             // 2 - DRUM 1
     768              :                             // 3 - DRUM 2
     769              :                             // 4 - DRUM 3
     770              :                             // 5 - DRUM 4
     771            0 :                             if (buffer[6] != 0 && buffer[4] == ev.channel()) {
     772            0 :                                 _drumTrack = true;
     773              :                             }
     774            0 :                             ev.setType(ME_INVALID);
     775            0 :                             continue;
     776              :                         }
     777              :                     }
     778              :                 }
     779              :             }
     780         1304 :             el.insert(std::pair<int, MidiEvent>(i->first, ev));
     781         1304 :             ev.setType(ME_INVALID);
     782         1304 :             continue;
     783         1304 :         }
     784          882 :         int tick = i->first;
     785          882 :         if (ev.type() == ME_NOTEOFF || ev.velo() == 0) {
     786            0 :             LOGD("-extra note off at %d", tick);
     787            0 :             ev.setType(ME_INVALID);
     788            0 :             continue;
     789              :         }
     790          882 :         MidiEvent note(ME_NOTE, ev.channel(), ev.dataA(), ev.dataB());
     791              : 
     792          882 :         auto k = i;
     793          882 :         ++k;
     794         3111 :         for (; k != _events.end(); ++k) {
     795         3092 :             MidiEvent& e = k->second;
     796         3092 :             if (e.type() != ME_NOTEON && e.type() != ME_NOTEOFF) {
     797         1130 :                 continue;
     798              :             }
     799         3778 :             if ((e.type() == ME_NOTEOFF || (e.type() == ME_NOTEON && e.velo() == 0))
     800         3778 :                 && (e.pitch() == note.pitch())) {
     801          863 :                 int t = k->first - tick;
     802          863 :                 if (t <= 0) {
     803            0 :                     t = 1;
     804              :                 }
     805          863 :                 note.setLen(t);
     806          863 :                 e.setType(ME_INVALID);
     807          863 :                 break;
     808              :             }
     809              :         }
     810          882 :         if (k == _events.end()) {
     811           19 :             LOGD("-no note-off for note at %d", tick);
     812           19 :             note.setLen(1);
     813              :         }
     814          882 :         el.insert(std::pair<int, MidiEvent>(tick, note));
     815          882 :         ev.setType(ME_INVALID);
     816          882 :     }
     817          185 :     _events = el;
     818          185 : }
     819              : 
     820              : //---------------------------------------------------------
     821              : //   separateChannel
     822              : //    if a track contains events for different midi channels,
     823              : //    then split events into separate tracks
     824              : //---------------------------------------------------------
     825              : 
     826           99 : void MidiFile::separateChannel()
     827              : {
     828          273 :     for (size_t i = 0; i < _tracks.size(); ++i) {
     829              :         // create a list of channels used in current track
     830          174 :         std::vector<int> channel;
     831          174 :         MidiTrack& midiTrack = _tracks[i];          // current track
     832         3698 :         for (const auto& ie : midiTrack.events()) {
     833         3524 :             const MidiEvent& e = ie.second;
     834         3524 :             if (e.isChannelEvent() && !muse::contains(channel, static_cast<int>(e.channel()))) {
     835          156 :                 channel.push_back(e.channel());
     836              :             }
     837              :         }
     838          174 :         midiTrack.setOutChannel(channel.empty() ? 0 : channel[0]);
     839          174 :         size_t nn = channel.size();
     840          174 :         if (nn <= 1) {
     841          168 :             continue;
     842              :         }
     843            6 :         std::sort(channel.begin(), channel.end());
     844              :         // -- split --
     845              :         // insert additional tracks, assign to them found channels
     846           17 :         for (size_t ii = 1; ii < nn; ++ii) {
     847           11 :             MidiTrack t;
     848           11 :             t.setOutChannel(channel[ii]);
     849           11 :             _tracks.insert(_tracks.begin() + i + ii, t);
     850           11 :         }
     851              : 
     852              :         //! NOTE: Midi track memory area may be invalid after inserting new elements into tracks
     853              :         //!       Let's get the actual track data again
     854            6 :         MidiTrack& actualMidiTrack = _tracks[i];
     855              : 
     856              :         // extract all different channel events from current track to inserted tracks
     857          266 :         for (auto ie = actualMidiTrack.events().begin(); ie != actualMidiTrack.events().end();) {
     858          260 :             const MidiEvent& e = ie->second;
     859          260 :             if (e.isChannelEvent()) {
     860          235 :                 int ch  = e.channel();
     861          235 :                 size_t idx = muse::indexOf(channel, ch);
     862          235 :                 MidiTrack& t = _tracks[i + idx];
     863          235 :                 if (&t != &actualMidiTrack) {
     864          143 :                     t.insert(ie->first, e);
     865          143 :                     ie = actualMidiTrack.events().erase(ie);
     866          143 :                     continue;
     867              :                 }
     868              :             }
     869          117 :             ++ie;
     870              :         }
     871            6 :         i += nn - 1;
     872          174 :     }
     873           99 : }
     874              : } // namespace mu::iex::midi
        

Generated by: LCOV version 2.0-1