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