Control Surface new-input
MIDI Control Surface library for Arduino
MIDI_Pipes.hpp
Go to the documentation of this file.
1 #pragma once
2 
4 #include <AH/STL/cstdint>
5 #include <AH/STL/limits>
6 #include <AH/STL/utility>
9 #include <Settings/NamespaceSettings.hpp>
10 
12 
14 
15 struct MIDIStaller;
17  reinterpret_cast<MIDIStaller *>(std::numeric_limits<std::uintptr_t>::max());
18 
62 class MIDI_Pipe;
63 class MIDI_Source;
64 class MIDI_Sink;
69 
71 class MIDI_Sink {
72  public:
74  MIDI_Sink() = default;
75 
77  MIDI_Sink(const MIDI_Sink &) = delete;
79  MIDI_Sink &operator=(const MIDI_Sink &) = delete;
80 
82  MIDI_Sink(MIDI_Sink &&other);
84  MIDI_Sink &operator=(MIDI_Sink &&other);
85 
87  virtual ~~MIDI_Sink();
88 
91 
93  virtual void sinkMIDIfromPipe(ChannelMessage) = 0;
95  virtual void sinkMIDIfromPipe(SysExMessage) = 0;
99  virtual void sinkMIDIfromPipe(RealTimeMessage) = 0;
100 
102 
105 
107  void connectSourcePipe(MIDI_Pipe *source);
109  void disconnectSourcePipes();
114  bool disconnect(TrueMIDI_Source &source);
115  bool disconnect(MIDI_Pipe &) = delete;
117  bool hasSourcePipe() const { return sourcePipe != nullptr; }
120  MIDI_Pipe *getSourcePipe() { return sourcePipe; }
121 
123 
124  private:
133  virtual MIDI_Sink *getFinalSink() { return this; }
139  void disconnectSourcePipesShallow();
140 
141  protected:
142  MIDI_Pipe *sourcePipe = nullptr;
143 
144  friend class MIDI_Pipe;
145 
146  public:
147  static void swap(MIDI_Sink &a, MIDI_Sink &b);
148  friend void swap(MIDI_Sink &a, MIDI_Sink &b) { swap(a, b); }
149 };
150 
151 // :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: //
152 
154 class MIDI_Source {
155  public:
157  MIDI_Source() = default;
158 
160  MIDI_Source(const MIDI_Source &) = delete;
162  MIDI_Source &operator=(const MIDI_Source &) = delete;
163 
165  MIDI_Source(MIDI_Source &&other);
168 
170  virtual ~MIDI_Source();
171 
174 
183 
185 
188 
196  void stall(MIDIStaller *cause = eternal_stall);
202  void unstall(MIDIStaller *cause = eternal_stall);
204  bool isStalled() const;
207  MIDIStaller *getStaller() const;
210  const char *getStallerName() const;
213  void handleStallers() const;
214 
216 
219 
221  void connectSinkPipe(MIDI_Pipe *sink);
223  void disconnectSinkPipes();
228  bool disconnect(TrueMIDI_Sink &sink);
229  bool disconnect(MIDI_Pipe &) = delete;
231  bool hasSinkPipe() const { return sinkPipe != nullptr; }
235 
237 
238  private:
241  virtual void stallUpstream(MIDIStaller *, MIDI_Sink *) {}
244  virtual void unstallUpstream(MIDIStaller *, MIDI_Sink *) {}
247  virtual MIDI_Source *getInitialSource() { return this; }
254 
255  protected:
256  MIDI_Pipe *sinkPipe = nullptr;
257 
258  friend class MIDI_Pipe;
259 
260  public:
261  static void swap(MIDI_Source &a, MIDI_Source &b);
262  friend void swap(MIDI_Source &a, MIDI_Source &b) { swap(a, b); }
263 };
264 
265 // :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: //
266 
326 class MIDI_Pipe : private MIDI_Sink, private MIDI_Source {
327  public:
329  MIDI_Pipe() = default;
330 
332  MIDI_Pipe(const MIDI_Pipe &) = delete;
334  MIDI_Pipe &operator=(const MIDI_Pipe &) = delete;
335 
338  MIDI_Pipe(MIDI_Pipe &&) = delete;
342 
344  virtual ~MIDI_Pipe();
345 
346  private:
349 
355  virtual void mapForwardMIDI(ChannelMessage msg) { sourceMIDItoSink(msg); }
357  virtual void mapForwardMIDI(SysExMessage msg) { sourceMIDItoSink(msg); }
361  virtual void mapForwardMIDI(RealTimeMessage msg) { sourceMIDItoSink(msg); }
362 
364 
365  public:
368 
370  bool isStalled() const { return sink_staller || through_staller; }
378  const char *getSinkStallerName() const;
386  const char *getThroughStallerName() const;
389  MIDIStaller *getStaller() const;
392  const char *getStallerName() const;
397  return sink_staller == nullptr || sink_staller == cause;
398  }
403  return through_staller == nullptr || through_staller == cause;
404  }
405 
408  void handleStallers() const;
409 
411 
412  public:
415 
417  bool hasSink() const { return sink != nullptr; }
419  bool hasSource() const { return source != nullptr; }
422  bool hasThroughOut() const { return throughOut != nullptr; }
425  bool hasThroughIn() const { return throughIn != nullptr; }
426 
428 
429  public:
432 
436  void disconnect();
442  if (getFinalSink() == &sink) {
443  disconnect();
444  return true;
445  }
446  if (hasThroughOut()) {
447  return throughOut->disconnect(sink);
448  }
449  return false;
450  }
456  if (getInitialSource() == &source) {
457  disconnect();
458  return true;
459  }
460  if (hasThroughIn()) {
461  return throughIn->disconnect(source);
462  }
463  return false;
464  }
465  bool disconnect(MIDI_Pipe &) = delete;
466 
468  MIDI_Source *getSource() { return source; }
470  MIDI_Sink *getSink() { return sink; }
475 
478  MIDI_Sink *getFinalSink() override {
479  return hasSink() ? sink->getFinalSink() : nullptr;
480  }
484  return hasSource() ? source->getInitialSource() : nullptr;
485  }
486 
488 
489  private:
492 
495  void connectSink(MIDI_Sink *sink);
498  void disconnectSink();
504  void disconnectSource();
505 
507 
508  protected:
511  template <class Message>
512  void sourceMIDItoSink(Message msg) {
513  if (hasSink())
514  sink->sinkMIDIfromPipe(msg);
515  }
516 
517  protected:
522  template <class Message>
523  void acceptMIDIfromSource(Message msg) {
524  if (hasThroughOut())
526  mapForwardMIDI(msg);
527  }
528 
529  private:
532  void sinkMIDIfromPipe(ChannelMessage msg) override {
533  sourceMIDItoSink(msg);
534  }
536  void sinkMIDIfromPipe(SysExMessage msg) override { sourceMIDItoSink(msg); }
538  void sinkMIDIfromPipe(SysCommonMessage msg) override {
539  sourceMIDItoSink(msg);
540  }
542  void sinkMIDIfromPipe(RealTimeMessage msg) override {
543  sourceMIDItoSink(msg);
544  }
545 
546  private:
549 
557  void stallDownstream(MIDIStaller *cause, MIDI_Source *stallsrc) override;
559  void unstallDownstream(MIDIStaller *cause, MIDI_Source *stallsrc) override;
560 
565  void stallUpstream(MIDIStaller *cause, MIDI_Sink *stallsrc) override;
567  void unstallUpstream(MIDIStaller *cause, MIDI_Sink *stallsrc) override;
568 
570 
571  private:
572  MIDI_Sink *sink = nullptr;
573  MIDI_Source *source = nullptr;
578 
579  friend class MIDI_Sink;
580  friend class MIDI_Source;
581 };
582 
583 // :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: //
584 
587 
589 using BidirectionalMIDI_Pipe = std::pair<MIDI_Pipe, MIDI_Pipe>;
590 
593  source.connectSinkPipe(&pipe);
594  return pipe;
595 }
596 
599  sink.connectSourcePipe(&pipe);
600  return sink;
601 }
602 
605  sink.connectSourcePipe(&pipe);
606  return pipe;
607 }
608 
611  source.connectSinkPipe(&pipe);
612  return source;
613 }
614 
617 
620  TrueMIDI_SinkSource &sinksource) {
621  sinksource.connectSinkPipe(&pipe.first);
622  sinksource.connectSourcePipe(&pipe.second);
623  return sinksource;
624 }
625 
628  BidirectionalMIDI_Pipe &pipe) {
629  sinksource.connectSinkPipe(&pipe.second);
630  sinksource.connectSourcePipe(&pipe.first);
631  return pipe;
632 }
633 
634 // :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: //
635 
644 template <size_t N, class Pipe = MIDI_Pipe>
646  Pipe pipes[N];
647  size_t index = 0;
648 
649  Pipe &getNext() {
650  if (index >= N)
651  FATAL_ERROR(F("Not enough pipes available"), 0x2459);
652  return pipes[index++];
653  }
654  Pipe &operator[](size_t i) { return pipes[i]; }
655  const Pipe &operator[](size_t i) const { return pipes[i]; }
656 };
657 
658 template <size_t N>
661 
662 template <size_t N, class Pipe>
664  MIDI_PipeFactory<N, Pipe> &pipe_fact) {
665  return source >> pipe_fact.getNext();
666 }
667 
668 template <size_t N, class Pipe>
670 
671 template <size_t N, class Pipe>
673  TrueMIDI_Sink &sink) {
674  return pipe_fact.getNext() >> sink;
675 }
676 
677 template <size_t N, class Pipe>
679 
680 template <size_t N, class Pipe>
682  MIDI_PipeFactory<N, Pipe> &pipe_fact) {
683  return sink << pipe_fact.getNext();
684 }
685 
686 template <size_t N, class Pipe>
688  TrueMIDI_Source &source) {
689  return pipe_fact.getNext() << source;
690 }
691 
692 template <size_t N>
693 inline TrueMIDI_SinkSource &
695  TrueMIDI_SinkSource &sinksource) {
696  return pipe_fact.getNext() | sinksource;
697 }
698 
699 template <size_t N>
700 inline BidirectionalMIDI_Pipe &
703  return sinksource | pipe_fact.getNext();
704 }
705 
707 
709 
MIDIStaller *const eternal_stall
Definition: MIDI_Pipes.hpp:16
#define END_CS_NAMESPACE
#define BEGIN_CS_NAMESPACE
#define AH_DIAGNOSTIC_POP()
Definition: Warnings.hpp:36
#define AH_DIAGNOSTIC_WERROR()
Definition: Warnings.hpp:35
Class that routes MIDI messages from a MIDI_Source to a MIDI_Sink.
Definition: MIDI_Pipes.hpp:326
MIDI_Sink * getSink()
Get the immediate sink of this pipe.
Definition: MIDI_Pipes.hpp:470
MIDI_Pipe * getThroughIn()
Get the pipe connected to the “through” input of this pipe.
Definition: MIDI_Pipes.hpp:474
void acceptMIDIfromSource(Message msg)
Accept a MIDI message from the source, forward it to the “through” output if necessary,...
Definition: MIDI_Pipes.hpp:523
MIDI_Sink * getFinalSink() override
Get the sink this pipe eventually sinks to, following the chain recursively.
Definition: MIDI_Pipes.hpp:478
MIDI_Pipe & operator=(MIDI_Pipe &&)=delete
Move assignment.
bool hasSink() const
Check if this pipe is connected to a sink.
Definition: MIDI_Pipes.hpp:417
void sourceMIDItoSink(Message msg)
Send the given MIDI message to the sink of this pipe.
Definition: MIDI_Pipes.hpp:512
const char * getStallerName() const
Get the name of any staller.
Definition: MIDI_Pipes.cpp:334
MIDI_Pipe(MIDI_Pipe &&)=delete
Move constructor.
MIDIStaller * getStaller() const
Get any staller: returns getSinkStaller() if it's not null, getThroughStaller() otherwise.
Definition: MIDI_Pipes.cpp:330
virtual void mapForwardMIDI(SysExMessage msg)
Function that maps, edits or filters MIDI messages, and then forwards them to the sink of the pipe.
Definition: MIDI_Pipes.hpp:357
MIDI_Source * source
Definition: MIDI_Pipes.hpp:573
bool disconnect(MIDI_Pipe &)=delete
void connectSource(MIDI_Source *source)
Set the source pointer to point to the given source.
Definition: MIDI_Pipes.cpp:200
MIDIStaller * getThroughStaller() const
Get the staller (cause of the stall) that causes the “through” output of this pipe to be stalled.
Definition: MIDI_Pipes.hpp:383
MIDI_Pipe * getThroughOut()
Get the pipe connected to the “through” output of this pipe.
Definition: MIDI_Pipes.hpp:472
MIDI_Sink * sink
Definition: MIDI_Pipes.hpp:572
MIDI_Pipe(const MIDI_Pipe &)=delete
Copy constructor (copying not allowed).
virtual void mapForwardMIDI(RealTimeMessage msg)
Function that maps, edits or filters MIDI messages, and then forwards them to the sink of the pipe.
Definition: MIDI_Pipes.hpp:361
void sinkMIDIfromPipe(SysCommonMessage msg) override
Called when data arrives from an upstream pipe connected to our “through” input, this function forwar...
Definition: MIDI_Pipes.hpp:538
virtual void mapForwardMIDI(SysCommonMessage msg)
Function that maps, edits or filters MIDI messages, and then forwards them to the sink of the pipe.
Definition: MIDI_Pipes.hpp:359
void unstallDownstream(MIDIStaller *cause, MIDI_Source *stallsrc) override
Undoes the stalling by stallDownstream.
Definition: MIDI_Pipes.cpp:280
bool hasSource() const
Check if this pipe is connected to a source.
Definition: MIDI_Pipes.hpp:419
void stallDownstream(MIDIStaller *cause, MIDI_Source *stallsrc) override
Stall this pipe and all other pipes further downstream (following the path of the sink and the “throu...
Definition: MIDI_Pipes.cpp:237
bool hasThroughOut() const
Check if this pipe has a “through” output that sends all incoming messages from the input (source) to...
Definition: MIDI_Pipes.hpp:422
MIDIStaller * through_staller
Definition: MIDI_Pipes.hpp:577
MIDI_Source * getInitialSource() override
Get the original source that sources to this pipe, following the chain recursively.
Definition: MIDI_Pipes.hpp:483
void connectSink(MIDI_Sink *sink)
Set the sink pointer to point to the given sink.
Definition: MIDI_Pipes.cpp:190
const char * getSinkStallerName() const
Get the name of the staller (cause of the stall) that causes the sink of this pipe to be stalled.
Definition: MIDI_Pipes.cpp:322
void sinkMIDIfromPipe(RealTimeMessage msg) override
Called when data arrives from an upstream pipe connected to our “through” input, this function forwar...
Definition: MIDI_Pipes.hpp:542
MIDI_Pipe *& throughOut
Definition: MIDI_Pipes.hpp:574
bool isStalled() const
Check if this pipe is stalled.
Definition: MIDI_Pipes.hpp:370
MIDI_Pipe & operator=(const MIDI_Pipe &)=delete
Copy assignment (copying not allowed).
MIDIStaller * sink_staller
Definition: MIDI_Pipes.hpp:576
virtual ~MIDI_Pipe()
Destructor.
Definition: MIDI_Pipes.cpp:235
const char * getThroughStallerName() const
Get the name of the staller (cause of the stall) that causes the “through” output of this pipe to be ...
Definition: MIDI_Pipes.cpp:326
MIDIStaller * getSinkStaller() const
Get the staller (cause of the stall) that causes the sink of this pipe to be stalled.
Definition: MIDI_Pipes.hpp:375
void unstallUpstream(MIDIStaller *cause, MIDI_Sink *stallsrc) override
Undoes the stalling by stallUpstream.
Definition: MIDI_Pipes.cpp:304
void disconnect()
Disconnect this pipe from all other pipes, sources and sinks.
Definition: MIDI_Pipes.cpp:210
bool disconnect(TrueMIDI_Sink &sink)
Disconnect the given sink from this pipe.
Definition: MIDI_Pipes.hpp:441
void sinkMIDIfromPipe(ChannelMessage msg) override
Called when data arrives from an upstream pipe connected to our “through” input, this function forwar...
Definition: MIDI_Pipes.hpp:532
virtual void mapForwardMIDI(ChannelMessage msg)
Function that maps, edits or filters MIDI messages, and then forwards them to the sink of the pipe.
Definition: MIDI_Pipes.hpp:355
MIDI_Source * getSource()
Get the immediate source of this pipe.
Definition: MIDI_Pipes.hpp:468
void handleStallers() const
Give the code that is stalling the MIDI pipe the opportunity to do its job and unstall the pipe.
Definition: MIDI_Pipes.cpp:338
MIDI_Pipe *& throughIn
Definition: MIDI_Pipes.hpp:575
void disconnectSink()
Set the sink pointer to null.
Definition: MIDI_Pipes.cpp:198
bool hasThroughIn() const
Check if this pipe has a “through” input that merges all messages from another pipe into the output (...
Definition: MIDI_Pipes.hpp:425
MIDI_Pipe()=default
Default constructor.
bool throughIsUnstalledOrStalledBy(MIDIStaller *cause)
Returns true if this pipe is either not stalled at all, or if the pipe is stalled by the given stalle...
Definition: MIDI_Pipes.hpp:402
void disconnectSource()
Set the source pointer to null.
Definition: MIDI_Pipes.cpp:208
bool disconnect(TrueMIDI_Source &source)
Disconnect the given source from this pipe.
Definition: MIDI_Pipes.hpp:455
bool sinkIsUnstalledOrStalledBy(MIDIStaller *cause)
Returns true if this pipe is either not stalled at all, or if the pipe is stalled by the given stalle...
Definition: MIDI_Pipes.hpp:396
void sinkMIDIfromPipe(SysExMessage msg) override
Called when data arrives from an upstream pipe connected to our “through” input, this function forwar...
Definition: MIDI_Pipes.hpp:536
void stallUpstream(MIDIStaller *cause, MIDI_Sink *stallsrc) override
Stall this pipe and all other pipes further upstream (following the path of the "trough" input).
Definition: MIDI_Pipes.cpp:262
Receives MIDI messages from a MIDI pipe.
Definition: MIDI_Pipes.hpp:71
virtual void sinkMIDIfromPipe(RealTimeMessage)=0
Accept an incoming MIDI Real-Time message.
void connectSourcePipe(MIDI_Pipe *source)
Fully connect a source pipe to this sink.
Definition: MIDI_Pipes.cpp:16
virtual void stallDownstream(MIDIStaller *, MIDI_Source *)
Base case for recursive stall function.
Definition: MIDI_Pipes.hpp:127
MIDI_Sink & operator=(const MIDI_Sink &)=delete
Copy assignment (copying not allowed).
virtual MIDI_Sink * getFinalSink()
Base case for recursive function.
Definition: MIDI_Pipes.hpp:133
bool disconnect(MIDI_Pipe &)=delete
MIDI_Sink(const MIDI_Sink &)=delete
Copy constructor (copying not allowed).
friend void swap(MIDI_Sink &a, MIDI_Sink &b)
Definition: MIDI_Pipes.hpp:148
virtual void unstallDownstream(MIDIStaller *, MIDI_Source *)
Base case for recursive un-stall function.
Definition: MIDI_Pipes.hpp:130
virtual void sinkMIDIfromPipe(SysCommonMessage)=0
Accept an incoming MIDI System Common message.
MIDI_Sink()=default
Default constructor.
virtual void sinkMIDIfromPipe(ChannelMessage)=0
Accept an incoming MIDI Channel message.
MIDI_Pipe * sourcePipe
Definition: MIDI_Pipes.hpp:142
virtual void sinkMIDIfromPipe(SysExMessage)=0
Accept an incoming MIDI System Exclusive message.
bool hasSourcePipe() const
Check if this sink is connected to a source pipe.
Definition: MIDI_Pipes.hpp:117
MIDI_Pipe * getSourcePipe()
Get a pointer to the pipe this sink is connected to, or nullptr if not connected.
Definition: MIDI_Pipes.hpp:120
Class that can send MIDI messages to a MIDI pipe.
Definition: MIDI_Pipes.hpp:154
MIDI_Source & operator=(const MIDI_Source &)=delete
Copy assignment (copying not allowed).
MIDI_Pipe * getSinkPipe()
Get a pointer to the pipe this source is connected to, or nullptr if not connected.
Definition: MIDI_Pipes.hpp:234
const char * getStallerName() const
Get the name of whatever is causing this MIDI source to be stalled.
Definition: MIDI_Pipes.cpp:179
void connectSinkPipe(MIDI_Pipe *sink)
Fully connect a sink pipe to this source.
Definition: MIDI_Pipes.cpp:75
virtual ~MIDI_Source()
Destructor.
Definition: MIDI_Pipes.cpp:130
MIDIStaller * getStaller() const
Get a pointer to whatever is causing this MIDI source to be stalled.
Definition: MIDI_Pipes.cpp:173
bool disconnect(MIDI_Pipe &)=delete
MIDI_Source()=default
Default constructor.
void sourceMIDItoPipe(ChannelMessage)
Send a MIDI Channel Message down the pipe.
Definition: MIDI_Pipes.cpp:132
MIDI_Pipe * sinkPipe
Definition: MIDI_Pipes.hpp:256
void stall(MIDIStaller *cause=eternal_stall)
Stall this MIDI source.
Definition: MIDI_Pipes.cpp:157
virtual MIDI_Source * getInitialSource()
Base case for recursive function.
Definition: MIDI_Pipes.hpp:247
virtual void stallUpstream(MIDIStaller *, MIDI_Sink *)
Base case for recursive stall function.
Definition: MIDI_Pipes.hpp:241
bool isStalled() const
Check if this source can write to the sinks it connects to.
Definition: MIDI_Pipes.cpp:167
static void swap(MIDI_Source &a, MIDI_Source &b)
Definition: MIDI_Pipes.cpp:113
void disconnectSinkPipesShallow()
Disconnect only the first pipe connected to this source.
Definition: MIDI_Pipes.cpp:92
bool disconnect(TrueMIDI_Sink &sink)
Disconnect the given sink from this source.
Definition: MIDI_Pipes.cpp:99
void disconnectSinkPipes()
Disconnect all sink pipes that this source sinks to (recursively).
Definition: MIDI_Pipes.cpp:84
void handleStallers() const
Give the code that is stalling the MIDI sink pipes the opportunity to do its job and un-stall the pip...
Definition: MIDI_Pipes.cpp:183
virtual void unstallUpstream(MIDIStaller *, MIDI_Sink *)
Base case for recursive un-stall function.
Definition: MIDI_Pipes.hpp:244
MIDI_Source(const MIDI_Source &)=delete
Copy constructor (copying not allowed).
bool hasSinkPipe() const
Check if this source is connected to a sink pipe.
Definition: MIDI_Pipes.hpp:231
void unstall(MIDIStaller *cause=eternal_stall)
Un-stall the pipes connected to this source, so other sources are allowed to send again.
Definition: MIDI_Pipes.cpp:162
#define FATAL_ERROR(msg, errc)
Print the error message and error code, and stop the execution.
Definition: Error.hpp:60
constexpr auto max(const T &a, const U &b) -> decltype(a< b ? b :a)
Return the larger of two numbers/objects.
Definition: MinMaxFix.hpp:22
TrueMIDI_SinkSource & operator|(BidirectionalMIDI_Pipe &pipe, TrueMIDI_SinkSource &sinksource)
Connect a pipe to a sink+source (pipe | source+sink).
Definition: MIDI_Pipes.hpp:619
MIDI_Pipe & operator>>(TrueMIDI_Source &source, MIDI_Pipe &pipe)
Connect a source to a pipe (source >> pipe).
Definition: MIDI_Pipes.hpp:592
std::pair< MIDI_Pipe, MIDI_Pipe > BidirectionalMIDI_Pipe
A bidirectional pipe consists of two unidirectional pipes.
Definition: MIDI_Pipes.hpp:589
Print & operator<<(Print &os, Quaternion e)
Printing.
Definition: Quaternion.cpp:28
void swap(T &t1, T &t2)
Definition: move.h:21
Struct that can cause a MIDI_Pipe to be stalled.
Class that produces multiple MIDI_Pipes.
Definition: MIDI_Pipes.hpp:645
const Pipe & operator[](size_t i) const
Definition: MIDI_Pipes.hpp:655
Pipe & operator[](size_t i)
Definition: MIDI_Pipes.hpp:654
A struct that is both a TrueMIDI_Sink and a TrueMIDI_Source.
Definition: MIDI_Pipes.hpp:586