LCOV - code coverage report
Current view: top level - src/MIDI_Interfaces - MIDI_Pipes.hpp (source / functions) Coverage Total Hit
Test: 73449d9b107c772cf65493691543348214e5d5eb Lines: 96.0 % 100 96
Test Date: 2026-06-06 17:44:35 Functions: 93.4 % 61 57
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 2.4-beta