1 /* GSequencer - Advanced GTK Sequencer
2  * Copyright (C) 2005-2020 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/ags_recycling_context.h>
21 
22 #include <ags/audio/ags_recall_id.h>
23 
24 #include <stdlib.h>
25 #include <string.h>
26 
27 #include <ags/i18n.h>
28 
29 void ags_recycling_context_class_init(AgsRecyclingContextClass *recycling_context_class);
30 void ags_recycling_context_init(AgsRecyclingContext *recycling_context);
31 void ags_recycling_context_set_property(GObject *gobject,
32 					guint prop_id,
33 					const GValue *value,
34 					GParamSpec *param_spec);
35 void ags_recycling_context_get_property(GObject *gobject,
36 					guint prop_id,
37 					GValue *value,
38 					GParamSpec *param_spec);
39 void ags_recycling_context_dispose(GObject *gobject);
40 void ags_recycling_context_finalize(GObject *gobject);
41 
42 /**
43  * SECTION:ags_recycling_context
44  * @short_description: A context of recycling acting as dynamic context.
45  * @title: AgsRecyclingContext
46  * @section_id:
47  * @include: ags/audio/ags_recycling_context.h
48  *
49  * #AgsRecyclingContext organizes #AgsRecycling objects as dynamic context
50  * within nested tree.
51  */
52 
53 enum{
54   PROP_0,
55   PROP_RECALL_ID,
56   PROP_PARENT,
57   PROP_CHILD,
58   PROP_LENGTH,
59 };
60 
61 static gpointer ags_recycling_context_parent_class = NULL;
62 
63 GType
ags_recycling_context_get_type(void)64 ags_recycling_context_get_type (void)
65 {
66   static volatile gsize g_define_type_id__volatile = 0;
67 
68   if(g_once_init_enter (&g_define_type_id__volatile)){
69     GType ags_type_recycling_context = 0;
70 
71     static const GTypeInfo ags_recycling_context_info = {
72       sizeof (AgsRecyclingContextClass),
73       (GBaseInitFunc) NULL, /* base_init */
74       (GBaseFinalizeFunc) NULL, /* base_finalize */
75       (GClassInitFunc) ags_recycling_context_class_init,
76       NULL, /* class_finalize */
77       NULL, /* class_data */
78       sizeof (AgsRecyclingContext),
79       0,    /* n_preallocs */
80       (GInstanceInitFunc) ags_recycling_context_init,
81     };
82 
83     ags_type_recycling_context = g_type_register_static(G_TYPE_OBJECT,
84 							"AgsRecyclingContext",
85 							&ags_recycling_context_info,
86 							0);
87 
88     g_once_init_leave(&g_define_type_id__volatile, ags_type_recycling_context);
89   }
90 
91   return g_define_type_id__volatile;
92 }
93 
94 void
ags_recycling_context_class_init(AgsRecyclingContextClass * recycling_context)95 ags_recycling_context_class_init(AgsRecyclingContextClass *recycling_context)
96 {
97   GObjectClass *gobject;
98   GParamSpec *param_spec;
99 
100   ags_recycling_context_parent_class = g_type_class_peek_parent(recycling_context);
101 
102   gobject = (GObjectClass *) recycling_context;
103 
104   gobject->set_property = ags_recycling_context_set_property;
105   gobject->get_property = ags_recycling_context_get_property;
106 
107   gobject->dispose = ags_recycling_context_dispose;
108   gobject->finalize = ags_recycling_context_finalize;
109 
110   /* properties */
111   /**
112    * AgsRecyclingContext:recall-id:
113    *
114    * The assigned #AgsRecallID.
115    *
116    * Since: 3.0.0
117    */
118   param_spec = g_param_spec_object("recall-id",
119 				   i18n_pspec("the default recall id"),
120 				   i18n_pspec("The recall id located in audio object as destiny"),
121 				   AGS_TYPE_RECALL_ID,
122 				   G_PARAM_READABLE | G_PARAM_WRITABLE);
123   g_object_class_install_property(gobject,
124 				  PROP_RECALL_ID,
125 				  param_spec);
126 
127   /**
128    * AgsRecyclingContext:parent:
129    *
130    * The parent recycling context within tree.
131    *
132    * Since: 3.0.0
133    */
134   param_spec = g_param_spec_object("parent",
135 				   i18n_pspec("parent context"),
136 				   i18n_pspec("The context this one is packed into"),
137 				   AGS_TYPE_RECYCLING_CONTEXT,
138 				   G_PARAM_READABLE | G_PARAM_WRITABLE);
139   g_object_class_install_property(gobject,
140 				  PROP_PARENT,
141 				  param_spec);
142 
143   /**
144    * AgsRecyclingContext:child: (type GList(AgsRecyclingContext)) (transfer full)
145    *
146    * The child recycling contexts.
147    *
148    * Since: 3.0.0
149    */
150   param_spec = g_param_spec_pointer("child",
151 				    i18n_pspec("child context"),
152 				    i18n_pspec("The child context"),
153 				    G_PARAM_READABLE | G_PARAM_WRITABLE);
154   g_object_class_install_property(gobject,
155 				  PROP_CHILD,
156 				  param_spec);
157 
158   /**
159    * AgsRecyclingContext:length:
160    *
161    * Boundary length.
162    *
163    * Since: 3.0.0
164    */
165   param_spec = g_param_spec_uint64("length",
166 				   i18n_pspec("length of the array of assigned recycling"),
167 				   i18n_pspec("The recycling array length"),
168 				   0, G_MAXUINT64,
169 				   0,
170 				   G_PARAM_READABLE | G_PARAM_WRITABLE);
171   g_object_class_install_property(gobject,
172 				  PROP_LENGTH,
173 				  param_spec);
174 }
175 
176 void
ags_recycling_context_init(AgsRecyclingContext * recycling_context)177 ags_recycling_context_init(AgsRecyclingContext *recycling_context)
178 {
179   recycling_context->flags = 0;
180   recycling_context->sound_scope = 0;
181 
182   /* recycling context mutex */
183   g_rec_mutex_init(&(recycling_context->obj_mutex));
184 
185   /* recall id */
186   recycling_context->recall_id = NULL;
187 
188   /* parent and child */
189   recycling_context->parent = NULL;
190   recycling_context->children = NULL;
191 
192   /* context */
193   recycling_context->recycling = NULL;
194   recycling_context->length = 0;
195 }
196 
197 void
ags_recycling_context_set_property(GObject * gobject,guint prop_id,const GValue * value,GParamSpec * param_spec)198 ags_recycling_context_set_property(GObject *gobject,
199 				   guint prop_id,
200 				   const GValue *value,
201 				   GParamSpec *param_spec)
202 {
203   AgsRecyclingContext *recycling_context;
204 
205   GRecMutex *recycling_context_mutex;
206 
207   recycling_context = AGS_RECYCLING_CONTEXT(gobject);
208 
209   /* get recycling context mutex */
210   recycling_context_mutex = AGS_RECYCLING_CONTEXT_GET_OBJ_MUTEX(recycling_context);
211 
212   switch(prop_id){
213   case PROP_RECALL_ID:
214     {
215       AgsRecallID *recall_id;
216 
217       recall_id = (AgsRecallID *) g_value_get_object(value);
218 
219       g_rec_mutex_lock(recycling_context_mutex);
220 
221       if(recycling_context->recall_id == (GObject *) recall_id){
222 	g_rec_mutex_unlock(recycling_context_mutex);
223 
224 	return;
225       }
226 
227       if(recycling_context->recall_id != NULL){
228 	g_object_unref(G_OBJECT(recycling_context->recall_id));
229       }
230 
231       if(recall_id != NULL){
232 	g_object_ref(G_OBJECT(recall_id));
233       }
234 
235       recycling_context->recall_id = (GObject *) recall_id;
236 
237       g_rec_mutex_unlock(recycling_context_mutex);
238     }
239     break;
240   case PROP_PARENT:
241     {
242       AgsRecyclingContext *parent;
243 
244       parent = (AgsRecyclingContext *) g_value_get_object(value);
245 
246       g_rec_mutex_lock(recycling_context_mutex);
247 
248       if(recycling_context->parent == parent){
249 	g_rec_mutex_unlock(recycling_context_mutex);
250 
251 	return;
252       }
253 
254       if(recycling_context->parent != NULL){
255 	g_object_unref(recycling_context->parent);
256       }
257 
258       if(parent != NULL){
259 	g_object_ref(parent);
260       }
261 
262       recycling_context->parent = parent;
263 
264       g_rec_mutex_unlock(recycling_context_mutex);
265     }
266     break;
267   case PROP_CHILD:
268     {
269       AgsRecyclingContext *child;
270 
271       child = (AgsRecyclingContext *) g_value_get_pointer(value);
272 
273       g_rec_mutex_lock(recycling_context_mutex);
274 
275       if(!AGS_IS_RECYCLING_CONTEXT(child) ||
276 	 g_list_find(recycling_context->children, child) != NULL){
277 	g_rec_mutex_unlock(recycling_context_mutex);
278 
279 	return;
280       }
281 
282       g_rec_mutex_unlock(recycling_context_mutex);
283 
284       ags_recycling_context_add_child(recycling_context,
285 				      child);
286     }
287     break;
288   case PROP_LENGTH:
289     {
290       guint64 length, old_length;
291       guint64 i;
292 
293       length = g_value_get_uint64(value);
294 
295       g_rec_mutex_lock(recycling_context_mutex);
296 
297       if(length == 0){
298 	if(recycling_context->recycling != NULL){
299 	  free(recycling_context->recycling);
300 	}
301 
302 	recycling_context->recycling = NULL;
303 	recycling_context->length = 0;
304 
305 	g_rec_mutex_unlock(recycling_context_mutex);
306 
307 	return;
308       }
309 
310       if(recycling_context->recycling == NULL){
311 	recycling_context->recycling = (AgsRecycling **) malloc(length * sizeof(AgsRecycling *));
312 	old_length = 0;
313       }else{
314 	recycling_context->recycling = (AgsRecycling **) realloc(recycling_context->recycling,
315 								 length * sizeof(AgsRecycling *));
316 	old_length = recycling_context->length;
317       }
318 
319       recycling_context->length = length;
320 
321       for(i = old_length; i < length; i++){
322 	recycling_context->recycling[i] = NULL;
323       }
324 
325       g_rec_mutex_unlock(recycling_context_mutex);
326     }
327     break;
328   default:
329     G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, param_spec);
330     break;
331   }
332 }
333 
334 void
ags_recycling_context_get_property(GObject * gobject,guint prop_id,GValue * value,GParamSpec * param_spec)335 ags_recycling_context_get_property(GObject *gobject,
336 				   guint prop_id,
337 				   GValue *value,
338 				   GParamSpec *param_spec)
339 {
340   AgsRecyclingContext *recycling_context;
341 
342   GRecMutex *recycling_context_mutex;
343 
344   recycling_context = AGS_RECYCLING_CONTEXT(gobject);
345 
346   /* get recycling context mutex */
347   recycling_context_mutex = AGS_RECYCLING_CONTEXT_GET_OBJ_MUTEX(recycling_context);
348 
349   switch(prop_id){
350   case PROP_RECALL_ID:
351     {
352       g_rec_mutex_lock(recycling_context_mutex);
353 
354       g_value_set_object(value, recycling_context->recall_id);
355 
356       g_rec_mutex_unlock(recycling_context_mutex);
357     }
358     break;
359   case PROP_PARENT:
360     {
361       g_rec_mutex_lock(recycling_context_mutex);
362 
363       g_value_set_object(value, recycling_context->parent);
364 
365       g_rec_mutex_unlock(recycling_context_mutex);
366     }
367     break;
368   case PROP_CHILD:
369     {
370       g_rec_mutex_lock(recycling_context_mutex);
371 
372       g_value_set_pointer(value, g_list_copy_deep(recycling_context->children,
373 						  (GCopyFunc) g_object_ref,
374 						  NULL));
375 
376       g_rec_mutex_unlock(recycling_context_mutex);
377     }
378     break;
379   case PROP_LENGTH:
380     {
381       g_rec_mutex_lock(recycling_context_mutex);
382 
383       g_value_set_uint64(value, recycling_context->length);
384 
385       g_rec_mutex_unlock(recycling_context_mutex);
386     }
387     break;
388   default:
389     G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, param_spec);
390     break;
391   }
392 }
393 
394 void
ags_recycling_context_dispose(GObject * gobject)395 ags_recycling_context_dispose(GObject *gobject)
396 {
397   AgsRecyclingContext *recycling_context;
398 
399   GList *list_start, *list;
400 
401   guint i;
402 
403   recycling_context = AGS_RECYCLING_CONTEXT(gobject);
404 
405   /* parent */
406   if(recycling_context->parent != NULL){
407     g_object_unref(recycling_context->parent);
408 
409     recycling_context->parent = NULL;
410   }
411 
412   /* recall id */
413   if(recycling_context->recall_id != NULL){
414     g_object_unref(recycling_context->recall_id);
415 
416     recycling_context->recall_id = NULL;
417   }
418 
419   /* recycling */
420   if(recycling_context->recycling != NULL){
421     for(i = 0; i < recycling_context->length; i++){
422       g_object_unref(recycling_context->recycling[i]);
423     }
424 
425     free(recycling_context->recycling);
426 
427     recycling_context->recycling = NULL;
428     recycling_context->length = 0;
429   }
430 
431   /* children */
432   list =
433     list_start = g_list_copy_deep(recycling_context->children,
434 				  (GCopyFunc) g_object_ref,
435 				  NULL);
436 
437   while(list != NULL){
438     g_object_set(list->data,
439 		 "parent", NULL,
440 		 NULL);
441     g_object_unref(list->data);
442 
443     list = list->next;
444   }
445 
446   g_list_free_full(list_start,
447 		   g_object_unref);
448 
449   /* call parent */
450   G_OBJECT_CLASS(ags_recycling_context_parent_class)->dispose(gobject);
451 }
452 
453 void
ags_recycling_context_finalize(GObject * gobject)454 ags_recycling_context_finalize(GObject *gobject)
455 {
456   AgsRecyclingContext *recycling_context;
457 
458   GList *list;
459 
460   guint i;
461 
462   recycling_context = AGS_RECYCLING_CONTEXT(gobject);
463 
464   /* parent */
465   if(recycling_context->parent != NULL){
466     g_object_unref(recycling_context->parent);
467   }
468 
469   /* recall id */
470   if(recycling_context->recall_id != NULL){
471     g_object_unref(recycling_context->recall_id);
472   }
473 
474   /* recycling */
475   if(recycling_context->recycling != NULL){
476     for(i = 0; i < recycling_context->length; i++){
477       g_object_unref(recycling_context->recycling[i]);
478     }
479 
480     free(recycling_context->recycling);
481   }
482 
483   /* children */
484   g_list_free_full(recycling_context->children,
485 		   g_object_unref);
486 
487   /* call parent */
488   G_OBJECT_CLASS(ags_recycling_context_parent_class)->finalize(gobject);
489 }
490 
491 /**
492  * ags_recycling_context_find_scope:
493  * @recycling_context: (element-type AgsAudio.RecyclingContext) (transfer none): the #GList-struct containing #AgsRecyclingContext
494  * @sound_scope: the sound scope
495  *
496  * Find matching @sound_scope in @recycling_context.
497  *
498  * Returns: (element-type AgsAudio.RecyclingContext) (transfer none): the matching #GList-struct or %NULL if not found
499  *
500  * Since: 3.0.0
501  */
502 GList*
ags_recycling_context_find_scope(GList * recycling_context,gint sound_scope)503 ags_recycling_context_find_scope(GList *recycling_context, gint sound_scope)
504 {
505   gboolean success;
506 
507   GRecMutex *recycling_context_mutex;
508 
509   while(recycling_context != NULL){
510     AgsRecyclingContext *current;
511 
512     current = AGS_RECYCLING_CONTEXT(recycling_context->data);
513 
514     /* get recycling context mutex */
515     recycling_context_mutex = AGS_RECYCLING_CONTEXT_GET_OBJ_MUTEX(current);
516 
517     /* check success */
518     g_rec_mutex_lock(recycling_context_mutex);
519 
520     success = (current->sound_scope == sound_scope) ? TRUE: FALSE;
521 
522     g_rec_mutex_unlock(recycling_context_mutex);
523 
524     if(success){
525       break;
526     }
527 
528     recycling_context = recycling_context->next;
529   }
530 
531   return(recycling_context);
532 }
533 
534 /**
535  * ags_recycling_context_replace:
536  * @recycling_context: the #AgsRecyclingContext
537  * @recycling: the #AgsRecycling to add
538  * @position: the index of @recycling
539  *
540  * Replaces one recycling entry in a context.
541  *
542  * Since: 3.0.0
543  */
544 void
ags_recycling_context_replace(AgsRecyclingContext * recycling_context,AgsRecycling * recycling,gint position)545 ags_recycling_context_replace(AgsRecyclingContext *recycling_context,
546 			      AgsRecycling *recycling,
547 			      gint position)
548 {
549   AgsRecycling *old_recycling;
550 
551   guint64 length;
552 
553   GRecMutex *recycling_context_mutex;
554 
555   if(!AGS_IS_RECYCLING_CONTEXT(recycling_context)){
556     return;
557   }
558 
559   /* get recycling context mutex */
560   recycling_context_mutex = AGS_RECYCLING_CONTEXT_GET_OBJ_MUTEX(recycling_context);
561 
562   /* get some fields */
563   g_rec_mutex_lock(recycling_context_mutex);
564 
565   length = recycling_context->length;
566 
567   g_rec_mutex_unlock(recycling_context_mutex);
568 
569   if(position >= length){
570     return;
571   }
572 
573   /* replace */
574   g_rec_mutex_lock(recycling_context_mutex);
575 
576   old_recycling = recycling_context->recycling[position];
577   recycling_context->recycling[position] = recycling;
578 
579   g_rec_mutex_unlock(recycling_context_mutex);
580 
581   /* ref count */
582   if(old_recycling != NULL){
583     g_object_unref(old_recycling);
584   }
585 
586   if(recycling != NULL){
587     g_object_ref(recycling);
588   }
589 }
590 
591 /**
592  * ags_recycling_context_add:
593  * @recycling_context: the #AgsRecyclingContext
594  * @recycling: the #AgsRecycling to add
595  *
596  * Adds a recycling to a context.
597  *
598  * Since: 3.0.0
599  */
600 void
ags_recycling_context_add(AgsRecyclingContext * recycling_context,AgsRecycling * recycling)601 ags_recycling_context_add(AgsRecyclingContext *recycling_context,
602 			  AgsRecycling *recycling)
603 {
604   gint new_length;
605 
606   GRecMutex *recycling_context_mutex;
607 
608   if(!AGS_IS_RECYCLING_CONTEXT(recycling_context) ||
609      !AGS_IS_RECYCLING(recycling)){
610     return;
611   }
612 
613   /* get recycling context mutex */
614   recycling_context_mutex = AGS_RECYCLING_CONTEXT_GET_OBJ_MUTEX(recycling_context);
615 
616   /* get some fields */
617   g_rec_mutex_lock(recycling_context_mutex);
618 
619   new_length = recycling_context->length + 1;
620 
621   g_rec_mutex_unlock(recycling_context_mutex);
622 
623   /* resize */
624   g_object_set(recycling_context,
625 	       "length", (guint64) new_length,
626 	       NULL);
627 
628   /*  */
629   recycling_context->recycling[new_length - 1] = recycling;
630 
631   /* ref count */
632   g_object_ref(recycling);
633 }
634 
635 /**
636  * ags_recycling_context_remove:
637  * @recycling_context: the #AgsRecyclingContext
638  * @recycling: the #AgsRecycling to remove
639  *
640  * Removes a recycling in a context.
641  *
642  * Since: 3.0.0
643  */
644 void
ags_recycling_context_remove(AgsRecyclingContext * recycling_context,AgsRecycling * recycling)645 ags_recycling_context_remove(AgsRecyclingContext *recycling_context,
646 			     AgsRecycling *recycling)
647 {
648   AgsRecycling **old_context;
649 
650   gint new_length;
651   guint i, j;
652 
653   GRecMutex *recycling_context_mutex;
654 
655   if(!AGS_IS_RECYCLING_CONTEXT(recycling_context) ||
656      !AGS_IS_RECYCLING(recycling)){
657     return;
658   }
659 
660   /* get recycling context mutex */
661   recycling_context_mutex = AGS_RECYCLING_CONTEXT_GET_OBJ_MUTEX(recycling_context);
662 
663   /* get some fields */
664   g_rec_mutex_lock(recycling_context_mutex);
665 
666   if(recycling_context->length > 0){
667     old_context = (AgsRecycling **) malloc(recycling_context->length * sizeof(AgsRecycling *));
668     memcpy(old_context, recycling_context->recycling, recycling_context->length * sizeof(AgsRecycling *));
669 
670     new_length = recycling_context->length - 1;
671   }else{
672     old_context = NULL;
673 
674     new_length = 0;
675   }
676 
677   g_rec_mutex_unlock(recycling_context_mutex);
678 
679   /* resize */
680   g_object_set(recycling_context,
681 	       "length", (guint64) new_length,
682 	       NULL);
683 
684   /* reset */
685   g_rec_mutex_lock(recycling_context_mutex);
686 
687   for(i = 0, j = 0; i < new_length; i++){
688     if(old_context[i] != recycling){
689       recycling_context->recycling[j] = old_context[i];
690       j++;
691     }
692   }
693 
694   g_rec_mutex_unlock(recycling_context_mutex);
695 
696   /* ref count */
697   g_object_unref(recycling);
698 
699   /* free old context */
700   g_free(old_context);
701 }
702 
703 /**
704  * ags_recycling_context_insert:
705  * @recycling_context: the #AgsRecyclingContext
706  * @recycling: the #AgsRecycling to insert
707  * @position: the index to insert at
708  *
709  * Inserts a recycling to a context.
710  *
711  * Since: 3.0.0
712  */
713 void
ags_recycling_context_insert(AgsRecyclingContext * recycling_context,AgsRecycling * recycling,gint position)714 ags_recycling_context_insert(AgsRecyclingContext *recycling_context,
715 			     AgsRecycling *recycling,
716 			     gint position)
717 {
718   AgsRecycling **old_context;
719 
720   gint new_length;
721   guint i, j;
722 
723   GRecMutex *recycling_context_mutex;
724 
725   if(!AGS_IS_RECYCLING_CONTEXT(recycling_context) ||
726      !AGS_IS_RECYCLING(recycling)){
727     return;
728   }
729 
730   /* get recycling context mutex */
731   recycling_context_mutex = AGS_RECYCLING_CONTEXT_GET_OBJ_MUTEX(recycling_context);
732 
733   /* get some fields */
734   g_rec_mutex_lock(recycling_context_mutex);
735 
736   if(recycling_context->length > 0){
737     old_context = (AgsRecycling **) malloc(recycling_context->length * sizeof(AgsRecycling *));
738     memcpy(old_context, recycling_context->recycling, recycling_context->length * sizeof(AgsRecycling *));
739   }else{
740     old_context = NULL;
741   }
742 
743   new_length = recycling_context->length + 1;
744 
745   g_rec_mutex_unlock(recycling_context_mutex);
746 
747   /* resize */
748   g_object_set(recycling_context,
749 	       "length", (guint64) new_length,
750 	       NULL);
751 
752   /* reset */
753   g_rec_mutex_lock(recycling_context_mutex);
754 
755   for(i = 0, j = 0; i < new_length - 1; i++){
756     if(i != position){
757       recycling_context->recycling[i] = old_context[j];
758       j++;
759     }
760   }
761 
762   recycling_context->recycling[position] = recycling;
763 
764   g_rec_mutex_unlock(recycling_context_mutex);
765 
766   /* ref count */
767   g_object_ref(recycling);
768 
769   if(old_context != NULL){
770     free(old_context);
771   }
772 }
773 
774 /**
775  * ags_recycling_context_get_toplevel:
776  * @recycling_context: the #AgsRecyclingContext
777  *
778  * Iterates the tree up to highest level.
779  *
780  * Returns: (transfer full): the topmost recycling context
781  *
782  * Since: 3.0.0
783  */
784 AgsRecyclingContext*
ags_recycling_context_get_toplevel(AgsRecyclingContext * recycling_context)785 ags_recycling_context_get_toplevel(AgsRecyclingContext *recycling_context)
786 {
787   AgsRecyclingContext *parent;
788 
789   GRecMutex *recycling_context_mutex;
790 
791   if(!AGS_IS_RECYCLING_CONTEXT(recycling_context)){
792     return(NULL);
793   }
794 
795   do{
796     /* get recycling context mutex */
797     recycling_context_mutex = AGS_RECYCLING_CONTEXT_GET_OBJ_MUTEX(recycling_context);
798 
799     /* get some fields */
800     g_rec_mutex_lock(recycling_context_mutex);
801 
802     parent = recycling_context->parent;
803 
804     g_rec_mutex_unlock(recycling_context_mutex);
805   }while(parent != NULL && (recycling_context = parent) != NULL);
806 
807   return(recycling_context);
808 }
809 
810 /**
811  * ags_recycling_context_find:
812  * @recycling_context: the #AgsRecyclingContext
813  * @recycling: the #AgsRecycling to look up
814  *
815  * Find position of recycling within array.
816  *
817  * Returns: recycling array index
818  *
819  * Since: 3.0.0
820  */
821 gint
ags_recycling_context_find(AgsRecyclingContext * recycling_context,AgsRecycling * recycling)822 ags_recycling_context_find(AgsRecyclingContext *recycling_context,
823 			   AgsRecycling *recycling)
824 {
825   gint i;
826 
827   GRecMutex *recycling_context_mutex;
828 
829   if(!AGS_IS_RECYCLING_CONTEXT(recycling_context) ||
830      !AGS_IS_RECYCLING(recycling)){
831     return(-1);
832   }
833 
834   /* get recycling context mutex */
835   recycling_context_mutex = AGS_RECYCLING_CONTEXT_GET_OBJ_MUTEX(recycling_context);
836 
837   /* find */
838   g_rec_mutex_lock(recycling_context_mutex);
839 
840   for(i = 0; i < recycling_context->length; i++){
841     if(recycling_context->recycling[i] == recycling){
842       g_rec_mutex_unlock(recycling_context_mutex);
843 
844       return(i);
845     }
846   }
847 
848   g_rec_mutex_unlock(recycling_context_mutex);
849 
850   return(-1);
851 }
852 
853 /**
854  * ags_recycling_context_find_child:
855  * @recycling_context: the #AgsRecyclingContext
856  * @recycling: the #AgsRecycling to look up
857  *
858  * Find position of recycling within arrays.
859  *
860  * Returns: recycling array index
861  *
862  * Since: 3.0.0
863  */
864 gint
ags_recycling_context_find_child(AgsRecyclingContext * recycling_context,AgsRecycling * recycling)865 ags_recycling_context_find_child(AgsRecyclingContext *recycling_context,
866 				 AgsRecycling *recycling)
867 {
868   GList *child_start, *child;
869 
870   gint i;
871 
872   GRecMutex *recycling_context_mutex;
873 
874   if(!AGS_IS_RECYCLING_CONTEXT(recycling_context) ||
875      !AGS_IS_RECYCLING(recycling)){
876     return(-1);
877   }
878 
879   /* get recycling context mutex */
880   recycling_context_mutex = AGS_RECYCLING_CONTEXT_GET_OBJ_MUTEX(recycling_context);
881 
882   /* get child */
883   g_rec_mutex_lock(recycling_context_mutex);
884 
885   child =
886     child_start = g_list_copy_deep(recycling_context->children,
887 				   (GCopyFunc) g_object_ref,
888 				   NULL);
889 
890   g_rec_mutex_unlock(recycling_context_mutex);
891 
892   for(i = 0; child != NULL; i++){
893     if(ags_recycling_context_find(AGS_RECYCLING_CONTEXT(child->data),
894 				  recycling) != -1){
895       g_list_free(child_start);
896 
897       return(i);
898     }
899 
900     child = child->next;
901   }
902 
903   g_list_free_full(child_start,
904 		   g_object_unref);
905 
906   return(-1);
907 }
908 
909 /**
910  * ags_recycling_context_find_parent:
911  * @recycling_context: the #AgsRecyclingContext
912  * @recycling: the #AgsRecycling to look up
913  *
914  * Find position of recycling within array.
915  *
916  * Returns: recycling array index
917  *
918  * Since: 3.0.0
919  */
920 gint
ags_recycling_context_find_parent(AgsRecyclingContext * recycling_context,AgsRecycling * recycling)921 ags_recycling_context_find_parent(AgsRecyclingContext *recycling_context,
922 				  AgsRecycling *recycling)
923 {
924   AgsRecyclingContext *parent;
925 
926   gint i;
927 
928   GRecMutex *recycling_context_mutex;
929   GRecMutex *parent_mutex;
930 
931   if(!AGS_IS_RECYCLING_CONTEXT(recycling_context) ||
932      !AGS_IS_RECYCLING(recycling)){
933     return(-1);
934   }
935 
936   /* get recycling context mutex */
937   recycling_context_mutex = AGS_RECYCLING_CONTEXT_GET_OBJ_MUTEX(recycling_context);
938 
939   /* get parent */
940   g_rec_mutex_lock(recycling_context_mutex);
941 
942   parent = recycling_context->parent;
943 
944   g_rec_mutex_unlock(recycling_context_mutex);
945 
946   if(parent != NULL){
947     /* get parent mutex */
948     parent_mutex = AGS_RECYCLING_CONTEXT_GET_OBJ_MUTEX(parent);
949 
950     /* find */
951     g_rec_mutex_lock(parent_mutex);
952 
953     for(i = 0; i < parent->length; i++){
954       if(parent->recycling[i] == recycling){
955 	g_rec_mutex_unlock(parent_mutex);
956 
957 	return(i);
958       }
959     }
960 
961     g_rec_mutex_unlock(parent_mutex);
962   }
963 
964   return(-1);
965 }
966 
967 /**
968  * ags_recycling_context_add_child:
969  * @parent: the parental #AgsRecyclingContext
970  * @child: the child
971  *
972  * Adds a recycling context as child.
973  *
974  * Since: 3.0.0
975  */
976 void
ags_recycling_context_add_child(AgsRecyclingContext * parent,AgsRecyclingContext * child)977 ags_recycling_context_add_child(AgsRecyclingContext *parent,
978 				AgsRecyclingContext *child)
979 {
980   GRecMutex *parent_mutex;
981 
982   if(!AGS_IS_RECYCLING_CONTEXT(parent) ||
983      !AGS_IS_RECYCLING_CONTEXT(child)){
984     return;
985   }
986 
987   g_object_set(child,
988 	       "parent", parent,
989 	       NULL);
990 
991   /* get parent mutex */
992   parent_mutex = AGS_RECYCLING_CONTEXT_GET_OBJ_MUTEX(parent);
993 
994   /* add child */
995   g_rec_mutex_lock(parent_mutex);
996 
997   if(g_list_find(parent->children, child) == NULL){
998     parent->children = g_list_append(parent->children,
999 				     child);
1000     g_object_ref(child);
1001   }
1002 
1003   g_rec_mutex_unlock(parent_mutex);
1004 }
1005 
1006 /**
1007  * ags_recycling_context_remove_child:
1008  * @parent: the #AgsRecyclingContext
1009  * @child: the child to remove
1010  *
1011  * Removes a recycling context of its parent.
1012  *
1013  * Since: 3.0.0
1014  */
1015 void
ags_recycling_context_remove_child(AgsRecyclingContext * parent,AgsRecyclingContext * child)1016 ags_recycling_context_remove_child(AgsRecyclingContext *parent,
1017 				   AgsRecyclingContext *child)
1018 {
1019   GRecMutex *parent_mutex;
1020 
1021   if(!AGS_IS_RECYCLING_CONTEXT(parent) ||
1022      !AGS_IS_RECYCLING_CONTEXT(child)){
1023     return;
1024   }
1025 
1026   g_object_set(child,
1027 	       "parent", NULL,
1028 	       NULL);
1029 
1030   /* get parent mutex */
1031   parent_mutex = AGS_RECYCLING_CONTEXT_GET_OBJ_MUTEX(parent);
1032 
1033   /* remove child */
1034   g_rec_mutex_lock(parent_mutex);
1035 
1036   if(g_list_find(parent->children, child) != NULL){
1037     parent->children = g_list_remove(parent->children,
1038 				     child);
1039     g_object_unref(child);
1040   }
1041 
1042   g_rec_mutex_unlock(parent_mutex);
1043 }
1044 
1045 /**
1046  * ags_recycling_context_get_child_recall_id:
1047  * @recycling_context: the #AgsRecyclingContext
1048  *
1049  * Retrieve all child recall ids.
1050  *
1051  * Returns: (element-type AgsAudio.RecallID) (transfer full): the #AgsRecallID as #GList-struct
1052  *
1053  * Since: 3.0.0
1054  */
1055 GList*
ags_recycling_context_get_child_recall_id(AgsRecyclingContext * recycling_context)1056 ags_recycling_context_get_child_recall_id(AgsRecyclingContext *recycling_context)
1057 {
1058   GList *child_start, *child;
1059 
1060   GList *recall_id_list;
1061 
1062   GRecMutex *recycling_context_mutex;
1063   GRecMutex *child_mutex;
1064 
1065   if(!AGS_IS_RECYCLING_CONTEXT(recycling_context)){
1066     return(NULL);
1067   }
1068 
1069   /* get recycling context mutex */
1070   recycling_context_mutex = AGS_RECYCLING_CONTEXT_GET_OBJ_MUTEX(recycling_context);
1071 
1072   /* get some fields */
1073   g_rec_mutex_lock(recycling_context_mutex);
1074 
1075   child =
1076     child_start = g_list_copy_deep(recycling_context->children,
1077 				   (GCopyFunc) g_object_ref,
1078 				   NULL);
1079 
1080   g_rec_mutex_unlock(recycling_context_mutex);
1081 
1082   recall_id_list = NULL;
1083 
1084   while(child != NULL){
1085     /* get recycling context mutex */
1086     child_mutex = AGS_RECYCLING_CONTEXT_GET_OBJ_MUTEX(child->data);
1087 
1088     /*  */
1089     g_rec_mutex_lock(child_mutex);
1090 
1091     if(AGS_RECYCLING_CONTEXT(child->data)->recall_id != NULL){
1092       recall_id_list = g_list_prepend(recall_id_list,
1093 				      AGS_RECYCLING_CONTEXT(child->data)->recall_id);
1094     }
1095 
1096     g_rec_mutex_unlock(child_mutex);
1097 
1098     child = child->next;
1099   }
1100 
1101   g_list_free_full(child_start,
1102 		   g_object_unref);
1103 
1104   /* reverse the result */
1105   recall_id_list = g_list_reverse(recall_id_list);
1106 
1107   return(recall_id_list);
1108 }
1109 
1110 /**
1111  * ags_recycling_context_reset_recycling:
1112  * @recycling_context: the #AgsRecyclingContext
1113  * @old_first_recycling: the first recycling to replace
1114  * @old_last_recycling: the last recycling to replace
1115  * @new_first_recycling: the first recycling to insert
1116  * @new_last_recycling: the last recycling to insert
1117  *
1118  * Modify recycling of context.
1119  *
1120  * Returns: (transfer full): the new #AgsRecyclingContext
1121  *
1122  * Since: 3.0.0
1123  */
1124 AgsRecyclingContext*
ags_recycling_context_reset_recycling(AgsRecyclingContext * recycling_context,AgsRecycling * old_first_recycling,AgsRecycling * old_last_recycling,AgsRecycling * new_first_recycling,AgsRecycling * new_last_recycling)1125 ags_recycling_context_reset_recycling(AgsRecyclingContext *recycling_context,
1126 				      AgsRecycling *old_first_recycling, AgsRecycling *old_last_recycling,
1127 				      AgsRecycling *new_first_recycling, AgsRecycling *new_last_recycling)
1128 {
1129   AgsRecycling *recycling;
1130   AgsRecyclingContext *new_recycling_context;
1131   AgsRecyclingContext *parent;
1132 
1133   GList *child_start, *child;
1134 
1135   gint new_length;
1136   guint i;
1137 
1138   GRecMutex *recycling_mutex;
1139   GRecMutex *recycling_context_mutex;
1140 
1141   if(!AGS_IS_RECYCLING_CONTEXT(recycling_context)){
1142     return(NULL);
1143   }
1144 
1145   parent = NULL;
1146 
1147   /* retrieve new length */
1148   new_length = ags_recycling_position(new_first_recycling, old_last_recycling,
1149 				      old_last_recycling);
1150   new_length++;
1151 
1152   /* instantiate */
1153   new_recycling_context = g_object_new(AGS_TYPE_RECYCLING_CONTEXT,
1154 				       "length", (guint64) new_length,
1155 				       NULL);
1156 
1157   recycling = new_first_recycling;
1158 
1159   for(i = 0; i < new_length; i++){
1160     /* get parent mutex */
1161     recycling_mutex = AGS_RECYCLING_GET_OBJ_MUTEX(recycling);
1162 
1163     /* set context */
1164     new_recycling_context->recycling[i] = recycling;
1165     g_object_ref(recycling);
1166 
1167     /* iterate */
1168     g_rec_mutex_lock(recycling_mutex);
1169 
1170     recycling = recycling->next;
1171 
1172     g_rec_mutex_unlock(recycling_mutex);
1173   }
1174 
1175   /* get recycling context mutex */
1176   recycling_context_mutex = AGS_RECYCLING_CONTEXT_GET_OBJ_MUTEX(recycling_context);
1177 
1178   /* get some fields */
1179   g_rec_mutex_lock(recycling_context_mutex);
1180 
1181   child =
1182     child_start = g_list_copy_deep(recycling_context->children,
1183 				   (GCopyFunc) g_object_ref,
1184 				   NULL);
1185 
1186   g_rec_mutex_unlock(recycling_context_mutex);
1187 
1188   while(child != NULL){
1189     g_object_set(new_recycling_context,
1190 		 "child", child->data,
1191 		 NULL);
1192 
1193     child = child->next;
1194   }
1195 
1196   if(parent != NULL){
1197     g_object_set(parent,
1198 		 "child", new_recycling_context,
1199 		 NULL);
1200   }
1201 
1202   g_list_free_full(child_start,
1203 		   g_object_unref);
1204 
1205   /* dispose old recycling context */
1206   g_object_run_dispose((GObject *) recycling_context);
1207 
1208   return(new_recycling_context);
1209 }
1210 
1211 /**
1212  * ags_recycling_context_new:
1213  * @length: array dimension of context
1214  *
1215  * Creates a #AgsRecyclingContext, boundaries are specified by @length
1216  *
1217  * Returns: a new #AgsRecyclingContext
1218  *
1219  * Since: 3.0.0
1220  */
1221 AgsRecyclingContext*
ags_recycling_context_new(guint64 length)1222 ags_recycling_context_new(guint64 length)
1223 {
1224   AgsRecyclingContext *recycling_context;
1225 
1226   recycling_context = g_object_new(AGS_TYPE_RECYCLING_CONTEXT,
1227 				   "length", length,
1228 				   NULL);
1229 
1230   return(recycling_context);
1231 }
1232