1 /*
2  * Copyright (C) 2019-2021 Alexandros Theodotou <alex at zrythm dot org>
3  *
4  * This file is part of Zrythm
5  *
6  * Zrythm is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Affero General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * Zrythm is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Affero General Public License for more details.
15  *
16  * You should have received a copy of the GNU Affero General Public License
17  * along with Zrythm.  If not, see <https://www.gnu.org/licenses/>.
18  */
19 
20 #include "zrythm-config.h"
21 
22 #include "audio/engine.h"
23 #include "audio/engine_jack.h"
24 #include "audio/engine_rtaudio.h"
25 #include "audio/engine_rtmidi.h"
26 #include "audio/ext_port.h"
27 #include "audio/rtaudio_device.h"
28 #include "audio/rtmidi_device.h"
29 #include "audio/windows_mme_device.h"
30 #include "project.h"
31 #include "utils/objects.h"
32 #include "zrythm_app.h"
33 
34 #ifdef HAVE_JACK
35 #include "weak_libjack.h"
36 #endif
37 
38 static ExtPort *
_create(void)39 _create (void)
40 {
41   ExtPort * self = object_new (ExtPort);
42   self->schema_version = EXT_PORT_SCHEMA_VERSION;
43 
44   return self;
45 }
46 
47 /**
48  * Inits the ExtPort after loading a project.
49  */
50 void
ext_port_init_loaded(ExtPort * self,HardwareProcessor * hw_processor)51 ext_port_init_loaded (
52   ExtPort *           self,
53   HardwareProcessor * hw_processor)
54 {
55   self->hw_processor = hw_processor;
56 }
57 
58 /**
59  * Returns the buffer of the external port.
60  */
61 float *
ext_port_get_buffer(ExtPort * self,nframes_t nframes)62 ext_port_get_buffer (
63   ExtPort * self,
64   nframes_t nframes)
65 {
66   switch (self->type)
67     {
68 #ifdef HAVE_JACK
69     case EXT_PORT_TYPE_JACK:
70       return
71         (float *)
72         jack_port_get_buffer (
73           self->jport,
74           nframes);
75       break;
76 #endif
77 #ifdef HAVE_ALSA
78     case EXT_PORT_TYPE_ALSA:
79 #endif
80 #ifdef HAVE_RTMIDI
81     case EXT_PORT_TYPE_RTMIDI:
82 #endif
83     default:
84       g_return_val_if_reached (NULL);
85       break;
86     }
87 g_return_val_if_reached (NULL);
88 }
89 
90 /**
91  * Returns a unique identifier (full name prefixed
92  * with backend type).
93  */
94 char *
ext_port_get_id(ExtPort * self)95 ext_port_get_id (
96   ExtPort * self)
97 {
98   return
99     g_strdup_printf (
100       "%s/%s",
101       ext_port_type_strings[self->type].str,
102       self->full_name);
103 }
104 
105 /**
106  * Clears the buffer of the external port.
107  */
108 void
ext_port_clear_buffer(ExtPort * ext_port,nframes_t nframes)109 ext_port_clear_buffer (
110   ExtPort * ext_port,
111   nframes_t nframes)
112 {
113   float * buf =
114     ext_port_get_buffer (ext_port, nframes);
115   if (!buf)
116     return;
117 
118   g_message ("clearing buffer of %p", ext_port);
119 
120   memset (buf, 0, nframes * sizeof (float));
121 }
122 
123 /**
124  * Activates the port (starts receiving data) or
125  * deactivates it.
126  *
127  * @param port Port to send the output to.
128  */
129 void
ext_port_activate(ExtPort * self,Port * port,bool activate)130 ext_port_activate (
131   ExtPort * self,
132   Port *    port,
133   bool      activate)
134 {
135   g_message (
136     "attempting to %sactivate ext port %s",
137     activate ? "" : "de", self->full_name);
138 
139   /*char str[600];*/
140   int ret;
141   (void) ret; /* avoid unused warnings */
142   if (activate)
143     {
144       if (self->is_midi)
145         {
146           switch (AUDIO_ENGINE->midi_backend)
147             {
148 #ifdef HAVE_JACK
149             case MIDI_BACKEND_JACK:
150               if (self->type != EXT_PORT_TYPE_JACK)
151                 {
152                   g_message (
153                     "skipping %s (not JACK)",
154                     self->full_name);
155                   return;
156                 }
157 
158               self->port = port;
159 
160               /* expose the port and connect to
161                * JACK port */
162               if (!self->jport)
163                 {
164                   self->jport =
165                     jack_port_by_name (
166                       AUDIO_ENGINE->client,
167                       self->full_name);
168                 }
169               if (!self->jport)
170                 {
171                   g_warning (
172                     "Could not find external JACK "
173                     "port '%s', skipping...",
174                     self->full_name);
175                   return;
176                 }
177               port_set_expose_to_backend (
178                 self->port, true);
179 
180               g_message (
181                 "connecting jack port %s to "
182                 "jack port %s",
183                 jack_port_name (self->jport),
184                 jack_port_name (
185                   JACK_PORT_T (self->port->data)));
186 
187               ret =
188                 jack_connect (
189                   AUDIO_ENGINE->client,
190                   jack_port_name (self->jport),
191                   jack_port_name (
192                     JACK_PORT_T (self->port->data)));
193               if (ret)
194                 {
195                   char msg[600];
196                   engine_jack_get_error_message (
197                     ret, msg);
198                   g_warning (
199                     "Failed connecting %s to %s:\n"
200                     "%s",
201                       jack_port_name (self->jport),
202                       jack_port_name (
203                         JACK_PORT_T (
204                           self->port->data)),
205                     msg);
206                   return;
207                 }
208               break;
209 #endif
210 #ifdef HAVE_RTMIDI
211             case MIDI_BACKEND_ALSA_RTMIDI:
212             case MIDI_BACKEND_JACK_RTMIDI:
213             case MIDI_BACKEND_WINDOWS_MME_RTMIDI:
214             case MIDI_BACKEND_COREMIDI_RTMIDI:
215               if (self->type != EXT_PORT_TYPE_RTMIDI)
216                 {
217                   g_message (
218                     "skipping %s (not RtMidi)",
219                     self->full_name);
220                   return;
221                 }
222               self->port = port;
223               self->rtmidi_dev =
224                 rtmidi_device_new (
225                   true, self->full_name,
226                   0, self->port);
227               if (!self->rtmidi_dev)
228                 {
229                   g_warning (
230                     "Failed creating RtMidi device "
231                     "for %s", self->full_name);
232                   return;
233                 }
234               ret =
235                 rtmidi_device_open (
236                   self->rtmidi_dev, true);
237               self->port->rtmidi_ins[0] =
238                 self->rtmidi_dev;
239               self->port->num_rtmidi_ins = 1;
240               g_warn_if_fail (ret == 0);
241               break;
242 #endif
243             default:
244               break;
245             }
246         }
247       else
248         {
249           switch (AUDIO_ENGINE->audio_backend)
250             {
251 #ifdef HAVE_JACK
252             case AUDIO_BACKEND_JACK:
253               if (self->type != EXT_PORT_TYPE_JACK)
254                 {
255                   g_message (
256                     "skipping %s (not JACK)",
257                     self->full_name);
258                   return;
259                 }
260               self->port = port;
261 
262               /* expose the port and connect to
263                * JACK port */
264               if (!self->jport)
265                 {
266                   self->jport =
267                     jack_port_by_name (
268                       AUDIO_ENGINE->client,
269                       self->full_name);
270                 }
271               if (!self->jport)
272                 {
273                   g_warning (
274                     "Could not find external JACK "
275                     "port '%s', skipping...",
276                     self->full_name);
277                   return;
278                 }
279               port_set_expose_to_backend (
280                 self->port, true);
281 
282               g_message (
283                 "connecting jack port %s to "
284                 "jack port %s",
285                 jack_port_name (self->jport),
286                 jack_port_name (
287                   JACK_PORT_T (self->port->data)));
288 
289               jack_connect (
290                 AUDIO_ENGINE->client,
291                 jack_port_name (self->jport),
292                 jack_port_name (
293                   JACK_PORT_T (self->port->data)));
294               break;
295 #endif
296 #ifdef HAVE_RTAUDIO
297             case AUDIO_BACKEND_ALSA_RTAUDIO:
298             case AUDIO_BACKEND_JACK_RTAUDIO:
299             case AUDIO_BACKEND_PULSEAUDIO_RTAUDIO:
300             case AUDIO_BACKEND_COREAUDIO_RTAUDIO:
301             case AUDIO_BACKEND_WASAPI_RTAUDIO:
302             case AUDIO_BACKEND_ASIO_RTAUDIO:
303               if (self->type != EXT_PORT_TYPE_RTAUDIO)
304                 {
305                   g_message (
306                     "skipping %s (not RtAudio)",
307                     self->full_name);
308                   return;
309                 }
310               self->port = port;
311               self->rtaudio_dev =
312                 rtaudio_device_new (
313                   true, self->rtaudio_dev_name,
314                   0, self->rtaudio_channel_idx,
315                   self->port);
316               ret =
317                 rtaudio_device_open (
318                   self->rtaudio_dev, true);
319               if (ret)
320                 {
321                   return;
322                 }
323               self->port->rtaudio_ins[0] =
324                 self->rtaudio_dev;
325               self->port->num_rtaudio_ins = 1;
326               break;
327 #endif
328             default:
329               break;
330             }
331         }
332     }
333 
334   self->active = activate;
335 }
336 
337 /**
338  * Exposes the given Port if not exposed and makes
339  * the connection from the Port to the ExtPort (eg in
340  * JACK) or backwards.
341  *
342  * @param src 1 if the ext_port is the source, 0 if it
343  *   is the destination.
344  */
345 void
ext_port_connect(ExtPort * ext_port,Port * port,int src)346 ext_port_connect (
347   ExtPort * ext_port,
348   Port *    port,
349   int       src)
350 {
351   /* TODO */
352 }
353 
354 /**
355  * Disconnects the Port from the ExtPort.
356  *
357  * @param src 1 if the ext_port is the source, 0 if it
358  *   is the destination.
359  */
360 void
ext_port_disconnect(ExtPort * ext_port,Port * port,int src)361 ext_port_disconnect (
362   ExtPort * ext_port,
363   Port *    port,
364   int       src)
365 {
366   /* TODO */
367 }
368 
369 /**
370  * Returns if the ext port matches the current
371  * backend.
372  */
373 bool
ext_port_matches_backend(ExtPort * self)374 ext_port_matches_backend (
375   ExtPort * self)
376 {
377   if (!self->is_midi)
378     {
379       switch (AUDIO_ENGINE->audio_backend)
380         {
381 #ifdef HAVE_JACK
382         case AUDIO_BACKEND_JACK:
383           if (self->type == EXT_PORT_TYPE_JACK)
384             return true;
385           else
386             return false;
387 #endif
388 #ifdef HAVE_RTAUDIO
389         case AUDIO_BACKEND_ALSA_RTAUDIO:
390         case AUDIO_BACKEND_JACK_RTAUDIO:
391         case AUDIO_BACKEND_PULSEAUDIO_RTAUDIO:
392         case AUDIO_BACKEND_COREAUDIO_RTAUDIO:
393         case AUDIO_BACKEND_WASAPI_RTAUDIO:
394         case AUDIO_BACKEND_ASIO_RTAUDIO:
395           if (self->type == EXT_PORT_TYPE_RTAUDIO)
396             return true;
397           else
398             return false;
399 #endif
400 #ifdef HAVE_ALSA
401         case AUDIO_BACKEND_ALSA:
402           break;
403 #endif
404         default:
405           break;
406         }
407     }
408   else
409     {
410       switch (AUDIO_ENGINE->midi_backend)
411         {
412 #ifdef HAVE_JACK
413         case MIDI_BACKEND_JACK:
414           if (self->type == EXT_PORT_TYPE_JACK)
415             return true;
416           else
417             return false;
418 #endif
419 #ifdef HAVE_ALSA
420         case MIDI_BACKEND_ALSA:
421           break;
422 #endif
423 #ifdef _WOE32
424         case MIDI_BACKEND_WINDOWS_MME:
425           g_warning ("TODO");
426           break;
427 #endif
428 #ifdef HAVE_RTMIDI
429         case MIDI_BACKEND_ALSA_RTMIDI:
430         case MIDI_BACKEND_JACK_RTMIDI:
431         case MIDI_BACKEND_WINDOWS_MME_RTMIDI:
432         case MIDI_BACKEND_COREMIDI_RTMIDI:
433           if (self->type == EXT_PORT_TYPE_RTMIDI)
434             return true;
435           else
436             return false;
437 #endif
438         default:
439           break;
440         }
441     }
442   return false;
443 }
444 
445 #ifdef HAVE_JACK
446 /**
447  * Creates an ExtPort from a JACK port.
448  */
449 static ExtPort *
ext_port_from_jack_port(jack_port_t * jport)450 ext_port_from_jack_port (
451   jack_port_t * jport)
452 {
453   ExtPort * self = _create ();
454 
455   self->jport = jport;
456   self->full_name =
457     g_strdup (jack_port_name (jport));
458   self->short_name  =
459     g_strdup (jack_port_short_name (jport));
460   self->type = EXT_PORT_TYPE_JACK;
461 
462   char * aliases[2];
463   aliases[0] =
464     (char*)
465     malloc (
466       (size_t) jack_port_name_size ());
467   aliases[1] =
468     (char*)
469     malloc (
470       (size_t) jack_port_name_size ());
471   self->num_aliases =
472     jack_port_get_aliases (
473       jport, aliases);
474 
475   if (self->num_aliases == 2)
476     {
477       self->alias2 = g_strdup (aliases[1]);
478       self->alias1 = g_strdup (aliases[0]);
479     }
480   else if (self->num_aliases == 1)
481     self->alias1 = g_strdup (aliases[0]);
482 
483   free (aliases[0]);
484   free (aliases[1]);
485 
486   return self;
487 }
488 
489 static void
get_ext_ports_from_jack(PortType type,PortFlow flow,int hw,GPtrArray * ports)490 get_ext_ports_from_jack (
491   PortType    type,
492   PortFlow    flow,
493   int         hw,
494   GPtrArray * ports)
495 {
496   long unsigned int flags = 0;
497   if (hw)
498     flags |= JackPortIsPhysical;
499   if (flow == FLOW_INPUT)
500     flags |= JackPortIsInput;
501   else if (flow == FLOW_OUTPUT)
502     flags |= JackPortIsOutput;
503   const char * jtype =
504     engine_jack_get_jack_type (type);
505   if (!jtype)
506     return;
507 
508   if (!AUDIO_ENGINE->client)
509     {
510       g_critical (
511         "JACK client is NULL. make sure to call "
512         "engine_pre_setup() before calling this");
513       return;
514     }
515 
516   const char ** jports =
517     jack_get_ports (
518       AUDIO_ENGINE->client,
519       NULL, jtype, flags);
520 
521   if (!jports)
522     return;
523 
524   size_t i = 0;
525   while (jports[i] != NULL)
526     {
527       jack_port_t * jport =
528         jack_port_by_name (
529           AUDIO_ENGINE->client, jports[i]);
530 
531       ExtPort * ext_port =
532         ext_port_from_jack_port (jport);
533       g_ptr_array_add (ports, ext_port);
534 
535       i++;
536     }
537 
538   jack_free (jports);
539 }
540 #endif
541 
542 #ifdef _WOE32
543 /**
544  * Creates an ExtPort from a Windows MME device.
545  */
546 static ExtPort *
ext_port_from_windows_mme_device(WindowsMmeDevice * dev)547 ext_port_from_windows_mme_device (
548   WindowsMmeDevice * dev)
549 {
550   ExtPort * self =
551     calloc (1, sizeof (ExtPort));
552 
553   self->mme_dev = dev;
554   self->full_name = g_strdup (dev->name);
555   self->type = EXT_PORT_TYPE_WINDOWS_MME;
556 
557   return self;
558 }
559 
560 static void
get_ext_ports_from_windows_mme(PortFlow flow,GPtrArray * ports)561 get_ext_ports_from_windows_mme (
562   PortFlow    flow,
563   GPtrArray * ports)
564 {
565   if (flow == FLOW_OUTPUT)
566     {
567       for (int i = 0;
568            i < AUDIO_ENGINE->num_mme_in_devs; i++)
569         {
570           WindowsMmeDevice * dev =
571             AUDIO_ENGINE->mme_in_devs[i];
572           g_return_if_fail (dev);
573           ExtPort * ext_port =
574             ext_port_from_windows_mme_device (dev);
575           g_ptr_array_add (ports, ext_port);
576         }
577     }
578   else if (flow == FLOW_INPUT)
579     {
580       for (int i = 0;
581            i < AUDIO_ENGINE->num_mme_out_devs; i++)
582         {
583           WindowsMmeDevice * dev =
584             AUDIO_ENGINE->mme_out_devs[i];
585           g_return_if_fail (dev);
586           ExtPort * ext_port =
587             ext_port_from_windows_mme_device (dev);
588           g_ptr_array_add (ports, ext_port);
589         }
590     }
591 }
592 #endif
593 
594 #ifdef HAVE_RTMIDI
595 /**
596  * Creates an ExtPort from a RtMidi port.
597  */
598 static ExtPort *
ext_port_from_rtmidi(unsigned int id)599 ext_port_from_rtmidi (
600   unsigned int id)
601 {
602   ExtPort * self = _create ();
603 
604   RtMidiDevice * dev =
605     rtmidi_device_new (1, NULL, id, NULL);
606   self->rtmidi_id = id;
607   self->full_name =
608     g_strdup (
609       rtmidi_get_port_name (dev->in_handle, id));
610   self->type = EXT_PORT_TYPE_RTMIDI;
611   rtmidi_device_free (dev);
612 
613   return self;
614 }
615 
616 static void
get_ext_ports_from_rtmidi(PortFlow flow,GPtrArray * ports)617 get_ext_ports_from_rtmidi (
618   PortFlow    flow,
619   GPtrArray * ports)
620 {
621   if (flow == FLOW_OUTPUT)
622     {
623       unsigned int num_ports =
624         engine_rtmidi_get_num_in_ports (
625           AUDIO_ENGINE);
626       unsigned int i;
627       for (i = 0; i < num_ports; i++)
628         {
629           ExtPort * ext_port =
630             ext_port_from_rtmidi (i);
631           g_ptr_array_add (ports, ext_port);
632         }
633     }
634   else if (flow == FLOW_INPUT)
635     {
636       /* MIDI out devices not handled yet */
637     }
638 }
639 #endif
640 
641 #ifdef HAVE_RTAUDIO
642 /**
643  * Creates an ExtPort from a RtAudio port.
644  */
645 static ExtPort *
ext_port_from_rtaudio(unsigned int id,unsigned int channel_idx,bool is_input,bool is_duplex)646 ext_port_from_rtaudio (
647   unsigned int id,
648   unsigned int channel_idx,
649   bool         is_input,
650   bool         is_duplex)
651 {
652   ExtPort * self = _create ();
653 
654   RtAudioDevice * dev =
655     rtaudio_device_new (
656       1, NULL, id, channel_idx, NULL);
657   self->rtaudio_id = id;
658   self->rtaudio_channel_idx = channel_idx;
659   self->rtaudio_is_input = is_input;
660   self->rtaudio_is_duplex = is_duplex;
661   self->rtaudio_dev_name = g_strdup (dev->name);
662   self->full_name =
663     g_strdup_printf (
664       "%s (in %d)", dev->name, channel_idx);
665   self->type = EXT_PORT_TYPE_RTAUDIO;
666   rtaudio_device_free (dev);
667 
668   return self;
669 }
670 
671 static void
get_ext_ports_from_rtaudio(PortFlow flow,GPtrArray * ports)672 get_ext_ports_from_rtaudio (
673   PortFlow    flow,
674   GPtrArray * ports)
675 {
676   /* note: this is an output port from the graph
677    * side that will be used as an input port on
678    * the zrythm side */
679   if (flow == FLOW_OUTPUT)
680     {
681       rtaudio_t rtaudio =
682         engine_rtaudio_create_rtaudio (AUDIO_ENGINE);
683       if (!rtaudio)
684         {
685           g_warn_if_reached ();
686           return;
687         }
688       int num_devs = rtaudio_device_count (rtaudio);
689       for (unsigned int i = 0;
690            i < (unsigned int) num_devs; i++)
691         {
692           rtaudio_device_info_t dev_nfo =
693             rtaudio_get_device_info (
694               rtaudio, (int) i);
695           if (dev_nfo.input_channels > 0)
696             {
697               for (unsigned int j = 0;
698                    j < dev_nfo.input_channels; j++)
699                 {
700                   ExtPort * ext_port =
701                     ext_port_from_rtaudio (
702                       i, j, true, false);
703                   g_ptr_array_add (
704                     ports, ext_port);
705                 }
706 #if 0
707               for (unsigned int j = 0;
708                    j < dev_nfo.duplex_channels; j++)
709                 {
710                   arr[*size] =
711                     ext_port_from_rtaudio (
712                       i, j, false, true);
713                   (*size)++;
714                   g_message ("\n\n found duplex device");
715                 }
716 #endif
717             }
718           else
719             {
720               continue;
721             }
722         }
723       rtaudio_destroy (rtaudio);
724     }
725   else if (flow == FLOW_INPUT)
726     {
727       rtaudio_t rtaudio =
728         engine_rtaudio_create_rtaudio (AUDIO_ENGINE);
729       if (!rtaudio)
730         {
731           g_warn_if_reached ();
732           return;
733         }
734       int num_devs = rtaudio_device_count (rtaudio);
735       for (unsigned int i = 0;
736            i < (unsigned int) num_devs; i++)
737         {
738           rtaudio_device_info_t dev_nfo =
739             rtaudio_get_device_info (
740               rtaudio, (int) i);
741           if (dev_nfo.output_channels > 0)
742             {
743               for (unsigned int j = 0;
744                    j < dev_nfo.output_channels; j++)
745                 {
746                   ExtPort * ext_port =
747                     ext_port_from_rtaudio (
748                       i, j, false, false);
749                   g_ptr_array_add (
750                     ports, ext_port);
751                 }
752 #if 0
753               for (unsigned int j = 0;
754                    j < dev_nfo.duplex_channels; j++)
755                 {
756                   arr[*size] =
757                     ext_port_from_rtaudio (
758                       i, j, false, true);
759                   (*size)++;
760                   g_message ("\n\n found duplex device");
761                 }
762 #endif
763             }
764           else
765             {
766               continue;
767             }
768         }
769     }
770 }
771 #endif
772 
773 /**
774  * Collects external ports of the given type.
775  *
776  * @param flow The signal flow. Note that this is
777  *   inverse to what Zrythm sees. E.g., to get
778  *   MIDI inputs like MIDI keyboards, pass
779  *   \ref FLOW_OUTPUT here.
780  * @param hw Hardware or not.
781  */
782 void
ext_ports_get(PortType type,PortFlow flow,bool hw,GPtrArray * ports)783 ext_ports_get (
784   PortType    type,
785   PortFlow    flow,
786   bool        hw,
787   GPtrArray * ports)
788 {
789   if (type == TYPE_AUDIO)
790     {
791       switch (AUDIO_ENGINE->audio_backend)
792         {
793 #ifdef HAVE_JACK
794         case AUDIO_BACKEND_JACK:
795           get_ext_ports_from_jack (
796             type, flow, hw, ports);
797           break;
798 #endif
799 #ifdef HAVE_RTAUDIO
800         case AUDIO_BACKEND_ALSA_RTAUDIO:
801         case AUDIO_BACKEND_JACK_RTAUDIO:
802         case AUDIO_BACKEND_PULSEAUDIO_RTAUDIO:
803         case AUDIO_BACKEND_COREAUDIO_RTAUDIO:
804         case AUDIO_BACKEND_WASAPI_RTAUDIO:
805         case AUDIO_BACKEND_ASIO_RTAUDIO:
806           get_ext_ports_from_rtaudio (flow, ports);
807           break;
808 #endif
809 #ifdef HAVE_ALSA
810         case AUDIO_BACKEND_ALSA:
811           break;
812 #endif
813         default:
814           break;
815         }
816     }
817   else if (type == TYPE_EVENT)
818     {
819       switch (AUDIO_ENGINE->midi_backend)
820         {
821 #ifdef HAVE_JACK
822         case MIDI_BACKEND_JACK:
823           get_ext_ports_from_jack (
824             type, flow, hw, ports);
825           break;
826 #endif
827 #ifdef HAVE_ALSA
828         case MIDI_BACKEND_ALSA:
829           break;
830 #endif
831 #ifdef _WOE32
832         case MIDI_BACKEND_WINDOWS_MME:
833           get_ext_ports_from_windows_mme (
834             flow, ports);
835           break;
836 #endif
837 #ifdef HAVE_RTMIDI
838         case MIDI_BACKEND_ALSA_RTMIDI:
839         case MIDI_BACKEND_JACK_RTMIDI:
840         case MIDI_BACKEND_WINDOWS_MME_RTMIDI:
841         case MIDI_BACKEND_COREMIDI_RTMIDI:
842           get_ext_ports_from_rtmidi (
843             flow, ports);
844           break;
845 #endif
846         default:
847           break;
848         } /* end switch MIDI backend */
849 
850       for (size_t i = 0; i < ports->len; i++)
851         {
852           ExtPort * ext_port =
853             g_ptr_array_index (ports, i);
854           ext_port->is_midi = true;
855         }
856 
857     } /* endif MIDI */
858 }
859 
860 /**
861  * Prints the port info.
862  */
863 void
ext_port_print(ExtPort * self)864 ext_port_print (
865   ExtPort * self)
866 {
867   g_message (
868     "Ext port:\n"
869     "full name: %s",
870     self->full_name);
871 }
872 
873 /**
874  * Creates a shallow clone of the port.
875  */
876 ExtPort *
ext_port_clone(ExtPort * ext_port)877 ext_port_clone (
878   ExtPort * ext_port)
879 {
880   g_return_val_if_fail (ext_port, NULL);
881 
882   ExtPort * newport = _create ();
883 
884 #ifdef HAVE_JACK
885   newport->jport = ext_port->jport;
886 #endif
887 #ifdef _WOE32
888   newport->mme_dev = ext_port->mme_dev;
889 #endif
890 #ifdef HAVE_RTMIDI
891   newport->rtmidi_id = ext_port->rtmidi_id;
892 #endif
893   newport->rtaudio_channel_idx =
894     ext_port->rtaudio_channel_idx;
895   newport->rtaudio_dev_name =
896     ext_port->rtaudio_dev_name;
897 #ifdef HAVE_RTAUDIO
898   newport->rtaudio_id = ext_port->rtaudio_id;
899   newport->rtaudio_is_input =
900     ext_port->rtaudio_is_input;
901   newport->rtaudio_is_duplex =
902     ext_port->rtaudio_is_duplex;
903 #endif
904   if (ext_port->full_name)
905     newport->full_name =
906       g_strdup (ext_port->full_name);
907   if (ext_port->short_name)
908     newport->short_name =
909       g_strdup (ext_port->short_name);
910   if (ext_port->alias1)
911     newport->alias1 =
912     g_strdup (ext_port->alias1);
913   if (ext_port->alias2)
914     newport->alias2 =
915     g_strdup (ext_port->alias2);
916   newport->num_aliases = ext_port->num_aliases;
917   newport->type = ext_port->type;
918 
919   return newport;
920 }
921 
922 /**
923  * Checks in the GSettings whether this port is
924  * marked as enabled by the user.
925  *
926  * @note Not realtime safe.
927  *
928  * @return Whether the port is enabled.
929  */
930 bool
ext_port_get_enabled(ExtPort * self)931 ext_port_get_enabled (
932   ExtPort * self)
933 {
934   /* TODO */
935   return true;
936 }
937 
938 /**
939  * Frees an array of ExtPort pointers.
940  */
941 void
ext_ports_free(ExtPort ** ext_ports,int size)942 ext_ports_free (
943   ExtPort ** ext_ports,
944   int        size)
945 {
946   for (int i = 0; i < size; i++)
947     {
948       g_warn_if_fail (!ext_ports[i]);
949       object_free_w_func_and_null (
950         ext_port_free, ext_ports[i]);
951     }
952 }
953 
954 /**
955  * Frees the ext_port.
956  */
957 void
ext_port_free(ExtPort * self)958 ext_port_free (
959   ExtPort * self)
960 {
961   g_free_and_null (self->full_name);
962   g_free_and_null (self->short_name);
963   g_free_and_null (self->alias1);
964   g_free_and_null (self->alias2);
965 
966   object_zero_and_free (self);
967 }
968