LCOV - code coverage report
Current view: top level - src/MIDI_Interfaces - MIDI_Pipes.cpp (source / functions) Hit Total Coverage
Test: 91b605873905a6fcb78324052c97dbac10849539 Lines: 225 230 97.8 %
Date: 2022-11-08 01:34:37 Functions: 41 45 91.1 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.15