Control Surface disable-pipes
MIDI Control Surface library for Arduino
MIDI_Pipes.cpp
Go to the documentation of this file.
1#include <Settings/SettingsWrapper.hpp>
2#if !DISABLE_PIPES
3
4#include "MIDI_Pipes.hpp"
5#include "MIDI_Staller.hpp"
6#include <AH/Error/Error.hpp>
7#include <AH/STL/utility>
8
9#if defined(ESP32) || !defined(ARDUINO)
10#include <mutex>
11#endif
12
14
16
17// :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: //
18
19void MIDI_Sink::connectSourcePipe(MIDI_Pipe *source) {
20 if (this->sourcePipe == nullptr) {
21 source->connectSink(this);
22 this->sourcePipe = source;
23 } else {
24 this->sourcePipe->connectSourcePipe(source);
25 }
26}
27
29 if (sourcePipe != nullptr) {
32 sourcePipe = nullptr;
33 }
34}
35
37 if (sourcePipe != nullptr) {
39 sourcePipe = nullptr;
40 }
41}
42
44 if (!hasSourcePipe())
45 return false;
46 return sourcePipe->disconnect(source);
47}
48
50
52 : sourcePipe(std::exchange(other.sourcePipe, nullptr)) {
53 if (this->hasSourcePipe()) {
55 this->sourcePipe->connectSink(this);
56 }
57}
58
61 if (a.hasSourcePipe()) {
64 }
65 if (b.hasSourcePipe()) {
68 }
69}
70
72 swap(*this, other);
73 return *this;
74}
75
76// :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: //
77
79 if (this->sinkPipe == nullptr) {
80 sink->connectSource(this);
81 this->sinkPipe = sink;
82 } else {
83 this->sinkPipe->connectSinkPipe(sink);
84 }
85}
86
88 if (sinkPipe != nullptr) {
91 sinkPipe = nullptr;
92 }
93}
94
96 if (sinkPipe != nullptr) {
98 sinkPipe = nullptr;
99 }
100}
101
103 if (!hasSinkPipe())
104 return false;
105 return sinkPipe->disconnect(sink);
106}
107
109 : sinkPipe(std::exchange(other.sinkPipe, nullptr)) {
110 if (this->hasSinkPipe()) {
111 this->sinkPipe->disconnectSource();
112 this->sinkPipe->connectSource(this);
113 }
114}
115
118 if (a.hasSinkPipe()) {
120 a.sinkPipe->connectSource(&a);
121 }
122 if (b.hasSinkPipe()) {
124 b.sinkPipe->connectSource(&b);
125 }
126}
127
129 swap(*this, other);
130 return *this;
131}
132
134
136 if (sinkPipe != nullptr) {
139 }
140}
142 if (sinkPipe != nullptr) {
145 }
146}
148 if (sinkPipe != nullptr) {
151 }
152}
154 if (sinkPipe != nullptr) {
155 // Always send write to pipe, don't check if it's stalled or not
157 }
158}
159
161 if (hasSinkPipe())
162 sinkPipe->stallDownstream(cause, this);
163}
164
166 if (hasSinkPipe())
167 sinkPipe->unstallDownstream(cause, this);
168}
169
171 if (hasSinkPipe())
172 return sinkPipe->isStalled();
173 return false;
174}
175
177 if (hasSinkPipe())
178 return sinkPipe->getStaller();
179 return nullptr;
180}
181
182const char *MIDI_Source::getStallerName() const {
184}
185
187 if (hasSinkPipe())
189}
190
191// :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: //
192
194 if (this->sink != nullptr) {
195 FATAL_ERROR(F("This pipe is already connected to a sink"), 0x9145);
196 return; // LCOV_EXCL_LINE
197 }
198 this->sink = sink;
199}
200
201void MIDI_Pipe::disconnectSink() { this->sink = nullptr; }
202
204 if (this->source != nullptr) {
205 FATAL_ERROR(F("This pipe is already connected to a source"), 0x9146);
206 return; // LCOV_EXCL_LINE
207 }
208 this->source = source;
209}
210
211void MIDI_Pipe::disconnectSource() { this->source = nullptr; }
212
214 if (hasSink() && hasThroughIn()) {
215 auto oldSink = sink;
216 auto oldThroughIn = throughIn;
218 this->disconnectSourcePipesShallow(); // disconnect throughIn
219 oldSink->connectSourcePipe(oldThroughIn);
220 }
221 if (hasSource() && hasThroughOut()) {
222 auto oldSource = source;
223 auto oldThroughOut = throughOut;
225 this->disconnectSinkPipesShallow(); // disconnect throughOut
226 oldSource->connectSinkPipe(oldThroughOut);
227 }
228 if (hasSink())
230
231 if (hasSource())
233
234 if (hasThroughIn() || hasThroughOut())
235 FATAL_ERROR(F("Invalid state"), 0x9147); // LCOV_EXCL_LINE
236}
237
239
241 if (!sinkIsUnstalledOrStalledBy(cause)) {
242 FATAL_ERROR(F("Cannot stall pipe from ")
244 << F(" because pipe is already stalled by ")
246 0x6665);
247 } // LCOV_EXCL_LINE
248 sink_staller = cause;
249 if (hasThroughOut() && stallsrc == source)
250 throughOut->stallDownstream(cause, this);
251 if (hasSink())
252 sink->stallDownstream(cause, this);
253 if (hasSource() && source != stallsrc) {
254 // If our through output is stalled, that means our upstream is stalled
255 // as well by this staller. Unstall it first, and replace it by the new
256 // staller
257 if (through_staller != nullptr)
259 source->stallUpstream(cause, this);
260 }
261 if (hasThroughIn() && throughIn != stallsrc)
262 throughIn->stallUpstream(cause, this);
263}
264
266 if (stallsrc == sink) {
267 // This cannot be a different cause, because then our sink would
268 // already have caught it in stallDownstream().
269 sink_staller = cause;
270 if (hasSource())
271 source->stallUpstream(cause, this);
272 if (hasThroughIn())
273 throughIn->stallUpstream(cause, this);
274 } else {
275 if (through_staller == nullptr) {
276 through_staller = cause;
277 if (hasSource())
278 source->stallUpstream(cause, this);
279 }
280 }
281}
282
284 if (!sinkIsUnstalledOrStalledBy(cause)) {
285 FATAL_ERROR(F("Cannot unstall pipe from ")
287 << F(" because pipe is stalled by ")
289 0x6666);
290 } // LCOV_EXCL_LINE
291 this->sink_staller = nullptr;
292 if (hasThroughOut() && stallsrc == source)
293 throughOut->unstallDownstream(cause, this);
294 if (hasSink())
295 sink->unstallDownstream(cause, this);
296 if (hasSource() && source != stallsrc) {
297 source->unstallUpstream(cause, this);
298 // If the through output of this pipe is stalled, we cannot just unstall
299 // our upstream, we have to update it our through output staller
300 if (through_staller != nullptr)
302 }
303 if (hasThroughIn() && throughIn != stallsrc)
304 throughIn->unstallUpstream(cause, this);
305}
306
308 if (stallsrc == sink) {
309 // This cannot be a different cause, because then our sink would
310 // already have caught it in unstallDownstream().
311 sink_staller = nullptr;
312 if (hasSource())
313 source->unstallUpstream(cause, this);
314 if (hasThroughIn())
315 throughIn->unstallUpstream(cause, this);
316 } else {
317 if (cause == through_staller) {
318 through_staller = nullptr;
319 if (hasSource())
320 source->unstallUpstream(cause, this);
321 }
322 }
323}
324
325const char *MIDI_Pipe::getSinkStallerName() const {
327}
328
331}
332
335}
336
337const char *MIDI_Pipe::getStallerName() const {
339}
340
342 if (!isStalled())
343 return;
345 FATAL_ERROR(F("Unable to unstall pipe (eternal stall)"), 0x4827);
346 uint8_t iterations = 10;
347 while(isStalled() && iterations-- > 0) {
348 if (sink_staller)
350 if (through_staller)
352 }
353}
354
355// :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: //
356
358 if (s == nullptr)
359 return "(null)";
360 if (s == eternal_stall)
361 return "(eternal stall)";
362 return s->getName();
363}
364
366
368
369#endif
void swap(AHEncoder &a, AHEncoder &b)
Definition: AHEncoder.cpp:25
MIDIStaller *const eternal_stall
Definition: MIDI_Pipes.hpp:19
#define END_CS_NAMESPACE
#define BEGIN_CS_NAMESPACE
#define AH_DIAGNOSTIC_POP()
Definition: Warnings.hpp:53
#define AH_DIAGNOSTIC_WERROR()
Definition: Warnings.hpp:52
Class that routes MIDI messages from a MIDI_Source to a MIDI_Sink.
Definition: MIDI_Pipes.hpp:329
void acceptMIDIfromSource(Message msg)
Accept a MIDI message from the source, forward it to the “through” output if necessary,...
Definition: MIDI_Pipes.hpp:526
bool hasSink() const
Check if this pipe is connected to a sink.
Definition: MIDI_Pipes.hpp:420
const char * getStallerName() const
Get the name of any staller.
Definition: MIDI_Pipes.cpp:337
MIDIStaller * getStaller() const
Get any staller: returns getSinkStaller() if it's not null, getThroughStaller() otherwise.
Definition: MIDI_Pipes.cpp:333
MIDI_Source * source
Definition: MIDI_Pipes.hpp:576
void connectSource(MIDI_Source *source)
Set the source pointer to point to the given source.
Definition: MIDI_Pipes.cpp:203
MIDI_Sink * sink
Definition: MIDI_Pipes.hpp:575
void unstallDownstream(MIDIStaller *cause, MIDI_Source *stallsrc) override
Undoes the stalling by stallDownstream.
Definition: MIDI_Pipes.cpp:283
bool hasSource() const
Check if this pipe is connected to a source.
Definition: MIDI_Pipes.hpp:422
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:240
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:425
MIDIStaller * through_staller
Definition: MIDI_Pipes.hpp:580
void connectSink(MIDI_Sink *sink)
Set the sink pointer to point to the given sink.
Definition: MIDI_Pipes.cpp:193
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:325
MIDI_Pipe *& throughOut
Definition: MIDI_Pipes.hpp:577
bool isStalled() const
Check if this pipe is stalled.
Definition: MIDI_Pipes.hpp:373
MIDIStaller * sink_staller
Definition: MIDI_Pipes.hpp:579
virtual ~MIDI_Pipe()
Destructor.
Definition: MIDI_Pipes.cpp:238
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:329
void unstallUpstream(MIDIStaller *cause, MIDI_Sink *stallsrc) override
Undoes the stalling by stallUpstream.
Definition: MIDI_Pipes.cpp:307
void disconnect()
Disconnect this pipe from all other pipes, sources and sinks.
Definition: MIDI_Pipes.cpp:213
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:341
MIDI_Pipe *& throughIn
Definition: MIDI_Pipes.hpp:578
void disconnectSink()
Set the sink pointer to null.
Definition: MIDI_Pipes.cpp:201
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:428
void disconnectSource()
Set the source pointer to null.
Definition: MIDI_Pipes.cpp:211
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:399
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:265
Receives MIDI messages from a MIDI pipe.
Definition: MIDI_Pipes.hpp:74
void connectSourcePipe(MIDI_Pipe *source)
Fully connect a source pipe to this sink.
Definition: MIDI_Pipes.cpp:19
virtual void stallDownstream(MIDIStaller *, MIDI_Source *)
Base case for recursive stall function.
Definition: MIDI_Pipes.hpp:130
virtual ~MIDI_Sink()
Destructor.
Definition: MIDI_Pipes.cpp:49
static void swap(MIDI_Sink &a, MIDI_Sink &b)
Definition: MIDI_Pipes.cpp:59
virtual void unstallDownstream(MIDIStaller *, MIDI_Source *)
Base case for recursive un-stall function.
Definition: MIDI_Pipes.hpp:133
MIDI_Sink()=default
Default constructor.
void disconnectSourcePipes()
Disconnect all source pipes that sink to this sink (recursively).
Definition: MIDI_Pipes.cpp:28
void disconnectSourcePipesShallow()
Disconnect only the first pipe connected to this sink.
Definition: MIDI_Pipes.cpp:36
MIDI_Pipe * sourcePipe
Definition: MIDI_Pipes.hpp:145
bool disconnect(TrueMIDI_Source &source)
Disconnect the given source from this sink.
Definition: MIDI_Pipes.cpp:43
bool hasSourcePipe() const
Check if this sink is connected to a source pipe.
Definition: MIDI_Pipes.hpp:120
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:157
const char * getStallerName() const
Get the name of whatever is causing this MIDI source to be stalled.
Definition: MIDI_Pipes.cpp:182
void connectSinkPipe(MIDI_Pipe *sink)
Fully connect a sink pipe to this source.
Definition: MIDI_Pipes.cpp:78
virtual ~MIDI_Source()
Destructor.
Definition: MIDI_Pipes.cpp:133
MIDIStaller * getStaller() const
Get a pointer to whatever is causing this MIDI source to be stalled.
Definition: MIDI_Pipes.cpp:176
MIDI_Source()=default
Default constructor.
void sourceMIDItoPipe(ChannelMessage)
Send a MIDI Channel Message down the pipe.
Definition: MIDI_Pipes.cpp:135
MIDI_Pipe * sinkPipe
Definition: MIDI_Pipes.hpp:259
void stall(MIDIStaller *cause=eternal_stall)
Stall this MIDI source.
Definition: MIDI_Pipes.cpp:160
virtual void stallUpstream(MIDIStaller *, MIDI_Sink *)
Base case for recursive stall function.
Definition: MIDI_Pipes.hpp:244
bool isStalled() const
Check if this source can write to the sinks it connects to.
Definition: MIDI_Pipes.cpp:170
static void swap(MIDI_Source &a, MIDI_Source &b)
Definition: MIDI_Pipes.cpp:116
void disconnectSinkPipesShallow()
Disconnect only the first pipe connected to this source.
Definition: MIDI_Pipes.cpp:95
bool disconnect(TrueMIDI_Sink &sink)
Disconnect the given sink from this source.
Definition: MIDI_Pipes.cpp:102
void disconnectSinkPipes()
Disconnect all sink pipes that this source sinks to (recursively).
Definition: MIDI_Pipes.cpp:87
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:186
virtual void unstallUpstream(MIDIStaller *, MIDI_Sink *)
Base case for recursive un-stall function.
Definition: MIDI_Pipes.hpp:247
bool hasSinkPipe() const
Check if this source is connected to a sink pipe.
Definition: MIDI_Pipes.hpp:234
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:165
#define FATAL_ERROR(msg, errc)
Print the error message and error code, and stop the execution.
Definition: Error.hpp:60
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:357