1 /*
2 * Copyright (C) 2020-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 "audio/engine.h"
21 #include "audio/ext_port.h"
22 #include "audio/hardware_processor.h"
23 #include "audio/midi_event.h"
24 #include "audio/port.h"
25 #include "project.h"
26 #include "settings/settings.h"
27 #include "utils/arrays.h"
28 #include "utils/flags.h"
29 #include "utils/mem.h"
30 #include "utils/objects.h"
31 #include "utils/string.h"
32 #include "zrythm.h"
33 #include "zrythm_app.h"
34
35 void
hardware_processor_init_loaded(HardwareProcessor * self,AudioEngine * engine)36 hardware_processor_init_loaded (
37 HardwareProcessor * self,
38 AudioEngine * engine)
39 {
40 self->engine = engine;
41 self->ext_audio_ports_size =
42 (size_t) self->num_ext_audio_ports;
43 self->ext_midi_ports_size =
44 (size_t) self->num_ext_midi_ports;
45 }
46
47 /**
48 * Returns a new empty instance.
49 */
50 HardwareProcessor *
hardware_processor_new(bool input,AudioEngine * engine)51 hardware_processor_new (
52 bool input,
53 AudioEngine * engine)
54 {
55 HardwareProcessor * self =
56 object_new (HardwareProcessor);
57 self->schema_version =
58 HW_PROCESSOR_SCHEMA_VERSION;
59 self->engine = engine;
60 self->is_input = input;
61
62 return self;
63 }
64
65 /**
66 * Finds an ext port from its ID (type + full name).
67 *
68 * @see ext_port_get_id()
69 */
70 ExtPort *
hardware_processor_find_ext_port(HardwareProcessor * self,const char * id)71 hardware_processor_find_ext_port (
72 HardwareProcessor * self,
73 const char * id)
74 {
75 g_return_val_if_fail (self && id, NULL);
76
77 for (int i = 0; i < self->num_ext_audio_ports;
78 i++)
79 {
80 ExtPort * port = self->ext_audio_ports[i];
81 char * port_id = ext_port_get_id (port);
82 if (string_is_equal (port_id, id))
83 {
84 g_free (port_id);
85 return port;
86 }
87 g_free (port_id);
88 }
89 for (int i = 0; i < self->num_ext_midi_ports; i++)
90 {
91 ExtPort * port = self->ext_midi_ports[i];
92 char * port_id = ext_port_get_id (port);
93 if (string_is_equal (port_id, id))
94 {
95 g_free (port_id);
96 return port;
97 }
98 g_free (port_id);
99 }
100
101 return NULL;
102 }
103
104 /**
105 * Finds a port from its ID (type + full name).
106 *
107 * @see ext_port_get_id()
108 */
109 Port *
hardware_processor_find_port(HardwareProcessor * self,const char * id)110 hardware_processor_find_port (
111 HardwareProcessor * self,
112 const char * id)
113 {
114 g_return_val_if_fail (self && id, NULL);
115
116 for (int i = 0; i < self->num_ext_audio_ports;
117 i++)
118 {
119 ExtPort * ext_port = self->ext_audio_ports[i];
120 char * ext_port_id =
121 ext_port_get_id (ext_port);
122 Port * port = self->audio_ports[i];
123 if (!PROJECT || !PROJECT->loaded)
124 {
125 port->magic = PORT_MAGIC;
126 }
127 g_return_val_if_fail (
128 ext_port && IS_PORT (port), NULL);
129 if (string_is_equal (ext_port_id, id))
130 {
131 g_free (ext_port_id);
132 return port;
133 }
134 g_free (ext_port_id);
135 }
136 for (int i = 0; i < self->num_ext_midi_ports; i++)
137 {
138 ExtPort * ext_port = self->ext_midi_ports[i];
139 char * ext_port_id =
140 ext_port_get_id (ext_port);
141 Port * port = self->midi_ports[i];
142 if (!PROJECT || !PROJECT->loaded)
143 {
144 port->magic = PORT_MAGIC;
145 }
146 g_return_val_if_fail (
147 ext_port && IS_PORT (port), NULL);
148 if (string_is_equal (ext_port_id, id))
149 {
150 g_free (ext_port_id);
151 return port;
152 }
153 g_free (ext_port_id);
154 }
155
156 return NULL;
157 }
158
159 static Port *
create_port_for_ext_port(ExtPort * ext_port,PortType type,PortFlow flow)160 create_port_for_ext_port (
161 ExtPort * ext_port,
162 PortType type,
163 PortFlow flow)
164 {
165 Port * port =
166 port_new_with_type_and_owner (
167 type, flow, ext_port->full_name,
168 PORT_OWNER_TYPE_HW, ext_port);
169 port->id.flags |= PORT_FLAG_HW;
170 port->id.ext_port_id = ext_port_get_id (ext_port);
171
172 return port;
173 }
174
175 /**
176 * Rescans the hardware ports and appends any missing
177 * ones.
178 */
179 bool
hardware_processor_rescan_ext_ports(HardwareProcessor * self)180 hardware_processor_rescan_ext_ports (
181 HardwareProcessor * self)
182 {
183 g_debug ("rescanning ports...");
184
185 /* get correct flow */
186 PortFlow flow =
187 /* these are reversed:
188 * input here -> port that outputs in backend */
189 self->is_input ? FLOW_OUTPUT : FLOW_INPUT;
190
191 /* collect audio ports */
192 GPtrArray * ports = g_ptr_array_new ();
193 ext_ports_get (
194 TYPE_AUDIO, flow, true, ports);
195
196 /* add missing ports to the list */
197 for (size_t i = 0; i < ports->len; i++)
198 {
199 ExtPort * ext_port =
200 g_ptr_array_index (ports, i);
201 ExtPort * existing_port =
202 hardware_processor_find_ext_port (
203 self, ext_port_get_id (ext_port));
204
205 if (!existing_port)
206 {
207 array_double_size_if_full (
208 self->ext_audio_ports,
209 self->num_ext_audio_ports,
210 self->ext_audio_ports_size,
211 ExtPort *);
212 ext_port->hw_processor = self;
213 array_append (
214 self->ext_audio_ports,
215 self->num_ext_audio_ports, ext_port);
216 char * id = ext_port_get_id (ext_port);
217 g_message (
218 "[HW] Added audio port %s", id);
219 g_free (id);
220 }
221 else
222 {
223 ext_port_free (ext_port);
224 }
225 }
226 g_ptr_array_unref (ports);
227
228 /* collect midi ports */
229 ports = g_ptr_array_new ();
230 ext_ports_get (TYPE_EVENT, flow, true, ports);
231
232 /* add missing ports to the list */
233 for (size_t i = 0; i < ports->len; i++)
234 {
235 ExtPort * ext_port =
236 g_ptr_array_index (ports, i);
237 ExtPort * existing_port =
238 hardware_processor_find_ext_port (
239 self, ext_port_get_id (ext_port));
240
241 if (!existing_port)
242 {
243 array_double_size_if_full (
244 self->ext_midi_ports,
245 self->num_ext_midi_ports,
246 self->ext_midi_ports_size,
247 ExtPort *);
248 ext_port->hw_processor = self;
249 array_append (
250 self->ext_midi_ports,
251 self->num_ext_midi_ports, ext_port);
252 char * id = ext_port_get_id (ext_port);
253 g_message (
254 "[HW] Added MIDI port %s", id);
255 g_free (id);
256 }
257 else
258 {
259 ext_port_free (ext_port);
260 }
261 }
262 g_ptr_array_unref (ports);
263
264 /* create ports for each ext port */
265 self->audio_ports =
266 realloc (
267 self->audio_ports,
268 MAX (1, self->ext_audio_ports_size) *
269 sizeof (Port *));
270 self->midi_ports =
271 realloc (
272 self->midi_ports,
273 MAX (1, self->ext_midi_ports_size) *
274 sizeof (Port *));
275 for (int i = 0; i < self->num_ext_audio_ports; i++)
276 {
277 ExtPort * ext_port = self->ext_audio_ports[i];
278 g_return_val_if_fail (ext_port, G_SOURCE_REMOVE);
279 if (i >= self->num_audio_ports)
280 {
281 self->audio_ports[i] =
282 create_port_for_ext_port (
283 ext_port, TYPE_AUDIO, FLOW_OUTPUT);
284 self->num_audio_ports++;
285 }
286
287 g_return_val_if_fail (
288 IS_PORT (self->audio_ports[i]),
289 G_SOURCE_REMOVE);
290 }
291 for (int i = 0; i < self->num_ext_midi_ports; i++)
292 {
293 ExtPort * ext_port = self->ext_midi_ports[i];
294 g_return_val_if_fail (ext_port, G_SOURCE_REMOVE);
295 if (i >= self->num_midi_ports)
296 {
297 self->midi_ports[i] =
298 create_port_for_ext_port (
299 ext_port, TYPE_EVENT, FLOW_OUTPUT);
300 self->num_midi_ports++;
301 }
302
303 g_return_val_if_fail (
304 IS_PORT (self->midi_ports[i]),
305 G_SOURCE_REMOVE);
306 }
307
308 /* TODO deactivate ports that weren't found
309 * (stop engine temporarily to remove) */
310
311 g_debug (
312 "[%s] have %d audio and %d MIDI ports",
313 self->is_input ?
314 "HW processor inputs" :
315 "HW processor outputs",
316 self->num_ext_audio_ports,
317 self->num_ext_midi_ports);
318
319 for (int i = 0; i < self->num_ext_audio_ports;
320 i++)
321 {
322 char * id =
323 ext_port_get_id (self->ext_audio_ports[i]);
324 g_debug (
325 "[%s] audio: %s",
326 self->is_input ?
327 "HW processor input" :
328 "HW processor output",
329 id);
330 g_free (id);
331 }
332 for (int i = 0; i < self->num_ext_midi_ports;
333 i++)
334 {
335 char * id =
336 ext_port_get_id (self->ext_midi_ports[i]);
337 g_debug (
338 "[%s] MIDI: %s",
339 self->is_input ?
340 "HW processor input" :
341 "HW processor output",
342 id);
343 g_free (id);
344 }
345
346 return G_SOURCE_CONTINUE;
347 }
348
349 /**
350 * Sets up the ports but does not start them.
351 *
352 * @return Non-zero on fail.
353 */
354 int
hardware_processor_setup(HardwareProcessor * self)355 hardware_processor_setup (
356 HardwareProcessor * self)
357 {
358 if (ZRYTHM_TESTING || ZRYTHM_GENERATING_PROJECT)
359 return 0;
360
361 g_return_val_if_fail (
362 ZRYTHM_APP_IS_GTK_THREAD && S_P_GENERAL_ENGINE,
363 -1);
364
365 if (self->is_input)
366 {
367 /* cache selections */
368 self->selected_midi_ports =
369 g_settings_get_strv (
370 S_P_GENERAL_ENGINE, "midi-controllers");
371 self->selected_audio_ports =
372 g_settings_get_strv (
373 S_P_GENERAL_ENGINE, "audio-inputs");
374
375 /* get counts */
376 self->num_selected_midi_ports = 0;
377 self->num_selected_audio_ports = 0;
378 while ((self->selected_midi_ports[
379 self->num_selected_midi_ports++]));
380 self->num_selected_midi_ports--;
381 while ((self->selected_audio_ports[
382 self->num_selected_audio_ports++]));
383 self->num_selected_audio_ports--;
384 }
385
386 /* ---- scan current ports ---- */
387
388 hardware_processor_rescan_ext_ports (self);
389
390 /* ---- end scan ---- */
391
392 self->setup = true;
393
394 return 0;
395 }
396
397 /*void*/
398 /*hardware_processor_find_or_create_rtaudio_dev (*/
399 /*HardwareProcessor * self,*/
400
401 /**
402 * Starts or stops the ports.
403 *
404 * @param activate True to activate, false to
405 * deactivate
406 */
407 void
hardware_processor_activate(HardwareProcessor * self,bool activate)408 hardware_processor_activate (
409 HardwareProcessor * self,
410 bool activate)
411 {
412 g_message ("hw processor activate: %d", activate);
413
414 /* go through each selected port and activate/
415 * deactivate */
416 for (int i = 0; i < self->num_selected_midi_ports;
417 i++)
418 {
419 char * selected_port =
420 self->selected_midi_ports[i];
421 ExtPort * ext_port =
422 hardware_processor_find_ext_port (
423 self, selected_port);
424 Port * port =
425 hardware_processor_find_port (
426 self, selected_port);
427 if (port && ext_port)
428 {
429 ext_port_activate (
430 ext_port, port, activate);
431 }
432 else
433 {
434 g_message (
435 "could not find port %s", selected_port);
436 }
437 }
438 for (int i = 0; i < self->num_selected_audio_ports;
439 i++)
440 {
441 char * selected_port =
442 self->selected_audio_ports[i];
443 ExtPort * ext_port =
444 hardware_processor_find_ext_port (
445 self, selected_port);
446 Port * port =
447 hardware_processor_find_port (
448 self, selected_port);
449 if (port && ext_port)
450 {
451 ext_port_activate (
452 ext_port, port, activate);
453 }
454 else
455 {
456 g_message (
457 "could not find port %s", selected_port);
458 }
459 }
460
461 if (activate && !self->rescan_timeout_id)
462 {
463 /* add timer to keep rescanning */
464 self->rescan_timeout_id =
465 g_timeout_add_seconds (
466 7,
467 (GSourceFunc)
468 hardware_processor_rescan_ext_ports,
469 self);
470 }
471 else if (!activate && self->rescan_timeout_id)
472 {
473 /* remove timeout */
474 g_source_remove (self->rescan_timeout_id);
475 self->rescan_timeout_id = 0;
476 }
477
478 self->activated = activate;
479 }
480
481 /**
482 * Processes the data.
483 */
484 void
hardware_processor_process(HardwareProcessor * self,nframes_t nframes)485 hardware_processor_process (
486 HardwareProcessor * self,
487 nframes_t nframes)
488 {
489 /* go through each selected port and fetch data */
490 for (int i = 0; i < self->num_audio_ports; i++)
491 {
492 ExtPort * ext_port = self->ext_audio_ports[i];
493 if (!ext_port->active)
494 continue;
495
496 Port * port = self->audio_ports[i];
497
498 /* clear the buffer */
499 port_clear_buffer (port);
500
501 switch (AUDIO_ENGINE->audio_backend)
502 {
503 #ifdef HAVE_JACK
504 case AUDIO_BACKEND_JACK:
505 port_receive_audio_data_from_jack (
506 port, 0, nframes);
507 break;
508 #endif
509 #ifdef HAVE_RTAUDIO
510 case AUDIO_BACKEND_ALSA_RTAUDIO:
511 case AUDIO_BACKEND_JACK_RTAUDIO:
512 case AUDIO_BACKEND_PULSEAUDIO_RTAUDIO:
513 case AUDIO_BACKEND_COREAUDIO_RTAUDIO:
514 case AUDIO_BACKEND_WASAPI_RTAUDIO:
515 case AUDIO_BACKEND_ASIO_RTAUDIO:
516 /* extract audio data from the RtAudio
517 * device ring buffer into RtAudio
518 * device temp buffer */
519 port_prepare_rtaudio_data (port);
520
521 /* copy data from RtAudio temp buffer
522 * to normal buffer */
523 port_sum_data_from_rtaudio (
524 port, 0, nframes);
525 break;
526 #endif
527 default:
528 break;
529 }
530 }
531 for (int i = 0; i < self->num_midi_ports; i++)
532 {
533 ExtPort * ext_port = self->ext_midi_ports[i];
534 if (!ext_port->active)
535 continue;
536
537 Port * port = self->midi_ports[i];
538
539 /* clear the buffer */
540 midi_events_clear (
541 port->midi_events, F_NOT_QUEUED);
542
543 switch (AUDIO_ENGINE->midi_backend)
544 {
545 #ifdef HAVE_JACK
546 case MIDI_BACKEND_JACK:
547 port_receive_midi_events_from_jack (
548 port, 0, nframes);
549 break;
550 #endif
551 #ifdef HAVE_RTMIDI
552 case MIDI_BACKEND_ALSA_RTMIDI:
553 case MIDI_BACKEND_JACK_RTMIDI:
554 case MIDI_BACKEND_WINDOWS_MME_RTMIDI:
555 case MIDI_BACKEND_COREMIDI_RTMIDI:
556 /* extract MIDI events from the RtMidi
557 * device ring buffer into RtMidi device */
558 port_prepare_rtmidi_events (port);
559
560 /* copy data from RtMidi device events
561 * to normal events */
562 port_sum_data_from_rtmidi (
563 port, 0, nframes);
564 break;
565 #endif
566 default:
567 break;
568 }
569 }
570 }
571
572 /**
573 * To be used during serialization.
574 */
575 HardwareProcessor *
hardware_processor_clone(const HardwareProcessor * src)576 hardware_processor_clone (
577 const HardwareProcessor * src)
578 {
579 HardwareProcessor * self =
580 object_new (HardwareProcessor);
581 self->schema_version =
582 HW_PROCESSOR_SCHEMA_VERSION;
583
584 self->is_input = src->is_input;
585
586 self->ext_audio_ports =
587 object_new_n (
588 (size_t) src->num_ext_audio_ports, ExtPort *);
589 for (int i = 0; i < src->num_ext_audio_ports; i++)
590 {
591 self->ext_audio_ports[i] =
592 ext_port_clone (src->ext_audio_ports[i]);
593 }
594 self->num_ext_audio_ports =
595 src->num_ext_audio_ports;
596
597 self->ext_midi_ports =
598 object_new_n (
599 (size_t) src->num_ext_midi_ports, ExtPort *);
600 for (int i = 0; i < src->num_ext_midi_ports; i++)
601 {
602 self->ext_midi_ports[i] =
603 ext_port_clone (src->ext_midi_ports[i]);
604 }
605 self->num_ext_midi_ports =
606 src->num_ext_midi_ports;
607
608 self->audio_ports =
609 object_new_n (
610 (size_t) src->num_audio_ports, Port *);
611 for (int i = 0; i < src->num_audio_ports; i++)
612 {
613 self->audio_ports[i] =
614 port_clone (src->audio_ports[i]);
615 }
616 self->num_audio_ports = src->num_audio_ports;
617
618 self->midi_ports =
619 object_new_n (
620 (size_t) src->num_midi_ports, Port *);
621 for (int i = 0; i < src->num_midi_ports; i++)
622 {
623 self->midi_ports[i] =
624 port_clone (src->midi_ports[i]);
625 }
626 self->num_midi_ports = src->num_midi_ports;
627
628 return self;
629 }
630
631 void
hardware_processor_free(HardwareProcessor * self)632 hardware_processor_free (
633 HardwareProcessor * self)
634 {
635 for (int i = 0;
636 i < self->num_selected_midi_ports; i++)
637 {
638 g_free_and_null (
639 self->selected_midi_ports[i]);
640 }
641 object_zero_and_free_if_nonnull (
642 self->selected_midi_ports);
643
644 for (int i = 0;
645 i < self->num_selected_audio_ports; i++)
646 {
647 g_free_and_null (
648 self->selected_audio_ports[i]);
649 }
650 object_zero_and_free_if_nonnull (
651 self->selected_audio_ports);
652
653 for (int i = 0; i < self->num_ext_audio_ports;
654 i++)
655 {
656 object_free_w_func_and_null (
657 ext_port_free, self->ext_audio_ports[i]);
658 }
659 object_zero_and_free_if_nonnull (
660 self->ext_audio_ports);
661
662 for (int i = 0; i < self->num_ext_midi_ports;
663 i++)
664 {
665 object_free_w_func_and_null (
666 ext_port_free, self->ext_midi_ports[i]);
667 }
668 object_zero_and_free_if_nonnull (
669 self->ext_midi_ports);
670
671 for (int i = 0; i < self->num_audio_ports;
672 i++)
673 {
674 object_free_w_func_and_null (
675 port_free, self->audio_ports[i]);
676 }
677 object_zero_and_free_if_nonnull (
678 self->audio_ports);
679
680 for (int i = 0; i < self->num_midi_ports;
681 i++)
682 {
683 object_free_w_func_and_null (
684 port_free, self->midi_ports[i]);
685 }
686 object_zero_and_free_if_nonnull (
687 self->midi_ports);
688
689 object_zero_and_free (self);
690 }
691