1 /* GSequencer - Advanced GTK Sequencer
2  * Copyright (C) 2005-2019 Joël Krähemann
3  *
4  * This file is part of GSequencer.
5  *
6  * GSequencer is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU 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  * GSequencer 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 General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with GSequencer.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include <ags/audio/recall/ags_play_channel_run_master.h>
21 
22 #include <ags/audio/ags_audio.h>
23 #include <ags/audio/ags_recycling.h>
24 #include <ags/audio/ags_recall_id.h>
25 #include <ags/audio/ags_recall_container.h>
26 
27 #include <ags/audio/recall/ags_play_channel.h>
28 #include <ags/audio/recall/ags_play_recycling.h>
29 #include <ags/audio/recall/ags_stream_channel_run.h>
30 
31 #include <stdlib.h>
32 #include <stdio.h>
33 
34 #include <ags/i18n.h>
35 
36 void ags_play_channel_run_master_class_init(AgsPlayChannelRunMasterClass *play_channel_run_master);
37 void ags_play_channel_run_master_connectable_interface_init(AgsConnectableInterface *connectable);
38 void ags_play_channel_run_master_init(AgsPlayChannelRunMaster *play_channel_run_master);
39 void ags_play_channel_run_master_set_property(GObject *gobject,
40 					      guint prop_id,
41 					      const GValue *value,
42 					      GParamSpec *param_spec);
43 void ags_play_channel_run_master_get_property(GObject *gobject,
44 					      guint prop_id,
45 					      GValue *value,
46 					      GParamSpec *param_spec);
47 void ags_play_channel_run_master_dispose(GObject *gobject);
48 void ags_play_channel_run_master_finalize(GObject *gobject);
49 
50 void ags_play_channel_run_master_connect(AgsConnectable *connectable);
51 void ags_play_channel_run_master_disconnect(AgsConnectable *connectable);
52 void ags_play_channel_run_master_connect_connection(AgsConnectable *connectable,
53 						    GObject *connection);
54 void ags_play_channel_run_master_disconnect_connection(AgsConnectable *connectable,
55 						       GObject *connection);
56 
57 void ags_play_channel_run_master_run_init_pre(AgsRecall *recall);
58 void ags_play_channel_run_master_resolve_dependency(AgsRecall *recall);
59 
60 void ags_play_channel_run_master_remap_child_source(AgsPlayChannelRunMaster *play_channel_run_master,
61 						    AgsRecycling *old_start_region, AgsRecycling *old_end_region,
62 						    AgsRecycling *new_start_region, AgsRecycling *new_end_region);
63 void ags_play_channel_run_master_remap_dependencies(AgsPlayChannelRunMaster *play_channel_run_master,
64 						    AgsRecycling *old_start_changed_region, AgsRecycling *old_end_changed_region,
65 						    AgsRecycling *new_start_changed_region, AgsRecycling *new_end_changed_region);
66 
67 void ags_play_channel_run_master_source_recycling_changed_callback(AgsChannel *channel,
68 								   AgsRecycling *old_start_region, AgsRecycling *old_end_region,
69 								   AgsRecycling *new_start_region, AgsRecycling *new_end_region,
70 								   AgsRecycling *old_start_changed_region, AgsRecycling *old_end_changed_region,
71 								   AgsRecycling *new_start_changed_region, AgsRecycling *new_end_changed_region,
72 								   AgsPlayChannelRunMaster *play_channel_run_master);
73 
74 void ags_play_channel_run_master_stream_channel_done_callback(AgsRecall *recall,
75 							      AgsPlayChannelRunMaster *play_channel_run_master);
76 
77 /**
78  * SECTION:ags_play_channel_run_master
79  * @short_description: plays channel as toplevel
80  * @title: AgsPlayChannelRunMaster
81  * @section_id:
82  * @include: ags/audio/recall/ags_play_channel_master.h
83  *
84  * The #AgsPlayChannelRunMaster class plays the channel within toplevel context.
85  */
86 
87 enum{
88   PROP_0,
89   PROP_STREAM_CHANNEL_RUN,
90 };
91 
92 static gpointer ags_play_channel_run_master_parent_class = NULL;
93 static AgsConnectableInterface *ags_play_channel_run_master_parent_connectable_interface;
94 
95 GType
ags_play_channel_run_master_get_type()96 ags_play_channel_run_master_get_type()
97 {
98   static volatile gsize g_define_type_id__volatile = 0;
99 
100   if(g_once_init_enter (&g_define_type_id__volatile)){
101     GType ags_type_play_channel_run_master = 0;
102 
103     static const GTypeInfo ags_play_channel_run_master_info = {
104       sizeof (AgsPlayChannelRunMasterClass),
105       NULL, /* base_init */
106       NULL, /* base_finalize */
107       (GClassInitFunc) ags_play_channel_run_master_class_init,
108       NULL, /* class_finalize */
109       NULL, /* class_data */
110       sizeof (AgsPlayChannelRunMaster),
111       0,    /* n_preallocs */
112       (GInstanceInitFunc) ags_play_channel_run_master_init,
113     };
114 
115     static const GInterfaceInfo ags_connectable_interface_info = {
116       (GInterfaceInitFunc) ags_play_channel_run_master_connectable_interface_init,
117       NULL, /* interface_finalize */
118       NULL, /* interface_data */
119     };
120 
121     ags_type_play_channel_run_master = g_type_register_static(AGS_TYPE_RECALL_CHANNEL_RUN,
122 							      "AgsPlayChannelRunMaster",
123 							      &ags_play_channel_run_master_info,
124 							      0);
125 
126     g_type_add_interface_static(ags_type_play_channel_run_master,
127 				AGS_TYPE_CONNECTABLE,
128 				&ags_connectable_interface_info);
129 
130     g_once_init_leave(&g_define_type_id__volatile, ags_type_play_channel_run_master);
131   }
132 
133   return g_define_type_id__volatile;
134 }
135 
136 void
ags_play_channel_run_master_class_init(AgsPlayChannelRunMasterClass * play_channel_run_master)137 ags_play_channel_run_master_class_init(AgsPlayChannelRunMasterClass *play_channel_run_master)
138 {
139   GObjectClass *gobject;
140   AgsRecallClass *recall;
141   GParamSpec *param_spec;
142 
143   ags_play_channel_run_master_parent_class = g_type_class_peek_parent(play_channel_run_master);
144 
145   /* GObjectClass */
146   gobject = (GObjectClass *) play_channel_run_master;
147 
148   gobject->set_property = ags_play_channel_run_master_set_property;
149   gobject->get_property = ags_play_channel_run_master_get_property;
150 
151   gobject->dispose = ags_play_channel_run_master_dispose;
152   gobject->finalize = ags_play_channel_run_master_finalize;
153 
154   /* properties */
155   /**
156    * AgsPlayChannelRunMaster:stream-channel-run: (type GList(AgsStreamChannelRun)) (transfer full)
157    *
158    * The assigned stream channel run.
159    *
160    * Since: 3.0.0
161    */
162   param_spec = g_param_spec_pointer("stream-channel-run",
163 				    i18n_pspec("assigned AgsStreamChannelRun"),
164 				    i18n_pspec("an assigned AgsStreamChannelRun"),
165 				    G_PARAM_READABLE | G_PARAM_WRITABLE);
166   g_object_class_install_property(gobject,
167 				  PROP_STREAM_CHANNEL_RUN,
168 				  param_spec);
169 
170   /* AgsRecallClass */
171   recall = (AgsRecallClass *) play_channel_run_master;
172 
173   recall->run_init_pre = ags_play_channel_run_master_run_init_pre;
174   recall->resolve_dependency = ags_play_channel_run_master_resolve_dependency;
175 }
176 
177 void
ags_play_channel_run_master_connectable_interface_init(AgsConnectableInterface * connectable)178 ags_play_channel_run_master_connectable_interface_init(AgsConnectableInterface *connectable)
179 {
180   ags_play_channel_run_master_parent_connectable_interface = g_type_interface_peek_parent(connectable);
181 
182   connectable->connect = ags_play_channel_run_master_connect;
183   connectable->disconnect = ags_play_channel_run_master_disconnect;
184 
185   connectable->connect_connection = ags_play_channel_run_master_connect_connection;
186   connectable->disconnect_connection = ags_play_channel_run_master_disconnect_connection;
187 }
188 
189 void
ags_play_channel_run_master_init(AgsPlayChannelRunMaster * play_channel_run_master)190 ags_play_channel_run_master_init(AgsPlayChannelRunMaster *play_channel_run_master)
191 {
192   AGS_RECALL(play_channel_run_master)->name = "ags-play";
193   AGS_RECALL(play_channel_run_master)->version = AGS_RECALL_DEFAULT_VERSION;
194   AGS_RECALL(play_channel_run_master)->build_id = AGS_RECALL_DEFAULT_BUILD_ID;
195   AGS_RECALL(play_channel_run_master)->xml_type = "ags-play-channel-run-master";
196   AGS_RECALL(play_channel_run_master)->port = NULL;
197 
198   AGS_RECALL(play_channel_run_master)->behaviour_flags |= (AGS_SOUND_BEHAVIOUR_PERSISTENT);
199   AGS_RECALL(play_channel_run_master)->child_type = AGS_TYPE_PLAY_RECYCLING;
200 
201   play_channel_run_master->flags = 0;
202 
203   play_channel_run_master->stream_channel_run = NULL;
204 }
205 
206 void
ags_play_channel_run_master_set_property(GObject * gobject,guint prop_id,const GValue * value,GParamSpec * param_spec)207 ags_play_channel_run_master_set_property(GObject *gobject,
208 					 guint prop_id,
209 					 const GValue *value,
210 					 GParamSpec *param_spec)
211 {
212   AgsPlayChannelRunMaster *play_channel_run_master;
213 
214   GRecMutex *recall_mutex;
215 
216   play_channel_run_master = AGS_PLAY_CHANNEL_RUN_MASTER(gobject);
217 
218   /* get recall mutex */
219   recall_mutex = AGS_RECALL_GET_OBJ_MUTEX(play_channel_run_master);
220 
221   switch(prop_id){
222   case PROP_STREAM_CHANNEL_RUN:
223     {
224       AgsStreamChannelRun  *stream_channel_run;
225 
226       gboolean is_template;
227 
228       stream_channel_run = (AgsStreamChannelRun *) g_value_get_pointer(value);
229 
230       g_rec_mutex_lock(recall_mutex);
231 
232       if(stream_channel_run == NULL ||
233 	 g_list_find(play_channel_run_master->stream_channel_run,
234 		     stream_channel_run) != NULL){
235 	g_rec_mutex_unlock(recall_mutex);
236 
237 	return;
238       }
239 
240       if(stream_channel_run != NULL){
241 	g_object_ref(G_OBJECT(stream_channel_run));
242       }
243 
244       play_channel_run_master->stream_channel_run = g_list_prepend(play_channel_run_master->stream_channel_run,
245 								   stream_channel_run);
246 
247       g_rec_mutex_unlock(recall_mutex);
248 
249       if(ags_recall_test_flags((AgsRecall *) stream_channel_run, AGS_RECALL_TEMPLATE)){
250 	is_template = TRUE;
251       }else{
252 	is_template = FALSE;
253       }
254 
255       if(is_template){
256 	ags_recall_add_recall_dependency((AgsRecall *) play_channel_run_master,
257 					 ags_recall_dependency_new((GObject *) stream_channel_run));
258       }else{
259 	if(ags_connectable_is_connected(AGS_CONNECTABLE(play_channel_run_master))){
260 	  ags_connectable_connect_connection(AGS_CONNECTABLE(play_channel_run_master),
261 					     (GObject *) stream_channel_run);
262 	}
263       }
264     }
265     break;
266   default:
267     G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, param_spec);
268     break;
269   }
270 }
271 
272 void
ags_play_channel_run_master_get_property(GObject * gobject,guint prop_id,GValue * value,GParamSpec * param_spec)273 ags_play_channel_run_master_get_property(GObject *gobject,
274 					 guint prop_id,
275 					 GValue *value,
276 					 GParamSpec *param_spec)
277 {
278   AgsPlayChannelRunMaster *play_channel_run_master;
279 
280   GRecMutex *recall_mutex;
281 
282   play_channel_run_master = AGS_PLAY_CHANNEL_RUN_MASTER(gobject);
283 
284   /* get recall mutex */
285   recall_mutex = AGS_RECALL_GET_OBJ_MUTEX(play_channel_run_master);
286 
287   switch(prop_id){
288   case PROP_STREAM_CHANNEL_RUN:
289     {
290       g_rec_mutex_lock(recall_mutex);
291 
292       g_value_set_pointer(value,
293 			  g_list_copy_deep(play_channel_run_master->stream_channel_run,
294 					   (GCopyFunc) g_object_ref,
295 					   NULL));
296 
297       g_rec_mutex_unlock(recall_mutex);
298     }
299     break;
300   default:
301     G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, param_spec);
302     break;
303   }
304 }
305 
306 void
ags_play_channel_run_master_dispose(GObject * gobject)307 ags_play_channel_run_master_dispose(GObject *gobject)
308 {
309   AgsPlayChannelRunMaster *play_channel_run_master;
310 
311   play_channel_run_master = AGS_PLAY_CHANNEL_RUN_MASTER(gobject);
312 
313   /* stream channel run */
314   if(play_channel_run_master->stream_channel_run != NULL){
315     g_list_free_full(play_channel_run_master->stream_channel_run,
316 		     g_object_unref);
317 
318     play_channel_run_master->stream_channel_run = NULL;
319   }
320 
321   /* call parent */
322   G_OBJECT_CLASS(ags_play_channel_run_master_parent_class)->dispose(gobject);
323 }
324 
325 void
ags_play_channel_run_master_finalize(GObject * gobject)326 ags_play_channel_run_master_finalize(GObject *gobject)
327 {
328   AgsPlayChannelRunMaster *play_channel_run_master;
329 
330   play_channel_run_master = AGS_PLAY_CHANNEL_RUN_MASTER(gobject);
331 
332   /* stream channel run */
333   if(play_channel_run_master->stream_channel_run != NULL){
334     g_list_free_full(play_channel_run_master->stream_channel_run,
335 		     g_object_unref);
336   }
337 
338   /* call parent */
339   G_OBJECT_CLASS(ags_play_channel_run_master_parent_class)->finalize(gobject);
340 }
341 
342 void
ags_play_channel_run_master_connect(AgsConnectable * connectable)343 ags_play_channel_run_master_connect(AgsConnectable *connectable)
344 {
345   AgsChannel *channel;
346   AgsPlayChannelRunMaster *play_channel_run_master;
347 
348   GList *list_start, *list;
349 
350   if(ags_connectable_is_connected(connectable)){
351     return;
352   }
353 
354   play_channel_run_master = AGS_PLAY_CHANNEL_RUN_MASTER(connectable);
355 
356   /* call parent */
357   ags_play_channel_run_master_parent_connectable_interface->connect(connectable);
358 
359   /* source */
360   g_object_get(play_channel_run_master,
361 	       "source", &channel,
362 	       NULL);
363 
364   g_signal_connect(channel, "recycling-changed",
365 		   G_CALLBACK(ags_play_channel_run_master_source_recycling_changed_callback), play_channel_run_master);
366 
367   /* connection */
368   g_object_get(play_channel_run_master,
369 	       "stream-channel-run", &list_start,
370 	       NULL);
371 
372   list = list_start;
373 
374   while(list != NULL){
375     ags_connectable_connect_connection(connectable,
376 				       (GObject *) list->data);
377 
378     list = list->next;
379   }
380 
381   /* unref */
382   g_object_unref(channel);
383 
384   g_list_free_full(list_start,
385 		   g_object_unref);
386 }
387 
388 void
ags_play_channel_run_master_disconnect(AgsConnectable * connectable)389 ags_play_channel_run_master_disconnect(AgsConnectable *connectable)
390 {
391   AgsChannel *channel;
392   AgsPlayChannelRunMaster *play_channel_run_master;
393 
394   GList *list_start, *list;
395 
396   if(!ags_connectable_is_connected(connectable)){
397     return;
398   }
399 
400   play_channel_run_master = AGS_PLAY_CHANNEL_RUN_MASTER(connectable);
401 
402   ags_play_channel_run_master_parent_connectable_interface->disconnect(connectable);
403 
404   /* source */
405   g_object_get(play_channel_run_master,
406 	       "source", &channel,
407 	       NULL);
408 
409   g_object_disconnect(channel,
410 		      "any_signal::recycling-changed",
411 		      G_CALLBACK(ags_play_channel_run_master_source_recycling_changed_callback),
412 		      play_channel_run_master,
413 		      NULL);
414 
415   /* connection */
416   g_object_get(play_channel_run_master,
417 	       "stream-channel-run", &list_start,
418 	       NULL);
419 
420   list = list_start;
421 
422   while(list != NULL){
423     ags_connectable_disconnect_connection(connectable,
424 					  (GObject *) list->data);
425 
426     list = list->next;
427   }
428 
429   /* unref */
430   g_object_unref(channel);
431 
432   g_list_free_full(list_start,
433 		   g_object_unref);
434 }
435 
436 void
ags_play_channel_run_master_connect_connection(AgsConnectable * connectable,GObject * connection)437 ags_play_channel_run_master_connect_connection(AgsConnectable *connectable,
438 					       GObject *connection)
439 {
440   AgsPlayChannelRunMaster *play_channel_run_master;
441 
442   GList *list_start;
443 
444   play_channel_run_master = AGS_PLAY_CHANNEL_RUN_MASTER(connectable);
445 
446   if(connection == NULL){
447     return;
448   }
449 
450   g_object_get(play_channel_run_master,
451 	       "stream-channel-run", &list_start,
452 	       NULL);
453 
454   if(g_list_find(list_start, connection) != NULL){
455     g_signal_connect(connection, "done",
456 		     G_CALLBACK(ags_play_channel_run_master_stream_channel_done_callback), play_channel_run_master);
457   }
458 
459   g_list_free_full(list_start,
460 		   g_object_unref);
461 }
462 
463 void
ags_play_channel_run_master_disconnect_connection(AgsConnectable * connectable,GObject * connection)464 ags_play_channel_run_master_disconnect_connection(AgsConnectable *connectable,
465 						  GObject *connection)
466 {
467   AgsPlayChannelRunMaster *play_channel_run_master;
468 
469   GList *list_start;
470 
471   play_channel_run_master = AGS_PLAY_CHANNEL_RUN_MASTER(connectable);
472 
473   if(connection == NULL){
474     return;
475   }
476 
477   g_object_get(play_channel_run_master,
478 	       "stream-channel-run", &list_start,
479 	       NULL);
480 
481   if(g_list_find(list_start, connection) != NULL){
482     g_object_disconnect(connection,
483 			"any_signal::done",
484 			G_CALLBACK(ags_play_channel_run_master_stream_channel_done_callback),
485 			play_channel_run_master,
486 			NULL);
487   }
488 
489   g_list_free_full(list_start,
490 		   g_object_unref);
491 }
492 
493 void
ags_play_channel_run_master_run_init_pre(AgsRecall * recall)494 ags_play_channel_run_master_run_init_pre(AgsRecall *recall)
495 {
496   AgsChannel *channel;
497   AgsRecycling *first_recycling, *last_recycling;
498   AgsPlayChannelRunMaster *play_channel_run_master;
499 
500   void (*parent_class_run_init_pre)(AgsRecall *recall);
501 
502   GRecMutex *recall_mutex;
503 
504   play_channel_run_master = AGS_PLAY_CHANNEL_RUN_MASTER(recall);
505 
506   /* get recall mutex */
507   recall_mutex = AGS_RECALL_GET_OBJ_MUTEX(recall);
508 
509   /* get parent class */
510   parent_class_run_init_pre = AGS_RECALL_CLASS(ags_play_channel_run_master_parent_class)->run_init_pre;
511 
512   /* call parent */
513   parent_class_run_init_pre(recall);
514 
515   /* remap */
516   g_object_get(play_channel_run_master,
517 	       "source", &channel,
518 	       NULL);
519 
520   g_object_get(channel,
521 	       "first-recycling", &first_recycling,
522 	       "last-recycling", &last_recycling,
523 	       NULL);
524 
525   ags_play_channel_run_master_remap_dependencies(play_channel_run_master,
526 						 NULL, NULL,
527 						 first_recycling, last_recycling);
528 
529   g_object_unref(channel);
530 
531   g_object_unref(first_recycling);
532   g_object_unref(last_recycling);
533 }
534 
535 void
ags_play_channel_run_master_resolve_dependency(AgsRecall * recall)536 ags_play_channel_run_master_resolve_dependency(AgsRecall *recall)
537 {
538   AgsRecall *template;
539   AgsRecallContainer *recall_container;
540   AgsRecallID *recall_id;
541   AgsRecallDependency *recall_dependency;
542   AgsStreamChannelRun *stream_channel_run;
543 
544   GObject *dependency;
545 
546   GList *list_start, *list;
547 
548   guint i, i_stop;
549 
550   /* get some fields */
551   g_object_get(recall,
552 	       "recall-container", &recall_container,
553 	       NULL);
554 
555   g_object_get(recall_container,
556 	       "recall-channel-run", &list_start,
557 	       NULL);
558 
559   list = ags_recall_find_template(list_start);
560 
561   if(list == NULL){
562     g_warning("AgsRecallClass::resolve - missing dependency");
563 
564     g_object_unref(recall_container);
565 
566     g_list_free_full(list_start,
567 		     g_object_unref);
568 
569     return;
570   }
571 
572   template = AGS_RECALL(list->data);
573   g_list_free_full(list_start,
574 		   g_object_unref);
575 
576   g_object_get(template,
577 	       "recall-dependency", &list_start,
578 	       NULL);
579 
580   g_object_get(recall,
581 	       "recall-id", &recall_id,
582 	       NULL);
583 
584   /* prepare to resolve */
585   stream_channel_run = NULL;
586 
587   list = list_start;
588 
589   for(i = 0; list != NULL;){
590     recall_dependency = AGS_RECALL_DEPENDENCY(list->data);
591 
592     g_object_get(recall_dependency,
593 		 "dependency", &dependency,
594 		 NULL);
595 
596     if(AGS_IS_STREAM_CHANNEL_RUN(dependency)){
597       stream_channel_run = (AgsStreamChannelRun *) ags_recall_dependency_resolve(recall_dependency,
598 										 recall_id);
599 
600       g_object_set(G_OBJECT(recall),
601 		   "stream-channel-run", stream_channel_run,
602 		   NULL);
603 
604       i++;
605     }
606 
607     g_object_unref(dependency);
608 
609     list = list->next;
610   }
611 
612   /* unref */
613   g_object_unref(recall_container);
614 
615   g_list_free_full(list_start,
616 		   g_object_unref);
617 
618   g_object_unref(recall_id);
619 }
620 
621 void
ags_play_channel_run_master_remap_dependencies(AgsPlayChannelRunMaster * play_channel_run_master,AgsRecycling * old_start_region,AgsRecycling * old_end_region,AgsRecycling * new_start_region,AgsRecycling * new_end_region)622 ags_play_channel_run_master_remap_dependencies(AgsPlayChannelRunMaster *play_channel_run_master,
623 					       AgsRecycling *old_start_region, AgsRecycling *old_end_region,
624 					       AgsRecycling *new_start_region, AgsRecycling *new_end_region)
625 {
626   AgsChannel *current;
627   AgsRecycling *recycling, *next_recycling;
628   AgsRecycling *end_recycling;
629   AgsRecallID *recall_id;
630   AgsRecyclingContext *recycling_context;
631 
632   GRecMutex *recall_mutex;
633 
634   if(!AGS_IS_PLAY_CHANNEL_RUN_MASTER(play_channel_run_master)){
635     return;
636   }
637 
638   /* get recall mutex */
639   recall_mutex = AGS_RECALL_GET_OBJ_MUTEX(play_channel_run_master);
640 
641   /* get recycling context */
642   g_object_get(play_channel_run_master,
643 	       "recall-id", &recall_id,
644 	       NULL);
645 
646   recycling_context = NULL;
647 
648   if(recall_id != NULL){
649     g_object_get(recall_id,
650 		 "recycling-context", &recycling_context,
651 		 NULL);
652   }
653 
654   /* remove old */
655   if(old_start_region != NULL){
656     GList *list_start, *list;
657 
658     current = NULL;
659 
660     recycling = old_start_region;
661     g_object_ref(recycling);
662 
663     end_recycling = ags_recycling_next(old_end_region);
664 
665     next_recycling = NULL;
666 
667     while(recycling != end_recycling){
668       AgsChannel *tmp_channel;
669 
670       g_object_get(recycling,
671 		   "channel", &tmp_channel,
672 		   NULL);
673 
674       if(current != tmp_channel){
675 	current = tmp_channel;
676 
677 	g_object_get(play_channel_run_master,
678 		     "recall-dependency", &list_start,
679 		     NULL);
680 
681 	list = list_start;
682 
683 	while((list = ags_recall_dependency_find_dependency_by_provider(list,
684 									(GObject *) current)) != NULL){
685 	  GObject *dependency;
686 
687 	  GList *start_stream_channel_run;
688 
689 	  g_object_get(list->data,
690 		       "dependency", &dependency,
691 		        NULL);
692 
693 	  /* remove dependency */
694 	  ags_recall_remove_recall_dependency((AgsRecall *) play_channel_run_master,
695 					      list->data);
696 
697 	  /* remove stream channel run */
698 	  g_object_get(play_channel_run_master,
699 		       "stream-channel-run", &start_stream_channel_run,
700 		       NULL);
701 
702 	  if(g_list_find(start_stream_channel_run,
703 			 dependency) != NULL){
704 	    g_rec_mutex_lock(recall_mutex);
705 
706 	    play_channel_run_master->stream_channel_run = g_list_remove(play_channel_run_master->stream_channel_run,
707 									dependency);
708 
709 	    g_rec_mutex_unlock(recall_mutex);
710 
711 	    g_object_unref(dependency);
712 	  }
713 
714 	  g_list_free_full(start_stream_channel_run,
715 			   g_object_unref);
716 
717 	  g_object_unref(dependency);
718 
719 	  /* iterate */
720 	  list = list->next;
721 	}
722 
723 	g_list_free_full(list_start,
724 			 g_object_unref);
725       }
726 
727       /* unref */
728       g_object_unref(tmp_channel);
729 
730       /* iterate */
731       next_recycling = ags_recycling_next(recycling);
732 
733       g_object_unref(recycling);
734 
735       recycling = next_recycling;
736     }
737 
738     /* unref */
739     if(end_recycling != NULL){
740       g_object_unref(end_recycling);
741     }
742 
743     if(next_recycling != NULL){
744       g_object_unref(next_recycling);
745     }
746   }
747 
748   /* add new */
749   if(new_start_region != NULL){
750     AgsRecallContainer *recall_container;
751     AgsPlayChannelRunMaster *current_master;
752 
753     GList *list_start, *list;
754     GList *master_start, *master;
755 
756     current = NULL;
757 
758     recycling = new_start_region;
759     g_object_ref(recycling);
760 
761     end_recycling = ags_recycling_next(new_end_region);
762 
763     next_recycling = NULL;
764 
765     while(recycling != end_recycling){
766       AgsChannel *tmp_channel;
767 
768       g_object_get(recycling,
769 		   "channel", &tmp_channel,
770 		   NULL);
771 
772       if(current != tmp_channel){
773 	current = tmp_channel;
774 
775 	g_object_get(current,
776 		     "play", &list_start,
777 		     NULL);
778 
779 	list = list_start;
780 
781 	while((list = ags_recall_find_type_with_recycling_context(list, AGS_TYPE_STREAM_CHANNEL_RUN, (GObject *) recycling_context)) != NULL){
782 	  g_object_set(play_channel_run_master,
783 		       "stream-channel-run", list->data,
784 		       NULL);
785 
786 	  g_object_get(play_channel_run_master,
787 		       "recall-container", &recall_container,
788 		       NULL);
789 
790 	  g_object_get(recall_container,
791 		       "recall-channel-run", &master_start,
792 		       NULL);
793 
794 	  master = master_start;
795 
796 	  while(master != NULL){
797 	    current_master = AGS_PLAY_CHANNEL_RUN_MASTER(master->data);
798 
799 	    if(!ags_recall_test_flags((AgsRecall *) current_master, AGS_RECALL_TEMPLATE)){
800 	      g_object_set(G_OBJECT(current_master),
801 			   "stream-channel-run", AGS_STREAM_CHANNEL_RUN(list->data),
802 			   NULL);
803 	    }
804 
805 	    /* iterate */
806 	    master = master->next;
807 	  }
808 
809 	  g_object_unref(recall_container);
810 
811 	  g_list_free_full(master_start,
812 			   g_object_unref);
813 
814 	  /* iterate */
815 	  list = list->next;
816 	}
817 
818 	g_list_free_full(list_start,
819 			 g_object_unref);
820       }
821 
822       /* unref */
823       g_object_unref(tmp_channel);
824 
825       /* iterate */
826       next_recycling = ags_recycling_next(recycling);
827 
828       g_object_unref(recycling);
829 
830       recycling = next_recycling;
831     }
832 
833     /* unref */
834     if(end_recycling != NULL){
835       g_object_unref(end_recycling);
836     }
837 
838     if(next_recycling != NULL){
839       g_object_unref(next_recycling);
840     }
841   }
842 
843   /* unref */
844   if(recall_id != NULL){
845     g_object_unref(recall_id);
846   }
847 
848   if(recycling_context != NULL){
849     g_object_unref(recycling_context);
850   }
851 }
852 
853 void
ags_play_channel_run_master_source_recycling_changed_callback(AgsChannel * channel,AgsRecycling * old_start_region,AgsRecycling * old_end_region,AgsRecycling * new_start_region,AgsRecycling * new_end_region,AgsRecycling * old_start_changed_region,AgsRecycling * old_end_changed_region,AgsRecycling * new_start_changed_region,AgsRecycling * new_end_changed_region,AgsPlayChannelRunMaster * play_channel_run_master)854 ags_play_channel_run_master_source_recycling_changed_callback(AgsChannel *channel,
855 							      AgsRecycling *old_start_region, AgsRecycling *old_end_region,
856 							      AgsRecycling *new_start_region, AgsRecycling *new_end_region,
857 							      AgsRecycling *old_start_changed_region, AgsRecycling *old_end_changed_region,
858 							      AgsRecycling *new_start_changed_region, AgsRecycling *new_end_changed_region,
859 							      AgsPlayChannelRunMaster *play_channel_run_master)
860 {
861   if(ags_recall_test_flags((AgsRecall *) play_channel_run_master, AGS_RECALL_TEMPLATE)){
862     ags_play_channel_run_master_remap_dependencies(play_channel_run_master,
863 						   old_start_changed_region, old_end_changed_region,
864 						   new_start_changed_region, new_end_changed_region);
865   }
866 }
867 
868 void
ags_play_channel_run_master_stream_channel_done_callback(AgsRecall * recall,AgsPlayChannelRunMaster * play_channel_run_master)869 ags_play_channel_run_master_stream_channel_done_callback(AgsRecall *recall,
870 							 AgsPlayChannelRunMaster *play_channel_run_master)
871 {
872   GRecMutex *recall_mutex;
873 
874   /* get recall mutex */
875   recall_mutex = AGS_RECALL_GET_OBJ_MUTEX(play_channel_run_master);
876 
877   /* remove stream channel run */
878   g_rec_mutex_lock(recall_mutex);
879 
880   play_channel_run_master->stream_channel_run = g_list_remove(play_channel_run_master->stream_channel_run,
881 							      recall);
882   g_object_unref(recall);
883 
884   g_rec_mutex_unlock(recall_mutex);
885 }
886 
887 /**
888  * ags_play_channel_master_run_new:
889  * @source: the #AgsChannel
890  *
891  * Create a new instance of #AgsPlayChannelRunMaster
892  *
893  * Returns: the new #AgsPlayChannelRunMaster
894  *
895  * Since: 3.0.0
896  */
897 AgsPlayChannelRunMaster*
ags_play_channel_run_master_new(AgsChannel * source)898 ags_play_channel_run_master_new(AgsChannel *source)
899 {
900   AgsPlayChannelRunMaster *play_channel_run_master;
901 
902   play_channel_run_master = (AgsPlayChannelRunMaster *) g_object_new(AGS_TYPE_PLAY_CHANNEL_RUN_MASTER,
903 								     "source", source,
904 								     NULL);
905 
906   return(play_channel_run_master);
907 }
908