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

Generated by: LCOV version 1.15