LCOV - code coverage report
Current view: top level - src/MIDI_Interfaces - MIDI_Pipes.hpp (source / functions) Hit Total Coverage
Test: e224b347cd670555e44f06608ac41bd1ace9d9d8 Lines: 141 141 100.0 %
Date: 2020-09-08 17:44:46 Functions: 69 72 95.8 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : #pragma once
       2             : 
       3             : #include <AH/Containers/BitArray.hpp>
       4             : #include <AH/STL/utility>
       5             : #include <AH/Settings/Warnings.hpp>
       6             : #include <MIDI_Parsers/MIDI_MessageTypes.hpp>
       7             : #include <Settings/NamespaceSettings.hpp>
       8             : 
       9             : AH_DIAGNOSTIC_WERROR()
      10             : 
      11             : BEGIN_CS_NAMESPACE
      12             : 
      13             : /// Data type for cable numbers.
      14             : using cn_t = uint8_t;
      15             : 
      16             : /**
      17             :  * @addtogroup MIDI_Routing MIDI Routing
      18             :  * @brief   Operators and utilities for MIDI routing.
      19             :  * 
      20             :  * Allows you to use syntax like:
      21             :  * 
      22             :  * ~~~cpp
      23             :  * HardwareSerialMIDI_Interface 
      24             :  *     midiA = Serial1, midiB = Serial2, midiC = Serial3;
      25             :  * MIDI_PipeFactory<3> pipes; // Factory that can produce 3 pipes
      26             :  * 
      27             :  * midiA >> pipes >> midiB;
      28             :  * midiC >> pipes >> midiB;
      29             :  * midiC << pipes << midiB;
      30             :  * ~~~
      31             :  * 
      32             :  * Or for bidirectional connections:
      33             :  * 
      34             :  * ~~~cpp
      35             :  * HardwareSerialMIDI_Interface 
      36             :  *     midiA = Serial1, midiB = Serial2, midiC = Serial3;
      37             :  * BidirectionalMIDI_PipeFactory<2> pipes; // Factory that can produce 2 pipes
      38             :  * 
      39             :  * midiA | pipes | midiB;
      40             :  * midiA | pipes | midiC;
      41             :  * ~~~
      42             :  * 
      43             :  * @{ 
      44             :  */
      45             : 
      46             : class MIDI_Pipe;
      47             : struct TrueMIDI_Sink;
      48             : struct TrueMIDI_Source;
      49             : 
      50             : /// Class that can receive MIDI messages from a MIDI pipe.
      51             : class MIDI_Sink {
      52             :   public:
      53             :     /// Default constructor.
      54         315 :     MIDI_Sink() = default;
      55             : 
      56             :     /// Copy constructor (copying not allowed).
      57             :     MIDI_Sink(const MIDI_Sink &) = delete;
      58             :     /// Copy assignment (copying not allowed).
      59             :     MIDI_Sink &operator=(const MIDI_Sink &) = delete;
      60             : 
      61             :     /// Move constructor.
      62             :     MIDI_Sink(MIDI_Sink &&other);
      63             :     /// Move assignment.
      64             :     MIDI_Sink &operator=(MIDI_Sink &&other);
      65             : 
      66             :     /// Destructor.
      67             :     virtual ~MIDI_Sink();
      68             : 
      69             :     /// @name Sending data over a MIDI Pipe
      70             :     /// @{
      71             : 
      72             :     /// Accept an incoming MIDI Channel message.
      73             :     virtual void sinkMIDIfromPipe(ChannelMessage) = 0;
      74             :     /// Accept an incoming MIDI System Exclusive message.
      75             :     virtual void sinkMIDIfromPipe(SysExMessage) = 0;
      76             :     /// Accept an incoming MIDI Real-Time message.
      77             :     virtual void sinkMIDIfromPipe(RealTimeMessage) = 0;
      78             : 
      79             :     /// @}
      80             : 
      81             :     /// @name Connecting and disconnecting MIDI Pipes
      82             :     /// @{
      83             : 
      84             :     /// Fully connect a source pipe to this sink.
      85             :     void connectSourcePipe(MIDI_Pipe *source);
      86             :     /// Disconnect all source pipes that sink to this sink (recursively).
      87             :     void disconnectSourcePipes();
      88             :     /// Disconnect the given source from this sink. Leaves other sources
      89             :     /// connected.
      90             :     /// Returns true if the source was found and disconnected, false if the
      91             :     /// given source was not a direct or indirect source to this sink.
      92             :     bool disconnect(TrueMIDI_Source &source);
      93             :     /// Check if this sink is connected to a source pipe.
      94         105 :     bool hasSourcePipe() const { return sourcePipe != nullptr; }
      95             : 
      96             : #ifndef ARDUINO
      97          60 :     MIDI_Pipe *getSourcePipe() { return sourcePipe; }
      98             : #endif
      99             : 
     100             :     /// @}
     101             : 
     102             :   private:
     103             :     /// Base case for recursive lock function.
     104             :     /// @see    MIDI_Pipe::lockDownstream
     105          14 :     virtual void lockDownstream(cn_t cn, bool lock) { (void)cn, (void)lock; }
     106             :     /// Base case for recursive function.
     107             :     /// @see    MIDI_Pipe::getFinalSink
     108          63 :     virtual MIDI_Sink *getFinalSink() { return this; }
     109             :     /// Disconnect only the first pipe connected to this sink. Leaves the
     110             :     /// other pipes connected to the original pipe, which doesn't have a sink
     111             :     /// anymore when this function finishes.
     112             :     /// Used to disconnect a MIDI_Pipe while preserving the connections of its
     113             :     /// "through" inputs.
     114             :     void disconnectSourcePipesShallow();
     115             : 
     116             :   protected:
     117         315 :     MIDI_Pipe *sourcePipe = nullptr;
     118             : 
     119             :     friend class MIDI_Pipe;
     120             : };
     121             : 
     122             : // :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: //
     123             : 
     124             : /// Class that can send MIDI messages to a MIDI pipe.
     125             : class MIDI_Source {
     126             :   public:
     127             :     /// @name Sending data over a MIDI Pipe
     128             :     /// @{
     129             : 
     130             :     /// Send a MIDI Channel Message.
     131             :     void sourceMIDItoPipe(ChannelMessage);
     132             :     /// Send a MIDI System Exclusive message.
     133             :     void sourceMIDItoPipe(SysExMessage);
     134             :     /// Send a MIDI Real-Time message.
     135             :     void sourceMIDItoPipe(RealTimeMessage);
     136             : 
     137             :     /** 
     138             :      * @brief   Enter or exit exclusive mode for the given cable number.
     139             :      *          This means that this becomes the only source that can sink to 
     140             :      *          the sinks connected to this source. Other sources have to wait
     141             :      *          until this source exits exclusive mode until they can send 
     142             :      *          again.
     143             :      * @param   cn
     144             :      *          Cable number to set the exclusive mode for [0, 15].
     145             :      * @param   exclusive
     146             :      *          True to enable exclusive mode, false to disable.
     147             :      */
     148             :     void exclusive(cn_t cn, bool exclusive = true);
     149             :     /** 
     150             :      * @brief   Check if this source can write to the sinks it connects to.
     151             :      *          Returns false if any of the sinks have another source that is
     152             :      *          in exclusive mode.
     153             :      * @param   cn
     154             :      *          Cable number to check [0, 15].
     155             :      */
     156             :     bool canWrite(cn_t cn) const;
     157             : 
     158             :     /// @}
     159             : 
     160             :     /// Default constructor.
     161         312 :     MIDI_Source() = default;
     162             : 
     163             :     /// Copy constructor (copying not allowed).
     164             :     MIDI_Source(const MIDI_Source &) = delete;
     165             :     /// Copy assignment (copying not allowed).
     166             :     MIDI_Source &operator=(const MIDI_Source &) = delete;
     167             : 
     168             :     /// Move constructor.
     169             :     MIDI_Source(MIDI_Source &&other);
     170             :     /// Move assignment.
     171             :     MIDI_Source &operator=(MIDI_Source &&other);
     172             : 
     173             :     /// Destructor.
     174             :     virtual ~MIDI_Source();
     175             : 
     176             :     /// @name Connecting and disconnecting MIDI Pipes
     177             :     /// @{
     178             : 
     179             :     /// Fully connect a sink pipe to this source.
     180             :     void connectSinkPipe(MIDI_Pipe *sink);
     181             :     /// Disconnect all sink pipes that this source sinks to (recursively).
     182             :     void disconnectSinkPipes();
     183             :     /// Disconnect the given sink from this source. Leaves other sinks
     184             :     /// connected.
     185             :     /// Returns true if the sink was found and disconnected, false if the
     186             :     /// given sink was not a direct or indirect sink of this source.
     187             :     bool disconnect(TrueMIDI_Sink &sink);
     188             :     /// Check if this source is connected to a sink pipe.
     189         179 :     bool hasSinkPipe() const { return sinkPipe != nullptr; }
     190             : 
     191             : #ifndef ARDUINO
     192          60 :     MIDI_Pipe *getSinkPipe() { return sinkPipe; }
     193             : #endif
     194             : 
     195             :     /// @}
     196             : 
     197             :   private:
     198             :     /// Base case for recursive function.
     199             :     /// @see    MIDI_Pipe::getInitialSource
     200          63 :     virtual MIDI_Source *getInitialSource() { return this; }
     201             :     /// Disconnect only the first pipe connected to this source. Leaves the
     202             :     /// other pipes connected to the original pipe, which doesn't have a source
     203             :     /// anymore when this function finishes.
     204             :     /// Used to disconnect a MIDI_Pipe while preserving the connections of its
     205             :     /// "through" outputs.
     206             :     void disconnectSinkPipesShallow();
     207             : 
     208             :   protected:
     209         312 :     MIDI_Pipe *sinkPipe = nullptr;
     210             : 
     211             :     friend class MIDI_Pipe;
     212             : };
     213             : 
     214             : // :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: //
     215             : 
     216             : /// A MIDI_Sink that is not a MIDI_Pipe.
     217         362 : struct TrueMIDI_Sink : MIDI_Sink {};
     218             : /// A MIDI_Source that is not a MIDI_Pipe.
     219         356 : struct TrueMIDI_Source : MIDI_Source {};
     220             : 
     221             : // :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: //
     222             : 
     223             : /** 
     224             :  * @brief   Class that routes MIDI messages from a MIDI_Source to a MIDI_Sink.
     225             :  * 
     226             :  * A pipe has at least two, at most four connections. In the simplest case, a 
     227             :  * pipe just has a sourca and a sink. The source sends data down the pipe, and
     228             :  * the pipe sends it to the sink. A mapping or filter can be applied to the 
     229             :  * data traveling down the pipe.
     230             :  * 
     231             :  * To be able to connect multiple pipes to a single sink or source, a pipe also
     232             :  * has a "through" output and a "through" input, both are pipes, not sinks or 
     233             :  * sources. All data that comes from the source is sent to the "through" output
     234             :  * as well, and all input that comes in from the "through" input is sent to the
     235             :  * sink as well. The mapping or filter is not applied to the data going from/to
     236             :  * the "through" connections.
     237             :  * 
     238             :  * Merging data from multiple sources into a single sink can cause problems
     239             :  * because messages can be interleaved (e.g. RPN/NRPN messages). To circumvent
     240             :  * this issue, a source can request exclusive access to the pipe it's connected
     241             :  * to. This locks all other pipes that sink into the same sinks as the exclusive
     242             :  * source.  
     243             :  * Other sources must then query its pipe before sending, to make sure it's not
     244             :  * locked by another source that has exclusive access.
     245             :  * 
     246             :  * **Pipe model**
     247             :  * 
     248             :  * ~~~
     249             :  *                  ╭────────────────>  through out
     250             :  *                  │ ┌────────┐
     251             :  *     source  >━━━━┷━┥ filter ┝━┯━━━>  sink
     252             :  *                    └────────┘ │
     253             :  * through in  >─────────────────╯
     254             :  * ~~~
     255             :  * 
     256             :  * For example, if you have one source that should connect to two sinks, the
     257             :  * configuration is as follows:
     258             :  * 
     259             :  * ~~~
     260             :  *                      through out         × ┌────────┐
     261             :  *                  ╭────────────────> >━━━━┷━┥ pipe 2 ┝━┯━━━>  sink 2
     262             :  *                  │ ┌────────┐              └────────┘ ×
     263             :  *     source  >━━━━┷━┥ pipe 1 ┝━┯━━━━━━━━━━━━━━━━━━━━━━━━━━━>  sink 1
     264             :  *                    └────────┘ ×
     265             :  * ~~~
     266             :  * 
     267             :  * Each connection between a source and a sink has its own pipe, and no two 
     268             :  * pipes are connected in series.
     269             :  */
     270             : class MIDI_Pipe : private MIDI_Sink, private MIDI_Source {
     271             :   public:
     272             :     /// Default constructor
     273         140 :     MIDI_Pipe() = default;
     274             : 
     275             :     /// Copy constructor (copying not allowed).
     276             :     MIDI_Pipe(const MIDI_Pipe &) = delete;
     277             :     /// Copy assignment (copying not allowed).
     278             :     MIDI_Pipe &operator=(const MIDI_Pipe &) = delete;
     279             : 
     280             :     /// Move constructor.
     281             :     /// @todo   Add move constructor.
     282             :     MIDI_Pipe(MIDI_Pipe &&) = delete;
     283             :     /// Move assignment.
     284             :     /// @todo   Add move assignment.
     285             :     MIDI_Pipe &operator=(MIDI_Pipe &&) = delete;
     286             : 
     287             :     /// Destructor.
     288             :     virtual ~MIDI_Pipe();
     289             : 
     290             :   private:
     291             :     /// Set the sink pointer to point to the given sink. Does not connect this
     292             :     /// pipe to the sink. Initiate the connection from the sink.
     293             :     void connectSink(MIDI_Sink *sink);
     294             :     /// Set the sink pointer to null. Does not disconnect this pipe from the
     295             :     /// sink. Initiate the disconnection from the sink.
     296             :     void disconnectSink();
     297             :     /// Set the source pointer to point to the given source. Does not connect
     298             :     /// this pipe to the source. Initiate the connection from the source.
     299             :     void connectSource(MIDI_Source *source);
     300             :     /// Set the source pointer to null. Does not disconnect this pipe from the
     301             :     /// source. Initiate the disconnection from the source.
     302             :     void disconnectSource();
     303             : 
     304             :   public:
     305             :     /// Check if this pipe is connected to a sink.
     306         961 :     bool hasSink() const { return sink != nullptr; }
     307             :     /// Check if this pipe is connected to a source.
     308         785 :     bool hasSource() const { return source != nullptr; }
     309             :     /// Check if this pipe has a "through" output that sends all incoming
     310             :     /// messages from the input (source) to another pipe.
     311         768 :     bool hasThroughOut() const { return throughOut != nullptr; }
     312             :     /// Check if this pipe has a "through" input that merges all messages from
     313             :     /// another pipe into the output (sink).
     314         604 :     bool hasThroughIn() const { return throughIn != nullptr; }
     315             : 
     316             :   protected:
     317             :     /// Accept a MIDI message from the source, forward it to the "through"
     318             :     /// output if necessary, map or filter the MIDI message if necessary,
     319             :     /// and send it to the sink.
     320         115 :     void pipeMIDI(ChannelMessage msg) {
     321             :         // Forward 
     322         115 :         if (hasThroughOut())
     323           5 :             throughOut->pipeMIDI(msg);
     324         115 :         mapForwardMIDI(msg);
     325         115 :     }
     326             :     /// @copydoc pipeMIDI
     327          12 :     void pipeMIDI(SysExMessage msg) {
     328          12 :         if (hasThroughOut())
     329           5 :             throughOut->pipeMIDI(msg);
     330          12 :         mapForwardMIDI(msg);
     331          12 :     }
     332             :     /// @copydoc pipeMIDI
     333          21 :     void pipeMIDI(RealTimeMessage msg) {
     334          21 :         if (hasThroughOut())
     335           6 :             throughOut->pipeMIDI(msg);
     336          21 :         mapForwardMIDI(msg);
     337          21 :     }
     338             : 
     339             :   protected:
     340             :     /// Send the given MIDI message to the sink of this pipe.
     341         115 :     void sourceMIDItoSink(ChannelMessage msg) {
     342         115 :         if (hasSink())
     343         115 :             sink->sinkMIDIfromPipe(msg);
     344         115 :     }
     345             : 
     346             :     /// @copydoc sourceMIDItoSink
     347          14 :     void sourceMIDItoSink(SysExMessage msg) {
     348          14 :         if (hasSink())
     349          14 :             sink->sinkMIDIfromPipe(msg);
     350          14 :     }
     351             : 
     352             :     /// @copydoc sourceMIDItoSink
     353          25 :     void sourceMIDItoSink(RealTimeMessage msg) {
     354          25 :         if (hasSink())
     355          25 :             sink->sinkMIDIfromPipe(msg);
     356          25 :     }
     357             : 
     358             :   private:
     359             :     /// Function that maps, edits or filters MIDI messages, and then forwards 
     360             :     /// them to the sink of the pipe.
     361         112 :     virtual void mapForwardMIDI(ChannelMessage msg) { 
     362             :         // Optionally edit the message before passing it on
     363         112 :         sourceMIDItoSink(msg); 
     364         112 :     }
     365             : 
     366             :     /// @copydoc    mapForwardMIDI
     367          12 :     virtual void mapForwardMIDI(SysExMessage msg) { 
     368             :         // Optionally edit the message before passing it on
     369          12 :         sourceMIDItoSink(msg); 
     370          12 :     }
     371             : 
     372             :     /// @copydoc    mapForwardMIDI
     373          21 :     virtual void mapForwardMIDI(RealTimeMessage msg) { 
     374             :         // Optionally edit the message before passing it on
     375          21 :         sourceMIDItoSink(msg); 
     376          21 :     }
     377             : 
     378             :   private:
     379           1 :     void sinkMIDIfromPipe(ChannelMessage msg) override {
     380             :         // Called when data from Through In arrives, forward it to the sink
     381           1 :         sourceMIDItoSink(msg);
     382           1 :     }
     383           2 :     void sinkMIDIfromPipe(SysExMessage msg) override {
     384             :         // Called when data from Through In arrives, forward it to the sink
     385           2 :         sourceMIDItoSink(msg);
     386           2 :     }
     387           4 :     void sinkMIDIfromPipe(RealTimeMessage msg) override {
     388             :         // Called when data from Through In arrives, forward it to the sink
     389           4 :         sourceMIDItoSink(msg);
     390           4 :     }
     391             : 
     392             :   private:
     393             :     /// Lock this pipe and all other pipes further downstream (following the
     394             :     /// path of the sink). Operates recursively until the end of the
     395             :     /// chain is reached.
     396           8 :     void lockDownstream(cn_t cn, bool lock) override {
     397           8 :         lockSelf(cn, lock);
     398           8 :         if (hasSink())
     399           8 :             sink->lockDownstream(cn, lock);
     400           8 :     }
     401             : 
     402             :     /// Lock this pipe and all other pipes further upstream (following the
     403             :     /// path of the "trough" input). Operates recursively until the end of the
     404             :     /// chain is reached.
     405          12 :     void lockUpstream(cn_t cn, bool lock) {
     406          12 :         lockSelf(cn, lock);
     407          12 :         if (hasThroughIn())
     408           2 :             throughIn->lockUpstream(cn, lock);
     409          12 :     }
     410             : 
     411             :     /// Lock this pipe, so sources cannot send messages through it.
     412          20 :     void lockSelf(cn_t cn, bool lock) { locks.set(cn, lock); }
     413             : 
     414             :   public:
     415             :     /// Disconnect this pipe from all other pipes, sources and sinks. If the
     416             :     /// "through" input and/or output were in use, they are reconnected to the
     417             :     /// original sink and/or source respectively.
     418             :     void disconnect();
     419             : 
     420             :     /// Get the sink this pipe eventually sinks to, following the chain
     421             :     /// recursively.
     422          93 :     MIDI_Sink *getFinalSink() override {
     423          93 :         return hasSink() ? sink->getFinalSink() : nullptr;
     424             :     }
     425             :     /// Get the original source that sources to this pipe, following the chain
     426             :     /// recursively.
     427          93 :     MIDI_Source *getInitialSource() override {
     428          93 :         return hasSource() ? source->getInitialSource() : nullptr;
     429             :     }
     430             : 
     431             :     /// Disconnect the given sink from this pipe. The sink can be connected
     432             :     /// directly, or via the "through" output.
     433             :     /// Returns true if the sink was found and disconnected, false if the given
     434             :     /// sink was not a direct or indirect sink of this pipe.
     435          11 :     bool disconnect(TrueMIDI_Sink &sink) {
     436          11 :         if (getFinalSink() == &sink) {
     437           5 :             disconnect();
     438           5 :             return true;
     439             :         }
     440           6 :         if (hasThroughOut()) {
     441           1 :             return throughOut->disconnect(sink);
     442             :         }
     443           5 :         return false;
     444          11 :     }
     445             : 
     446             :     /// Disconnect the given source from this pipe. The source can be connected
     447             :     /// directly, or via the "through" input.
     448             :     /// Returns true if the source was found and disconnected, false if the
     449             :     /// given source was not a direct or indirect source to this pipe.
     450          11 :     bool disconnect(TrueMIDI_Source &source) {
     451          11 :         if (getInitialSource() == &source) {
     452           5 :             disconnect();
     453           5 :             return true;
     454             :         }
     455           6 :         if (hasThroughIn()) {
     456           1 :             return throughIn->disconnect(source);
     457             :         }
     458           5 :         return false;
     459          11 :     }
     460             : 
     461             : #ifdef ARDUINO
     462             :   protected:
     463             : #endif
     464           8 :     MIDI_Pipe *getThroughOut() { return throughOut; }
     465           8 :     MIDI_Pipe *getThroughIn() { return throughIn; }
     466           4 :     MIDI_Source *getSource() { return source; }
     467           4 :     MIDI_Sink *getSink() { return sink; }
     468             : 
     469             :   public:
     470             :     /// @copydoc    MIDI_Source::exclusive
     471             :     void exclusive(cn_t cn, bool exclusive = true);
     472             : 
     473             :     /// Check if this pipe is locked for a given cable number.
     474          53 :     bool isLocked(cn_t cn) const { return locks.get(cn); }
     475             : 
     476             :     /** 
     477             :      * @brief   Check if any of the sinks or outputs of this chain of pipes are
     478             :      *          locked for the given cable number.
     479             :      * @param   cn
     480             :      *          The cable number to check.
     481             :      */
     482          53 :     bool isAvailableForWrite(cn_t cn) const {
     483          97 :         return !isLocked(cn) &&
     484          44 :                (!hasThroughOut() || throughOut->isAvailableForWrite(cn));
     485             :     }
     486             : 
     487             :   private:
     488         140 :     MIDI_Sink *sink = nullptr;
     489         140 :     MIDI_Source *source = nullptr;
     490         140 :     MIDI_Pipe *&throughOut = MIDI_Source::sinkPipe;
     491         140 :     MIDI_Pipe *&throughIn = MIDI_Sink::sourcePipe;
     492             :     AH::BitArray<16> locks;
     493             : 
     494             :     friend class MIDI_Sink;
     495             :     friend class MIDI_Source;
     496             : };
     497             : 
     498             : // :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: //
     499             : 
     500             : /// A struct that is both a TrueMIDI_Sink and a TrueMIDI_Source.
     501         198 : struct TrueMIDI_SinkSource : TrueMIDI_Sink, TrueMIDI_Source {};
     502             : 
     503             : /// A bidirectional pipe consists of two unidirectional pipes.
     504             : using BidirectionalMIDI_Pipe = std::pair<MIDI_Pipe, MIDI_Pipe>;
     505             : 
     506             : /// Connect a source to a pipe (`source >> pipe`).
     507         125 : inline MIDI_Pipe &operator>>(TrueMIDI_Source &source, MIDI_Pipe &pipe) {
     508         125 :     source.connectSinkPipe(&pipe);
     509         125 :     return pipe;
     510             : }
     511             : 
     512             : /// Connect a pipe to a sink (`pipe >> sink`).
     513         127 : inline TrueMIDI_Sink &operator>>(MIDI_Pipe &pipe, TrueMIDI_Sink &sink) {
     514         127 :     sink.connectSourcePipe(&pipe);
     515         127 :     return sink;
     516             : }
     517             : 
     518             : /// Connect a sink to a pipe (`sink << pipe`).
     519          68 : inline MIDI_Pipe &operator<<(TrueMIDI_Sink &sink, MIDI_Pipe &pipe) {
     520          68 :     sink.connectSourcePipe(&pipe);
     521          68 :     return pipe;
     522             : }
     523             : 
     524             : /// Connect a pipe to a source (`pipe << source`).
     525          68 : inline TrueMIDI_Source &operator<<(MIDI_Pipe &pipe, TrueMIDI_Source &source) {
     526          68 :     source.connectSinkPipe(&pipe);
     527          68 :     return source;
     528             : }
     529             : 
     530             : /// Connect a pipe to a sink+source (`pipe | source+sink`).
     531           5 : inline TrueMIDI_SinkSource &operator|(BidirectionalMIDI_Pipe &pipe,
     532             :                                       TrueMIDI_SinkSource &sinksource) {
     533           5 :     sinksource.connectSinkPipe(&pipe.first);
     534           5 :     sinksource.connectSourcePipe(&pipe.second);
     535           5 :     return sinksource;
     536             : }
     537             : 
     538             : /// Connect a sink+source to a pipe (`source+sink | pipe`).
     539           5 : inline BidirectionalMIDI_Pipe &operator|(TrueMIDI_SinkSource &sinksource,
     540             :                                          BidirectionalMIDI_Pipe &pipe) {
     541           5 :     sinksource.connectSinkPipe(&pipe.second);
     542           5 :     sinksource.connectSourcePipe(&pipe.first);
     543           5 :     return pipe;
     544             : }
     545             : 
     546             : // :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: //
     547             : 
     548             : /**
     549             :  * @brief   Class that produces multiple MIDI_Pipe%s.
     550             :  * 
     551             :  * @tparam  N 
     552             :  *          The maximum number of pipes it can produce.
     553             :  * @tparam  Pipe 
     554             :  *          The type of pipes to produce.
     555             :  */
     556             : template <size_t N, class Pipe = MIDI_Pipe>
     557         116 : struct MIDI_PipeFactory {
     558             :     Pipe pipes[N];
     559          12 :     size_t index = 0;
     560             : 
     561          58 :     Pipe &getNext() {
     562          58 :         if (index >= N)
     563           1 :             FATAL_ERROR(F("Not enough pipes available"), 0x2459);
     564          57 :         return pipes[index++];
     565           1 :     }
     566          10 :     Pipe &operator[](size_t i) { return pipes[i]; }
     567             :     const Pipe &operator[](size_t i) const { return pipes[i]; }
     568             : };
     569             : 
     570             : template <size_t N>
     571             : using BidirectionalMIDI_PipeFactory =
     572             :     MIDI_PipeFactory<N, BidirectionalMIDI_Pipe>;
     573             : 
     574             : template <size_t N, class Pipe>
     575          30 : inline MIDI_Pipe &operator>>(TrueMIDI_Source &source,
     576             :                              MIDI_PipeFactory<N, Pipe> &pipe_fact) {
     577          30 :     return source >> pipe_fact.getNext();
     578             : }
     579             : 
     580             : template <size_t N, class Pipe>
     581           3 : inline TrueMIDI_Sink &operator>>(MIDI_PipeFactory<N, Pipe> &pipe_fact,
     582             :                                  TrueMIDI_Sink &sink) {
     583           3 :     return pipe_fact.getNext() >> sink;
     584             : }
     585             : 
     586             : template <size_t N, class Pipe>
     587          25 : inline MIDI_Pipe &operator<<(TrueMIDI_Sink &sink,
     588             :                              MIDI_PipeFactory<N, Pipe> &pipe_fact) {
     589          25 :     return sink << pipe_fact.getNext();
     590             : }
     591             : 
     592             : template <size_t N, class Pipe>
     593             : inline TrueMIDI_Source &operator<<(MIDI_PipeFactory<N, Pipe> &pipe_fact,
     594             :                                    TrueMIDI_Source &source) {
     595             :     return pipe_fact.getNext() << source;
     596             : }
     597             : 
     598             : template <size_t N>
     599             : inline TrueMIDI_SinkSource &
     600             : operator|(BidirectionalMIDI_PipeFactory<N> &pipe_fact,
     601             :           TrueMIDI_SinkSource &sinksource) {
     602             :     return pipe_fact.getNext() | sinksource;
     603             : }
     604             : 
     605             : template <size_t N>
     606             : inline BidirectionalMIDI_Pipe &
     607             : operator|(TrueMIDI_SinkSource &sinksource,
     608             :           BidirectionalMIDI_PipeFactory<N> &pipe_fact) {
     609             :     return sinksource | pipe_fact.getNext();
     610             : }
     611             : 
     612             : /// @}
     613             : 
     614             : END_CS_NAMESPACE
     615             : 
     616             : AH_DIAGNOSTIC_POP()

Generated by: LCOV version 1.14-6-g40580cd