Line data Source code
1 : #pragma once
2 :
3 : #include <AH/Containers/BitArray.hpp>
4 : #include <AH/STL/cstdint>
5 : #include <AH/STL/limits>
6 : #include <AH/STL/utility>
7 : #include <AH/Settings/Warnings.hpp>
8 : #include <MIDI_Parsers/MIDI_MessageTypes.hpp>
9 : #include <Settings/NamespaceSettings.hpp>
10 :
11 : AH_DIAGNOSTIC_WERROR()
12 :
13 : BEGIN_CS_NAMESPACE
14 :
15 : struct MIDIStaller;
16 : MIDIStaller *const eternal_stall =
17 : reinterpret_cast<MIDIStaller *>(std::numeric_limits<std::uintptr_t>::max());
18 :
19 : /**
20 : * @addtogroup MIDI_Routing MIDI Routing
21 : * @brief Operators and utilities for MIDI routing.
22 : *
23 : * Two or more MIDI interfaces can be connected
24 : * using @ref MIDI_Pipe "MIDI Pipes". The simplest pipe just carries messages
25 : * from the input interface to the output interface, but you can write rules for
26 : * filtering out certain messages, changing the channel of some messages, etc.
27 : *
28 : * Allows you to use syntax like:
29 : *
30 : * ~~~cpp
31 : * HardwareSerialMIDI_Interface
32 : * midiA = Serial1, midiB = Serial2, midiC = Serial3;
33 : * MIDI_PipeFactory<3> pipes; // Factory that can produce 3 pipes
34 : *
35 : * midiA >> pipes >> midiB;
36 : * midiC >> pipes >> midiB;
37 : * midiC << pipes << midiB;
38 : * ~~~
39 : *
40 : * Or for bidirectional connections:
41 : *
42 : * ~~~cpp
43 : * HardwareSerialMIDI_Interface
44 : * midiA = Serial1, midiB = Serial2, midiC = Serial3;
45 : * BidirectionalMIDI_PipeFactory<2> pipes; // Factory that can produce 2 pipes
46 : *
47 : * midiA | pipes | midiB;
48 : * midiA | pipes | midiC;
49 : * ~~~
50 : *
51 : * Have a look at the following examples on MIDI routing:
52 : *
53 : * - @ref MIDI_Pipes-Routing.ino
54 : * - @ref Dual-MIDI-Interface.ino
55 : *
56 : * If you're interested how the pipes work, see the documentation for
57 : * @ref MIDI_Pipe.
58 : *
59 : * @{
60 : */
61 :
62 : class MIDI_Pipe;
63 : class MIDI_Source;
64 : class MIDI_Sink;
65 : /// A MIDI_Sink that is not a MIDI_Pipe.
66 : using TrueMIDI_Sink = MIDI_Sink;
67 : /// A MIDI_Source that is not a MIDI_Pipe.
68 : using TrueMIDI_Source = MIDI_Source;
69 :
70 : /// Receives MIDI messages from a MIDI pipe.
71 : class MIDI_Sink {
72 : public:
73 : /// Default constructor.
74 2192 : MIDI_Sink() = default;
75 :
76 : /// Copy constructor (copying not allowed).
77 : MIDI_Sink(const MIDI_Sink &) = delete;
78 : /// Copy assignment (copying not allowed).
79 : MIDI_Sink &operator=(const MIDI_Sink &) = delete;
80 :
81 : /// Move constructor.
82 : MIDI_Sink(MIDI_Sink &&other);
83 : /// Move assignment.
84 : MIDI_Sink &operator=(MIDI_Sink &&other);
85 :
86 : /// Destructor.
87 : virtual ~MIDI_Sink();
88 :
89 : /// @name Sending data over a MIDI Pipe
90 : /// @{
91 :
92 : /// Accept an incoming MIDI Channel message.
93 : virtual void sinkMIDIfromPipe(ChannelMessage) = 0;
94 : /// Accept an incoming MIDI System Exclusive message.
95 : virtual void sinkMIDIfromPipe(SysExMessage) = 0;
96 : /// Accept an incoming MIDI System Common message.
97 : virtual void sinkMIDIfromPipe(SysCommonMessage) = 0;
98 : /// Accept an incoming MIDI Real-Time message.
99 : virtual void sinkMIDIfromPipe(RealTimeMessage) = 0;
100 :
101 : /// @}
102 :
103 : /// @name Connecting and disconnecting MIDI Pipes
104 : /// @{
105 :
106 : /// Fully connect a source pipe to this sink.
107 : void connectSourcePipe(MIDI_Pipe *source);
108 : /// Disconnect all source pipes that sink to this sink (recursively).
109 : void disconnectSourcePipes();
110 : /// Disconnect the given source from this sink. Leaves other sources
111 : /// connected.
112 : /// Returns true if the source was found and disconnected, false if the
113 : /// given source was not a direct or indirect source to this sink.
114 : bool disconnect(TrueMIDI_Source &source);
115 : bool disconnect(MIDI_Pipe &) = delete;
116 : /// Check if this sink is connected to a source pipe.
117 114 : bool hasSourcePipe() const { return sourcePipe != nullptr; }
118 : /// Get a pointer to the pipe this sink is connected to, or `nullptr` if
119 : /// not connected.
120 64 : MIDI_Pipe *getSourcePipe() { return sourcePipe; }
121 :
122 : /// @}
123 :
124 : private:
125 : /// Base case for recursive stall function.
126 : /// @see MIDI_Pipe::stallDownstream
127 36 : virtual void stallDownstream(MIDIStaller *, MIDI_Source *) {}
128 : /// Base case for recursive un-stall function.
129 : /// @see MIDI_Pipe::unstallDownstream
130 22 : virtual void unstallDownstream(MIDIStaller *, MIDI_Source *) {}
131 : /// Base case for recursive function.
132 : /// @see MIDI_Pipe::getFinalSink
133 67 : virtual MIDI_Sink *getFinalSink() { return this; }
134 : /// Disconnect only the first pipe connected to this sink. Leaves the
135 : /// other pipes connected to the original pipe, which doesn't have a sink
136 : /// anymore when this function finishes.
137 : /// Used to disconnect a MIDI_Pipe while preserving the connections of its
138 : /// “through” inputs.
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 1 : friend void swap(MIDI_Sink &a, MIDI_Sink &b) { swap(a, b); }
149 : };
150 :
151 : // :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: //
152 :
153 : /// Class that can send MIDI messages to a MIDI pipe.
154 : class MIDI_Source {
155 : public:
156 : /// Default constructor.
157 2101 : MIDI_Source() = default;
158 :
159 : /// Copy constructor (copying not allowed).
160 : MIDI_Source(const MIDI_Source &) = delete;
161 : /// Copy assignment (copying not allowed).
162 : MIDI_Source &operator=(const MIDI_Source &) = delete;
163 :
164 : /// Move constructor.
165 : MIDI_Source(MIDI_Source &&other);
166 : /// Move assignment.
167 : MIDI_Source &operator=(MIDI_Source &&other);
168 :
169 : /// Destructor.
170 : virtual ~MIDI_Source();
171 :
172 : /// @name Sending data over a MIDI Pipe
173 : /// @{
174 :
175 : /// Send a MIDI Channel Message down the pipe.
176 : void sourceMIDItoPipe(ChannelMessage);
177 : /// Send a MIDI System Exclusive message down the pipe.
178 : void sourceMIDItoPipe(SysExMessage);
179 : /// Send a MIDI System Common message down the pipe.
180 : void sourceMIDItoPipe(SysCommonMessage);
181 : /// Send a MIDI Real-Time message down the pipe.
182 : void sourceMIDItoPipe(RealTimeMessage);
183 :
184 : /// @}
185 :
186 : /// @name Stalling the sink pipes and exclusive access
187 : /// @{
188 :
189 : /// Stall this MIDI source.
190 : /// This means that this becomes the only source that can sink to the sinks
191 : /// connected to this source. Other sources have to wait until this source
192 : /// un-stalls the pipe before they can send again.
193 : /// @param cause
194 : /// Pointer to the reason for this stall, can be called back to
195 : /// un-stall the pipes.
196 : void stall(MIDIStaller *cause = eternal_stall);
197 : /// Un-stall the pipes connected to this source, so other sources
198 : /// are allowed to send again.
199 : /// @param cause
200 : /// Pointer to the reason for the stall (this has to be the same one
201 : /// that was used to stall).
202 : void unstall(MIDIStaller *cause = eternal_stall);
203 : /// Check if this source can write to the sinks it connects to.
204 : bool isStalled() const;
205 : /// Get a pointer to whatever is causing this MIDI source to be stalled.
206 : /// There could be multiple stallers, this function just returns one.
207 : MIDIStaller *getStaller() const;
208 : /// Get the name of whatever is causing this MIDI source to be stalled.
209 : /// There could be multiple stallers, this function just returns one.
210 : const char *getStallerName() const;
211 : /// Give the code that is stalling the MIDI sink pipes the opportunity to do
212 : /// its job and un-stall the pipes.
213 : void handleStallers() const;
214 :
215 : /// @}
216 :
217 : /// @name Connecting and disconnecting MIDI Pipes
218 : /// @{
219 :
220 : /// Fully connect a sink pipe to this source.
221 : void connectSinkPipe(MIDI_Pipe *sink);
222 : /// Disconnect all sink pipes that this source sinks to (recursively).
223 : void disconnectSinkPipes();
224 : /// Disconnect the given sink from this source. Leaves other sinks
225 : /// connected.
226 : /// Returns true if the sink was found and disconnected, false if the
227 : /// given sink was not a direct or indirect sink of this source.
228 : bool disconnect(TrueMIDI_Sink &sink);
229 : bool disconnect(MIDI_Pipe &) = delete;
230 : /// Check if this source is connected to a sink pipe.
231 391 : bool hasSinkPipe() const { return sinkPipe != nullptr; }
232 : /// Get a pointer to the pipe this source is connected to, or `nullptr` if
233 : /// not connected.
234 64 : MIDI_Pipe *getSinkPipe() { return sinkPipe; }
235 :
236 : /// @}
237 :
238 : private:
239 : /// Base case for recursive stall function.
240 : /// @see MIDI_Pipe::stallUpstream
241 41 : virtual void stallUpstream(MIDIStaller *, MIDI_Sink *) {}
242 : /// Base case for recursive un-stall function.
243 : /// @see MIDI_Pipe::unstallUpstream
244 23 : virtual void unstallUpstream(MIDIStaller *, MIDI_Sink *) {}
245 : /// Base case for recursive function.
246 : /// @see MIDI_Pipe::getInitialSource
247 67 : virtual MIDI_Source *getInitialSource() { return this; }
248 : /// Disconnect only the first pipe connected to this source. Leaves the
249 : /// other pipes connected to the original pipe, which doesn't have a source
250 : /// anymore when this function finishes.
251 : /// Used to disconnect a @ref MIDI_Pipe while preserving the connections of
252 : /// its “through” outputs.
253 : void disconnectSinkPipesShallow();
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 1 : friend void swap(MIDI_Source &a, MIDI_Source &b) { swap(a, b); }
263 : };
264 :
265 : // :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: //
266 :
267 : /**
268 : * @brief Class that routes MIDI messages from a MIDI_Source to a MIDI_Sink.
269 : *
270 : * A pipe has at least two, at most four connections. In the simplest case, a
271 : * pipe just has a source and a sink. The source sends MIDI messages down the
272 : * pipe, and the pipe sends them to the sink. A mapping or filter can be applied
273 : * to the messages traveling down the pipe.
274 : *
275 : * To be able to connect multiple pipes to a single sink or source, a pipe also
276 : * has a “through” output and a “through” input. Both are pipes, not sinks or
277 : * sources. All data that comes from the source is sent to the “through” output
278 : * as well, and all input that comes in from the “through” input is sent to the
279 : * sink as well. The mapping or filter is not applied to the data going from/to
280 : * the “through” connections.
281 : *
282 : * Merging data from multiple sources into a single sink can cause problems
283 : * because messages can be interleaved (e.g. RPN/NRPN or chunked system
284 : * exclusive messages). To circumvent this issue, a source can request exclusive
285 : * access to the pipe it's connected to. This stalls all other pipes that sink
286 : * into the same sinks as the exclusive source.
287 : * When other sources try to send to a stalled pipe, this will automatically
288 : * call back the source that originally stalled the pipes, so it can finish its
289 : * message, and then un-stall the pipes so the other pipe can send its data.
290 : *
291 : * **Pipe model**
292 : *
293 : * ~~~
294 : * ╭────────────────> through out
295 : * │ ┌────────┐
296 : * source >━━━━┷━┥ filter ┝━┯━━━> sink
297 : * └────────┘ │
298 : * through in >─────────────────╯
299 : * ~~~
300 : *
301 : * For example, if you have one source that should connect to two sinks, the
302 : * configuration is as follows:
303 : *
304 : * ~~~
305 : * through out × ┌────────┐
306 : * ╭────────────────> >━━━━┷━┥ pipe 2 ┝━┯━━━> sink 2
307 : * │ ┌────────┐ └────────┘ ×
308 : * source >━━━━┷━┥ pipe 1 ┝━┯━━━━━━━━━━━━━━━━━━━━━━━━━━━> sink 1
309 : * └────────┘ ×
310 : * ~~~
311 : *
312 : * If you have two sources that should connect to the same sink, a possible
313 : * configuration is:
314 : *
315 : * ~~~
316 : * × ┌────────┐
317 : * source 1 >━━━━━━━━━━━━━━━━━━━━━━━━━━━━┷━┥ pipe 1 ┝━┯━━━> sink
318 : * × ┌────────┐ └────────┘ │
319 : * source 2 >━━━━┷━┥ pipe 2 ┝━┯━━━> >─────────────────╯
320 : * └────────┘ ×
321 : * ~~~
322 : *
323 : * Each connection between a source and a sink has its own pipe, and no two
324 : * pipes are connected in series (only through the “through“ inputs/outputs).
325 : */
326 : class MIDI_Pipe : private MIDI_Sink, private MIDI_Source {
327 : public:
328 : /// Default constructor
329 1380 : MIDI_Pipe() = default;
330 :
331 : /// Copy constructor (copying not allowed).
332 : MIDI_Pipe(const MIDI_Pipe &) = delete;
333 : /// Copy assignment (copying not allowed).
334 : MIDI_Pipe &operator=(const MIDI_Pipe &) = delete;
335 :
336 : /// Move constructor.
337 : /// @todo Add move constructor.
338 : MIDI_Pipe(MIDI_Pipe &&) = delete;
339 : /// Move assignment.
340 : /// @todo Add move assignment.
341 : MIDI_Pipe &operator=(MIDI_Pipe &&) = delete;
342 :
343 : /// Destructor.
344 : virtual ~MIDI_Pipe();
345 :
346 : private:
347 : /// @name Mapping and filtering
348 : /// @{
349 :
350 : /// Function that maps, edits or filters MIDI messages, and then forwards
351 : /// them to the sink of the pipe.
352 : /// The MIDI_Pipe base class just forwards the messages to the sink, but
353 : /// you can override this method to create pipes that filter out some
354 : /// messages, transposes notes, changes the channel, etc.
355 137 : virtual void mapForwardMIDI(ChannelMessage msg) { sourceMIDItoSink(msg); }
356 : /// @copydoc mapForwardMIDI
357 15 : virtual void mapForwardMIDI(SysExMessage msg) { sourceMIDItoSink(msg); }
358 : /// @copydoc mapForwardMIDI
359 0 : virtual void mapForwardMIDI(SysCommonMessage msg) { sourceMIDItoSink(msg); }
360 : /// @copydoc mapForwardMIDI
361 21 : virtual void mapForwardMIDI(RealTimeMessage msg) { sourceMIDItoSink(msg); }
362 :
363 : /// @}
364 :
365 : public:
366 : /// @name Dealing with stalled pipes
367 : /// @{
368 :
369 : /// Check if this pipe is stalled.
370 241 : bool isStalled() const { return sink_staller || through_staller; }
371 : /// Get the staller (cause of the stall) that causes the sink of this pipe
372 : /// to be stalled.
373 : /// This pipe could sink to more than one stalled sink, this function just
374 : /// returns one of the causes.
375 72 : MIDIStaller *getSinkStaller() const { return sink_staller; }
376 : /// Get the name of the staller (cause of the stall) that causes the sink
377 : /// of this pipe to be stalled.
378 : const char *getSinkStallerName() const;
379 : /// Get the staller (cause of the stall) that causes the “through” output
380 : /// of this pipe to be stalled.
381 : /// The “through” output of this pipe could sink to more than one stalled
382 : /// sink, this function just returns one of the causes.
383 64 : MIDIStaller *getThroughStaller() const { return through_staller; }
384 : /// Get the name of the staller (cause of the stall) that causes the
385 : /// “through” output of this pipe to be stalled.
386 : const char *getThroughStallerName() const;
387 : /// Get any staller: returns @ref getSinkStaller() if it's not null,
388 : /// @ref getThroughStaller() otherwise.
389 : MIDIStaller *getStaller() const;
390 : /// Get the name of any staller.
391 : /// @see getStaller
392 : const char *getStallerName() const;
393 : /// Returns true if this pipe is either not stalled at all, or if the pipe
394 : /// is stalled by the given staller (cause).
395 : /// @see getSinkStaller
396 115 : bool sinkIsUnstalledOrStalledBy(MIDIStaller *cause) {
397 115 : return sink_staller == nullptr || sink_staller == cause;
398 : }
399 : /// Returns true if this pipe is either not stalled at all, or if the pipe
400 : /// is stalled by the given staller (cause).
401 : /// @see getThroughStaller
402 : bool throughIsUnstalledOrStalledBy(MIDIStaller *cause) {
403 : return through_staller == nullptr || through_staller == cause;
404 : }
405 :
406 : /// Give the code that is stalling the MIDI pipe the opportunity to do
407 : /// its job and unstall the pipe.
408 : void handleStallers() const;
409 :
410 : /// @}
411 :
412 : public:
413 : /// @name Check connections
414 : /// @{
415 :
416 : /// Check if this pipe is connected to a sink.
417 3711 : bool hasSink() const { return sink != nullptr; }
418 : /// Check if this pipe is connected to a source.
419 3573 : bool hasSource() const { return source != nullptr; }
420 : /// Check if this pipe has a “through” output that sends all incoming
421 : /// messages from the input (source) to another pipe.
422 2253 : bool hasThroughOut() const { return throughOut != nullptr; }
423 : /// Check if this pipe has a “through” input that merges all messages from
424 : /// another pipe into the output (sink).
425 2096 : bool hasThroughIn() const { return throughIn != nullptr; }
426 :
427 : /// @}
428 :
429 : public:
430 : /// @name MIDI Pipe connection management and inspection
431 : /// @{
432 :
433 : /// Disconnect this pipe from all other pipes, sources and sinks. If the
434 : /// “through” input and/or output were in use, they are reconnected to their
435 : /// original sink and/or source respectively, their behavior doesn't change.
436 : void disconnect();
437 : /// Disconnect the given sink from this pipe. The sink can be connected
438 : /// directly, or via the “through” output.
439 : /// Returns true if the sink was found and disconnected, false if the given
440 : /// sink was not a direct or indirect sink of this pipe.
441 11 : bool disconnect(TrueMIDI_Sink &sink) {
442 11 : if (getFinalSink() == &sink) {
443 5 : disconnect();
444 5 : return true;
445 : }
446 6 : if (hasThroughOut()) {
447 1 : return throughOut->disconnect(sink);
448 : }
449 5 : return false;
450 : }
451 : /// Disconnect the given source from this pipe. The source can be connected
452 : /// directly, or via the “through” input.
453 : /// Returns true if the source was found and disconnected, false if the
454 : /// given source was not a direct or indirect source to this pipe.
455 11 : bool disconnect(TrueMIDI_Source &source) {
456 11 : if (getInitialSource() == &source) {
457 5 : disconnect();
458 5 : return true;
459 : }
460 6 : if (hasThroughIn()) {
461 1 : return throughIn->disconnect(source);
462 : }
463 5 : return false;
464 : }
465 : bool disconnect(MIDI_Pipe &) = delete;
466 :
467 : /// Get the immediate source of this pipe.
468 4 : MIDI_Source *getSource() { return source; }
469 : /// Get the immediate sink of this pipe.
470 4 : MIDI_Sink *getSink() { return sink; }
471 : /// Get the pipe connected to the “through” output of this pipe.
472 8 : MIDI_Pipe *getThroughOut() { return throughOut; }
473 : /// Get the pipe connected to the “through” input of this pipe.
474 8 : MIDI_Pipe *getThroughIn() { return throughIn; }
475 :
476 : /// Get the sink this pipe eventually sinks to, following the chain
477 : /// recursively.
478 97 : MIDI_Sink *getFinalSink() override {
479 97 : return hasSink() ? sink->getFinalSink() : nullptr;
480 : }
481 : /// Get the original source that sources to this pipe, following the chain
482 : /// recursively.
483 97 : MIDI_Source *getInitialSource() override {
484 97 : return hasSource() ? source->getInitialSource() : nullptr;
485 : }
486 :
487 : /// @}
488 :
489 : private:
490 : /// @name Private functions to connect and disconnect sinks and sources
491 : /// @{
492 :
493 : /// Set the sink pointer to point to the given sink. Does not connect this
494 : /// pipe to the sink. Initiate the connection from the sink.
495 : void connectSink(MIDI_Sink *sink);
496 : /// Set the sink pointer to null. Does not disconnect this pipe from the
497 : /// sink. Initiate the disconnection from the sink.
498 : void disconnectSink();
499 : /// Set the source pointer to point to the given source. Does not connect
500 : /// this pipe to the source. Initiate the connection from the source.
501 : void connectSource(MIDI_Source *source);
502 : /// Set the source pointer to null. Does not disconnect this pipe from the
503 : /// source. Initiate the disconnection from the source.
504 : void disconnectSource();
505 :
506 : /// @}
507 :
508 : protected:
509 : /// Send the given MIDI message to the sink of this pipe.
510 : /// Useful when overriding @ref mapForwardMIDI.
511 : template <class Message>
512 183 : void sourceMIDItoSink(Message msg) {
513 183 : if (hasSink())
514 183 : sink->sinkMIDIfromPipe(msg);
515 183 : }
516 :
517 : protected:
518 : /// Accept a MIDI message from the source, forward it to the “through”
519 : /// output if necessary, map or filter the MIDI message if necessary,
520 : /// and send it to the sink. This function transfers messages from a
521 : /// @ref MIDI_Source to its @ref MIDI_Pipe.
522 : template <class Message>
523 176 : void acceptMIDIfromSource(Message msg) {
524 176 : if (hasThroughOut())
525 16 : throughOut->acceptMIDIfromSource(msg);
526 176 : mapForwardMIDI(msg);
527 176 : }
528 :
529 : private:
530 : /// Called when data arrives from an upstream pipe connected to our
531 : /// “through” input, this function forwards it to the sink.
532 2 : void sinkMIDIfromPipe(ChannelMessage msg) override {
533 2 : sourceMIDItoSink(msg);
534 2 : }
535 : /// @copydoc sinkMIDIfromPipe
536 2 : void sinkMIDIfromPipe(SysExMessage msg) override { sourceMIDItoSink(msg); }
537 : /// @copydoc sinkMIDIfromPipe
538 0 : void sinkMIDIfromPipe(SysCommonMessage msg) override {
539 0 : sourceMIDItoSink(msg);
540 0 : }
541 : /// @copydoc sinkMIDIfromPipe
542 4 : void sinkMIDIfromPipe(RealTimeMessage msg) override {
543 4 : sourceMIDItoSink(msg);
544 4 : }
545 :
546 : private:
547 : /// @name Private functions to stall and un-stall pipes
548 : /// @{
549 :
550 : /// Stall this pipe and all other pipes further downstream (following the
551 : /// path of the sink and the “through” output). Operates recursively until
552 : /// the end of the chain is reached, and then continues upstream (using
553 : /// @ref stallUpstream) to stall all pipes that connect to sources that
554 : /// sink to the same sink as this pipe and its “through” output.
555 : /// In short: stall all pipes that sink to the same sink as this pipe, and
556 : /// then stall all pipes that source to this first set of pipes.
557 : void stallDownstream(MIDIStaller *cause, MIDI_Source *stallsrc) override;
558 : /// Undoes the stalling by @ref stallDownstream.
559 : void unstallDownstream(MIDIStaller *cause, MIDI_Source *stallsrc) override;
560 :
561 : /// Stall this pipe and all other pipes further upstream (following the
562 : /// path of the "trough" input). Operates recursively until the end of the
563 : /// chain is reached. This function is called in the second stage of
564 : /// @ref stallDownstream.
565 : void stallUpstream(MIDIStaller *cause, MIDI_Sink *stallsrc) override;
566 : /// Undoes the stalling by @ref stallUpstream.
567 : void unstallUpstream(MIDIStaller *cause, MIDI_Sink *stallsrc) override;
568 :
569 : /// @}
570 :
571 : private:
572 : MIDI_Sink *sink = nullptr;
573 : MIDI_Source *source = nullptr;
574 : MIDI_Pipe *&throughOut = MIDI_Source::sinkPipe;
575 : MIDI_Pipe *&throughIn = MIDI_Sink::sourcePipe;
576 : MIDIStaller *sink_staller = nullptr;
577 : MIDIStaller *through_staller = nullptr;
578 :
579 : friend class MIDI_Sink;
580 : friend class MIDI_Source;
581 : };
582 :
583 : // :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: //
584 :
585 : /// A struct that is both a TrueMIDI_Sink and a TrueMIDI_Source.
586 : struct TrueMIDI_SinkSource : TrueMIDI_Sink, TrueMIDI_Source {};
587 :
588 : /// A bidirectional pipe consists of two unidirectional pipes.
589 : using BidirectionalMIDI_Pipe = std::pair<MIDI_Pipe, MIDI_Pipe>;
590 :
591 : /// Connect a source to a pipe (`source >> pipe`).
592 195 : inline MIDI_Pipe &operator>>(TrueMIDI_Source &source, MIDI_Pipe &pipe) {
593 195 : source.connectSinkPipe(&pipe);
594 194 : return pipe;
595 : }
596 :
597 : /// Connect a pipe to a sink (`pipe >> sink`).
598 197 : inline TrueMIDI_Sink &operator>>(MIDI_Pipe &pipe, TrueMIDI_Sink &sink) {
599 197 : sink.connectSourcePipe(&pipe);
600 196 : return sink;
601 : }
602 :
603 : /// Connect a sink to a pipe (`sink << pipe`).
604 71 : inline MIDI_Pipe &operator<<(TrueMIDI_Sink &sink, MIDI_Pipe &pipe) {
605 71 : sink.connectSourcePipe(&pipe);
606 71 : return pipe;
607 : }
608 :
609 : /// Connect a pipe to a source (`pipe << source`).
610 71 : inline TrueMIDI_Source &operator<<(MIDI_Pipe &pipe, TrueMIDI_Source &source) {
611 71 : source.connectSinkPipe(&pipe);
612 71 : return source;
613 : }
614 :
615 : /// Don't connect two pipes to eachother.
616 : MIDI_Pipe &operator<<(MIDI_Pipe &, MIDI_Pipe &) = delete;
617 :
618 : /// Connect a pipe to a sink+source (`pipe | source+sink`).
619 5 : inline TrueMIDI_SinkSource &operator|(BidirectionalMIDI_Pipe &pipe,
620 : TrueMIDI_SinkSource &sinksource) {
621 5 : sinksource.connectSinkPipe(&pipe.first);
622 5 : sinksource.connectSourcePipe(&pipe.second);
623 5 : return sinksource;
624 : }
625 :
626 : /// Connect a sink+source to a pipe (`source+sink | pipe`).
627 5 : inline BidirectionalMIDI_Pipe &operator|(TrueMIDI_SinkSource &sinksource,
628 : BidirectionalMIDI_Pipe &pipe) {
629 5 : sinksource.connectSinkPipe(&pipe.second);
630 5 : sinksource.connectSourcePipe(&pipe.first);
631 5 : return pipe;
632 : }
633 :
634 : // :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: //
635 :
636 : /**
637 : * @brief Class that produces multiple MIDI_Pipe%s.
638 : *
639 : * @tparam N
640 : * The maximum number of pipes it can produce.
641 : * @tparam Pipe
642 : * The type of pipes to produce.
643 : */
644 : template <size_t N, class Pipe = MIDI_Pipe>
645 : struct MIDI_PipeFactory {
646 : Pipe pipes[N];
647 : size_t index = 0;
648 :
649 93 : Pipe &getNext() {
650 93 : if (index >= N)
651 1 : FATAL_ERROR(F("Not enough pipes available"), 0x2459);
652 92 : return pipes[index++];
653 : }
654 10 : 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>
659 : using BidirectionalMIDI_PipeFactory =
660 : MIDI_PipeFactory<N, BidirectionalMIDI_Pipe>;
661 :
662 : template <size_t N, class Pipe>
663 65 : inline MIDI_Pipe &operator>>(TrueMIDI_Source &source,
664 : MIDI_PipeFactory<N, Pipe> &pipe_fact) {
665 65 : return source >> pipe_fact.getNext();
666 : }
667 :
668 : template <size_t N, class Pipe>
669 : MIDI_Pipe &operator>>(MIDI_Pipe &, MIDI_PipeFactory<N, Pipe> &) = delete;
670 :
671 : template <size_t N, class Pipe>
672 3 : inline TrueMIDI_Sink &operator>>(MIDI_PipeFactory<N, Pipe> &pipe_fact,
673 : TrueMIDI_Sink &sink) {
674 3 : return pipe_fact.getNext() >> sink;
675 : }
676 :
677 : template <size_t N, class Pipe>
678 : MIDI_Pipe &operator>>(MIDI_PipeFactory<N, Pipe> &, MIDI_Pipe &) = delete;
679 :
680 : template <size_t N, class Pipe>
681 25 : inline MIDI_Pipe &operator<<(TrueMIDI_Sink &sink,
682 : MIDI_PipeFactory<N, Pipe> &pipe_fact) {
683 25 : return sink << pipe_fact.getNext();
684 : }
685 :
686 : template <size_t N, class Pipe>
687 : inline TrueMIDI_Source &operator<<(MIDI_PipeFactory<N, Pipe> &pipe_fact,
688 : TrueMIDI_Source &source) {
689 : return pipe_fact.getNext() << source;
690 : }
691 :
692 : template <size_t N>
693 : inline TrueMIDI_SinkSource &
694 : operator|(BidirectionalMIDI_PipeFactory<N> &pipe_fact,
695 : TrueMIDI_SinkSource &sinksource) {
696 : return pipe_fact.getNext() | sinksource;
697 : }
698 :
699 : template <size_t N>
700 : inline BidirectionalMIDI_Pipe &
701 : operator|(TrueMIDI_SinkSource &sinksource,
702 : BidirectionalMIDI_PipeFactory<N> &pipe_fact) {
703 : return sinksource | pipe_fact.getNext();
704 : }
705 :
706 : /// @}
707 :
708 : END_CS_NAMESPACE
709 :
710 : AH_DIAGNOSTIC_POP()
|