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