Control Surface master
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
15struct MIDIStaller;
17 reinterpret_cast<MIDIStaller *>(std::numeric_limits<std::uintptr_t>::max());
18
62class MIDI_Pipe;
63class MIDI_Source;
64class MIDI_Sink;
69
71class 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
95 virtual void sinkMIDIfromPipe(SysExMessage) = 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
155 public:
157 MIDI_Source() = default;
158
160 MIDI_Source(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 *) {}
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
326class 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
357 virtual void mapForwardMIDI(SysExMessage 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
470 MIDI_Sink *getSink() { return sink; }
475
479 return hasSink() ? sink->getFinalSink() : nullptr;
480 }
484 return hasSource() ? source->getInitialSource() : nullptr;
485 }
486
488
489 private:
492
498 void disconnectSink();
504 void disconnectSource();
505
507
508 protected:
511 template <class Message>
512 void sourceMIDItoSink(Message msg) {
513 if (hasSink())
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); }
539 sourceMIDItoSink(msg);
540 }
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
589using 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
629 sinksource.connectSinkPipe(&pipe.second);
630 sinksource.connectSourcePipe(&pipe.first);
631 return pipe;
632}
633
634// :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: //
635
644template <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
658template <size_t N>
661
662template <size_t N, class Pipe>
664 MIDI_PipeFactory<N, Pipe> &pipe_fact) {
665 return source >> pipe_fact.getNext();
666}
667
668template <size_t N, class Pipe>
670
671template <size_t N, class Pipe>
673 TrueMIDI_Sink &sink) {
674 return pipe_fact.getNext() >> sink;
675}
676
677template <size_t N, class Pipe>
679
680template <size_t N, class Pipe>
682 MIDI_PipeFactory<N, Pipe> &pipe_fact) {
683 return sink << pipe_fact.getNext();
684}
685
686template <size_t N, class Pipe>
688 TrueMIDI_Source &source) {
689 return pipe_fact.getNext() << source;
690}
691
692template <size_t N>
693inline TrueMIDI_SinkSource &
695 TrueMIDI_SinkSource &sinksource) {
696 return pipe_fact.getNext() | sinksource;
697}
698
699template <size_t N>
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
void acceptMIDIfromSource(Message msg)
Accept a MIDI message from the source, forward it to the “through” output if necessary,...
Definition: MIDI_Pipes.hpp:523
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
MIDI_Pipe * getThroughOut()
Get the pipe connected to the “through” output of this pipe.
Definition: MIDI_Pipes.hpp:472
void connectSource(MIDI_Source *source)
Set the source pointer to point to the given source.
Definition: MIDI_Pipes.cpp:200
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
MIDI_Pipe & operator=(MIDI_Pipe &&)=delete
Move assignment.
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 * 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
MIDIStaller * through_staller
Definition: MIDI_Pipes.hpp:577
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
MIDI_Pipe & operator=(const MIDI_Pipe &)=delete
Copy assignment (copying not allowed).
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
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
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
MIDI_Source * getSource()
Get the immediate source of this pipe.
Definition: MIDI_Pipes.hpp:468
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_Sink * getFinalSink() override
Get the sink this pipe eventually sinks to, following the chain recursively.
Definition: MIDI_Pipes.hpp:478
MIDI_Sink * getSink()
Get the immediate sink of this pipe.
Definition: MIDI_Pipes.hpp:470
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
MIDI_Pipe * getThroughIn()
Get the pipe connected to the “through” input of this pipe.
Definition: MIDI_Pipes.hpp:474
void disconnectSink()
Set the sink pointer to null.
Definition: MIDI_Pipes.cpp:198
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
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
MIDI_Source * getInitialSource() override
Get the original source that sources to this pipe, following the chain recursively.
Definition: MIDI_Pipes.hpp:483
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
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 MIDI_Sink * getFinalSink()
Base case for recursive function.
Definition: MIDI_Pipes.hpp:133
virtual void sinkMIDIfromPipe(ChannelMessage)=0
Accept an incoming MIDI Channel message.
MIDI_Pipe * getSourcePipe()
Get a pointer to the pipe this sink is connected to, or nullptr if not connected.
Definition: MIDI_Pipes.hpp:120
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_Sink & operator=(const MIDI_Sink &)=delete
Copy assignment (copying not allowed).
Class that can send MIDI messages to a MIDI pipe.
Definition: MIDI_Pipes.hpp:154
MIDI_Pipe * getSinkPipe()
Get a pointer to the pipe this source is connected to, or nullptr if not connected.
Definition: MIDI_Pipes.hpp:234
virtual MIDI_Source * getInitialSource()
Base case for recursive function.
Definition: MIDI_Pipes.hpp:247
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 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
MIDI_Source & operator=(const MIDI_Source &)=delete
Copy assignment (copying not allowed).
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
std::pair< MIDI_Pipe, MIDI_Pipe > BidirectionalMIDI_Pipe
A bidirectional pipe consists of two unidirectional pipes.
Definition: MIDI_Pipes.hpp:589
MIDI_Pipe & operator>>(TrueMIDI_Source &source, MIDI_Pipe &pipe)
Connect a source to a pipe (source >> pipe).
Definition: MIDI_Pipes.hpp:592
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
Pipe & operator[](size_t i)
Definition: MIDI_Pipes.hpp:654
const Pipe & operator[](size_t i) const
Definition: MIDI_Pipes.hpp:655
A struct that is both a TrueMIDI_Sink and a TrueMIDI_Source.
Definition: MIDI_Pipes.hpp:586