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 * This file incorporates work covered by the following copyright and
20 * permission notice:
21 *
22 * Copyright (C) 2017, 2019 Robin Gareus <robin@gareus.org>
23 *
24 * This program is free software: you can redistribute it and/or modify
25 * it under the terms of the GNU General Public License as published by
26 * the Free Software Foundation, either version 2 of the License, or
27 * (at your option) any later version.
28 *
29 * This program is distributed in the hope that it will be useful,
30 * but WITHOUT ANY WARRANTY; without even the implied warranty of
31 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
32 * GNU General Public License for more details.
33 *
34 * You should have received a copy of the GNU General Public License
35 * along with this program. If not, see <https://www.gnu.org/licenses/>.
36 */
37
38 #include "zrythm-config.h"
39
40 #include "audio/audio_track.h"
41 #include "audio/control_port.h"
42 #include "audio/engine.h"
43 #include "audio/engine_alsa.h"
44 #ifdef HAVE_JACK
45 #include "audio/engine_jack.h"
46 #endif
47 #ifdef HAVE_PORT_AUDIO
48 #include "audio/engine_pa.h"
49 #endif
50 #include "audio/graph.h"
51 #include "audio/graph_thread.h"
52 #include "audio/master_track.h"
53 #include "audio/midi.h"
54 #include "audio/midi_track.h"
55 #include "audio/pan.h"
56 #include "audio/port.h"
57 #include "audio/router.h"
58 #include "audio/stretcher.h"
59 #include "audio/tempo_track.h"
60 #include "audio/track.h"
61 #include "audio/track_processor.h"
62 #include "project.h"
63 #include "utils/arrays.h"
64 #include "utils/flags.h"
65 #include "utils/env.h"
66 #include "utils/mpmc_queue.h"
67 #include "utils/object_utils.h"
68 #include "utils/objects.h"
69 #include "utils/stoat.h"
70 #include "zrythm_app.h"
71
72 #ifdef HAVE_JACK
73 #include "weak_libjack.h"
74 #endif
75
76 /**
77 * Returns the max playback latency of the trigger
78 * nodes.
79 */
80 nframes_t
router_get_max_route_playback_latency(Router * router)81 router_get_max_route_playback_latency (
82 Router * router)
83 {
84 g_return_val_if_fail (
85 router && router->graph, 0);
86 router->max_route_playback_latency =
87 graph_get_max_route_playback_latency (
88 router->graph, false);
89
90 return router->max_route_playback_latency;
91 }
92
93 /**
94 * Starts a new cycle.
95 */
96 void
router_start_cycle(Router * self,EngineProcessTimeInfo time_nfo)97 router_start_cycle (
98 Router * self,
99 EngineProcessTimeInfo time_nfo)
100 {
101 g_return_if_fail (self && self->graph);
102 g_return_if_fail (
103 time_nfo.local_offset + time_nfo.nframes <=
104 AUDIO_ENGINE->nframes);
105
106 /* only set the kickoff thread when not called
107 * from the gtk thread (sometimes this is called
108 * from the gtk thread to force some
109 * processing) */
110 if (g_thread_self () != zrythm_app->gtk_thread)
111 self->process_kickoff_thread = g_thread_self ();
112
113 if (!zix_sem_try_wait (&self->graph_access))
114 {
115 g_message (
116 "graph access is busy, returning...");
117 return;
118 }
119
120 self->global_offset =
121 self->max_route_playback_latency -
122 AUDIO_ENGINE->remaining_latency_preroll;
123 memcpy (
124 &self->time_nfo, &time_nfo,
125 sizeof (EngineProcessTimeInfo));
126
127 /* read control port change events */
128 while (zix_ring_read_space (
129 self->ctrl_port_change_queue)
130 >= sizeof (ControlPortChange))
131 {
132 ControlPortChange change = { 0 };
133 zix_ring_read (
134 self->ctrl_port_change_queue, &change,
135 sizeof (change));
136 if (change.flag1 & PORT_FLAG_BPM)
137 {
138 tempo_track_set_bpm (
139 P_TEMPO_TRACK, change.real_val, 0.f,
140 true, F_PUBLISH_EVENTS);
141 }
142 else if (change.flag2 &
143 PORT_FLAG2_BEATS_PER_BAR)
144 {
145 tempo_track_set_beats_per_bar (
146 P_TEMPO_TRACK, change.ival);
147 }
148 else if (change.flag2 & PORT_FLAG2_BEAT_UNIT)
149 {
150 tempo_track_set_beat_unit_from_enum (
151 P_TEMPO_TRACK, change.beat_unit);
152 }
153 }
154
155 /* process tempo track ports first */
156 if (self->graph->bpm_node)
157 {
158 graph_node_process (
159 self->graph->bpm_node, time_nfo);
160 }
161 if (self->graph->beats_per_bar_node)
162 {
163 graph_node_process (
164 self->graph->beats_per_bar_node, time_nfo);
165 }
166 if (self->graph->beat_unit_node)
167 {
168 graph_node_process (
169 self->graph->beat_unit_node, time_nfo);
170 }
171
172 self->callback_in_progress = true;
173 zix_sem_post (&self->graph->callback_start);
174 zix_sem_wait (&self->graph->callback_done);
175 self->callback_in_progress = false;
176
177 zix_sem_post (&self->graph_access);
178 }
179
180 /**
181 * Recalculates the process acyclic directed graph.
182 *
183 * @param soft If true, only readjusts latencies.
184 */
185 void
router_recalc_graph(Router * self,bool soft)186 router_recalc_graph (
187 Router * self,
188 bool soft)
189 {
190 g_message (
191 "Recalculating%s...", soft ? " (soft)" : "");
192
193 g_return_if_fail (self);
194
195 if (!self->graph && !soft)
196 {
197 self->graph = graph_new (self);
198 graph_setup (self->graph, 1, 1);
199 graph_start (self->graph);
200 return;
201 }
202
203 if (soft)
204 {
205 zix_sem_wait (&self->graph_access);
206 graph_update_latencies (self->graph, false);
207 zix_sem_post (&self->graph_access);
208 }
209 else
210 {
211 zix_sem_wait (&self->graph_access);
212 graph_setup (self->graph, 1, 1);
213 zix_sem_post (&self->graph_access);
214 }
215
216 g_message ("done");
217 }
218
219 /**
220 * Queues a control port change to be applied
221 * when processing starts.
222 *
223 * Currently only applies to BPM/time signature
224 * changes.
225 */
226 void
router_queue_control_port_change(Router * self,const ControlPortChange * change)227 router_queue_control_port_change (
228 Router * self,
229 const ControlPortChange * change)
230 {
231 if (zix_ring_write_space (
232 self->ctrl_port_change_queue) <
233 sizeof (ControlPortChange))
234 {
235 zix_ring_skip (
236 self->ctrl_port_change_queue,
237 sizeof (ControlPortChange));
238 }
239
240 zix_ring_write (
241 self->ctrl_port_change_queue, change,
242 sizeof (ControlPortChange));
243 }
244
245 /**
246 * Creates a new Router.
247 *
248 * There is only 1 router needed in a project.
249 */
250 Router *
router_new(void)251 router_new (void)
252 {
253 g_message ("Creating new router...");
254
255 Router * self = object_new (Router);
256
257 zix_sem_init (&self->graph_access, 1);
258
259 self->ctrl_port_change_queue =
260 zix_ring_new (
261 sizeof (ControlPortChange) * (size_t) 24);
262
263 g_message ("done");
264
265 return self;
266 }
267
268 /**
269 * Returns whether this is the thread that kicks
270 * off processing (thread that calls
271 * router_start_cycle()).
272 */
273 bool
router_is_processing_kickoff_thread(const Router * const self)274 router_is_processing_kickoff_thread (
275 const Router * const self)
276 {
277 return
278 g_thread_self () ==
279 self->process_kickoff_thread;
280 }
281
282 /**
283 * Returns if the current thread is a
284 * processing thread.
285 */
286 bool
router_is_processing_thread(const Router * const self)287 router_is_processing_thread (
288 const Router * const self)
289 {
290 if (!self->graph)
291 return false;
292
293 for (int j = 0;
294 j < self->graph->num_threads; j++)
295 {
296 #ifdef HAVE_JACK
297 if (AUDIO_ENGINE->audio_backend ==
298 AUDIO_BACKEND_JACK)
299 {
300 if (pthread_equal (
301 pthread_self (),
302 self->graph->threads[j]->jthread))
303 return true;
304 }
305 else
306 {
307 #endif
308 if (pthread_equal (
309 pthread_self (),
310 self->graph->threads[j]->pthread))
311 return true;
312 #ifdef HAVE_JACK
313 }
314 #endif
315 }
316
317 #ifdef HAVE_JACK
318 if (AUDIO_ENGINE->audio_backend ==
319 AUDIO_BACKEND_JACK)
320 {
321 if (self->graph->main_thread
322 &&
323 pthread_equal (
324 pthread_self (),
325 self->graph->main_thread->jthread))
326 return true;
327 }
328 else
329 {
330 #endif
331 if (self->graph->main_thread
332 &&
333 pthread_equal (
334 pthread_self (),
335 self->graph->main_thread->pthread))
336 return true;
337 #ifdef HAVE_JACK
338 }
339 #endif
340
341 return false;
342 }
343
344 void
router_free(Router * self)345 router_free (
346 Router * self)
347 {
348 g_debug ("%s: freeing...", __func__);
349
350 if (self->graph)
351 graph_destroy (self->graph);
352 self->graph = NULL;
353
354 zix_sem_destroy (&self->graph_access);
355 object_set_to_zero (&self->graph_access);
356
357 object_free_w_func_and_null (
358 zix_ring_free, self->ctrl_port_change_queue);
359
360 object_zero_and_free (self);
361
362 g_debug ("%s: done", __func__);
363 }
364