Control Surface master
MIDI Control Surface library for Arduino
MIDI_Pipes.cpp
Go to the documentation of this file.
1#include "MIDI_Pipes.hpp"
2#include "MIDI_Staller.hpp"
3#include <AH/Error/Error.hpp>
4#include <AH/STL/utility>
5
6#if defined(ESP32) || !defined(ARDUINO)
7#include <mutex>
8#endif
9
11
13
14// :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: //
15
16void MIDI_Sink::connectSourcePipe(MIDI_Pipe *source) {
17 if (this->sourcePipe == nullptr) {
18 source->connectSink(this);
19 this->sourcePipe = source;
20 } else {
21 this->sourcePipe->connectSourcePipe(source);
22 }
23}
24
26 if (sourcePipe != nullptr) {
29 sourcePipe = nullptr;
30 }
31}
32
34 if (sourcePipe != nullptr) {
36 sourcePipe = nullptr;
37 }
38}
39
41 if (!hasSourcePipe())
42 return false;
43 return sourcePipe->disconnect(source);
44}
45
47
49 : sourcePipe(std::exchange(other.sourcePipe, nullptr)) {
50 if (this->hasSourcePipe()) {
52 this->sourcePipe->connectSink(this);
53 }
54}
55
58 if (a.hasSourcePipe()) {
61 }
62 if (b.hasSourcePipe()) {
65 }
66}
67
69 swap(*this, other);
70 return *this;
71}
72
73// :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: //
74
76 if (this->sinkPipe == nullptr) {
77 sink->connectSource(this);
78 this->sinkPipe = sink;
79 } else {
80 this->sinkPipe->connectSinkPipe(sink);
81 }
82}
83
85 if (sinkPipe != nullptr) {
88 sinkPipe = nullptr;
89 }
90}
91
93 if (sinkPipe != nullptr) {
95 sinkPipe = nullptr;
96 }
97}
98
100 if (!hasSinkPipe())
101 return false;
102 return sinkPipe->disconnect(sink);
103}
104
106 : sinkPipe(std::exchange(other.sinkPipe, nullptr)) {
107 if (this->hasSinkPipe()) {
108 this->sinkPipe->disconnectSource();
109 this->sinkPipe->connectSource(this);
110 }
111}
112
115 if (a.hasSinkPipe()) {
117 a.sinkPipe->connectSource(&a);
118 }
119 if (b.hasSinkPipe()) {
121 b.sinkPipe->connectSource(&b);
122 }
123}
124
126 swap(*this, other);
127 return *this;
128}
129
131
133 if (sinkPipe != nullptr) {
136 }
137}
139 if (sinkPipe != nullptr) {
142 }
143}
145 if (sinkPipe != nullptr) {
148 }
149}
151 if (sinkPipe != nullptr) {
152 // Always send write to pipe, don't check if it's stalled or not
154 }
155}
156
158 if (hasSinkPipe())
159 sinkPipe->stallDownstream(cause, this);
160}
161
163 if (hasSinkPipe())
164 sinkPipe->unstallDownstream(cause, this);
165}
166
168 if (hasSinkPipe())
169 return sinkPipe->isStalled();
170 return false;
171}
172
174 if (hasSinkPipe())
175 return sinkPipe->getStaller();
176 return nullptr;
177}
178
179const char *MIDI_Source::getStallerName() const {
181}
182
184 if (hasSinkPipe())
186}
187
188// :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: //
189
191 if (this->sink != nullptr) {
192 FATAL_ERROR(F("This pipe is already connected to a sink"), 0x9145);
193 return; // LCOV_EXCL_LINE
194 }
195 this->sink = sink;
196}
197
198void MIDI_Pipe::disconnectSink() { this->sink = nullptr; }
199
201 if (this->source != nullptr) {
202 FATAL_ERROR(F("This pipe is already connected to a source"), 0x9146);
203 return; // LCOV_EXCL_LINE
204 }
205 this->source = source;
206}
207
208void MIDI_Pipe::disconnectSource() { this->source = nullptr; }
209
211 if (hasSink() && hasThroughIn()) {
212 auto oldSink = sink;
213 auto oldThroughIn = throughIn;
215 this->disconnectSourcePipesShallow(); // disconnect throughIn
216 oldSink->connectSourcePipe(oldThroughIn);
217 }
218 if (hasSource() && hasThroughOut()) {
219 auto oldSource = source;
220 auto oldThroughOut = throughOut;
222 this->disconnectSinkPipesShallow(); // disconnect throughOut
223 oldSource->connectSinkPipe(oldThroughOut);
224 }
225 if (hasSink())
227
228 if (hasSource())
230
231 if (hasThroughIn() || hasThroughOut())
232 FATAL_ERROR(F("Invalid state"), 0x9147); // LCOV_EXCL_LINE
233}
234
236
238 if (!sinkIsUnstalledOrStalledBy(cause)) {
239 FATAL_ERROR(F("Cannot stall pipe from ")
241 << F(" because pipe is already stalled by ")
243 0x6665);
244 } // LCOV_EXCL_LINE
245 sink_staller = cause;
246 if (hasThroughOut() && stallsrc == source)
247 throughOut->stallDownstream(cause, this);
248 if (hasSink())
249 sink->stallDownstream(cause, this);
250 if (hasSource() && source != stallsrc) {
251 // If our through output is stalled, that means our upstream is stalled
252 // as well by this staller. Unstall it first, and replace it by the new
253 // staller
254 if (through_staller != nullptr)
256 source->stallUpstream(cause, this);
257 }
258 if (hasThroughIn() && throughIn != stallsrc)
259 throughIn->stallUpstream(cause, this);
260}
261
263 if (stallsrc == sink) {
264 // This cannot be a different cause, because then our sink would
265 // already have caught it in stallDownstream().
266 sink_staller = cause;
267 if (hasSource())
268 source->stallUpstream(cause, this);
269 if (hasThroughIn())
270 throughIn->stallUpstream(cause, this);
271 } else {
272 if (through_staller == nullptr) {
273 through_staller = cause;
274 if (hasSource())
275 source->stallUpstream(cause, this);
276 }
277 }
278}
279
281 if (!sinkIsUnstalledOrStalledBy(cause)) {
282 FATAL_ERROR(F("Cannot unstall pipe from ")
284 << F(" because pipe is stalled by ")
286 0x6666);
287 } // LCOV_EXCL_LINE
288 this->sink_staller = nullptr;
289 if (hasThroughOut() && stallsrc == source)
290 throughOut->unstallDownstream(cause, this);
291 if (hasSink())
292 sink->unstallDownstream(cause, this);
293 if (hasSource() && source != stallsrc) {
294 source->unstallUpstream(cause, this);
295 // If the through output of this pipe is stalled, we cannot just unstall
296 // our upstream, we have to update it our through output staller
297 if (through_staller != nullptr)
299 }
300 if (hasThroughIn() && throughIn != stallsrc)
301 throughIn->unstallUpstream(cause, this);
302}
303
305 if (stallsrc == sink) {
306 // This cannot be a different cause, because then our sink would
307 // already have caught it in unstallDownstream().
308 sink_staller = nullptr;
309 if (hasSource())
310 source->unstallUpstream(cause, this);
311 if (hasThroughIn())
312 throughIn->unstallUpstream(cause, this);
313 } else {
314 if (cause == through_staller) {
315 through_staller = nullptr;
316 if (hasSource())
317 source->unstallUpstream(cause, this);
318 }
319 }
320}
321
322const char *MIDI_Pipe::getSinkStallerName() const {
324}
325
328}
329
332}
333
334const char *MIDI_Pipe::getStallerName() const {
336}
337
339 if (!isStalled())
340 return;
342 FATAL_ERROR(F("Unable to unstall pipe (eternal stall)"), 0x4827);
343 uint8_t iterations = 10;
344 while(isStalled() && iterations-- > 0) {
345 if (sink_staller)
347 if (through_staller)
349 }
350}
351
352// :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: //
353
355 if (s == nullptr)
356 return "(null)";
357 if (s == eternal_stall)
358 return "(eternal stall)";
359 return s->getName();
360}
361
363
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
const char * getStallerName() const
Get the name of any staller.
Definition: MIDI_Pipes.cpp:334
MIDIStaller * getStaller() const
Get any staller: returns getSinkStaller() if it's not null, getThroughStaller() otherwise.
Definition: MIDI_Pipes.cpp:330
MIDI_Source * source
Definition: MIDI_Pipes.hpp:573
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
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
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 *& 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
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
void disconnectSource()
Set the source pointer to null.
Definition: MIDI_Pipes.cpp:208
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 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
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
virtual ~MIDI_Sink()
Destructor.
Definition: MIDI_Pipes.cpp:46
static void swap(MIDI_Sink &a, MIDI_Sink &b)
Definition: MIDI_Pipes.cpp:56
virtual void unstallDownstream(MIDIStaller *, MIDI_Source *)
Base case for recursive un-stall function.
Definition: MIDI_Pipes.hpp:130
MIDI_Sink()=default
Default constructor.
void disconnectSourcePipes()
Disconnect all source pipes that sink to this sink (recursively).
Definition: MIDI_Pipes.cpp:25
void disconnectSourcePipesShallow()
Disconnect only the first pipe connected to this sink.
Definition: MIDI_Pipes.cpp:33
MIDI_Pipe * sourcePipe
Definition: MIDI_Pipes.hpp:142
bool disconnect(TrueMIDI_Source &source)
Disconnect the given source from this sink.
Definition: MIDI_Pipes.cpp:40
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
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
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
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
void swap(T &t1, T &t2)
Definition: move.h:21
Struct that can cause a MIDI_Pipe to be stalled.
virtual const char * getName() const
Get the staller's name for debugging purposes.
virtual void handleStall()=0
Call back that should finish any MIDI messages that are in progress, and un-stall the pipe or MIDI so...
static const char * getNameNull(MIDIStaller *s)
Get the staller's name for debugging purposes.
Definition: MIDI_Pipes.cpp:354