LCOV - code coverage report
Current view: top level - src/MIDI_Interfaces - MIDI_Pipes.hpp (source / functions) Hit Total Coverage
Test: ffed98f648fe78e7aa7bdd228474317d40dadbec Lines: 96 100 96.0 %
Date: 2022-05-28 15:22:59 Functions: 57 61 93.4 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : #pragma once
       2             : 
       3             : #include <AH/Containers/BitArray.hpp>
       4             : #include <AH/STL/cstdint>
       5             : #include <AH/STL/limits>
       6             : #include <AH/STL/utility>
       7             : #include <AH/Settings/Warnings.hpp>
       8             : #include <MIDI_Parsers/MIDI_MessageTypes.hpp>
       9             : #include <Settings/NamespaceSettings.hpp>
      10             : 
      11             : AH_DIAGNOSTIC_WERROR()
      12             : 
      13             : BEGIN_CS_NAMESPACE
      14             : 
      15             : struct MIDIStaller;
      16             : MIDIStaller *const eternal_stall =
      17             :     reinterpret_cast<MIDIStaller *>(std::numeric_limits<std::uintptr_t>::max());
      18             : 
      19             : /**
      20             :  * @addtogroup MIDI_Routing MIDI Routing
      21             :  * @brief   Operators and utilities for MIDI routing.
      22             :  * 
      23             :  * Two or more MIDI interfaces can be connected
      24             :  * using @ref MIDI_Pipe "MIDI Pipes". The simplest pipe just carries messages 
      25             :  * from the input interface to the output interface, but you can write rules for 
      26             :  * filtering out certain messages, changing the channel of some messages, etc.
      27             :  * 
      28             :  * Allows you to use syntax like:
      29             :  * 
      30             :  * ~~~cpp
      31             :  * HardwareSerialMIDI_Interface 
      32             :  *     midiA = Serial1, midiB = Serial2, midiC = Serial3;
      33             :  * MIDI_PipeFactory<3> pipes; // Factory that can produce 3 pipes
      34             :  * 
      35             :  * midiA >> pipes >> midiB;
      36             :  * midiC >> pipes >> midiB;
      37             :  * midiC << pipes << midiB;
      38             :  * ~~~
      39             :  * 
      40             :  * Or for bidirectional connections:
      41             :  * 
      42             :  * ~~~cpp
      43             :  * HardwareSerialMIDI_Interface 
      44             :  *     midiA = Serial1, midiB = Serial2, midiC = Serial3;
      45             :  * BidirectionalMIDI_PipeFactory<2> pipes; // Factory that can produce 2 pipes
      46             :  * 
      47             :  * midiA | pipes | midiB;
      48             :  * midiA | pipes | midiC;
      49             :  * ~~~
      50             :  * 
      51             :  * Have a look at the following examples on MIDI routing:
      52             :  * 
      53             :  *  - @ref MIDI_Pipes-Routing.ino
      54             :  *  - @ref Dual-MIDI-Interface.ino
      55             :  * 
      56             :  * If you're interested how the pipes work, see the documentation for 
      57             :  * @ref MIDI_Pipe.
      58             :  * 
      59             :  * @{ 
      60             :  */
      61             : 
      62             : class MIDI_Pipe;
      63             : class MIDI_Source;
      64             : class MIDI_Sink;
      65             : /// A MIDI_Sink that is not a MIDI_Pipe.
      66             : using TrueMIDI_Sink = MIDI_Sink;
      67             : /// A MIDI_Source that is not a MIDI_Pipe.
      68             : using TrueMIDI_Source = MIDI_Source;
      69             : 
      70             : /// Receives MIDI messages from a MIDI pipe.
      71             : class MIDI_Sink {
      72             :   public:
      73             :     /// Default constructor.
      74        2192 :     MIDI_Sink() = default;
      75             : 
      76             :     /// Copy constructor (copying not allowed).
      77             :     MIDI_Sink(const MIDI_Sink &) = delete;
      78             :     /// Copy assignment (copying not allowed).
      79             :     MIDI_Sink &operator=(const MIDI_Sink &) = delete;
      80             : 
      81             :     /// Move constructor.
      82             :     MIDI_Sink(MIDI_Sink &&other);
      83             :     /// Move assignment.
      84             :     MIDI_Sink &operator=(MIDI_Sink &&other);
      85             : 
      86             :     /// Destructor.
      87             :     virtual ~MIDI_Sink();
      88             : 
      89             :     /// @name Sending data over a MIDI Pipe
      90             :     /// @{
      91             : 
      92             :     /// Accept an incoming MIDI Channel message.
      93             :     virtual void sinkMIDIfromPipe(ChannelMessage) = 0;
      94             :     /// Accept an incoming MIDI System Exclusive message.
      95             :     virtual void sinkMIDIfromPipe(SysExMessage) = 0;
      96             :     /// Accept an incoming MIDI System Common message.
      97             :     virtual void sinkMIDIfromPipe(SysCommonMessage) = 0;
      98             :     /// Accept an incoming MIDI Real-Time message.
      99             :     virtual void sinkMIDIfromPipe(RealTimeMessage) = 0;
     100             : 
     101             :     /// @}
     102             : 
     103             :     /// @name Connecting and disconnecting MIDI Pipes
     104             :     /// @{
     105             : 
     106             :     /// Fully connect a source pipe to this sink.
     107             :     void connectSourcePipe(MIDI_Pipe *source);
     108             :     /// Disconnect all source pipes that sink to this sink (recursively).
     109             :     void disconnectSourcePipes();
     110             :     /// Disconnect the given source from this sink. Leaves other sources
     111             :     /// connected.
     112             :     /// Returns true if the source was found and disconnected, false if the
     113             :     /// given source was not a direct or indirect source to this sink.
     114             :     bool disconnect(TrueMIDI_Source &source);
     115             :     bool disconnect(MIDI_Pipe &) = delete;
     116             :     /// Check if this sink is connected to a source pipe.
     117         114 :     bool hasSourcePipe() const { return sourcePipe != nullptr; }
     118             :     /// Get a pointer to the pipe this sink is connected to, or `nullptr` if
     119             :     /// not connected.
     120          64 :     MIDI_Pipe *getSourcePipe() { return sourcePipe; }
     121             : 
     122             :     /// @}
     123             : 
     124             :   private:
     125             :     /// Base case for recursive stall function.
     126             :     /// @see    MIDI_Pipe::stallDownstream
     127          36 :     virtual void stallDownstream(MIDIStaller *, MIDI_Source *) {}
     128             :     /// Base case for recursive un-stall function.
     129             :     /// @see    MIDI_Pipe::unstallDownstream
     130          22 :     virtual void unstallDownstream(MIDIStaller *, MIDI_Source *) {}
     131             :     /// Base case for recursive function.
     132             :     /// @see    MIDI_Pipe::getFinalSink
     133          67 :     virtual MIDI_Sink *getFinalSink() { return this; }
     134             :     /// Disconnect only the first pipe connected to this sink. Leaves the
     135             :     /// other pipes connected to the original pipe, which doesn't have a sink
     136             :     /// anymore when this function finishes.
     137             :     /// Used to disconnect a MIDI_Pipe while preserving the connections of its
     138             :     /// “through” inputs.
     139             :     void disconnectSourcePipesShallow();
     140             : 
     141             :   protected:
     142             :     MIDI_Pipe *sourcePipe = nullptr;
     143             : 
     144             :     friend class MIDI_Pipe;
     145             : 
     146             :   public:
     147             :     static void swap(MIDI_Sink &a, MIDI_Sink &b);
     148           1 :     friend void swap(MIDI_Sink &a, MIDI_Sink &b) { swap(a, b); }
     149             : };
     150             : 
     151             : // :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: //
     152             : 
     153             : /// Class that can send MIDI messages to a MIDI pipe.
     154             : class MIDI_Source {
     155             :   public:
     156             :     /// Default constructor.
     157        2101 :     MIDI_Source() = default;
     158             : 
     159             :     /// Copy constructor (copying not allowed).
     160             :     MIDI_Source(const MIDI_Source &) = delete;
     161             :     /// Copy assignment (copying not allowed).
     162             :     MIDI_Source &operator=(const MIDI_Source &) = delete;
     163             : 
     164             :     /// Move constructor.
     165             :     MIDI_Source(MIDI_Source &&other);
     166             :     /// Move assignment.
     167             :     MIDI_Source &operator=(MIDI_Source &&other);
     168             : 
     169             :     /// Destructor.
     170             :     virtual ~MIDI_Source();
     171             : 
     172             :     /// @name Sending data over a MIDI Pipe
     173             :     /// @{
     174             : 
     175             :     /// Send a MIDI Channel Message down the pipe.
     176             :     void sourceMIDItoPipe(ChannelMessage);
     177             :     /// Send a MIDI System Exclusive message down the pipe.
     178             :     void sourceMIDItoPipe(SysExMessage);
     179             :     /// Send a MIDI System Common message down the pipe.
     180             :     void sourceMIDItoPipe(SysCommonMessage);
     181             :     /// Send a MIDI Real-Time message down the pipe.
     182             :     void sourceMIDItoPipe(RealTimeMessage);
     183             : 
     184             :     /// @}
     185             : 
     186             :     /// @name Stalling the sink pipes and exclusive access
     187             :     /// @{
     188             : 
     189             :     /// Stall this MIDI source.
     190             :     /// This means that this becomes the only source that can sink to the sinks
     191             :     /// connected to this source. Other sources have to wait until this source
     192             :     /// un-stalls the pipe before they can send again.
     193             :     /// @param  cause
     194             :     ///         Pointer to the reason for this stall, can be called back to
     195             :     ///         un-stall the pipes.
     196             :     void stall(MIDIStaller *cause = eternal_stall);
     197             :     /// Un-stall the pipes connected to this source, so other sources
     198             :     /// are allowed to send again.
     199             :     /// @param  cause
     200             :     ///         Pointer to the reason for the stall (this has to be the same one
     201             :     ///         that was used to stall).
     202             :     void unstall(MIDIStaller *cause = eternal_stall);
     203             :     /// Check if this source can write to the sinks it connects to.
     204             :     bool isStalled() const;
     205             :     /// Get a pointer to whatever is causing this MIDI source to be stalled.
     206             :     /// There could be multiple stallers, this function just returns one.
     207             :     MIDIStaller *getStaller() const;
     208             :     /// Get the name of whatever is causing this MIDI source to be stalled.
     209             :     /// There could be multiple stallers, this function just returns one.
     210             :     const char *getStallerName() const;
     211             :     /// Give the code that is stalling the MIDI sink pipes the opportunity to do
     212             :     /// its job and un-stall the pipes.
     213             :     void handleStallers() const;
     214             : 
     215             :     /// @}
     216             : 
     217             :     /// @name Connecting and disconnecting MIDI Pipes
     218             :     /// @{
     219             : 
     220             :     /// Fully connect a sink pipe to this source.
     221             :     void connectSinkPipe(MIDI_Pipe *sink);
     222             :     /// Disconnect all sink pipes that this source sinks to (recursively).
     223             :     void disconnectSinkPipes();
     224             :     /// Disconnect the given sink from this source. Leaves other sinks
     225             :     /// connected.
     226             :     /// Returns true if the sink was found and disconnected, false if the
     227             :     /// given sink was not a direct or indirect sink of this source.
     228             :     bool disconnect(TrueMIDI_Sink &sink);
     229             :     bool disconnect(MIDI_Pipe &) = delete;
     230             :     /// Check if this source is connected to a sink pipe.
     231         391 :     bool hasSinkPipe() const { return sinkPipe != nullptr; }
     232             :     /// Get a pointer to the pipe this source is connected to, or `nullptr` if
     233             :     /// not connected.
     234          64 :     MIDI_Pipe *getSinkPipe() { return sinkPipe; }
     235             : 
     236             :     /// @}
     237             : 
     238             :   private:
     239             :     /// Base case for recursive stall function.
     240             :     /// @see    MIDI_Pipe::stallUpstream
     241          41 :     virtual void stallUpstream(MIDIStaller *, MIDI_Sink *) {}
     242             :     /// Base case for recursive un-stall function.
     243             :     /// @see    MIDI_Pipe::unstallUpstream
     244          23 :     virtual void unstallUpstream(MIDIStaller *, MIDI_Sink *) {}
     245             :     /// Base case for recursive function.
     246             :     /// @see    MIDI_Pipe::getInitialSource
     247          67 :     virtual MIDI_Source *getInitialSource() { return this; }
     248             :     /// Disconnect only the first pipe connected to this source. Leaves the
     249             :     /// other pipes connected to the original pipe, which doesn't have a source
     250             :     /// anymore when this function finishes.
     251             :     /// Used to disconnect a @ref MIDI_Pipe while preserving the connections of
     252             :     /// its “through” outputs.
     253             :     void disconnectSinkPipesShallow();
     254             : 
     255             :   protected:
     256             :     MIDI_Pipe *sinkPipe = nullptr;
     257             : 
     258             :     friend class MIDI_Pipe;
     259             : 
     260             :   public:
     261             :     static void swap(MIDI_Source &a, MIDI_Source &b);
     262           1 :     friend void swap(MIDI_Source &a, MIDI_Source &b) { swap(a, b); }
     263             : };
     264             : 
     265             : // :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: //
     266             : 
     267             : /** 
     268             :  * @brief   Class that routes MIDI messages from a MIDI_Source to a MIDI_Sink.
     269             :  * 
     270             :  * A pipe has at least two, at most four connections. In the simplest case, a 
     271             :  * pipe just has a source and a sink. The source sends MIDI messages down the 
     272             :  * pipe, and the pipe sends them to the sink. A mapping or filter can be applied
     273             :  * to the messages traveling down the pipe.
     274             :  * 
     275             :  * To be able to connect multiple pipes to a single sink or source, a pipe also
     276             :  * has a “through” output and a “through” input. Both are pipes, not sinks or 
     277             :  * sources. All data that comes from the source is sent to the “through” output
     278             :  * as well, and all input that comes in from the “through” input is sent to the
     279             :  * sink as well. The mapping or filter is not applied to the data going from/to
     280             :  * the “through” connections.
     281             :  * 
     282             :  * Merging data from multiple sources into a single sink can cause problems
     283             :  * because messages can be interleaved (e.g. RPN/NRPN or chunked system 
     284             :  * exclusive messages). To circumvent this issue, a source can request exclusive
     285             :  * access to the pipe it's connected to. This stalls all other pipes that sink
     286             :  * into the same sinks as the exclusive source.  
     287             :  * When other sources try to send to a stalled pipe, this will automatically 
     288             :  * call back the source that originally stalled the pipes, so it can finish its
     289             :  * message, and then un-stall the pipes so the other pipe can send its data.
     290             :  * 
     291             :  * **Pipe model**
     292             :  * 
     293             :  * ~~~
     294             :  *                  ╭────────────────>  through out
     295             :  *                  │ ┌────────┐
     296             :  *     source  >━━━━┷━┥ filter ┝━┯━━━>  sink
     297             :  *                    └────────┘ │
     298             :  * through in  >─────────────────╯
     299             :  * ~~~
     300             :  * 
     301             :  * For example, if you have one source that should connect to two sinks, the
     302             :  * configuration is as follows:
     303             :  * 
     304             :  * ~~~
     305             :  *                      through out         × ┌────────┐
     306             :  *                  ╭────────────────> >━━━━┷━┥ pipe 2 ┝━┯━━━>  sink 2
     307             :  *                  │ ┌────────┐              └────────┘ ×
     308             :  *     source  >━━━━┷━┥ pipe 1 ┝━┯━━━━━━━━━━━━━━━━━━━━━━━━━━━>  sink 1
     309             :  *                    └────────┘ ×
     310             :  * ~~~
     311             :  * 
     312             :  * If you have two sources that should connect to the same sink, a possible
     313             :  * configuration is:
     314             :  * 
     315             :  * ~~~
     316             :  *                                          × ┌────────┐
     317             :  *   source 1  >━━━━━━━━━━━━━━━━━━━━━━━━━━━━┷━┥ pipe 1 ┝━┯━━━>  sink
     318             :  *                  × ┌────────┐              └────────┘ │
     319             :  *   source 2  >━━━━┷━┥ pipe 2 ┝━┯━━━> >─────────────────╯
     320             :  *                    └────────┘ ×
     321             :  * ~~~
     322             :  * 
     323             :  * Each connection between a source and a sink has its own pipe, and no two 
     324             :  * pipes are connected in series (only through the “through“ inputs/outputs).
     325             :  */
     326             : class MIDI_Pipe : private MIDI_Sink, private MIDI_Source {
     327             :   public:
     328             :     /// Default constructor
     329        1380 :     MIDI_Pipe() = default;
     330             : 
     331             :     /// Copy constructor (copying not allowed).
     332             :     MIDI_Pipe(const MIDI_Pipe &) = delete;
     333             :     /// Copy assignment (copying not allowed).
     334             :     MIDI_Pipe &operator=(const MIDI_Pipe &) = delete;
     335             : 
     336             :     /// Move constructor.
     337             :     /// @todo   Add move constructor.
     338             :     MIDI_Pipe(MIDI_Pipe &&) = delete;
     339             :     /// Move assignment.
     340             :     /// @todo   Add move assignment.
     341             :     MIDI_Pipe &operator=(MIDI_Pipe &&) = delete;
     342             : 
     343             :     /// Destructor.
     344             :     virtual ~MIDI_Pipe();
     345             : 
     346             :   private:
     347             :     /// @name Mapping and filtering
     348             :     /// @{
     349             : 
     350             :     /// Function that maps, edits or filters MIDI messages, and then forwards
     351             :     /// them to the sink of the pipe.
     352             :     /// The MIDI_Pipe base class just forwards the messages to the sink, but
     353             :     /// you can override this method to create pipes that filter out some
     354             :     /// messages, transposes notes, changes the channel, etc.
     355         137 :     virtual void mapForwardMIDI(ChannelMessage msg) { sourceMIDItoSink(msg); }
     356             :     /// @copydoc    mapForwardMIDI
     357          15 :     virtual void mapForwardMIDI(SysExMessage msg) { sourceMIDItoSink(msg); }
     358             :     /// @copydoc    mapForwardMIDI
     359           0 :     virtual void mapForwardMIDI(SysCommonMessage msg) { sourceMIDItoSink(msg); }
     360             :     /// @copydoc    mapForwardMIDI
     361          21 :     virtual void mapForwardMIDI(RealTimeMessage msg) { sourceMIDItoSink(msg); }
     362             : 
     363             :     /// @}
     364             : 
     365             :   public:
     366             :     /// @name Dealing with stalled pipes
     367             :     /// @{
     368             : 
     369             :     /// Check if this pipe is stalled.
     370         241 :     bool isStalled() const { return sink_staller || through_staller; }
     371             :     /// Get the staller (cause of the stall) that causes the sink of this pipe
     372             :     /// to be stalled.
     373             :     /// This pipe could sink to more than one stalled sink, this function just
     374             :     /// returns one of the causes.
     375          72 :     MIDIStaller *getSinkStaller() const { return sink_staller; }
     376             :     /// Get the name of the staller (cause of the stall) that causes the sink
     377             :     /// of this pipe to be stalled.
     378             :     const char *getSinkStallerName() const;
     379             :     /// Get the staller (cause of the stall) that causes the “through” output
     380             :     /// of this pipe to be stalled.
     381             :     /// The “through” output of this pipe could sink to more than one stalled
     382             :     /// sink, this function just returns one of the causes.
     383          64 :     MIDIStaller *getThroughStaller() const { return through_staller; }
     384             :     /// Get the name of the staller (cause of the stall) that causes the
     385             :     /// “through” output of this pipe to be stalled.
     386             :     const char *getThroughStallerName() const;
     387             :     /// Get any staller: returns @ref getSinkStaller() if it's not null,
     388             :     /// @ref getThroughStaller() otherwise.
     389             :     MIDIStaller *getStaller() const;
     390             :     /// Get the name of any staller.
     391             :     /// @see getStaller
     392             :     const char *getStallerName() const;
     393             :     /// Returns true if this pipe is either not stalled at all, or if the pipe
     394             :     /// is stalled by the given staller (cause).
     395             :     /// @see getSinkStaller
     396         115 :     bool sinkIsUnstalledOrStalledBy(MIDIStaller *cause) {
     397         115 :         return sink_staller == nullptr || sink_staller == cause;
     398             :     }
     399             :     /// Returns true if this pipe is either not stalled at all, or if the pipe
     400             :     /// is stalled by the given staller (cause).
     401             :     /// @see getThroughStaller
     402             :     bool throughIsUnstalledOrStalledBy(MIDIStaller *cause) {
     403             :         return through_staller == nullptr || through_staller == cause;
     404             :     }
     405             : 
     406             :     /// Give the code that is stalling the MIDI pipe the opportunity to do
     407             :     /// its job and unstall the pipe.
     408             :     void handleStallers() const;
     409             : 
     410             :     /// @}
     411             : 
     412             :   public:
     413             :     /// @name Check connections
     414             :     /// @{
     415             : 
     416             :     /// Check if this pipe is connected to a sink.
     417        3711 :     bool hasSink() const { return sink != nullptr; }
     418             :     /// Check if this pipe is connected to a source.
     419        3573 :     bool hasSource() const { return source != nullptr; }
     420             :     /// Check if this pipe has a “through” output that sends all incoming
     421             :     /// messages from the input (source) to another pipe.
     422        2253 :     bool hasThroughOut() const { return throughOut != nullptr; }
     423             :     /// Check if this pipe has a “through” input that merges all messages from
     424             :     /// another pipe into the output (sink).
     425        2096 :     bool hasThroughIn() const { return throughIn != nullptr; }
     426             : 
     427             :     /// @}
     428             : 
     429             :   public:
     430             :     /// @name MIDI Pipe connection management and inspection
     431             :     /// @{
     432             : 
     433             :     /// Disconnect this pipe from all other pipes, sources and sinks. If the
     434             :     /// “through” input and/or output were in use, they are reconnected to their
     435             :     /// original sink and/or source respectively, their behavior doesn't change.
     436             :     void disconnect();
     437             :     /// Disconnect the given sink from this pipe. The sink can be connected
     438             :     /// directly, or via the “through” output.
     439             :     /// Returns true if the sink was found and disconnected, false if the given
     440             :     /// sink was not a direct or indirect sink of this pipe.
     441          11 :     bool disconnect(TrueMIDI_Sink &sink) {
     442          11 :         if (getFinalSink() == &sink) {
     443           5 :             disconnect();
     444           5 :             return true;
     445             :         }
     446           6 :         if (hasThroughOut()) {
     447           1 :             return throughOut->disconnect(sink);
     448             :         }
     449           5 :         return false;
     450             :     }
     451             :     /// Disconnect the given source from this pipe. The source can be connected
     452             :     /// directly, or via the “through” input.
     453             :     /// Returns true if the source was found and disconnected, false if the
     454             :     /// given source was not a direct or indirect source to this pipe.
     455          11 :     bool disconnect(TrueMIDI_Source &source) {
     456          11 :         if (getInitialSource() == &source) {
     457           5 :             disconnect();
     458           5 :             return true;
     459             :         }
     460           6 :         if (hasThroughIn()) {
     461           1 :             return throughIn->disconnect(source);
     462             :         }
     463           5 :         return false;
     464             :     }
     465             :     bool disconnect(MIDI_Pipe &) = delete;
     466             : 
     467             :     /// Get the immediate source of this pipe.
     468           4 :     MIDI_Source *getSource() { return source; }
     469             :     /// Get the immediate sink of this pipe.
     470           4 :     MIDI_Sink *getSink() { return sink; }
     471             :     /// Get the pipe connected to the “through” output of this pipe.
     472           8 :     MIDI_Pipe *getThroughOut() { return throughOut; }
     473             :     /// Get the pipe connected to the “through” input of this pipe.
     474           8 :     MIDI_Pipe *getThroughIn() { return throughIn; }
     475             : 
     476             :     /// Get the sink this pipe eventually sinks to, following the chain
     477             :     /// recursively.
     478          97 :     MIDI_Sink *getFinalSink() override {
     479          97 :         return hasSink() ? sink->getFinalSink() : nullptr;
     480             :     }
     481             :     /// Get the original source that sources to this pipe, following the chain
     482             :     /// recursively.
     483          97 :     MIDI_Source *getInitialSource() override {
     484          97 :         return hasSource() ? source->getInitialSource() : nullptr;
     485             :     }
     486             : 
     487             :     /// @}
     488             : 
     489             :   private:
     490             :     /// @name Private functions to connect and disconnect sinks and sources
     491             :     /// @{
     492             : 
     493             :     /// Set the sink pointer to point to the given sink. Does not connect this
     494             :     /// pipe to the sink. Initiate the connection from the sink.
     495             :     void connectSink(MIDI_Sink *sink);
     496             :     /// Set the sink pointer to null. Does not disconnect this pipe from the
     497             :     /// sink. Initiate the disconnection from the sink.
     498             :     void disconnectSink();
     499             :     /// Set the source pointer to point to the given source. Does not connect
     500             :     /// this pipe to the source. Initiate the connection from the source.
     501             :     void connectSource(MIDI_Source *source);
     502             :     /// Set the source pointer to null. Does not disconnect this pipe from the
     503             :     /// source. Initiate the disconnection from the source.
     504             :     void disconnectSource();
     505             : 
     506             :     /// @}
     507             : 
     508             :   protected:
     509             :     /// Send the given MIDI message to the sink of this pipe.
     510             :     /// Useful when overriding @ref mapForwardMIDI.
     511             :     template <class Message>
     512         183 :     void sourceMIDItoSink(Message msg) {
     513         183 :         if (hasSink())
     514         183 :             sink->sinkMIDIfromPipe(msg);
     515         183 :     }
     516             : 
     517             :   protected:
     518             :     /// Accept a MIDI message from the source, forward it to the “through”
     519             :     /// output if necessary, map or filter the MIDI message if necessary,
     520             :     /// and send it to the sink. This function transfers messages from a
     521             :     /// @ref MIDI_Source to its @ref MIDI_Pipe.
     522             :     template <class Message>
     523         176 :     void acceptMIDIfromSource(Message msg) {
     524         176 :         if (hasThroughOut())
     525          16 :             throughOut->acceptMIDIfromSource(msg);
     526         176 :         mapForwardMIDI(msg);
     527         176 :     }
     528             : 
     529             :   private:
     530             :     /// Called when data arrives from an upstream pipe connected to our
     531             :     /// “through” input, this function forwards it to the sink.
     532           2 :     void sinkMIDIfromPipe(ChannelMessage msg) override {
     533           2 :         sourceMIDItoSink(msg);
     534           2 :     }
     535             :     /// @copydoc sinkMIDIfromPipe
     536           2 :     void sinkMIDIfromPipe(SysExMessage msg) override { sourceMIDItoSink(msg); }
     537             :     /// @copydoc sinkMIDIfromPipe
     538           0 :     void sinkMIDIfromPipe(SysCommonMessage msg) override {
     539           0 :         sourceMIDItoSink(msg);
     540           0 :     }
     541             :     /// @copydoc sinkMIDIfromPipe
     542           4 :     void sinkMIDIfromPipe(RealTimeMessage msg) override {
     543           4 :         sourceMIDItoSink(msg);
     544           4 :     }
     545             : 
     546             :   private:
     547             :     /// @name Private functions to stall and un-stall pipes
     548             :     /// @{
     549             : 
     550             :     /// Stall this pipe and all other pipes further downstream (following the
     551             :     /// path of the sink and the “through” output). Operates recursively until
     552             :     /// the end of the chain is reached, and then continues upstream (using
     553             :     /// @ref stallUpstream) to stall all pipes that connect to sources that
     554             :     /// sink to the same sink as this pipe and its “through” output.
     555             :     /// In short: stall all pipes that sink to the same sink as this pipe, and
     556             :     /// then stall all pipes that source to this first set of pipes.
     557             :     void stallDownstream(MIDIStaller *cause, MIDI_Source *stallsrc) override;
     558             :     /// Undoes the stalling by @ref stallDownstream.
     559             :     void unstallDownstream(MIDIStaller *cause, MIDI_Source *stallsrc) override;
     560             : 
     561             :     /// Stall this pipe and all other pipes further upstream (following the
     562             :     /// path of the "trough" input). Operates recursively until the end of the
     563             :     /// chain is reached. This function is called in the second stage of
     564             :     /// @ref stallDownstream.
     565             :     void stallUpstream(MIDIStaller *cause, MIDI_Sink *stallsrc) override;
     566             :     /// Undoes the stalling by @ref stallUpstream.
     567             :     void unstallUpstream(MIDIStaller *cause, MIDI_Sink *stallsrc) override;
     568             : 
     569             :     /// @}
     570             : 
     571             :   private:
     572             :     MIDI_Sink *sink = nullptr;
     573             :     MIDI_Source *source = nullptr;
     574             :     MIDI_Pipe *&throughOut = MIDI_Source::sinkPipe;
     575             :     MIDI_Pipe *&throughIn = MIDI_Sink::sourcePipe;
     576             :     MIDIStaller *sink_staller = nullptr;
     577             :     MIDIStaller *through_staller = nullptr;
     578             : 
     579             :     friend class MIDI_Sink;
     580             :     friend class MIDI_Source;
     581             : };
     582             : 
     583             : // :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: //
     584             : 
     585             : /// A struct that is both a TrueMIDI_Sink and a TrueMIDI_Source.
     586             : struct TrueMIDI_SinkSource : TrueMIDI_Sink, TrueMIDI_Source {};
     587             : 
     588             : /// A bidirectional pipe consists of two unidirectional pipes.
     589             : using BidirectionalMIDI_Pipe = std::pair<MIDI_Pipe, MIDI_Pipe>;
     590             : 
     591             : /// Connect a source to a pipe (`source >> pipe`).
     592         195 : inline MIDI_Pipe &operator>>(TrueMIDI_Source &source, MIDI_Pipe &pipe) {
     593         195 :     source.connectSinkPipe(&pipe);
     594         194 :     return pipe;
     595             : }
     596             : 
     597             : /// Connect a pipe to a sink (`pipe >> sink`).
     598         197 : inline TrueMIDI_Sink &operator>>(MIDI_Pipe &pipe, TrueMIDI_Sink &sink) {
     599         197 :     sink.connectSourcePipe(&pipe);
     600         196 :     return sink;
     601             : }
     602             : 
     603             : /// Connect a sink to a pipe (`sink << pipe`).
     604          71 : inline MIDI_Pipe &operator<<(TrueMIDI_Sink &sink, MIDI_Pipe &pipe) {
     605          71 :     sink.connectSourcePipe(&pipe);
     606          71 :     return pipe;
     607             : }
     608             : 
     609             : /// Connect a pipe to a source (`pipe << source`).
     610          71 : inline TrueMIDI_Source &operator<<(MIDI_Pipe &pipe, TrueMIDI_Source &source) {
     611          71 :     source.connectSinkPipe(&pipe);
     612          71 :     return source;
     613             : }
     614             : 
     615             : /// Don't connect two pipes to eachother.
     616             : MIDI_Pipe &operator<<(MIDI_Pipe &, MIDI_Pipe &) = delete;
     617             : 
     618             : /// Connect a pipe to a sink+source (`pipe | source+sink`).
     619           5 : inline TrueMIDI_SinkSource &operator|(BidirectionalMIDI_Pipe &pipe,
     620             :                                       TrueMIDI_SinkSource &sinksource) {
     621           5 :     sinksource.connectSinkPipe(&pipe.first);
     622           5 :     sinksource.connectSourcePipe(&pipe.second);
     623           5 :     return sinksource;
     624             : }
     625             : 
     626             : /// Connect a sink+source to a pipe (`source+sink | pipe`).
     627           5 : inline BidirectionalMIDI_Pipe &operator|(TrueMIDI_SinkSource &sinksource,
     628             :                                          BidirectionalMIDI_Pipe &pipe) {
     629           5 :     sinksource.connectSinkPipe(&pipe.second);
     630           5 :     sinksource.connectSourcePipe(&pipe.first);
     631           5 :     return pipe;
     632             : }
     633             : 
     634             : // :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: //
     635             : 
     636             : /**
     637             :  * @brief   Class that produces multiple MIDI_Pipe%s.
     638             :  * 
     639             :  * @tparam  N 
     640             :  *          The maximum number of pipes it can produce.
     641             :  * @tparam  Pipe 
     642             :  *          The type of pipes to produce.
     643             :  */
     644             : template <size_t N, class Pipe = MIDI_Pipe>
     645             : struct MIDI_PipeFactory {
     646             :     Pipe pipes[N];
     647             :     size_t index = 0;
     648             : 
     649          93 :     Pipe &getNext() {
     650          93 :         if (index >= N)
     651           1 :             FATAL_ERROR(F("Not enough pipes available"), 0x2459);
     652          92 :         return pipes[index++];
     653             :     }
     654          10 :     Pipe &operator[](size_t i) { return pipes[i]; }
     655             :     const Pipe &operator[](size_t i) const { return pipes[i]; }
     656             : };
     657             : 
     658             : template <size_t N>
     659             : using BidirectionalMIDI_PipeFactory =
     660             :     MIDI_PipeFactory<N, BidirectionalMIDI_Pipe>;
     661             : 
     662             : template <size_t N, class Pipe>
     663          65 : inline MIDI_Pipe &operator>>(TrueMIDI_Source &source,
     664             :                              MIDI_PipeFactory<N, Pipe> &pipe_fact) {
     665          65 :     return source >> pipe_fact.getNext();
     666             : }
     667             : 
     668             : template <size_t N, class Pipe>
     669             : MIDI_Pipe &operator>>(MIDI_Pipe &, MIDI_PipeFactory<N, Pipe> &) = delete;
     670             : 
     671             : template <size_t N, class Pipe>
     672           3 : inline TrueMIDI_Sink &operator>>(MIDI_PipeFactory<N, Pipe> &pipe_fact,
     673             :                                  TrueMIDI_Sink &sink) {
     674           3 :     return pipe_fact.getNext() >> sink;
     675             : }
     676             : 
     677             : template <size_t N, class Pipe>
     678             : MIDI_Pipe &operator>>(MIDI_PipeFactory<N, Pipe> &, MIDI_Pipe &) = delete;
     679             : 
     680             : template <size_t N, class Pipe>
     681          25 : inline MIDI_Pipe &operator<<(TrueMIDI_Sink &sink,
     682             :                              MIDI_PipeFactory<N, Pipe> &pipe_fact) {
     683          25 :     return sink << pipe_fact.getNext();
     684             : }
     685             : 
     686             : template <size_t N, class Pipe>
     687             : inline TrueMIDI_Source &operator<<(MIDI_PipeFactory<N, Pipe> &pipe_fact,
     688             :                                    TrueMIDI_Source &source) {
     689             :     return pipe_fact.getNext() << source;
     690             : }
     691             : 
     692             : template <size_t N>
     693             : inline TrueMIDI_SinkSource &
     694             : operator|(BidirectionalMIDI_PipeFactory<N> &pipe_fact,
     695             :           TrueMIDI_SinkSource &sinksource) {
     696             :     return pipe_fact.getNext() | sinksource;
     697             : }
     698             : 
     699             : template <size_t N>
     700             : inline BidirectionalMIDI_Pipe &
     701             : operator|(TrueMIDI_SinkSource &sinksource,
     702             :           BidirectionalMIDI_PipeFactory<N> &pipe_fact) {
     703             :     return sinksource | pipe_fact.getNext();
     704             : }
     705             : 
     706             : /// @}
     707             : 
     708             : END_CS_NAMESPACE
     709             : 
     710             : AH_DIAGNOSTIC_POP()

Generated by: LCOV version 1.15