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