LCOV - code coverage report
Current view: top level - src/MIDI_Interfaces - MIDI_Pipes.hpp (source / functions) Hit Total Coverage
Test: 91b605873905a6fcb78324052c97dbac10849539 Lines: 96 100 96.0 %
Date: 2022-11-08 01:34:37 Functions: 57 61 93.4 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.15