Control Surface main
MIDI Control Surface library for Arduino
Loading...
Searching...
No Matches
MIDI_Pipes.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <Settings/SettingsWrapper.hpp>
4#if !DISABLE_PIPES
5
7#include <AH/STL/cstdint>
8#include <AH/STL/limits>
9#include <AH/STL/utility>
11#include <Settings/NamespaceSettings.hpp>
12
14
15struct MIDIStaller;
17 reinterpret_cast<MIDIStaller *>(std::numeric_limits<std::uintptr_t>::max());
18
64class MIDI_Pipe;
65class MIDI_Source;
66class MIDI_Sink;
71
75class MIDI_Sink {
76 public:
78 MIDI_Sink() = default;
79
81 MIDI_Sink(const MIDI_Sink &) = delete;
83 MIDI_Sink &operator=(const MIDI_Sink &) = delete;
84
86 MIDI_Sink(MIDI_Sink &&other);
89
91 virtual ~MIDI_Sink();
92
95
99 virtual void sinkMIDIfromPipe(SysExMessage) = 0;
104
106
109
111 void connectSourcePipe(MIDI_Pipe *source);
118 bool disconnect(TrueMIDI_Source &source);
119 bool disconnect(MIDI_Pipe &) = delete;
121 bool hasSourcePipe() const { return sourcePipe != nullptr; }
124 MIDI_Pipe *getSourcePipe() const { return sourcePipe; }
125
127
128 private:
137 virtual MIDI_Sink *getFinalSink() { return this; }
144
145 protected:
147
148 friend class MIDI_Pipe;
149
150 public:
151 static void swap(MIDI_Sink &a, MIDI_Sink &b);
152 friend void swap(MIDI_Sink &a, MIDI_Sink &b) { swap(a, b); }
153};
154
155// :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: //
156
161 public:
163 MIDI_Source() = default;
164
166 MIDI_Source(const MIDI_Source &) = delete;
169
171 MIDI_Source(MIDI_Source &&other);
174
176 virtual ~MIDI_Source();
177
180
189
191
194
202 void stall(MIDIStaller *cause = eternal_stall);
208 void unstall(MIDIStaller *cause = eternal_stall);
210 bool isStalled() const;
213 MIDIStaller *getStaller() const;
216 const char *getStallerName() const;
219 void handleStallers() const;
220
222
225
227 void connectSinkPipe(MIDI_Pipe *sink);
229 void disconnectSinkPipes();
234 bool disconnect(TrueMIDI_Sink &sink);
235 bool disconnect(MIDI_Pipe &) = delete;
237 bool hasSinkPipe() const { return sinkPipe != nullptr; }
241
243
244 private:
247 virtual void stallUpstream(MIDIStaller *, MIDI_Sink *) {}
253 virtual MIDI_Source *getInitialSource() { return this; }
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 friend void swap(MIDI_Source &a, MIDI_Source &b) { swap(a, b); }
269};
270
271// :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: //
272
335class MIDI_Pipe : private MIDI_Sink, private MIDI_Source {
336 public:
338 MIDI_Pipe() = default;
339
341 MIDI_Pipe(const MIDI_Pipe &) = delete;
343 MIDI_Pipe &operator=(const MIDI_Pipe &) = delete;
344
347 MIDI_Pipe(MIDI_Pipe &&) = delete;
351
353 virtual ~MIDI_Pipe();
354
355 private:
358
366 virtual void mapForwardMIDI(SysExMessage msg) { sourceMIDItoSink(msg); }
371
373
374 public:
377
379 bool isStalled() const { return sink_staller || through_staller; }
387 const char *getSinkStallerName() const;
395 const char *getThroughStallerName() const;
398 MIDIStaller *getStaller() const;
401 const char *getStallerName() const;
406 return sink_staller == nullptr || sink_staller == cause;
407 }
412 return through_staller == nullptr || through_staller == cause;
413 }
414
417 void handleStallers() const;
418
420
421 public:
424
426 bool hasSink() const { return sink != nullptr; }
428 bool hasSource() const { return source != nullptr; }
431 bool hasThroughOut() const { return getThroughOut() != nullptr; }
434 bool hasThroughIn() const { return getThroughIn() != nullptr; }
435
437
438 public:
441
445 void disconnect();
451 if (getFinalSink() == &sink) {
452 disconnect();
453 return true;
454 }
455 if (hasThroughOut()) {
456 return getThroughOut()->disconnect(sink);
457 }
458 return false;
459 }
465 if (getInitialSource() == &source) {
466 disconnect();
467 return true;
468 }
469 if (hasThroughIn()) {
470 return getThroughIn()->disconnect(source);
471 }
472 return false;
473 }
474 bool disconnect(MIDI_Pipe &) = delete;
475
477 MIDI_Source *getSource() const { return source; }
479 MIDI_Sink *getSink() const { return sink; }
484
488 return hasSink() ? sink->getFinalSink() : nullptr;
489 }
493 return hasSource() ? source->getInitialSource() : nullptr;
494 }
495
497
498 private:
501
507 void disconnectSink();
513 void disconnectSource();
514
516
517 protected:
520 template <class Message>
521 void sourceMIDItoSink(Message msg) {
522 if (hasSink())
524 }
525
526 protected:
531 template <class Message>
532 void acceptMIDIfromSource(Message msg) {
533 if (hasThroughOut())
535 mapForwardMIDI(msg);
536 }
537
538 private:
541 void sinkMIDIfromPipe(ChannelMessage msg) override {
542 sourceMIDItoSink(msg);
543 }
545 void sinkMIDIfromPipe(SysExMessage msg) override { sourceMIDItoSink(msg); }
548 sourceMIDItoSink(msg);
549 }
552 sourceMIDItoSink(msg);
553 }
554
555 private:
558
566 void stallDownstream(MIDIStaller *cause, MIDI_Source *stallsrc) override;
568 void unstallDownstream(MIDIStaller *cause, MIDI_Source *stallsrc) override;
569
574 void stallUpstream(MIDIStaller *cause, MIDI_Sink *stallsrc) override;
576 void unstallUpstream(MIDIStaller *cause, MIDI_Sink *stallsrc) override;
577
579
580 private:
581 MIDI_Sink *sink = nullptr;
582 MIDI_Source *source = nullptr;
585
586 friend class MIDI_Sink;
587 friend class MIDI_Source;
588};
589
590// :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: //
591
596
600using BidirectionalMIDI_Pipe = std::pair<MIDI_Pipe, MIDI_Pipe>;
601
604 source.connectSinkPipe(&pipe);
605 return pipe;
606}
607
610 sink.connectSourcePipe(&pipe);
611 return sink;
612}
613
616 sink.connectSourcePipe(&pipe);
617 return pipe;
618}
619
622 source.connectSinkPipe(&pipe);
623 return source;
624}
625
628
631 TrueMIDI_SinkSource &sinksource) {
632 sinksource.connectSinkPipe(&pipe.first);
633 sinksource.connectSourcePipe(&pipe.second);
634 return sinksource;
635}
636
640 sinksource.connectSinkPipe(&pipe.second);
641 sinksource.connectSourcePipe(&pipe.first);
642 return pipe;
643}
644
645// :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: //
646
658template <size_t N, class Pipe = MIDI_Pipe>
660 Pipe pipes[N];
661 size_t index = 0;
662
663 Pipe &getNext() {
664 if (index >= N)
665 FATAL_ERROR(F("Not enough pipes available"), 0x2459);
666 return pipes[index++];
667 }
668 Pipe &operator[](size_t i) { return pipes[i]; }
669 const Pipe &operator[](size_t i) const { return pipes[i]; }
670};
671
672template <size_t N>
675
676template <size_t N, class Pipe>
678 MIDI_PipeFactory<N, Pipe> &pipe_fact) {
679 return source >> pipe_fact.getNext();
680}
681
682template <size_t N, class Pipe>
684
685template <size_t N, class Pipe>
687 TrueMIDI_Sink &sink) {
688 return pipe_fact.getNext() >> sink;
689}
690
691template <size_t N, class Pipe>
693
694template <size_t N, class Pipe>
696 MIDI_PipeFactory<N, Pipe> &pipe_fact) {
697 return sink << pipe_fact.getNext();
698}
699
700template <size_t N, class Pipe>
702 TrueMIDI_Source &source) {
703 return pipe_fact.getNext() << source;
704}
705
706template <size_t N>
707inline TrueMIDI_SinkSource &
709 TrueMIDI_SinkSource &sinksource) {
710 return pipe_fact.getNext() | sinksource;
711}
712
713template <size_t N>
717 return sinksource | pipe_fact.getNext();
718}
719
721
723
724#else
725
727
728struct TrueMIDI_Source {
729 template <class... Args>
730 void sourceMIDItoPipe(Args &&...) {}
731};
732struct TrueMIDI_Sink {};
734
736
737#endif
MIDIStaller *const eternal_stall
#define END_CS_NAMESPACE
#define BEGIN_CS_NAMESPACE
Class that routes MIDI messages from a MIDI_Source to a MIDI_Sink.
MIDI_Sink * getSink() const
Get the immediate sink of this pipe.
void acceptMIDIfromSource(Message msg)
Accept a MIDI message from the source, forward it to the “through” output if necessary,...
bool hasSink() const
Check if this pipe is connected to a sink.
void sourceMIDItoSink(Message msg)
Send the given MIDI message to the sink of this pipe.
const char * getStallerName() const
Get the name of any staller.
MIDI_Pipe(MIDI_Pipe &&)=delete
Move constructor.
MIDIStaller * getStaller() const
Get any staller: returns getSinkStaller() if it's not null, getThroughStaller() otherwise.
virtual void mapForwardMIDI(SysExMessage msg)
Function that maps, edits or filters MIDI messages, and then forwards them to the sink of the pipe.
MIDI_Source * source
bool disconnect(MIDI_Pipe &)=delete
void connectSource(MIDI_Source *source)
Set the source pointer to point to the given source.
MIDI_Sink * sink
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.
void sinkMIDIfromPipe(SysCommonMessage msg) override
Called when data arrives from an upstream pipe connected to our “through” input, this function forwar...
virtual void mapForwardMIDI(SysCommonMessage msg)
Function that maps, edits or filters MIDI messages, and then forwards them to the sink of the pipe.
void unstallDownstream(MIDIStaller *cause, MIDI_Source *stallsrc) override
Undoes the stalling by stallDownstream.
bool hasSource() const
Check if this pipe is connected to a source.
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...
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...
MIDIStaller * getThroughStaller() const
Get the staller (cause of the stall) that causes the “through” output of this pipe to be stalled.
MIDIStaller * through_staller
void connectSink(MIDI_Sink *sink)
Set the sink pointer to point to the given sink.
const char * getSinkStallerName() const
Get the name of the staller (cause of the stall) that causes the sink of this pipe to be stalled.
MIDI_Pipe * getThroughOut() const
Get the pipe connected to the “through” output of this pipe.
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...
bool isStalled() const
Check if this pipe is stalled.
MIDIStaller * sink_staller
virtual ~MIDI_Pipe()
Destructor.
const char * getThroughStallerName() const
Get the name of the staller (cause of the stall) that causes the “through” output of this pipe to be ...
void unstallUpstream(MIDIStaller *cause, MIDI_Sink *stallsrc) override
Undoes the stalling by stallUpstream.
void disconnect()
Disconnect this pipe from all other pipes, sources and sinks.
bool disconnect(TrueMIDI_Sink &sink)
Disconnect the given sink from this pipe.
void sinkMIDIfromPipe(ChannelMessage msg) override
Called when data arrives from an upstream pipe connected to our “through” input, this function forwar...
virtual void mapForwardMIDI(ChannelMessage msg)
Function that maps, edits or filters MIDI messages, and then forwards them to the sink of the pipe.
MIDI_Sink * getFinalSink() override
Get the sink this pipe eventually sinks to, following the chain recursively.
void handleStallers() const
Give the code that is stalling the MIDI pipe the opportunity to do its job and unstall the pipe.
MIDI_Source * getSource() const
Get the immediate source of this pipe.
void disconnectSink()
Set the sink pointer to null.
MIDIStaller * getSinkStaller() const
Get the staller (cause of the stall) that causes the sink of this pipe to be stalled.
bool hasThroughIn() const
Check if this pipe has a “through” input that merges all messages from another pipe into the output (...
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...
void disconnectSource()
Set the source pointer to null.
bool disconnect(TrueMIDI_Source &source)
Disconnect the given source from this pipe.
MIDI_Source * getInitialSource() override
Get the original source that sources to this pipe, following the chain recursively.
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...
void sinkMIDIfromPipe(SysExMessage msg) override
Called when data arrives from an upstream pipe connected to our “through” input, this function forwar...
void stallUpstream(MIDIStaller *cause, MIDI_Sink *stallsrc) override
Stall this pipe and all other pipes further upstream (following the path of the "trough" input).
MIDI_Pipe * getThroughIn() const
Get the pipe connected to the “through” input of this pipe.
Receives MIDI messages from a MIDI pipe.
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.
virtual void stallDownstream(MIDIStaller *, MIDI_Source *)
Base case for recursive stall function.
bool disconnect(MIDI_Pipe &)=delete
virtual ~MIDI_Sink()
Destructor.
MIDI_Sink(const MIDI_Sink &)=delete
Copy constructor (copying not allowed).
friend void swap(MIDI_Sink &a, MIDI_Sink &b)
virtual void unstallDownstream(MIDIStaller *, MIDI_Source *)
Base case for recursive un-stall function.
virtual void sinkMIDIfromPipe(SysCommonMessage)=0
Accept an incoming MIDI System Common message.
MIDI_Sink()=default
Default constructor.
MIDI_Pipe * getSourcePipe() const
Get a pointer to the pipe this sink is connected to, or nullptr if not connected.
virtual MIDI_Sink * getFinalSink()
Base case for recursive function.
void disconnectSourcePipes()
Disconnect all source pipes that sink to this sink (recursively).
virtual void sinkMIDIfromPipe(ChannelMessage)=0
Accept an incoming MIDI Channel message.
void disconnectSourcePipesShallow()
Disconnect only the first pipe connected to this sink.
MIDI_Pipe * sourcePipe
bool disconnect(TrueMIDI_Source &source)
Disconnect the given source from this sink.
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.
MIDI_Sink & operator=(const MIDI_Sink &)=delete
Copy assignment (copying not allowed).
Class that can send MIDI messages to a MIDI pipe.
MIDI_Pipe * getSinkPipe()
Get a pointer to the pipe this source is connected to, or nullptr if not connected.
virtual MIDI_Source * getInitialSource()
Base case for recursive function.
const char * getStallerName() const
Get the name of whatever is causing this MIDI source to be stalled.
void connectSinkPipe(MIDI_Pipe *sink)
Fully connect a sink pipe to this source.
virtual ~MIDI_Source()
Destructor.
MIDIStaller * getStaller() const
Get a pointer to whatever is causing this MIDI source to be stalled.
bool disconnect(MIDI_Pipe &)=delete
MIDI_Source()=default
Default constructor.
void sourceMIDItoPipe(ChannelMessage)
Send a MIDI Channel Message down the pipe.
MIDI_Pipe * sinkPipe
void stall(MIDIStaller *cause=eternal_stall)
Stall this MIDI source.
virtual void stallUpstream(MIDIStaller *, MIDI_Sink *)
Base case for recursive stall function.
bool isStalled() const
Check if this source can write to the sinks it connects to.
friend void swap(MIDI_Source &a, MIDI_Source &b)
void disconnectSinkPipesShallow()
Disconnect only the first pipe connected to this source.
bool disconnect(TrueMIDI_Sink &sink)
Disconnect the given sink from this source.
void disconnectSinkPipes()
Disconnect all sink pipes that this source sinks to (recursively).
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...
virtual void unstallUpstream(MIDIStaller *, MIDI_Sink *)
Base case for recursive un-stall function.
MIDI_Source(const MIDI_Source &)=delete
Copy constructor (copying not allowed).
bool hasSinkPipe() const
Check if this source is connected to a sink pipe.
void unstall(MIDIStaller *cause=eternal_stall)
Un-stall the pipes connected to this source, so other sources are allowed to send again.
#define FATAL_ERROR(msg, errc)
Print the error message and error code, and stop the execution.
Definition Error.hpp:57
TrueMIDI_SinkSource & operator|(BidirectionalMIDI_Pipe &pipe, TrueMIDI_SinkSource &sinksource)
Connect a pipe to a sink+source (pipe | source+sink).
std::pair< MIDI_Pipe, MIDI_Pipe > BidirectionalMIDI_Pipe
A bidirectional pipe consists of two unidirectional pipes.
MIDI_Pipe & operator>>(TrueMIDI_Source &source, MIDI_Pipe &pipe)
Connect a source to a pipe (source >> pipe).
MIDI_Pipe & operator<<(TrueMIDI_Sink &sink, MIDI_Pipe &pipe)
Connect a sink to a pipe (sink << pipe).
Struct that can cause a MIDI_Pipe to be stalled.
Class that produces multiple MIDI_Pipes.
Pipe & operator[](size_t i)
const Pipe & operator[](size_t i) const
A struct that is both a TrueMIDI_Sink and a TrueMIDI_Source.