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