LCOV - code coverage report
Current view: top level - src/MIDI_Interfaces - MIDI_Pipes.cpp (source / functions) Hit Total Coverage
Test: ffed98f648fe78e7aa7bdd228474317d40dadbec Lines: 225 230 97.8 %
Date: 2022-05-28 15:22:59 Functions: 41 45 91.1 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : #include "MIDI_Pipes.hpp"
       2             : #include "MIDI_Staller.hpp"
       3             : #include <AH/Error/Error.hpp>
       4             : #include <AH/STL/utility>
       5             : 
       6             : #if defined(ESP32) || !defined(ARDUINO)
       7             : #include <mutex>
       8             : #endif
       9             : 
      10             : AH_DIAGNOSTIC_WERROR()
      11             : 
      12             : BEGIN_CS_NAMESPACE
      13             : 
      14             : // :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: //
      15             : 
      16         399 : void MIDI_Sink::connectSourcePipe(MIDI_Pipe *source) {
      17         399 :     if (this->sourcePipe == nullptr) {
      18         304 :         source->connectSink(this);
      19         303 :         this->sourcePipe = source;
      20             :     } else {
      21          95 :         this->sourcePipe->connectSourcePipe(source);
      22             :     }
      23         398 : }
      24             : 
      25        2277 : void MIDI_Sink::disconnectSourcePipes() {
      26        2277 :     if (sourcePipe != nullptr) {
      27          67 :         sourcePipe->disconnectSourcePipes();
      28          67 :         sourcePipe->disconnect();
      29          67 :         sourcePipe = nullptr;
      30             :     }
      31        2277 : }
      32             : 
      33         303 : void MIDI_Sink::disconnectSourcePipesShallow() {
      34         303 :     if (sourcePipe != nullptr) {
      35         303 :         sourcePipe->disconnectSink();
      36         303 :         sourcePipe = nullptr;
      37             :     }
      38         303 : }
      39             : 
      40          13 : bool MIDI_Sink::disconnect(TrueMIDI_Source &source) {
      41          13 :     if (!hasSourcePipe())
      42           3 :         return false;
      43          10 :     return sourcePipe->disconnect(source);
      44             : }
      45             : 
      46        2209 : MIDI_Sink::~MIDI_Sink() { disconnectSourcePipes(); }
      47             : 
      48           5 : MIDI_Sink::MIDI_Sink(MIDI_Sink &&other)
      49           5 :     : sourcePipe(std::exchange(other.sourcePipe, nullptr)) {
      50           5 :     if (this->hasSourcePipe()) {
      51           5 :         this->sourcePipe->disconnectSink();
      52           5 :         this->sourcePipe->connectSink(this);
      53             :     }
      54           5 : }
      55             : 
      56           3 : void MIDI_Sink::swap(MIDI_Sink &a, MIDI_Sink &b) {
      57           3 :     std::swap(a.sourcePipe, b.sourcePipe);
      58           3 :     if (a.hasSourcePipe()) {
      59           3 :         a.sourcePipe->disconnectSink();
      60           3 :         a.sourcePipe->connectSink(&a);
      61             :     }
      62           3 :     if (b.hasSourcePipe()) {
      63           1 :         b.sourcePipe->disconnectSink();
      64           1 :         b.sourcePipe->connectSink(&b);
      65             :     }
      66           3 : }
      67             : 
      68           2 : MIDI_Sink &MIDI_Sink::operator=(MIDI_Sink &&other) {
      69           2 :     swap(*this, other);
      70           2 :     return *this;
      71             : }
      72             : 
      73             : // :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: //
      74             : 
      75         362 : void MIDI_Source::connectSinkPipe(MIDI_Pipe *sink) {
      76         362 :     if (this->sinkPipe == nullptr) {
      77         290 :         sink->connectSource(this);
      78         289 :         this->sinkPipe = sink;
      79             :     } else {
      80          72 :         this->sinkPipe->connectSinkPipe(sink);
      81             :     }
      82         361 : }
      83             : 
      84        2391 : void MIDI_Source::disconnectSinkPipes() {
      85        2391 :     if (sinkPipe != nullptr) {
      86         164 :         sinkPipe->disconnectSinkPipes();
      87         164 :         sinkPipe->disconnect();
      88         164 :         sinkPipe = nullptr;
      89             :     }
      90        2391 : }
      91             : 
      92         289 : void MIDI_Source::disconnectSinkPipesShallow() {
      93         289 :     if (sinkPipe != nullptr) {
      94         289 :         sinkPipe->disconnectSource();
      95         289 :         sinkPipe = nullptr;
      96             :     }
      97         289 : }
      98             : 
      99          13 : bool MIDI_Source::disconnect(TrueMIDI_Sink &sink) {
     100          13 :     if (!hasSinkPipe())
     101           3 :         return false;
     102          10 :     return sinkPipe->disconnect(sink);
     103             : }
     104             : 
     105           5 : MIDI_Source::MIDI_Source(MIDI_Source &&other)
     106           5 :     : sinkPipe(std::exchange(other.sinkPipe, nullptr)) {
     107           5 :     if (this->hasSinkPipe()) {
     108           5 :         this->sinkPipe->disconnectSource();
     109           5 :         this->sinkPipe->connectSource(this);
     110             :     }
     111           5 : }
     112             : 
     113           3 : void MIDI_Source::swap(MIDI_Source &a, MIDI_Source &b) {
     114           3 :     std::swap(a.sinkPipe, b.sinkPipe);
     115           3 :     if (a.hasSinkPipe()) {
     116           3 :         a.sinkPipe->disconnectSource();
     117           3 :         a.sinkPipe->connectSource(&a);
     118             :     }
     119           3 :     if (b.hasSinkPipe()) {
     120           1 :         b.sinkPipe->disconnectSource();
     121           1 :         b.sinkPipe->connectSource(&b);
     122             :     }
     123           3 : }
     124             : 
     125           2 : MIDI_Source &MIDI_Source::operator=(MIDI_Source &&other) {
     126           2 :     swap(*this, other);
     127           2 :     return *this;
     128             : }
     129             : 
     130        2225 : MIDI_Source::~MIDI_Source() { disconnectSinkPipes(); }
     131             : 
     132         149 : void MIDI_Source::sourceMIDItoPipe(ChannelMessage msg) {
     133         149 :     if (sinkPipe != nullptr) {
     134         135 :         handleStallers();
     135         135 :         sinkPipe->acceptMIDIfromSource(msg);
     136             :     }
     137         149 : }
     138          15 : void MIDI_Source::sourceMIDItoPipe(SysExMessage msg) {
     139          15 :     if (sinkPipe != nullptr) {
     140          10 :         handleStallers();
     141          10 :         sinkPipe->acceptMIDIfromSource(msg);
     142             :     }
     143          15 : }
     144           0 : void MIDI_Source::sourceMIDItoPipe(SysCommonMessage msg) {
     145           0 :     if (sinkPipe != nullptr) {
     146           0 :         handleStallers();
     147           0 :         sinkPipe->acceptMIDIfromSource(msg);
     148             :     }
     149           0 : }
     150          19 : void MIDI_Source::sourceMIDItoPipe(RealTimeMessage msg) {
     151          19 :     if (sinkPipe != nullptr) {
     152             :         // Always send write to pipe, don't check if it's stalled or not
     153          15 :         sinkPipe->acceptMIDIfromSource(msg);
     154             :     }
     155          19 : }
     156             : 
     157          36 : void MIDI_Source::stall(MIDIStaller *cause) {
     158          36 :     if (hasSinkPipe())
     159          36 :         sinkPipe->stallDownstream(cause, this);
     160          35 : }
     161             : 
     162          22 : void MIDI_Source::unstall(MIDIStaller *cause) {
     163          22 :     if (hasSinkPipe())
     164          22 :         sinkPipe->unstallDownstream(cause, this);
     165          21 : }
     166             : 
     167          40 : bool MIDI_Source::isStalled() const {
     168          40 :     if (hasSinkPipe())
     169          36 :         return sinkPipe->isStalled();
     170           4 :     return false;
     171             : }
     172             : 
     173          30 : MIDIStaller *MIDI_Source::getStaller() const {
     174          30 :     if (hasSinkPipe())
     175          14 :         return sinkPipe->getStaller();
     176          16 :     return nullptr;
     177             : }
     178             : 
     179           4 : const char *MIDI_Source::getStallerName() const {
     180           4 :     return MIDIStaller::getNameNull(getStaller());
     181             : }
     182             : 
     183         150 : void MIDI_Source::handleStallers() const {
     184         150 :     if (hasSinkPipe())
     185         150 :         sinkPipe->handleStallers();
     186         149 : }
     187             : 
     188             : // :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: //
     189             : 
     190         313 : void MIDI_Pipe::connectSink(MIDI_Sink *sink) {
     191         313 :     if (this->sink != nullptr) {
     192           1 :         FATAL_ERROR(F("This pipe is already connected to a sink"), 0x9145);
     193             :         return; // LCOV_EXCL_LINE
     194             :     }
     195         312 :     this->sink = sink;
     196             : }
     197             : 
     198         312 : void MIDI_Pipe::disconnectSink() { this->sink = nullptr; }
     199             : 
     200         299 : void MIDI_Pipe::connectSource(MIDI_Source *source) {
     201         299 :     if (this->source != nullptr) {
     202           1 :         FATAL_ERROR(F("This pipe is already connected to a source"), 0x9146);
     203             :         return; // LCOV_EXCL_LINE
     204             :     }
     205         298 :     this->source = source;
     206             : }
     207             : 
     208         298 : void MIDI_Pipe::disconnectSource() { this->source = nullptr; }
     209             : 
     210        1629 : void MIDI_Pipe::disconnect() {
     211        1629 :     if (hasSink() && hasThroughIn()) {
     212          26 :         auto oldSink = sink;
     213          26 :         auto oldThroughIn = throughIn;
     214          26 :         sink->disconnectSourcePipesShallow();
     215          26 :         this->disconnectSourcePipesShallow(); // disconnect throughIn
     216          26 :         oldSink->connectSourcePipe(oldThroughIn);
     217             :     }
     218        1629 :     if (hasSource() && hasThroughOut()) {
     219          14 :         auto oldSource = source;
     220          14 :         auto oldThroughOut = throughOut;
     221          14 :         source->disconnectSinkPipesShallow();
     222          14 :         this->disconnectSinkPipesShallow(); // disconnect throughOut
     223          14 :         oldSource->connectSinkPipe(oldThroughOut);
     224             :     }
     225        1629 :     if (hasSink())
     226         251 :         sink->disconnectSourcePipesShallow();
     227             : 
     228        1629 :     if (hasSource())
     229         261 :         source->disconnectSinkPipesShallow();
     230             : 
     231        1629 :     if (hasThroughIn() || hasThroughOut())
     232             :         FATAL_ERROR(F("Invalid state"), 0x9147); // LCOV_EXCL_LINE
     233        1629 : }
     234             : 
     235        1380 : MIDI_Pipe::~MIDI_Pipe() { disconnect(); }
     236             : 
     237          74 : void MIDI_Pipe::stallDownstream(MIDIStaller *cause, MIDI_Source *stallsrc) {
     238          74 :     if (!sinkIsUnstalledOrStalledBy(cause)) {
     239           1 :         FATAL_ERROR(F("Cannot stall pipe from ")
     240             :                         << MIDIStaller::getNameNull(cause)
     241             :                         << F(" because pipe is already stalled by ")
     242             :                         << MIDIStaller::getNameNull(sink_staller),
     243             :                     0x6665);
     244             :     } // LCOV_EXCL_LINE
     245          73 :     sink_staller = cause;
     246          73 :     if (hasThroughOut() && stallsrc == source)
     247           1 :         throughOut->stallDownstream(cause, this);
     248          73 :     if (hasSink())
     249          73 :         sink->stallDownstream(cause, this);
     250          73 :     if (hasSource() && source != stallsrc) {
     251             :         // If our through output is stalled, that means our upstream is stalled
     252             :         // as well by this staller. Unstall it first, and replace it by the new
     253             :         // staller
     254          37 :         if (through_staller != nullptr)
     255           3 :             source->unstallUpstream(through_staller, this);
     256          37 :         source->stallUpstream(cause, this);
     257             :     }
     258          73 :     if (hasThroughIn() && throughIn != stallsrc)
     259           8 :         throughIn->stallUpstream(cause, this);
     260          73 : }
     261             : 
     262          35 : void MIDI_Pipe::stallUpstream(MIDIStaller *cause, MIDI_Sink *stallsrc) {
     263          35 :     if (stallsrc == sink) {
     264             :         // This cannot be a different cause, because then our sink would
     265             :         // already have caught it in stallDownstream().
     266           9 :         sink_staller = cause;
     267           9 :         if (hasSource())
     268           9 :             source->stallUpstream(cause, this);
     269           9 :         if (hasThroughIn())
     270           1 :             throughIn->stallUpstream(cause, this);
     271             :     } else {
     272          26 :         if (through_staller == nullptr) {
     273          17 :             through_staller = cause;
     274          17 :             if (hasSource())
     275          17 :                 source->stallUpstream(cause, this);
     276             :         }
     277             :     }
     278          35 : }
     279             : 
     280          41 : void MIDI_Pipe::unstallDownstream(MIDIStaller *cause, MIDI_Source *stallsrc) {
     281          41 :     if (!sinkIsUnstalledOrStalledBy(cause)) {
     282           1 :         FATAL_ERROR(F("Cannot unstall pipe from ")
     283             :                         << MIDIStaller::getNameNull(cause)
     284             :                         << F(" because pipe is stalled by ")
     285             :                         << MIDIStaller::getNameNull(sink_staller),
     286             :                     0x6666);
     287             :     } // LCOV_EXCL_LINE
     288          40 :     this->sink_staller = nullptr;
     289          40 :     if (hasThroughOut() && stallsrc == source)
     290           1 :         throughOut->unstallDownstream(cause, this);
     291          40 :     if (hasSink())
     292          40 :         sink->unstallDownstream(cause, this);
     293          40 :     if (hasSource() && source != stallsrc) {
     294          18 :         source->unstallUpstream(cause, this);
     295             :         // If the through output of this pipe is stalled, we cannot just unstall
     296             :         // our upstream, we have to update it our through output staller
     297          18 :         if (through_staller != nullptr)
     298           4 :             source->stallUpstream(through_staller, this);
     299             :     }
     300          40 :     if (hasThroughIn() && throughIn != stallsrc)
     301           7 :         throughIn->unstallUpstream(cause, this);
     302          40 : }
     303             : 
     304          25 : void MIDI_Pipe::unstallUpstream(MIDIStaller *cause, MIDI_Sink *stallsrc) {
     305          25 :     if (stallsrc == sink) {
     306             :         // This cannot be a different cause, because then our sink would
     307             :         // already have caught it in unstallDownstream().
     308           8 :         sink_staller = nullptr;
     309           8 :         if (hasSource())
     310           8 :             source->unstallUpstream(cause, this);
     311           8 :         if (hasThroughIn())
     312           1 :             throughIn->unstallUpstream(cause, this);
     313             :     } else {
     314          17 :         if (cause == through_staller) {
     315          11 :             through_staller = nullptr;
     316          11 :             if (hasSource())
     317          11 :                 source->unstallUpstream(cause, this);
     318             :         }
     319             :     }
     320          25 : }
     321             : 
     322           4 : const char *MIDI_Pipe::getSinkStallerName() const {
     323           4 :     return MIDIStaller::getNameNull(sink_staller);
     324             : }
     325             : 
     326           2 : const char *MIDI_Pipe::getThroughStallerName() const {
     327           2 :     return MIDIStaller::getNameNull(through_staller);
     328             : }
     329             : 
     330          20 : MIDIStaller *MIDI_Pipe::getStaller() const {
     331          20 :     return sink_staller ? sink_staller : through_staller;
     332             : }
     333             : 
     334           3 : const char *MIDI_Pipe::getStallerName() const {
     335           3 :     return MIDIStaller::getNameNull(getStaller());
     336             : }
     337             : 
     338         150 : void MIDI_Pipe::handleStallers() const {
     339         150 :     if (!isStalled())
     340         142 :         return;
     341           8 :     if (sink_staller == eternal_stall || through_staller == eternal_stall)
     342           1 :         FATAL_ERROR(F("Unable to unstall pipe (eternal stall)"), 0x4827);
     343           7 :     uint8_t iterations = 10;
     344          23 :     while(isStalled() && iterations-- > 0) {
     345          16 :         if (sink_staller)
     346          15 :             sink_staller->handleStall();
     347          16 :         if (through_staller)
     348           1 :             through_staller->handleStall();
     349             :     }
     350             : }
     351             : 
     352             : // :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: //
     353             : 
     354          17 : const char *MIDIStaller::getNameNull(MIDIStaller *s) {
     355          17 :     if (s == nullptr)
     356           4 :         return "(null)";
     357          13 :     if (s == eternal_stall)
     358           1 :         return "(eternal stall)";
     359          12 :     return s->getName();
     360             : }
     361             : 
     362             : END_CS_NAMESPACE
     363             : 
     364             : AH_DIAGNOSTIC_POP()

Generated by: LCOV version 1.15