LCOV - code coverage report
Current view: top level - src/engraving/compat/midi - event.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 59.9 % 152 91
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 "event.h"
      24              : 
      25              : #include "dom/note.h"
      26              : #include "dom/sig.h"
      27              : 
      28              : namespace mu::engraving {
      29              : //---------------------------------------------------------
      30              : //   Event::Event
      31              : //---------------------------------------------------------
      32              : 
      33            0 : Event::Event()
      34              : {
      35            0 :     _type            = 0;
      36            0 :     _ontime          = -1;
      37            0 :     _noquantOntime   = 0;
      38            0 :     _noquantDuration = 0;
      39            0 :     _channel         = 0;
      40            0 :     _a               = 0;
      41            0 :     _b               = 0;
      42            0 :     _duration        = 0;
      43            0 :     _tpc             = 0;
      44            0 :     _voice           = 0;
      45            0 :     _edata           = 0;
      46            0 :     _len             = 0;
      47            0 :     _metaType        = 0;
      48            0 :     _note            = 0;
      49            0 :     _tuning          = 0.0;
      50            0 : }
      51              : 
      52            3 : Event::Event(int t)
      53              : {
      54            3 :     _type            = t;
      55            3 :     _ontime          = -1;
      56            3 :     _noquantOntime   = 0;
      57            3 :     _noquantDuration = 0;
      58            3 :     _channel         = 0;
      59            3 :     _a               = 0;
      60            3 :     _b               = 0;
      61            3 :     _duration        = 0;
      62            3 :     _tpc             = 0;
      63            3 :     _voice           = 0;
      64            3 :     _edata           = 0;
      65            3 :     _len             = 0;
      66            3 :     _metaType        = 0;
      67            3 :     _note            = 0;
      68            3 :     _tuning          = 0.0;
      69            3 : }
      70              : 
      71            4 : Event::Event(const Event& e)
      72            4 :     : PlayEvent(e)
      73              : {
      74            4 :     _type       = e._type;
      75            4 :     _ontime     = e._ontime;
      76            4 :     _noquantOntime   = e._noquantOntime;
      77            4 :     _noquantDuration = e._noquantDuration;
      78            4 :     _channel    = e._channel;
      79            4 :     _a          = e._a;
      80            4 :     _b          = e._b;
      81            4 :     _duration   = e._duration;
      82            4 :     _tpc        = e._tpc;
      83            4 :     _voice      = e._voice;
      84            4 :     _notes      = e._notes;
      85            4 :     if (e._edata) {
      86            0 :         _edata = new unsigned char[e._len + 1];      // don’t forget trailing zero
      87            0 :         memcpy(_edata, e._edata, e._len + 1);
      88              :     } else {
      89            4 :         _edata = 0;
      90              :     }
      91            4 :     _len        = e._len;
      92            4 :     _metaType   = e._metaType;
      93            4 :     _note       = e._note;
      94            4 :     _tuning     = e._tuning;
      95            4 : }
      96              : 
      97            5 : Event::~Event()
      98              : {
      99            5 :     delete[] _edata;
     100            5 : }
     101              : 
     102              : //---------------------------------------------------------
     103              : //   NPlayEvent::NPlayEvent (beatType2metronomeEvent)
     104              : //---------------------------------------------------------
     105              : 
     106            0 : NPlayEvent::NPlayEvent(BeatType beatType)
     107              : {
     108            0 :     setType(ME_TICK2);
     109            0 :     setVelo(127);
     110            0 :     switch (beatType) {
     111            0 :     case BeatType::DOWNBEAT:
     112            0 :         setType(ME_TICK1);
     113            0 :         break;
     114            0 :     case BeatType::SIMPLE_STRESSED:
     115              :     case BeatType::COMPOUND_STRESSED:
     116              :         // use defaults
     117            0 :         break;
     118            0 :     case BeatType::SIMPLE_UNSTRESSED:
     119              :     case BeatType::COMPOUND_UNSTRESSED:
     120            0 :         setVelo(80);
     121            0 :         break;
     122            0 :     case BeatType::COMPOUND_SUBBEAT:
     123            0 :         setVelo(25);
     124            0 :         break;
     125            0 :     case BeatType::SUBBEAT:
     126            0 :         setVelo(15);
     127            0 :         break;
     128              :     }
     129            0 : }
     130              : 
     131              : //---------------------------------------------------------
     132              : //   isMuted
     133              : //---------------------------------------------------------
     134              : 
     135         1080 : bool NPlayEvent::isMuted() const
     136              : {
     137              :     //! TODO
     138              : 
     139              : //    const Note* n = note();
     140              : //    if (n) {
     141              : //        MasterScore* cs = n->masterScore();
     142              : //        Staff* staff = n->staff();
     143              : //        Instrument* instr = staff->part()->instrument(n->tick());
     144              : //        const Channel* a = instr->playbackChannel(n->subchannel(), cs);
     145              : //        return a->mute() || a->soloMute() || !staff->playbackVoice(n->voice());
     146              : //    }
     147              : 
     148              : //    const Harmony* h = harmony();
     149              : //    if (h) {
     150              : //        const Channel* hCh = h->part()->harmonyChannel();
     151              : //        if (hCh) { //if there is a harmony channel
     152              : //            const Channel* pCh = h->masterScore()->playbackChannel(hCh);
     153              : //            return pCh->mute() || pCh->soloMute();
     154              : //        }
     155              : //    }
     156              : 
     157         1080 :     return false;
     158              : }
     159              : 
     160              : //---------------------------------------------------------
     161              : //   dump
     162              : //---------------------------------------------------------
     163              : 
     164            0 : void Event::dump() const
     165              : {
     166            0 :     printf("event ");
     167            0 :     switch (_type) {
     168            0 :     case ME_NOTEON:     printf("noteon    ");
     169            0 :         break;
     170            0 :     case ME_CONTROLLER: printf("controller");
     171            0 :         break;
     172            0 :     case ME_PROGRAM:    printf("program   ");
     173            0 :         break;
     174            0 :     default:            printf("0x%02x    ", _type);
     175            0 :         break;
     176              :     }
     177            0 :     printf(" 0x%02x 0x%02x\n", _a, _b);
     178            0 : }
     179              : 
     180            0 : bool Event::operator==(const Event&) const
     181              : {
     182            0 :     return false;             // TODO
     183              : }
     184              : 
     185              : //---------------------------------------------------------
     186              : // insert
     187              : //---------------------------------------------------------
     188              : 
     189            0 : void EventList::insert(const Event& e)
     190              : {
     191            0 :     int ontime = e.ontime();
     192            0 :     if (!empty() && back().ontime() > ontime) {
     193            0 :         for (auto i = begin(); i != end(); ++i) {
     194            0 :             if (i->ontime() > ontime) {
     195            0 :                 std::vector<Event>::insert(i, e);
     196            0 :                 return;
     197              :             }
     198              :         }
     199              :     }
     200            0 :     push_back(e);
     201              : }
     202              : 
     203        13355 : EventsHolder::events_multimap_t& EventsHolder::operator[](std::size_t idx)
     204              : {
     205        13355 :     if (size() == 0) {
     206          204 :         _channels.emplace_back();
     207              :     }
     208        13355 :     if (idx >= size()) {
     209           69 :         auto diff = idx - size();
     210              :         do {
     211          306 :             _channels.emplace_back();
     212          306 :         } while (diff-- != 0);
     213              :     }
     214        13355 :     return _channels[idx];
     215              : }
     216              : 
     217           53 : const EventsHolder::events_multimap_t& EventsHolder::operator[](std::size_t idx) const
     218              : {
     219              :     // Since EventsHolder acts more like a vector
     220              :     // Using const subscript operator for a nonexistent element is UB
     221              :     /*
     222              :      * cppreference.com
     223              :      * Unlike std::map::operator[], this operator never inserts a new element into the container.
     224              :      * Accessing a nonexistent element through this operator is undefined behavior.
     225              :      */
     226           53 :     assert(idx < size());
     227           53 :     return _channels[idx];
     228              : }
     229              : 
     230           80 : void EventsHolder::mergePitchWheelEvents(EventsHolder& pitchWheelEvents)
     231              : {
     232          182 :     for (size_t i = 0; i < size(); ++i) {
     233         1365 :         for (const auto& eventPair : _channels[i]) {
     234         1263 :             const auto& event = eventPair.second;
     235         1263 :             const auto& tick = eventPair.first;
     236         1263 :             if (event.type() == ME_NOTEON && event.velo() != 0) {
     237          505 :                 const auto& pwEvent = muse::findLess(pitchWheelEvents[i], tick);
     238          505 :                 if (pwEvent != pitchWheelEvents[i].end()
     239          505 :                     && pwEvent->second.type() == ME_PITCHBEND) {
     240           20 :                     PitchWheelSpecs specs;
     241           20 :                     NPlayEvent pwReset(ME_PITCHBEND, (uint8_t)i, specs.mLimit % 128, specs.mLimit / 128);
     242           20 :                     pwReset.setOriginatingStaff(pwEvent->second.getOriginatingStaff());
     243              : 
     244           20 :                     int tickForPwReset = 0;
     245           20 :                     if (tick - specs.mStep > pwEvent->first) {
     246           15 :                         tickForPwReset = tick - specs.mStep;
     247              :                     } else {
     248            5 :                         tickForPwReset = (tick + pwEvent->first) / 2;
     249              :                     }
     250              : 
     251           20 :                     _channels[i].insert(std::pair<int, NPlayEvent>(tickForPwReset, pwReset));
     252              :                 }
     253              :             }
     254              :         }
     255          102 :         _channels[i].merge(pitchWheelEvents[i]);
     256              :     }
     257           80 : }
     258              : 
     259              : //---------------------------------------------------------
     260              : //   class EventsHolder::fixupMIDI
     261              : //---------------------------------------------------------
     262              : 
     263           79 : void EventsHolder::fixupMIDI()
     264              : {
     265              :     /* track info for each of the 128 possible MIDI notes */
     266              :     struct channelInfo {
     267              :         /* which event the first ME_NOTEON came from */
     268              :         NPlayEvent* event[128];
     269              :         /* how often is the note on right now? */
     270              :         unsigned short nowPlaying[128];
     271              :     };
     272              : 
     273              :     /* track info for each channel (on the heap, 0-initialised) */
     274           79 :     auto* info = (struct channelInfo*)calloc(size() + 1, sizeof(struct channelInfo));
     275              : 
     276          180 :     for (auto& mm : _channels) {
     277          101 :         auto it = mm.begin();
     278         1360 :         while (it != mm.end()) {
     279         1259 :             NPlayEvent& event = it->second;
     280              :             /* ME_NOTEOFF is never emitted, no need to check for it */
     281         1259 :             if (event.type() == ME_NOTEON) {
     282         1006 :                 uint8_t np = info[event.channel()].nowPlaying[event.pitch()];
     283         1006 :                 if (event.velo() == 0) {
     284              :                     /* already off (should not happen) or still playing? */
     285          503 :                     if (np == 0 || --np > 0) {
     286            1 :                         event.setDiscard(1);
     287              :                     } else {
     288              :                         /* hoist NOTEOFF to same track as NOTEON */
     289          502 :                         event.setOriginatingStaff(info[event.channel()].event[event.pitch()]->getOriginatingStaff());
     290              :                     }
     291              :                 } else {
     292          503 :                     if (++np > 1) {
     293              :                         /* restrike, possibly on different track */
     294            1 :                         event.setDiscard(info[event.channel()].event[event.pitch()]->getOriginatingStaff() + 1);
     295              :                     }
     296          503 :                     info[event.channel()].event[event.pitch()] = &event;
     297              :                 }
     298         1006 :                 info[event.channel()].nowPlaying[event.pitch()] = np;
     299              :             }
     300              : 
     301         1259 :             ++it;
     302              :         }
     303              :     }
     304              : 
     305           79 :     free((void*)info);
     306           79 : }
     307              : }
        

Generated by: LCOV version 2.0-1