cs-midi/MIDI_Interfaces/MIDI_Pipes.hpp

737 lines
27 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#pragma once
#include <Settings/SettingsWrapper.hpp>
#if !DISABLE_PIPES
#include <AH/Containers/BitArray.hpp>
#include <AH/STL/cstdint>
#include <AH/STL/limits>
#include <AH/STL/utility>
#include <MIDI_Parsers/MIDI_MessageTypes.hpp>
#include <Settings/NamespaceSettings.hpp>
BEGIN_CS_NAMESPACE
struct MIDIStaller;
MIDIStaller *const eternal_stall =
reinterpret_cast<MIDIStaller *>(std::numeric_limits<std::uintptr_t>::max());
/**
* @addtogroup MIDI_Routing MIDI Routing
* @brief Operators and utilities for MIDI routing.
*
* Two or more MIDI interfaces can be connected
* using @ref MIDI_Pipe "MIDI Pipes". The simplest pipe just carries messages
* from the input interface to the output interface, but you can write rules for
* filtering out certain messages, changing the channel of some messages, etc.
*
* Allows you to use syntax like:
*
* ~~~cpp
* HardwareSerialMIDI_Interface
* midiA = Serial1, midiB = Serial2, midiC = Serial3;
* MIDI_PipeFactory<3> pipes; // Factory that can produce 3 pipes
*
* midiA >> pipes >> midiB;
* midiC >> pipes >> midiB;
* midiC << pipes << midiB;
* ~~~
*
* Or for bidirectional connections:
*
* ~~~cpp
* HardwareSerialMIDI_Interface
* midiA = Serial1, midiB = Serial2, midiC = Serial3;
* BidirectionalMIDI_PipeFactory<2> pipes; // Factory that can produce 2 pipes
*
* midiA | pipes | midiB;
* midiA | pipes | midiC;
* ~~~
*
* Have a look at the following examples on MIDI routing:
*
* - @ref MIDI_Pipes-Routing.ino
* - @ref Dual-MIDI-Interface.ino
*
* If you're interested how the pipes work, see the documentation for
* @ref MIDI_Pipe.
*
* @see @ref midi_md-routing (MIDI tutorial)
*
* @{
*/
class MIDI_Pipe;
class MIDI_Source;
class MIDI_Sink;
/// A MIDI_Sink that is not a MIDI_Pipe.
using TrueMIDI_Sink = MIDI_Sink;
/// A MIDI_Source that is not a MIDI_Pipe.
using TrueMIDI_Source = MIDI_Source;
/// Receives MIDI messages from a MIDI pipe.
/// @see @ref MIDI_Routing
/// @see @ref midi_md-routing (MIDI tutorial)
class MIDI_Sink {
public:
/// Default constructor.
MIDI_Sink() = default;
/// Copy constructor (copying not allowed).
MIDI_Sink(const MIDI_Sink &) = delete;
/// Copy assignment (copying not allowed).
MIDI_Sink &operator=(const MIDI_Sink &) = delete;
/// Move constructor.
MIDI_Sink(MIDI_Sink &&other);
/// Move assignment.
MIDI_Sink &operator=(MIDI_Sink &&other);
/// Destructor.
virtual ~MIDI_Sink();
/// @name Sending data over a MIDI Pipe
/// @{
/// Accept an incoming MIDI Channel message.
virtual void sinkMIDIfromPipe(ChannelMessage) = 0;
/// Accept an incoming MIDI System Exclusive message.
virtual void sinkMIDIfromPipe(SysExMessage) = 0;
/// Accept an incoming MIDI System Common message.
virtual void sinkMIDIfromPipe(SysCommonMessage) = 0;
/// Accept an incoming MIDI Real-Time message.
virtual void sinkMIDIfromPipe(RealTimeMessage) = 0;
/// @}
/// @name Connecting and disconnecting MIDI Pipes
/// @{
/// Fully connect a source pipe to this sink.
void connectSourcePipe(MIDI_Pipe *source);
/// Disconnect all source pipes that sink to this sink (recursively).
void disconnectSourcePipes();
/// Disconnect the given source from this sink. Leaves other sources
/// connected.
/// Returns true if the source was found and disconnected, false if the
/// given source was not a direct or indirect source to this sink.
bool disconnect(TrueMIDI_Source &source);
bool disconnect(MIDI_Pipe &) = delete;
/// Check if this sink is connected to a source pipe.
bool hasSourcePipe() const { return sourcePipe != nullptr; }
/// Get a pointer to the pipe this sink is connected to, or `nullptr` if
/// not connected.
MIDI_Pipe *getSourcePipe() const { return sourcePipe; }
/// @}
private:
/// Base case for recursive stall function.
/// @see MIDI_Pipe::stallDownstream
virtual void stallDownstream(MIDIStaller *, MIDI_Source *) {}
/// Base case for recursive un-stall function.
/// @see MIDI_Pipe::unstallDownstream
virtual void unstallDownstream(MIDIStaller *, MIDI_Source *) {}
/// Base case for recursive function.
/// @see MIDI_Pipe::getFinalSink
virtual MIDI_Sink *getFinalSink() { return this; }
/// Disconnect only the first pipe connected to this sink. Leaves the
/// other pipes connected to the original pipe, which doesn't have a sink
/// anymore when this function finishes.
/// Used to disconnect a MIDI_Pipe while preserving the connections of its
/// “through” inputs.
void disconnectSourcePipesShallow();
protected:
MIDI_Pipe *sourcePipe = nullptr;
friend class MIDI_Pipe;
public:
static void swap(MIDI_Sink &a, MIDI_Sink &b);
friend void swap(MIDI_Sink &a, MIDI_Sink &b) { swap(a, b); }
};
// :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: //
/// Class that can send MIDI messages to a MIDI pipe.
/// @see @ref MIDI_Routing
/// @see @ref midi_md-routing (MIDI tutorial)
class MIDI_Source {
public:
/// Default constructor.
MIDI_Source() = default;
/// Copy constructor (copying not allowed).
MIDI_Source(const MIDI_Source &) = delete;
/// Copy assignment (copying not allowed).
MIDI_Source &operator=(const MIDI_Source &) = delete;
/// Move constructor.
MIDI_Source(MIDI_Source &&other);
/// Move assignment.
MIDI_Source &operator=(MIDI_Source &&other);
/// Destructor.
virtual ~MIDI_Source();
/// @name Sending data over a MIDI Pipe
/// @{
/// Send a MIDI Channel Message down the pipe.
void sourceMIDItoPipe(ChannelMessage);
/// Send a MIDI System Exclusive message down the pipe.
void sourceMIDItoPipe(SysExMessage);
/// Send a MIDI System Common message down the pipe.
void sourceMIDItoPipe(SysCommonMessage);
/// Send a MIDI Real-Time message down the pipe.
void sourceMIDItoPipe(RealTimeMessage);
/// @}
/// @name Stalling the sink pipes and exclusive access
/// @{
/// Stall this MIDI source.
/// This means that this becomes the only source that can sink to the sinks
/// connected to this source. Other sources have to wait until this source
/// un-stalls the pipe before they can send again.
/// @param cause
/// Pointer to the reason for this stall, can be called back to
/// un-stall the pipes.
void stall(MIDIStaller *cause = eternal_stall);
/// Un-stall the pipes connected to this source, so other sources
/// are allowed to send again.
/// @param cause
/// Pointer to the reason for the stall (this has to be the same one
/// that was used to stall).
void unstall(MIDIStaller *cause = eternal_stall);
/// Check if this source can write to the sinks it connects to.
bool isStalled() const;
/// Get a pointer to whatever is causing this MIDI source to be stalled.
/// There could be multiple stallers, this function just returns one.
MIDIStaller *getStaller() const;
/// Get the name of whatever is causing this MIDI source to be stalled.
/// There could be multiple stallers, this function just returns one.
const char *getStallerName() const;
/// Give the code that is stalling the MIDI sink pipes the opportunity to do
/// its job and un-stall the pipes.
void handleStallers() const;
/// @}
/// @name Connecting and disconnecting MIDI Pipes
/// @{
/// Fully connect a sink pipe to this source.
void connectSinkPipe(MIDI_Pipe *sink);
/// Disconnect all sink pipes that this source sinks to (recursively).
void disconnectSinkPipes();
/// Disconnect the given sink from this source. Leaves other sinks
/// connected.
/// Returns true if the sink was found and disconnected, false if the
/// given sink was not a direct or indirect sink of this source.
bool disconnect(TrueMIDI_Sink &sink);
bool disconnect(MIDI_Pipe &) = delete;
/// Check if this source is connected to a sink pipe.
bool hasSinkPipe() const { return sinkPipe != nullptr; }
/// Get a pointer to the pipe this source is connected to, or `nullptr` if
/// not connected.
MIDI_Pipe *getSinkPipe() { return sinkPipe; }
/// @}
private:
/// Base case for recursive stall function.
/// @see MIDI_Pipe::stallUpstream
virtual void stallUpstream(MIDIStaller *, MIDI_Sink *) {}
/// Base case for recursive un-stall function.
/// @see MIDI_Pipe::unstallUpstream
virtual void unstallUpstream(MIDIStaller *, MIDI_Sink *) {}
/// Base case for recursive function.
/// @see MIDI_Pipe::getInitialSource
virtual MIDI_Source *getInitialSource() { return this; }
/// Disconnect only the first pipe connected to this source. Leaves the
/// other pipes connected to the original pipe, which doesn't have a source
/// anymore when this function finishes.
/// Used to disconnect a @ref MIDI_Pipe while preserving the connections of
/// its “through” outputs.
void disconnectSinkPipesShallow();
protected:
MIDI_Pipe *sinkPipe = nullptr;
friend class MIDI_Pipe;
public:
static void swap(MIDI_Source &a, MIDI_Source &b);
friend void swap(MIDI_Source &a, MIDI_Source &b) { swap(a, b); }
};
// :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: //
/**
* @brief Class that routes MIDI messages from a MIDI_Source to a MIDI_Sink.
*
* A pipe has at least two, at most four connections. In the simplest case, a
* pipe just has a source and a sink. The source sends MIDI messages down the
* pipe, and the pipe sends them to the sink. A mapping or filter can be applied
* to the messages traveling down the pipe.
*
* To be able to connect multiple pipes to a single sink or source, a pipe also
* has a “through” output and a “through” input. Both are pipes, not sinks or
* sources. All data that comes from the source is sent to the “through” output
* as well, and all input that comes in from the “through” input is sent to the
* sink as well. The mapping or filter is not applied to the data going from/to
* the “through” connections.
*
* Merging data from multiple sources into a single sink can cause problems
* because messages can be interleaved (e.g. RPN/NRPN or chunked system
* exclusive messages). To circumvent this issue, a source can request exclusive
* access to the pipe it's connected to. This stalls all other pipes that sink
* into the same sinks as the exclusive source.
* When other sources try to send to a stalled pipe, this will automatically
* call back the source that originally stalled the pipes, so it can finish its
* message, and then un-stall the pipes so the other pipe can send its data.
*
* **Pipe model**
*
* ~~~
* ╭────────────────> through out
* │ ┌────────┐
* source >━━━━┷━┥ filter ┝━┯━━━> sink
* └────────┘ │
* through in >─────────────────╯
* ~~~
*
* For example, if you have one source that should connect to two sinks, the
* configuration is as follows:
*
* ~~~
* through out × ┌────────┐
* ╭────────────────> >━━━━┷━┥ pipe 2 ┝━┯━━━> sink 2
* │ ┌────────┐ └────────┘ ×
* source >━━━━┷━┥ pipe 1 ┝━┯━━━━━━━━━━━━━━━━━━━━━━━━━━━> sink 1
* └────────┘ ×
* ~~~
*
* If you have two sources that should connect to the same sink, a possible
* configuration is:
*
* ~~~
* × ┌────────┐
* source 1 >━━━━━━━━━━━━━━━━━━━━━━━━━━━━┷━┥ pipe 1 ┝━┯━━━> sink
* × ┌────────┐ └────────┘ │
* source 2 >━━━━┷━┥ pipe 2 ┝━┯━━━> >─────────────────╯
* └────────┘ ×
* ~~~
*
* Each connection between a source and a sink has its own pipe, and no two
* pipes are connected in series (only through the “through“ inputs/outputs).
*
* @see @ref MIDI_Routing
* @see @ref midi_md-routing (MIDI tutorial)
*/
class MIDI_Pipe : private MIDI_Sink, private MIDI_Source {
public:
/// Default constructor
MIDI_Pipe() = default;
/// Copy constructor (copying not allowed).
MIDI_Pipe(const MIDI_Pipe &) = delete;
/// Copy assignment (copying not allowed).
MIDI_Pipe &operator=(const MIDI_Pipe &) = delete;
/// Move constructor.
/// @todo Add move constructor.
MIDI_Pipe(MIDI_Pipe &&) = delete;
/// Move assignment.
/// @todo Add move assignment.
MIDI_Pipe &operator=(MIDI_Pipe &&) = delete;
/// Destructor.
virtual ~MIDI_Pipe();
private:
/// @name Mapping and filtering
/// @{
/// Function that maps, edits or filters MIDI messages, and then forwards
/// them to the sink of the pipe.
/// The MIDI_Pipe base class just forwards the messages to the sink, but
/// you can override this method to create pipes that filter out some
/// messages, transposes notes, changes the channel, etc.
virtual void mapForwardMIDI(ChannelMessage msg) { sourceMIDItoSink(msg); }
/// @copydoc mapForwardMIDI
virtual void mapForwardMIDI(SysExMessage msg) { sourceMIDItoSink(msg); }
/// @copydoc mapForwardMIDI
virtual void mapForwardMIDI(SysCommonMessage msg) { sourceMIDItoSink(msg); }
/// @copydoc mapForwardMIDI
virtual void mapForwardMIDI(RealTimeMessage msg) { sourceMIDItoSink(msg); }
/// @}
public:
/// @name Dealing with stalled pipes
/// @{
/// Check if this pipe is stalled.
bool isStalled() const { return sink_staller || through_staller; }
/// Get the staller (cause of the stall) that causes the sink of this pipe
/// to be stalled.
/// This pipe could sink to more than one stalled sink, this function just
/// returns one of the causes.
MIDIStaller *getSinkStaller() const { return sink_staller; }
/// Get the name of the staller (cause of the stall) that causes the sink
/// of this pipe to be stalled.
const char *getSinkStallerName() const;
/// Get the staller (cause of the stall) that causes the “through” output
/// of this pipe to be stalled.
/// The “through” output of this pipe could sink to more than one stalled
/// sink, this function just returns one of the causes.
MIDIStaller *getThroughStaller() const { return through_staller; }
/// Get the name of the staller (cause of the stall) that causes the
/// “through” output of this pipe to be stalled.
const char *getThroughStallerName() const;
/// Get any staller: returns @ref getSinkStaller() if it's not null,
/// @ref getThroughStaller() otherwise.
MIDIStaller *getStaller() const;
/// Get the name of any staller.
/// @see getStaller
const char *getStallerName() const;
/// Returns true if this pipe is either not stalled at all, or if the pipe
/// is stalled by the given staller (cause).
/// @see getSinkStaller
bool sinkIsUnstalledOrStalledBy(MIDIStaller *cause) {
return sink_staller == nullptr || sink_staller == cause;
}
/// Returns true if this pipe is either not stalled at all, or if the pipe
/// is stalled by the given staller (cause).
/// @see getThroughStaller
bool throughIsUnstalledOrStalledBy(MIDIStaller *cause) {
return through_staller == nullptr || through_staller == cause;
}
/// Give the code that is stalling the MIDI pipe the opportunity to do
/// its job and unstall the pipe.
void handleStallers() const;
/// @}
public:
/// @name Check connections
/// @{
/// Check if this pipe is connected to a sink.
bool hasSink() const { return sink != nullptr; }
/// Check if this pipe is connected to a source.
bool hasSource() const { return source != nullptr; }
/// Check if this pipe has a “through” output that sends all incoming
/// messages from the input (source) to another pipe.
bool hasThroughOut() const { return getThroughOut() != nullptr; }
/// Check if this pipe has a “through” input that merges all messages from
/// another pipe into the output (sink).
bool hasThroughIn() const { return getThroughIn() != nullptr; }
/// @}
public:
/// @name MIDI Pipe connection management and inspection
/// @{
/// Disconnect this pipe from all other pipes, sources and sinks. If the
/// “through” input and/or output were in use, they are reconnected to their
/// original sink and/or source respectively, their behavior doesn't change.
void disconnect();
/// Disconnect the given sink from this pipe. The sink can be connected
/// directly, or via the “through” output.
/// Returns true if the sink was found and disconnected, false if the given
/// sink was not a direct or indirect sink of this pipe.
bool disconnect(TrueMIDI_Sink &sink) {
if (getFinalSink() == &sink) {
disconnect();
return true;
}
if (hasThroughOut()) {
return getThroughOut()->disconnect(sink);
}
return false;
}
/// Disconnect the given source from this pipe. The source can be connected
/// directly, or via the “through” input.
/// Returns true if the source was found and disconnected, false if the
/// given source was not a direct or indirect source to this pipe.
bool disconnect(TrueMIDI_Source &source) {
if (getInitialSource() == &source) {
disconnect();
return true;
}
if (hasThroughIn()) {
return getThroughIn()->disconnect(source);
}
return false;
}
bool disconnect(MIDI_Pipe &) = delete;
/// Get the immediate source of this pipe.
MIDI_Source *getSource() const { return source; }
/// Get the immediate sink of this pipe.
MIDI_Sink *getSink() const { return sink; }
/// Get the pipe connected to the “through” output of this pipe.
MIDI_Pipe *getThroughOut() const { return MIDI_Source::sinkPipe; }
/// Get the pipe connected to the “through” input of this pipe.
MIDI_Pipe *getThroughIn() const { return MIDI_Sink::sourcePipe; }
/// Get the sink this pipe eventually sinks to, following the chain
/// recursively.
MIDI_Sink *getFinalSink() override {
return hasSink() ? sink->getFinalSink() : nullptr;
}
/// Get the original source that sources to this pipe, following the chain
/// recursively.
MIDI_Source *getInitialSource() override {
return hasSource() ? source->getInitialSource() : nullptr;
}
/// @}
private:
/// @name Private functions to connect and disconnect sinks and sources
/// @{
/// Set the sink pointer to point to the given sink. Does not connect this
/// pipe to the sink. Initiate the connection from the sink.
void connectSink(MIDI_Sink *sink);
/// Set the sink pointer to null. Does not disconnect this pipe from the
/// sink. Initiate the disconnection from the sink.
void disconnectSink();
/// Set the source pointer to point to the given source. Does not connect
/// this pipe to the source. Initiate the connection from the source.
void connectSource(MIDI_Source *source);
/// Set the source pointer to null. Does not disconnect this pipe from the
/// source. Initiate the disconnection from the source.
void disconnectSource();
/// @}
protected:
/// Send the given MIDI message to the sink of this pipe.
/// Useful when overriding @ref mapForwardMIDI.
template <class Message>
void sourceMIDItoSink(Message msg) {
if (hasSink())
sink->sinkMIDIfromPipe(msg);
}
protected:
/// Accept a MIDI message from the source, forward it to the “through”
/// output if necessary, map or filter the MIDI message if necessary,
/// and send it to the sink. This function transfers messages from a
/// @ref MIDI_Source to its @ref MIDI_Pipe.
template <class Message>
void acceptMIDIfromSource(Message msg) {
if (hasThroughOut())
getThroughOut()->acceptMIDIfromSource(msg);
mapForwardMIDI(msg);
}
private:
/// Called when data arrives from an upstream pipe connected to our
/// “through” input, this function forwards it to the sink.
void sinkMIDIfromPipe(ChannelMessage msg) override {
sourceMIDItoSink(msg);
}
/// @copydoc sinkMIDIfromPipe
void sinkMIDIfromPipe(SysExMessage msg) override { sourceMIDItoSink(msg); }
/// @copydoc sinkMIDIfromPipe
void sinkMIDIfromPipe(SysCommonMessage msg) override {
sourceMIDItoSink(msg);
}
/// @copydoc sinkMIDIfromPipe
void sinkMIDIfromPipe(RealTimeMessage msg) override {
sourceMIDItoSink(msg);
}
private:
/// @name Private functions to stall and un-stall pipes
/// @{
/// Stall this pipe and all other pipes further downstream (following the
/// path of the sink and the “through” output). Operates recursively until
/// the end of the chain is reached, and then continues upstream (using
/// @ref stallUpstream) to stall all pipes that connect to sources that
/// sink to the same sink as this pipe and its “through” output.
/// In short: stall all pipes that sink to the same sink as this pipe, and
/// then stall all pipes that source to this first set of pipes.
void stallDownstream(MIDIStaller *cause, MIDI_Source *stallsrc) override;
/// Undoes the stalling by @ref stallDownstream.
void unstallDownstream(MIDIStaller *cause, MIDI_Source *stallsrc) override;
/// Stall this pipe and all other pipes further upstream (following the
/// path of the "trough" input). Operates recursively until the end of the
/// chain is reached. This function is called in the second stage of
/// @ref stallDownstream.
void stallUpstream(MIDIStaller *cause, MIDI_Sink *stallsrc) override;
/// Undoes the stalling by @ref stallUpstream.
void unstallUpstream(MIDIStaller *cause, MIDI_Sink *stallsrc) override;
/// @}
private:
MIDI_Sink *sink = nullptr;
MIDI_Source *source = nullptr;
MIDIStaller *sink_staller = nullptr;
MIDIStaller *through_staller = nullptr;
friend class MIDI_Sink;
friend class MIDI_Source;
};
// :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: //
/// A struct that is both a TrueMIDI_Sink and a TrueMIDI_Source.
/// @see @ref MIDI_Routing
/// @see @ref midi_md-routing (MIDI tutorial)
struct TrueMIDI_SinkSource : TrueMIDI_Sink, TrueMIDI_Source {};
/// A bidirectional pipe consists of two unidirectional pipes.
/// @see @ref MIDI_Routing
/// @see @ref midi_md-routing (MIDI tutorial)
using BidirectionalMIDI_Pipe = std::pair<MIDI_Pipe, MIDI_Pipe>;
/// Connect a source to a pipe (`source >> pipe`).
inline MIDI_Pipe &operator>>(TrueMIDI_Source &source, MIDI_Pipe &pipe) {
source.connectSinkPipe(&pipe);
return pipe;
}
/// Connect a pipe to a sink (`pipe >> sink`).
inline TrueMIDI_Sink &operator>>(MIDI_Pipe &pipe, TrueMIDI_Sink &sink) {
sink.connectSourcePipe(&pipe);
return sink;
}
/// Connect a sink to a pipe (`sink << pipe`).
inline MIDI_Pipe &operator<<(TrueMIDI_Sink &sink, MIDI_Pipe &pipe) {
sink.connectSourcePipe(&pipe);
return pipe;
}
/// Connect a pipe to a source (`pipe << source`).
inline TrueMIDI_Source &operator<<(MIDI_Pipe &pipe, TrueMIDI_Source &source) {
source.connectSinkPipe(&pipe);
return source;
}
/// Don't connect two pipes to eachother.
MIDI_Pipe &operator<<(MIDI_Pipe &, MIDI_Pipe &) = delete;
/// Connect a pipe to a sink+source (`pipe | source+sink`).
inline TrueMIDI_SinkSource &operator|(BidirectionalMIDI_Pipe &pipe,
TrueMIDI_SinkSource &sinksource) {
sinksource.connectSinkPipe(&pipe.first);
sinksource.connectSourcePipe(&pipe.second);
return sinksource;
}
/// Connect a sink+source to a pipe (`source+sink | pipe`).
inline BidirectionalMIDI_Pipe &operator|(TrueMIDI_SinkSource &sinksource,
BidirectionalMIDI_Pipe &pipe) {
sinksource.connectSinkPipe(&pipe.second);
sinksource.connectSourcePipe(&pipe.first);
return pipe;
}
// :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: //
/**
* @brief Class that produces multiple MIDI_Pipe%s.
*
* @tparam N
* The maximum number of pipes it can produce.
* @tparam Pipe
* The type of pipes to produce.
*
* @see @ref MIDI_Routing
* @see @ref midi_md-routing (MIDI tutorial)
*/
template <size_t N, class Pipe = MIDI_Pipe>
struct MIDI_PipeFactory {
Pipe pipes[N];
size_t index = 0;
Pipe &getNext() {
if (index >= N)
FATAL_ERROR(F("Not enough pipes available"), 0x2459);
return pipes[index++];
}
Pipe &operator[](size_t i) { return pipes[i]; }
const Pipe &operator[](size_t i) const { return pipes[i]; }
};
template <size_t N>
using BidirectionalMIDI_PipeFactory =
MIDI_PipeFactory<N, BidirectionalMIDI_Pipe>;
template <size_t N, class Pipe>
inline MIDI_Pipe &operator>>(TrueMIDI_Source &source,
MIDI_PipeFactory<N, Pipe> &pipe_fact) {
return source >> pipe_fact.getNext();
}
template <size_t N, class Pipe>
MIDI_Pipe &operator>>(MIDI_Pipe &, MIDI_PipeFactory<N, Pipe> &) = delete;
template <size_t N, class Pipe>
inline TrueMIDI_Sink &operator>>(MIDI_PipeFactory<N, Pipe> &pipe_fact,
TrueMIDI_Sink &sink) {
return pipe_fact.getNext() >> sink;
}
template <size_t N, class Pipe>
MIDI_Pipe &operator>>(MIDI_PipeFactory<N, Pipe> &, MIDI_Pipe &) = delete;
template <size_t N, class Pipe>
inline MIDI_Pipe &operator<<(TrueMIDI_Sink &sink,
MIDI_PipeFactory<N, Pipe> &pipe_fact) {
return sink << pipe_fact.getNext();
}
template <size_t N, class Pipe>
inline TrueMIDI_Source &operator<<(MIDI_PipeFactory<N, Pipe> &pipe_fact,
TrueMIDI_Source &source) {
return pipe_fact.getNext() << source;
}
template <size_t N>
inline TrueMIDI_SinkSource &
operator|(BidirectionalMIDI_PipeFactory<N> &pipe_fact,
TrueMIDI_SinkSource &sinksource) {
return pipe_fact.getNext() | sinksource;
}
template <size_t N>
inline BidirectionalMIDI_Pipe &
operator|(TrueMIDI_SinkSource &sinksource,
BidirectionalMIDI_PipeFactory<N> &pipe_fact) {
return sinksource | pipe_fact.getNext();
}
/// @}
END_CS_NAMESPACE
#else
BEGIN_CS_NAMESPACE
struct TrueMIDI_Source {
template <class... Args>
void sourceMIDItoPipe(Args &&...) {}
};
struct TrueMIDI_Sink {};
struct TrueMIDI_SinkSource : TrueMIDI_Source, TrueMIDI_Sink {};
END_CS_NAMESPACE
#endif