1 /* GSequencer - Advanced GTK Sequencer
2  * Copyright (C) 2005-2021 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.h>
21 
22 #include <ags/audio/ags_audio.h>
23 #include <ags/audio/ags_channel.h>
24 #include <ags/audio/ags_audio_buffer_util.h>
25 
26 #include <string.h>
27 #include <math.h>
28 
29 #include <ags/i18n.h>
30 
31 void ags_recycling_class_init(AgsRecyclingClass *recycling_class);
32 void ags_recycling_connectable_interface_init(AgsConnectableInterface *connectable);
33 void ags_recycling_set_property(GObject *gobject,
34 				guint prop_id,
35 				const GValue *value,
36 				GParamSpec *param_spec);
37 void ags_recycling_get_property(GObject *gobject,
38 				guint prop_id,
39 				GValue *value,
40 				GParamSpec *param_spec);
41 void ags_recycling_init(AgsRecycling *recycling);
42 void ags_recycling_dispose(GObject *gobject);
43 void ags_recycling_finalize(GObject *gobject);
44 
45 AgsUUID* ags_recycling_get_uuid(AgsConnectable *connectable);
46 gboolean ags_recycling_has_resource(AgsConnectable *connectable);
47 gboolean ags_recycling_is_ready(AgsConnectable *connectable);
48 void ags_recycling_add_to_registry(AgsConnectable *connectable);
49 void ags_recycling_remove_from_registry(AgsConnectable *connectable);
50 xmlNode* ags_recycling_list_resource(AgsConnectable *connectable);
51 xmlNode* ags_recycling_xml_compose(AgsConnectable *connectable);
52 void ags_recycling_xml_parse(AgsConnectable *connectable,
53 			     xmlNode *node);
54 gboolean ags_recycling_is_connected(AgsConnectable *connectable);
55 void ags_recycling_connect(AgsConnectable *connectable);
56 void ags_recycling_disconnect(AgsConnectable *connectable);
57 
58 void ags_recycling_real_set_output_soundcard(AgsRecycling *recycling, GObject *output_soundcard);
59 
60 void ags_recycling_real_set_input_soundcard(AgsRecycling *recycling, GObject *input_soundcard);
61 
62 void ags_recycling_real_add_audio_signal(AgsRecycling *recycling,
63 					 AgsAudioSignal *audio_signal);
64 
65 void ags_recycling_real_remove_audio_signal(AgsRecycling *recycling,
66 					    AgsAudioSignal *audio_signal);
67 
68 /**
69  * SECTION:ags_recycling
70  * @short_description: recycling container of audio signals
71  * @title: AgsRecycling
72  * @section_id:
73  * @include: ags/audio/ags_recycling.h
74  *
75  * #AgsRecycling forms the nested tree of AgsChannel. Every channel
76  * owning audio signal contains therefor an #AgsRecycling.
77  */
78 
79 enum{
80   PROP_0,
81   PROP_CHANNEL,
82   PROP_OUTPUT_SOUNDCARD,
83   PROP_OUTPUT_SOUNDCARD_CHANNEL,
84   PROP_INPUT_SOUNDCARD,
85   PROP_INPUT_SOUNDCARD_CHANNEL,
86   PROP_SAMPLERATE,
87   PROP_BUFFER_SIZE,
88   PROP_FORMAT,
89   PROP_PARENT,
90   PROP_NEXT,
91   PROP_PREV,
92   PROP_AUDIO_SIGNAL,
93 };
94 
95 enum{
96   ADD_AUDIO_SIGNAL,
97   REMOVE_AUDIO_SIGNAL,
98   DATA_REQUEST,
99   LAST_SIGNAL,
100 };
101 
102 static gpointer ags_recycling_parent_class = NULL;
103 static guint recycling_signals[LAST_SIGNAL];
104 
105 GType
ags_recycling_get_type(void)106 ags_recycling_get_type(void)
107 {
108   static volatile gsize g_define_type_id__volatile = 0;
109 
110   if(g_once_init_enter (&g_define_type_id__volatile)){
111     GType ags_type_recycling = 0;
112 
113     static const GTypeInfo ags_recycling_info = {
114       sizeof (AgsRecyclingClass),
115       NULL, /* base_init */
116       NULL, /* base_finalize */
117       (GClassInitFunc) ags_recycling_class_init,
118       NULL, /* class_finalize */
119       NULL, /* class_data */
120       sizeof (AgsRecycling),
121       0,    /* n_preallocs */
122       (GInstanceInitFunc) ags_recycling_init,
123     };
124 
125     static const GInterfaceInfo ags_connectable_interface_info = {
126       (GInterfaceInitFunc) ags_recycling_connectable_interface_init,
127       NULL, /* interface_finalize */
128       NULL, /* interface_data */
129     };
130 
131     ags_type_recycling = g_type_register_static(G_TYPE_OBJECT,
132 						"AgsRecycling",
133 						&ags_recycling_info, 0);
134 
135     g_type_add_interface_static(ags_type_recycling,
136 				AGS_TYPE_CONNECTABLE,
137 				&ags_connectable_interface_info);
138 
139     g_once_init_leave(&g_define_type_id__volatile, ags_type_recycling);
140   }
141 
142   return g_define_type_id__volatile;
143 }
144 
145 void
ags_recycling_class_init(AgsRecyclingClass * recycling)146 ags_recycling_class_init(AgsRecyclingClass *recycling)
147 {
148   GObjectClass *gobject;
149   GParamSpec *param_spec;
150 
151   ags_recycling_parent_class = g_type_class_peek_parent(recycling);
152 
153   /* GObjectClass */
154   gobject = (GObjectClass *) recycling;
155 
156   gobject->set_property = ags_recycling_set_property;
157   gobject->get_property = ags_recycling_get_property;
158 
159   gobject->dispose = ags_recycling_dispose;
160   gobject->finalize = ags_recycling_finalize;
161 
162   /* properties */
163   /**
164    * AgsRecycling:channel:
165    *
166    * The assigned #AgsChannel.
167    *
168    * Since: 3.0.0
169    */
170   param_spec = g_param_spec_object("channel",
171 				   "assigned channel",
172 				   "The channel it is assigned with",
173 				   AGS_TYPE_CHANNEL,
174 				   G_PARAM_READABLE | G_PARAM_WRITABLE);
175   g_object_class_install_property(gobject,
176 				  PROP_CHANNEL,
177 				  param_spec);
178 
179   /**
180    * AgsRecycling:output-soundcard:
181    *
182    * The assigned output soundcard acting as default sink.
183    *
184    * Since: 3.0.0
185    */
186   param_spec = g_param_spec_object("output-soundcard",
187 				   "assigned output soundcard",
188 				   "The output soundcard it is assigned with",
189 				   G_TYPE_OBJECT,
190 				   G_PARAM_READABLE | G_PARAM_WRITABLE);
191   g_object_class_install_property(gobject,
192 				  PROP_OUTPUT_SOUNDCARD,
193 				  param_spec);
194 
195   /**
196    * AgsRecycling:output-soundcard-channel:
197    *
198    * The output soundcard channel.
199    *
200    * Since: 3.0.0
201    */
202   param_spec =  g_param_spec_int("output-soundcard-channel",
203 				 i18n_pspec("output soundcard channel"),
204 				 i18n_pspec("The output soundcard channel"),
205 				 -1,
206 				 G_MAXINT32,
207 				 0,
208 				 G_PARAM_READABLE | G_PARAM_WRITABLE);
209   g_object_class_install_property(gobject,
210 				  PROP_OUTPUT_SOUNDCARD_CHANNEL,
211 				  param_spec);
212 
213   /**
214    * AgsRecycling:input-soundcard:
215    *
216    * The assigned input soundcard.
217    *
218    * Since: 3.0.0
219    */
220   param_spec = g_param_spec_object("input-soundcard",
221 				   "assigned input soundcard",
222 				   "The input soundcard it is assigned with",
223 				   G_TYPE_OBJECT,
224 				   G_PARAM_READABLE | G_PARAM_WRITABLE);
225   g_object_class_install_property(gobject,
226 				  PROP_INPUT_SOUNDCARD,
227 				  param_spec);
228 
229   /**
230    * AgsRecycling:input-soundcard-channel:
231    *
232    * The input soundcard channel.
233    *
234    * Since: 3.0.0
235    */
236   param_spec =  g_param_spec_int("input-soundcard-channel",
237 				 i18n_pspec("input soundcard channel"),
238 				 i18n_pspec("The input soundcard channel"),
239 				 -1,
240 				 G_MAXINT32,
241 				 0,
242 				 G_PARAM_READABLE | G_PARAM_WRITABLE);
243   g_object_class_install_property(gobject,
244 				  PROP_INPUT_SOUNDCARD_CHANNEL,
245 				  param_spec);
246 
247   /**
248    * AgsRecycling:samplerate:
249    *
250    * The samplerate.
251    *
252    * Since: 3.0.0
253    */
254   param_spec =  g_param_spec_uint("samplerate",
255 				  i18n_pspec("samplerate"),
256 				  i18n_pspec("The samplerate"),
257 				  0,
258 				  G_MAXUINT32,
259 				  0,
260 				  G_PARAM_READABLE | G_PARAM_WRITABLE);
261   g_object_class_install_property(gobject,
262 				  PROP_SAMPLERATE,
263 				  param_spec);
264 
265   /**
266    * AgsRecycling:buffer-size:
267    *
268    * The buffer size.
269    *
270    * Since: 3.0.0
271    */
272   param_spec =  g_param_spec_uint("buffer-size",
273 				  i18n_pspec("buffer size"),
274 				  i18n_pspec("The buffer size"),
275 				  0,
276 				  G_MAXUINT32,
277 				  0,
278 				  G_PARAM_READABLE | G_PARAM_WRITABLE);
279   g_object_class_install_property(gobject,
280 				  PROP_BUFFER_SIZE,
281 				  param_spec);
282 
283   /**
284    * AgsRecycling:format:
285    *
286    * The format.
287    *
288    * Since: 3.0.0
289    */
290   param_spec =  g_param_spec_uint("format",
291 				  i18n_pspec("format"),
292 				  i18n_pspec("The format"),
293 				  0,
294 				  G_MAXUINT32,
295 				  0,
296 				  G_PARAM_READABLE | G_PARAM_WRITABLE);
297   g_object_class_install_property(gobject,
298 				  PROP_FORMAT,
299 				  param_spec);
300 
301   /**
302    * AgsRecycling:parent:
303    *
304    * The assigned parent #AgsRecycling.
305    *
306    * Since: 3.0.0
307    */
308   param_spec = g_param_spec_object("parent",
309 				   "assigned parent",
310 				   "The parent it is assigned with",
311 				   AGS_TYPE_RECYCLING,
312 				   G_PARAM_READABLE | G_PARAM_WRITABLE);
313   g_object_class_install_property(gobject,
314 				  PROP_PARENT,
315 				  param_spec);
316 
317   /**
318    * AgsRecycling:prev:
319    *
320    * The assigned prev #AgsRecycling.
321    *
322    * Since: 3.0.0
323    */
324   param_spec = g_param_spec_object("prev",
325 				   "assigned prev",
326 				   "The prev it is assigned with",
327 				   AGS_TYPE_RECYCLING,
328 				   G_PARAM_READABLE | G_PARAM_WRITABLE);
329   g_object_class_install_property(gobject,
330 				  PROP_PREV,
331 				  param_spec);
332 
333   /**
334    * AgsRecycling:next:
335    *
336    * The assigned next #AgsRecycling.
337    *
338    * Since: 3.0.0
339    */
340   param_spec = g_param_spec_object("next",
341 				   "assigned next",
342 				   "The next it is assigned with",
343 				   AGS_TYPE_RECYCLING,
344 				   G_PARAM_READABLE | G_PARAM_WRITABLE);
345   g_object_class_install_property(gobject,
346 				  PROP_NEXT,
347 				  param_spec);
348 
349   /**
350    * AgsRecycling:audio-signal: (type GList(AgsAudioSignal)) (transfer full)
351    *
352    * The containing  #AgsAudioSignal.
353    *
354    * Since: 3.0.0
355    */
356   param_spec = g_param_spec_pointer("audio-signal",
357 				    "containing audio signal",
358 				    "The audio signal it contains",
359 				    G_PARAM_READABLE | G_PARAM_WRITABLE);
360   g_object_class_install_property(gobject,
361 				  PROP_AUDIO_SIGNAL,
362 				  param_spec);
363 
364   /*  */
365   recycling->add_audio_signal = ags_recycling_real_add_audio_signal;
366   recycling->remove_audio_signal = ags_recycling_real_remove_audio_signal;
367 
368   /**
369    * AgsRecycling::add-audio-signal
370    * @recycling: an #AgsRecycling
371    * @audio_signal: the #AgsAudioSignal to add
372    *
373    * The ::add-audio-signal signal is emited as adding #AgsAudioSignal.
374    *
375    * Since: 3.0.0
376    */
377   recycling_signals[ADD_AUDIO_SIGNAL] =
378     g_signal_new("add-audio-signal",
379 		 G_TYPE_FROM_CLASS(recycling),
380 		 G_SIGNAL_RUN_LAST,
381 		 G_STRUCT_OFFSET(AgsRecyclingClass, add_audio_signal),
382 		 NULL, NULL,
383 		 g_cclosure_marshal_VOID__OBJECT,
384 		 G_TYPE_NONE, 1,
385 		 G_TYPE_OBJECT);
386 
387   /**
388    * AgsRecycling::remove-audio-signal:
389    * @recycling: an #AgsRecycling
390    * @audio_signal: the #AgsAudioSignal to remove
391    *
392    * The ::remove-audio-signal signal is emited as removing #AgsAudioSignal.
393    *
394    * Since: 3.0.0
395    */
396   recycling_signals[REMOVE_AUDIO_SIGNAL] =
397     g_signal_new("remove-audio-signal",
398 		 G_TYPE_FROM_CLASS(recycling),
399 		 G_SIGNAL_RUN_LAST,
400 		 G_STRUCT_OFFSET(AgsRecyclingClass, remove_audio_signal),
401 		 NULL, NULL,
402 		 g_cclosure_marshal_VOID__OBJECT,
403 		 G_TYPE_NONE, 1,
404 		 G_TYPE_OBJECT);
405 
406   /**
407    * AgsRecycling::data-request
408    * @recycling: an #AgsRecycling
409    * @audio_signal: the #AgsAudioSignal to request
410    *
411    * The ::data-request signal is emited as requesting data for @audio_signal.
412    *
413    * Since: 3.0.0
414    */
415   recycling_signals[DATA_REQUEST] =
416     g_signal_new("data-request",
417 		 G_TYPE_FROM_CLASS(recycling),
418 		 G_SIGNAL_RUN_LAST,
419 		 G_STRUCT_OFFSET(AgsRecyclingClass, data_request),
420 		 NULL, NULL,
421 		 g_cclosure_marshal_VOID__OBJECT,
422 		 G_TYPE_NONE, 1,
423 		 G_TYPE_OBJECT);
424 }
425 
426 void
ags_recycling_connectable_interface_init(AgsConnectableInterface * connectable)427 ags_recycling_connectable_interface_init(AgsConnectableInterface *connectable)
428 {
429   connectable->get_uuid = ags_recycling_get_uuid;
430   connectable->has_resource = ags_recycling_has_resource;
431 
432   connectable->is_ready = ags_recycling_is_ready;
433   connectable->add_to_registry = ags_recycling_add_to_registry;
434   connectable->remove_from_registry = ags_recycling_remove_from_registry;
435 
436   connectable->list_resource = ags_recycling_list_resource;
437   connectable->xml_compose = ags_recycling_xml_compose;
438   connectable->xml_parse = ags_recycling_xml_parse;
439 
440   connectable->is_connected = ags_recycling_is_connected;
441   connectable->connect = ags_recycling_connect;
442   connectable->disconnect = ags_recycling_disconnect;
443 
444   connectable->connect_connection = NULL;
445   connectable->disconnect_connection = NULL;
446 }
447 
448 void
ags_recycling_init(AgsRecycling * recycling)449 ags_recycling_init(AgsRecycling *recycling)
450 {
451   AgsAudioSignal *audio_signal;
452 
453   AgsConfig *config;
454 
455   gchar *str;
456   gchar *str0, *str1;
457 
458   recycling->flags = 0;
459 
460   /* add recycling mutex */
461   g_rec_mutex_init(&(recycling->obj_mutex));
462 
463   /* uuid */
464   recycling->uuid = ags_uuid_alloc();
465   ags_uuid_generate(recycling->uuid);
466 
467   /* config */
468   config = ags_config_get_instance();
469 
470   /* base init */
471   recycling->channel = NULL;
472 
473   recycling->output_soundcard = NULL;
474   recycling->output_soundcard_channel = 0;
475 
476   recycling->input_soundcard = NULL;
477   recycling->input_soundcard_channel = 0;
478 
479   /* presets */
480   recycling->samplerate = ags_soundcard_helper_config_get_samplerate(config);
481   recycling->buffer_size = ags_soundcard_helper_config_get_buffer_size(config);
482   recycling->format = ags_soundcard_helper_config_get_format(config);
483 
484   /* nested tree */
485   recycling->parent = NULL;
486 
487   recycling->next = NULL;
488   recycling->prev = NULL;
489 
490   /* audio signal */
491   audio_signal = ags_audio_signal_new(NULL,
492 				      (GObject *) recycling,
493 				      NULL);
494   audio_signal->flags |= AGS_AUDIO_SIGNAL_TEMPLATE;
495 
496   recycling->audio_signal = g_list_alloc();
497   recycling->audio_signal->data = audio_signal;
498 }
499 
500 void
ags_recycling_set_property(GObject * gobject,guint prop_id,const GValue * value,GParamSpec * param_spec)501 ags_recycling_set_property(GObject *gobject,
502 			   guint prop_id,
503 			   const GValue *value,
504 			   GParamSpec *param_spec)
505 {
506   AgsRecycling *recycling;
507 
508   GRecMutex *recycling_mutex;
509 
510   recycling = AGS_RECYCLING(gobject);
511 
512   /* get recycling mutex */
513   recycling_mutex = AGS_RECYCLING_GET_OBJ_MUTEX(recycling);
514 
515   switch(prop_id){
516   case PROP_CHANNEL:
517     {
518       AgsChannel *channel;
519 
520       channel = (AgsChannel *) g_value_get_object(value);
521 
522       g_rec_mutex_lock(recycling_mutex);
523 
524       if(recycling->channel == (GObject *) channel){
525 	g_rec_mutex_unlock(recycling_mutex);
526 
527 	return;
528       }
529 
530       if(recycling->channel != NULL){
531 	g_object_unref(recycling->channel);
532       }
533 
534       if(channel != NULL){
535 	g_object_ref(channel);
536       }
537 
538       recycling->channel = (GObject *) channel;
539 
540       g_rec_mutex_unlock(recycling_mutex);
541     }
542     break;
543   case PROP_OUTPUT_SOUNDCARD:
544     {
545       GObject *soundcard;
546 
547       soundcard = (GObject *) g_value_get_object(value);
548 
549       ags_recycling_real_set_output_soundcard(recycling, (GObject *) soundcard);
550     }
551     break;
552   case PROP_OUTPUT_SOUNDCARD_CHANNEL:
553     {
554       g_rec_mutex_lock(recycling_mutex);
555 
556       recycling->output_soundcard_channel = g_value_get_int(value);
557 
558       g_rec_mutex_unlock(recycling_mutex);
559     }
560     break;
561   case PROP_INPUT_SOUNDCARD:
562     {
563       GObject *soundcard;
564 
565       soundcard = (GObject *) g_value_get_object(value);
566 
567       ags_recycling_real_set_input_soundcard(recycling, (GObject *) soundcard);
568     }
569     break;
570   case PROP_INPUT_SOUNDCARD_CHANNEL:
571     {
572       g_rec_mutex_lock(recycling_mutex);
573 
574       recycling->input_soundcard_channel = g_value_get_int(value);
575 
576       g_rec_mutex_unlock(recycling_mutex);
577     }
578     break;
579   case PROP_SAMPLERATE:
580     {
581       guint samplerate;
582 
583       samplerate = g_value_get_uint(value);
584 
585       ags_recycling_set_samplerate(recycling,
586 				   samplerate);
587     }
588     break;
589   case PROP_BUFFER_SIZE:
590     {
591       guint buffer_size;
592 
593       buffer_size = g_value_get_uint(value);
594 
595       ags_recycling_set_buffer_size(recycling,
596 				    buffer_size);
597     }
598     break;
599   case PROP_FORMAT:
600     {
601       guint format;
602 
603       format = g_value_get_uint(value);
604 
605       ags_recycling_set_format(recycling,
606 			       format);
607     }
608     break;
609   case PROP_PARENT:
610     {
611       AgsRecycling *parent;
612 
613       parent = (AgsRecycling *) g_value_get_object(value);
614 
615       g_rec_mutex_lock(recycling_mutex);
616 
617       if(recycling->parent == parent){
618 	g_rec_mutex_unlock(recycling_mutex);
619 
620 	return;
621       }
622 
623       if(recycling->parent != NULL){
624 	g_object_unref(recycling->parent);
625       }
626 
627       if(parent != NULL){
628 	g_object_ref(parent);
629       }
630 
631       recycling->parent = parent;
632 
633       g_rec_mutex_unlock(recycling_mutex);
634     }
635     break;
636   case PROP_NEXT:
637     {
638       AgsRecycling *next;
639 
640       next = (AgsRecycling *) g_value_get_object(value);
641 
642       g_rec_mutex_lock(recycling_mutex);
643 
644       if(recycling->next == next){
645 	g_rec_mutex_unlock(recycling_mutex);
646 
647 	return;
648       }
649 
650       if(recycling->next != NULL){
651 	g_object_unref(recycling->next);
652       }
653 
654       if(next != NULL){
655 	g_object_ref(next);
656       }
657 
658       recycling->next = next;
659 
660       g_rec_mutex_unlock(recycling_mutex);
661     }
662     break;
663   case PROP_PREV:
664     {
665       AgsRecycling *prev;
666 
667       prev = (AgsRecycling *) g_value_get_object(value);
668 
669       g_rec_mutex_lock(recycling_mutex);
670 
671       if(recycling->prev == prev){
672 	g_rec_mutex_unlock(recycling_mutex);
673 
674 	return;
675       }
676 
677       if(recycling->prev != NULL){
678 	g_object_unref(recycling->prev);
679       }
680 
681       if(prev != NULL){
682 	g_object_ref(prev);
683       }
684 
685       recycling->prev = prev;
686 
687       g_rec_mutex_unlock(recycling_mutex);
688     }
689     break;
690   case PROP_AUDIO_SIGNAL:
691     {
692       AgsAudioSignal *audio_signal;
693 
694       audio_signal = g_value_get_pointer(value);
695 
696       ags_recycling_add_audio_signal(recycling,
697 				     audio_signal);
698     }
699     break;
700   default:
701     G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, param_spec);
702     break;
703   }
704 }
705 
706 void
ags_recycling_get_property(GObject * gobject,guint prop_id,GValue * value,GParamSpec * param_spec)707 ags_recycling_get_property(GObject *gobject,
708 			   guint prop_id,
709 			   GValue *value,
710 			   GParamSpec *param_spec)
711 {
712   AgsRecycling *recycling;
713 
714   GRecMutex *recycling_mutex;
715 
716   recycling = AGS_RECYCLING(gobject);
717 
718   /* get recycling mutex */
719   recycling_mutex = AGS_RECYCLING_GET_OBJ_MUTEX(recycling);
720 
721   switch(prop_id){
722   case PROP_CHANNEL:
723     {
724       g_rec_mutex_lock(recycling_mutex);
725 
726       g_value_set_object(value, recycling->channel);
727 
728       g_rec_mutex_unlock(recycling_mutex);
729     }
730     break;
731   case PROP_OUTPUT_SOUNDCARD:
732     {
733       g_rec_mutex_lock(recycling_mutex);
734 
735       g_value_set_object(value, recycling->output_soundcard);
736 
737       g_rec_mutex_unlock(recycling_mutex);
738     }
739     break;
740   case PROP_OUTPUT_SOUNDCARD_CHANNEL:
741     {
742       g_rec_mutex_lock(recycling_mutex);
743 
744       g_value_set_int(value, recycling->output_soundcard_channel);
745 
746       g_rec_mutex_unlock(recycling_mutex);
747     }
748     break;
749   case PROP_INPUT_SOUNDCARD:
750     {
751       g_rec_mutex_lock(recycling_mutex);
752 
753       g_value_set_object(value, recycling->input_soundcard);
754 
755       g_rec_mutex_unlock(recycling_mutex);
756     }
757     break;
758   case PROP_INPUT_SOUNDCARD_CHANNEL:
759     {
760       g_rec_mutex_lock(recycling_mutex);
761 
762       g_value_set_int(value, recycling->input_soundcard_channel);
763 
764       g_rec_mutex_unlock(recycling_mutex);
765     }
766     break;
767   case PROP_SAMPLERATE:
768     {
769       g_rec_mutex_lock(recycling_mutex);
770 
771       g_value_set_uint(value, recycling->samplerate);
772 
773       g_rec_mutex_unlock(recycling_mutex);
774     }
775     break;
776   case PROP_BUFFER_SIZE:
777     {
778       g_rec_mutex_lock(recycling_mutex);
779 
780       g_value_set_uint(value, recycling->buffer_size);
781 
782       g_rec_mutex_unlock(recycling_mutex);
783     }
784     break;
785   case PROP_FORMAT:
786     {
787       g_rec_mutex_lock(recycling_mutex);
788 
789       g_value_set_uint(value, recycling->format);
790 
791       g_rec_mutex_unlock(recycling_mutex);
792     }
793     break;
794   case PROP_PARENT:
795     {
796       g_rec_mutex_lock(recycling_mutex);
797 
798       g_value_set_object(value, recycling->parent);
799 
800       g_rec_mutex_unlock(recycling_mutex);
801     }
802     break;
803   case PROP_NEXT:
804     {
805       g_rec_mutex_lock(recycling_mutex);
806 
807       g_value_set_object(value, recycling->next);
808 
809       g_rec_mutex_unlock(recycling_mutex);
810     }
811     break;
812   case PROP_PREV:
813     {
814       g_rec_mutex_lock(recycling_mutex);
815 
816       g_value_set_object(value, recycling->prev);
817 
818       g_rec_mutex_unlock(recycling_mutex);
819     }
820     break;
821   case PROP_AUDIO_SIGNAL:
822     {
823       g_rec_mutex_lock(recycling_mutex);
824 
825       g_value_set_pointer(value, g_list_copy_deep(recycling->audio_signal,
826 						  (GCopyFunc) g_object_ref,
827 						  NULL));
828 
829       g_rec_mutex_unlock(recycling_mutex);
830     }
831     break;
832   default:
833     G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, param_spec);
834     break;
835   }
836 }
837 
838 void
ags_recycling_dispose(GObject * gobject)839 ags_recycling_dispose(GObject *gobject)
840 {
841   AgsRecycling *recycling;
842 
843   GList *start_list, *list;
844 
845   GRecMutex *recycling_mutex;
846 
847   recycling = AGS_RECYCLING(gobject);
848 
849   /* get recycling mutex */
850   recycling_mutex = AGS_RECYCLING_GET_OBJ_MUTEX(recycling);
851 
852   g_rec_mutex_lock(recycling_mutex);
853 
854   /* channel */
855   if(recycling->channel != NULL){
856     AgsChannel *channel;
857 
858     channel = recycling->channel;
859 
860     recycling->channel = NULL;
861 
862     g_object_unref(channel);
863   }
864 
865   /* output soundcard */
866   if(recycling->output_soundcard != NULL){
867     g_object_unref(recycling->output_soundcard);
868 
869     recycling->output_soundcard = NULL;
870   }
871 
872   /* input soundcard */
873   if(recycling->input_soundcard != NULL){
874     g_object_unref(recycling->input_soundcard);
875 
876     recycling->input_soundcard = NULL;
877   }
878 
879   /* parent */
880   if(recycling->parent != NULL){
881     g_object_unref(recycling->parent);
882 
883     recycling->parent = NULL;
884   }
885 
886   /* next and prev */
887   if(recycling->next != NULL){
888     g_object_unref(recycling->next);
889 
890     recycling->next = NULL;
891   }
892 
893   if(recycling->prev != NULL){
894     g_object_unref(recycling->prev);
895 
896     recycling->prev = NULL;
897   }
898 
899   g_rec_mutex_unlock(recycling_mutex);
900 
901   /* AgsAudioSignal */
902   g_rec_mutex_lock(recycling_mutex);
903 
904   list =
905     start_list = recycling->audio_signal;
906 
907   recycling->audio_signal = NULL;
908 
909   g_rec_mutex_unlock(recycling_mutex);
910 
911   while(list != NULL){
912     g_object_run_dispose(list->data);
913 
914     list = list->next;
915   }
916 
917   g_list_free_full(start_list,
918 		   g_object_unref);
919 
920   /* call parent */
921   G_OBJECT_CLASS(ags_recycling_parent_class)->dispose(gobject);
922 }
923 
924 void
ags_recycling_finalize(GObject * gobject)925 ags_recycling_finalize(GObject *gobject)
926 {
927   AgsRecycling *recycling;
928 
929   GList *start_list, *list;
930 
931   recycling = AGS_RECYCLING(gobject);
932 
933   ags_uuid_free(recycling->uuid);
934 
935   /* channel */
936   if(recycling->channel != NULL){
937     g_object_unref(recycling->channel);
938   }
939 
940   /* output soundcard */
941   if(recycling->output_soundcard != NULL){
942     g_object_unref(recycling->output_soundcard);
943   }
944 
945   /* input soundcard */
946   if(recycling->input_soundcard != NULL){
947     g_object_unref(recycling->input_soundcard);
948   }
949 
950   /* parent */
951   if(recycling->parent != NULL){
952     g_object_unref(recycling->parent);
953   }
954 
955   /* next and prev */
956   if(recycling->next != NULL){
957     g_object_unref(recycling->next);
958   }
959 
960   if(recycling->prev != NULL){
961     g_object_unref(recycling->prev);
962   }
963 
964   /* AgsAudioSignal */
965   list =
966     start_list = recycling->audio_signal;
967 
968   while(list != NULL){
969     g_object_run_dispose(list->data);
970 
971     list = list->next;
972   }
973 
974   g_list_free_full(start_list,
975 		   g_object_unref);
976 
977   /* call parent */
978   G_OBJECT_CLASS(ags_recycling_parent_class)->finalize(gobject);
979 }
980 
981 AgsUUID*
ags_recycling_get_uuid(AgsConnectable * connectable)982 ags_recycling_get_uuid(AgsConnectable *connectable)
983 {
984   AgsRecycling *recycling;
985 
986   AgsUUID *ptr;
987 
988   GRecMutex *recycling_mutex;
989 
990   recycling = AGS_RECYCLING(connectable);
991 
992   /* get recycling signal mutex */
993   recycling_mutex = AGS_RECYCLING_GET_OBJ_MUTEX(recycling);
994 
995   /* get UUID */
996   g_rec_mutex_lock(recycling_mutex);
997 
998   ptr = recycling->uuid;
999 
1000   g_rec_mutex_unlock(recycling_mutex);
1001 
1002   return(ptr);
1003 }
1004 
1005 gboolean
ags_recycling_has_resource(AgsConnectable * connectable)1006 ags_recycling_has_resource(AgsConnectable *connectable)
1007 {
1008   return(TRUE);
1009 }
1010 
1011 gboolean
ags_recycling_is_ready(AgsConnectable * connectable)1012 ags_recycling_is_ready(AgsConnectable *connectable)
1013 {
1014   AgsRecycling *recycling;
1015 
1016   gboolean is_ready;
1017 
1018   recycling = AGS_RECYCLING(connectable);
1019 
1020   is_ready = ags_recycling_test_flags(recycling, AGS_RECYCLING_ADDED_TO_REGISTRY);
1021 
1022   return(is_ready);
1023 }
1024 
1025 void
ags_recycling_add_to_registry(AgsConnectable * connectable)1026 ags_recycling_add_to_registry(AgsConnectable *connectable)
1027 {
1028   AgsRecycling *recycling;
1029 
1030   AgsRegistry *registry;
1031   AgsRegistryEntry *entry;
1032 
1033   AgsApplicationContext *application_context;
1034 
1035   GList *start_list, *list;
1036 
1037   if(ags_connectable_is_ready(connectable)){
1038     return;
1039   }
1040 
1041   recycling = AGS_RECYCLING(connectable);
1042 
1043   application_context = ags_application_context_get_instance();
1044 
1045   ags_recycling_set_flags(recycling, AGS_RECYCLING_ADDED_TO_REGISTRY);
1046 
1047   registry = (AgsRegistry *) ags_service_provider_get_registry(AGS_SERVICE_PROVIDER(application_context));
1048 
1049   if(registry != NULL){
1050     entry = ags_registry_entry_alloc(registry);
1051     g_value_set_object(entry->entry,
1052 		       (gpointer) recycling);
1053     ags_registry_add_entry(registry,
1054 			   entry);
1055   }
1056 
1057   /* add audio signal */
1058   g_object_get(recycling,
1059 	       "audio-signal", &start_list,
1060 	       NULL);
1061 
1062   list = start_list;
1063 
1064   while(list != NULL){
1065     ags_connectable_add_to_registry(AGS_CONNECTABLE(list->data));
1066 
1067     list = list->next;
1068   }
1069 
1070   g_list_free_full(start_list,
1071 		   g_object_unref);
1072 }
1073 
1074 void
ags_recycling_remove_from_registry(AgsConnectable * connectable)1075 ags_recycling_remove_from_registry(AgsConnectable *connectable)
1076 {
1077   if(!ags_connectable_is_ready(connectable)){
1078     return;
1079   }
1080 
1081   //TODO:JK: implement me
1082 }
1083 
1084 xmlNode*
ags_recycling_list_resource(AgsConnectable * connectable)1085 ags_recycling_list_resource(AgsConnectable *connectable)
1086 {
1087   xmlNode *node;
1088 
1089   node = NULL;
1090 
1091   //TODO:JK: implement me
1092 
1093   return(node);
1094 }
1095 
1096 xmlNode*
ags_recycling_xml_compose(AgsConnectable * connectable)1097 ags_recycling_xml_compose(AgsConnectable *connectable)
1098 {
1099   xmlNode *node;
1100 
1101   node = NULL;
1102 
1103   //TODO:JK: implement me
1104 
1105   return(node);
1106 }
1107 
1108 void
ags_recycling_xml_parse(AgsConnectable * connectable,xmlNode * node)1109 ags_recycling_xml_parse(AgsConnectable *connectable,
1110 		    xmlNode *node)
1111 {
1112   //TODO:JK: implement me
1113 }
1114 
1115 gboolean
ags_recycling_is_connected(AgsConnectable * connectable)1116 ags_recycling_is_connected(AgsConnectable *connectable)
1117 {
1118   AgsRecycling *recycling;
1119 
1120   gboolean is_connected;
1121 
1122   recycling = AGS_RECYCLING(connectable);
1123 
1124   is_connected = ags_recycling_test_flags(recycling, AGS_RECYCLING_CONNECTED);
1125 
1126   return(is_connected);
1127 }
1128 
1129 void
ags_recycling_connect(AgsConnectable * connectable)1130 ags_recycling_connect(AgsConnectable *connectable)
1131 {
1132   AgsRecycling *recycling;
1133 
1134   GList *start_list, *list;
1135 
1136   if(ags_connectable_is_connected(connectable)){
1137     return;
1138   }
1139 
1140   recycling = AGS_RECYCLING(connectable);
1141 
1142   ags_recycling_set_flags(recycling, AGS_RECYCLING_CONNECTED);
1143 
1144 #ifdef AGS_DEBUG
1145   g_message("connecting recycling");
1146 #endif
1147 
1148   /* audio signal */
1149   g_object_get(recycling,
1150 	       "audio-signal", &start_list,
1151 	       NULL);
1152 
1153   list = start_list;
1154 
1155   while(list != NULL){
1156     ags_connectable_connect(AGS_CONNECTABLE(list->data));
1157 
1158     list = list->next;
1159   }
1160 
1161   g_list_free_full(start_list,
1162 		   g_object_unref);
1163 }
1164 
1165 void
ags_recycling_disconnect(AgsConnectable * connectable)1166 ags_recycling_disconnect(AgsConnectable *connectable)
1167 {
1168   AgsRecycling *recycling;
1169 
1170   GList *start_list, *list;
1171 
1172   if(!ags_connectable_is_connected(connectable)){
1173     return;
1174   }
1175 
1176   recycling = AGS_RECYCLING(connectable);
1177 
1178   ags_recycling_unset_flags(recycling, AGS_RECYCLING_CONNECTED);
1179 
1180 #ifdef AGS_DEBUG
1181   g_message("disconnecting recycling");
1182 #endif
1183 
1184   /* audio signal */
1185   g_object_get(recycling,
1186 	       "audio-signal", &start_list,
1187 	       NULL);
1188 
1189   list = start_list;
1190 
1191   while(list != NULL){
1192     ags_connectable_disconnect(AGS_CONNECTABLE(list->data));
1193 
1194     list = list->next;
1195   }
1196 
1197   g_list_free_full(start_list,
1198 		   g_object_unref);
1199 }
1200 
1201 /**
1202  * ags_recycling_get_obj_mutex:
1203  * @recycling: the #AgsRecycling
1204  *
1205  * Get object mutex.
1206  *
1207  * Returns: the #GRecMutex to lock @recycling
1208  *
1209  * Since: 3.1.0
1210  */
1211 GRecMutex*
ags_recycling_get_obj_mutex(AgsRecycling * recycling)1212 ags_recycling_get_obj_mutex(AgsRecycling *recycling)
1213 {
1214   if(!AGS_IS_RECYCLING(recycling)){
1215     return(NULL);
1216   }
1217 
1218   return(AGS_RECYCLING_GET_OBJ_MUTEX(recycling));
1219 }
1220 
1221 /**
1222  * ags_recycling_test_flags:
1223  * @recycling: the #AgsRecycling
1224  * @flags: the flags
1225  *
1226  * Test @flags to be set on @recycling.
1227  *
1228  * Returns: %TRUE if flags are set, else %FALSE
1229  *
1230  * Since: 3.0.0
1231  */
1232 gboolean
ags_recycling_test_flags(AgsRecycling * recycling,guint flags)1233 ags_recycling_test_flags(AgsRecycling *recycling, guint flags)
1234 {
1235   gboolean retval;
1236 
1237   GRecMutex *recycling_mutex;
1238 
1239   if(!AGS_IS_RECYCLING(recycling)){
1240     return(FALSE);
1241   }
1242 
1243   /* get recycling mutex */
1244   recycling_mutex = AGS_RECYCLING_GET_OBJ_MUTEX(recycling);
1245 
1246   /* test */
1247   g_rec_mutex_lock(recycling_mutex);
1248 
1249   retval = (flags & (recycling->flags)) ? TRUE: FALSE;
1250 
1251   g_rec_mutex_unlock(recycling_mutex);
1252 
1253   return(retval);
1254 }
1255 
1256 /**
1257  * ags_recycling_set_flags:
1258  * @recycling: the #AgsRecycling
1259  * @flags: see #AgsRecyclingFlags-enum
1260  *
1261  * Enable a feature of @recycling.
1262  *
1263  * Since: 3.0.0
1264  */
1265 void
ags_recycling_set_flags(AgsRecycling * recycling,guint flags)1266 ags_recycling_set_flags(AgsRecycling *recycling, guint flags)
1267 {
1268   GRecMutex *recycling_mutex;
1269 
1270   if(!AGS_IS_RECYCLING(recycling)){
1271     return;
1272   }
1273 
1274   /* get recycling mutex */
1275   recycling_mutex = AGS_RECYCLING_GET_OBJ_MUTEX(recycling);
1276 
1277   //TODO:JK: add more?
1278 
1279   /* set flags */
1280   g_rec_mutex_lock(recycling_mutex);
1281 
1282   recycling->flags |= flags;
1283 
1284   g_rec_mutex_unlock(recycling_mutex);
1285 }
1286 
1287 /**
1288  * ags_recycling_unset_flags:
1289  * @recycling: the #AgsRecycling
1290  * @flags: see #AgsRecyclingFlags-enum
1291  *
1292  * Disable a feature of @recycling.
1293  *
1294  * Since: 3.0.0
1295  */
1296 void
ags_recycling_unset_flags(AgsRecycling * recycling,guint flags)1297 ags_recycling_unset_flags(AgsRecycling *recycling, guint flags)
1298 {
1299   GRecMutex *recycling_mutex;
1300 
1301   if(!AGS_IS_RECYCLING(recycling)){
1302     return;
1303   }
1304 
1305   /* get recycling mutex */
1306   recycling_mutex = AGS_RECYCLING_GET_OBJ_MUTEX(recycling);
1307 
1308   //TODO:JK: add more?
1309 
1310   /* unset flags */
1311   g_rec_mutex_lock(recycling_mutex);
1312 
1313   recycling->flags &= (~flags);
1314 
1315   g_rec_mutex_unlock(recycling_mutex);
1316 }
1317 
1318 /**
1319  * ags_recycling_get_channel:
1320  * @recycling: the #AgsRecycling
1321  *
1322  * Get channel.
1323  *
1324  * Returns: (transfer full): the #AgsChannel
1325  *
1326  * Since: 3.1.0
1327  */
1328 GObject*
ags_recycling_get_channel(AgsRecycling * recycling)1329 ags_recycling_get_channel(AgsRecycling *recycling)
1330 {
1331   GObject *channel;
1332 
1333   if(!AGS_IS_RECYCLING(recycling)){
1334     return(NULL);
1335   }
1336 
1337   g_object_get(recycling,
1338 	       "channel", &channel,
1339 	       NULL);
1340 
1341   return(channel);
1342 }
1343 
1344 /**
1345  * ags_recycling_set_channel:
1346  * @recycling: the #AgsRecycling
1347  * @channel: the #AgsChannel
1348  *
1349  * Set channel.
1350  *
1351  * Since: 3.1.0
1352  */
1353 void
ags_recycling_set_channel(AgsRecycling * recycling,GObject * channel)1354 ags_recycling_set_channel(AgsRecycling *recycling, GObject *channel)
1355 {
1356   if(!AGS_IS_RECYCLING(recycling)){
1357     return;
1358   }
1359 
1360   g_object_set(recycling,
1361 	       "channel", channel,
1362 	       NULL);
1363 }
1364 
1365 /**
1366  * ags_recycling_next:
1367  * @recycling: the #AgsRecycling
1368  *
1369  * Iterate @recycling.
1370  *
1371  * Returns: (transfer full): the next of #AgsRecycling if available, otherwise %NULL
1372  *
1373  * Since: 3.0.0
1374  */
1375 AgsRecycling*
ags_recycling_next(AgsRecycling * recycling)1376 ags_recycling_next(AgsRecycling *recycling)
1377 {
1378   AgsRecycling *next;
1379 
1380   GRecMutex *recycling_mutex;
1381 
1382   if(!AGS_IS_RECYCLING(recycling)){
1383     return(NULL);
1384   }
1385 
1386   /* get recycling mutex */
1387   recycling_mutex = AGS_RECYCLING_GET_OBJ_MUTEX(recycling);
1388 
1389   /* next */
1390   g_rec_mutex_lock(recycling_mutex);
1391 
1392   next = recycling->next;
1393 
1394   if(next != NULL){
1395     g_object_ref(next);
1396   }
1397 
1398   g_rec_mutex_unlock(recycling_mutex);
1399 
1400   return(next);
1401 }
1402 
1403 /**
1404  * ags_recycling_prev:
1405  * @recycling: the #AgsRecycling
1406  *
1407  * Iterate @recycling.
1408  *
1409  * Returns: (transfer full): the prev of #AgsRecycling if available, otherwise %NULL
1410  *
1411  * Since: 3.0.0
1412  */
1413 AgsRecycling*
ags_recycling_prev(AgsRecycling * recycling)1414 ags_recycling_prev(AgsRecycling *recycling)
1415 {
1416   AgsRecycling *prev;
1417 
1418   GRecMutex *recycling_mutex;
1419 
1420   if(!AGS_IS_RECYCLING(recycling)){
1421     return(NULL);
1422   }
1423 
1424   /* get recycling mutex */
1425   recycling_mutex = AGS_RECYCLING_GET_OBJ_MUTEX(recycling);
1426 
1427   /* prev */
1428   g_rec_mutex_lock(recycling_mutex);
1429 
1430   prev = recycling->prev;
1431 
1432   if(prev != NULL){
1433     g_object_ref(prev);
1434   }
1435 
1436   g_rec_mutex_unlock(recycling_mutex);
1437 
1438   return(prev);
1439 }
1440 
1441 /**
1442  * ags_recycling_get_output_soundcard:
1443  * @recycling: the #AgsRecycling
1444  *
1445  * Get the output soundcard object of @recycling.
1446  *
1447  * Returns: (transfer full): the output soundcard
1448  *
1449  * Since: 3.1.0
1450  */
1451 GObject*
ags_recycling_get_output_soundcard(AgsRecycling * recycling)1452 ags_recycling_get_output_soundcard(AgsRecycling *recycling)
1453 {
1454   GObject *output_soundcard;
1455 
1456   if(!AGS_IS_RECYCLING(recycling)){
1457     return(NULL);
1458   }
1459 
1460   g_object_get(recycling,
1461 	       "output-soundcard", &output_soundcard,
1462 	       NULL);
1463 
1464   return(output_soundcard);
1465 }
1466 
1467 void
ags_recycling_real_set_output_soundcard(AgsRecycling * recycling,GObject * output_soundcard)1468 ags_recycling_real_set_output_soundcard(AgsRecycling *recycling, GObject *output_soundcard)
1469 {
1470   GList *start_list, *list;
1471 
1472   GRecMutex *recycling_mutex;
1473 
1474   if(!AGS_IS_RECYCLING(recycling)){
1475     return;
1476   }
1477 
1478   /* get recycling mutex */
1479   recycling_mutex = AGS_RECYCLING_GET_OBJ_MUTEX(recycling);
1480 
1481   /* recycling */
1482   g_rec_mutex_lock(recycling_mutex);
1483 
1484   if(recycling->output_soundcard == output_soundcard){
1485     g_rec_mutex_unlock(recycling_mutex);
1486 
1487     return;
1488   }
1489 
1490   if(recycling->output_soundcard != NULL){
1491     g_object_unref(recycling->output_soundcard);
1492   }
1493 
1494   if(output_soundcard != NULL){
1495     g_object_ref(output_soundcard);
1496   }
1497 
1498   recycling->output_soundcard = (GObject *) output_soundcard;
1499 
1500   g_rec_mutex_unlock(recycling_mutex);
1501 
1502   /* audio signal */
1503   g_object_get(recycling,
1504 	       "audio-signal", &start_list,
1505 	       NULL);
1506 
1507   list = start_list;
1508 
1509   while(list != NULL){
1510     g_object_set(list->data,
1511 		 "output-soundcard", output_soundcard,
1512 		 NULL);
1513 
1514     list = list->next;
1515   }
1516 
1517   g_list_free_full(start_list,
1518 		   g_object_unref);
1519 }
1520 
1521 /**
1522  * ags_recycling_set_output_soundcard:
1523  * @recycling: the #AgsRecycling
1524  * @output_soundcard: the #GObject implementing #AgsSoundcard
1525  *
1526  * Set the output soundcard object of @recycling.
1527  *
1528  * Since: 3.0.0
1529  */
1530 void
ags_recycling_set_output_soundcard(AgsRecycling * recycling,GObject * output_soundcard)1531 ags_recycling_set_output_soundcard(AgsRecycling *recycling, GObject *output_soundcard)
1532 {
1533   if(!AGS_IS_RECYCLING(recycling)){
1534     return;
1535   }
1536 
1537   g_object_set(recycling,
1538 	       "output-soundcard", output_soundcard,
1539 	       NULL);
1540 }
1541 
1542 /**
1543  * ags_recycling_get_input_soundcard:
1544  * @recycling: the #AgsRecycling
1545  *
1546  * Get the input soundcard object of @recycling.
1547  *
1548  * Returns: (transfer full): the input soundcard
1549  *
1550  * Since: 3.1.0
1551  */
1552 GObject*
ags_recycling_get_input_soundcard(AgsRecycling * recycling)1553 ags_recycling_get_input_soundcard(AgsRecycling *recycling)
1554 {
1555   GObject *input_soundcard;
1556 
1557   if(!AGS_IS_RECYCLING(recycling)){
1558     return(NULL);
1559   }
1560 
1561   g_object_get(recycling,
1562 	       "input-soundcard", &input_soundcard,
1563 	       NULL);
1564 
1565   return(input_soundcard);
1566 }
1567 
1568 void
ags_recycling_real_set_input_soundcard(AgsRecycling * recycling,GObject * input_soundcard)1569 ags_recycling_real_set_input_soundcard(AgsRecycling *recycling, GObject *input_soundcard)
1570 {
1571   GList *start_list, *list;
1572 
1573   GRecMutex *recycling_mutex;
1574 
1575   if(!AGS_IS_RECYCLING(recycling)){
1576     return;
1577   }
1578 
1579   /* get recycling mutex */
1580   recycling_mutex = AGS_RECYCLING_GET_OBJ_MUTEX(recycling);
1581 
1582   /* recycling */
1583   g_rec_mutex_lock(recycling_mutex);
1584 
1585   if(recycling->input_soundcard == input_soundcard){
1586     g_rec_mutex_unlock(recycling_mutex);
1587 
1588     return;
1589   }
1590 
1591   if(recycling->input_soundcard != NULL){
1592     g_object_unref(recycling->input_soundcard);
1593   }
1594 
1595   if(input_soundcard != NULL){
1596     g_object_ref(input_soundcard);
1597   }
1598 
1599   recycling->input_soundcard = (GObject *) input_soundcard;
1600 
1601   g_rec_mutex_unlock(recycling_mutex);
1602 
1603   /* audio signal */
1604   g_object_get(recycling,
1605 	       "audio-signal", &start_list,
1606 	       NULL);
1607 
1608   list = start_list;
1609 
1610   while(list != NULL){
1611     g_object_set(list->data,
1612 		 "input-soundcard", input_soundcard,
1613 		 NULL);
1614 
1615     list = list->next;
1616   }
1617 
1618   g_list_free_full(start_list,
1619 		   g_object_unref);
1620 }
1621 
1622 /**
1623  * ags_recycling_set_input_soundcard:
1624  * @recycling: an #AgsRecycling
1625  * @input_soundcard: the #GObject implementing #AgsSoundcard
1626  *
1627  * Set the input soundcard object of @recycling.
1628  *
1629  * Since: 3.0.0
1630  */
1631 void
ags_recycling_set_input_soundcard(AgsRecycling * recycling,GObject * input_soundcard)1632 ags_recycling_set_input_soundcard(AgsRecycling *recycling, GObject *input_soundcard)
1633 {
1634   if(!AGS_IS_RECYCLING(recycling)){
1635     return;
1636   }
1637 
1638   g_object_set(recycling,
1639 	       "input-soundcard", input_soundcard,
1640 	       NULL);
1641 }
1642 
1643 /**
1644  * ags_recycling_get_samplerate:
1645  * @recycling: the #AgsRecycling
1646  *
1647  * Gets samplerate.
1648  *
1649  * Returns: the samplerate
1650  *
1651  * Since: 3.1.0
1652  */
1653 guint
ags_recycling_get_samplerate(AgsRecycling * recycling)1654 ags_recycling_get_samplerate(AgsRecycling *recycling)
1655 {
1656   guint samplerate;
1657 
1658   if(!AGS_IS_RECYCLING(recycling)){
1659     return(0);
1660   }
1661 
1662   g_object_get(recycling,
1663 	       "samplerate", &samplerate,
1664 	       NULL);
1665 
1666   return(samplerate);
1667 }
1668 
1669 /**
1670  * ags_recycling_set_samplerate:
1671  * @recycling: the #AgsRecycling
1672  * @samplerate: the samplerate
1673  *
1674  * Sets samplerate.
1675  *
1676  * Since: 3.0.0
1677  */
1678 void
ags_recycling_set_samplerate(AgsRecycling * recycling,guint samplerate)1679 ags_recycling_set_samplerate(AgsRecycling *recycling, guint samplerate)
1680 {
1681   AgsAudioSignal *template;
1682 
1683   GList *audio_signal;
1684   GList *start_rt_template, *rt_template;
1685 
1686   GRecMutex *recycling_mutex;
1687 
1688   if(!AGS_IS_RECYCLING(recycling)){
1689     return;
1690   }
1691 
1692   /* get recycling mutex */
1693   recycling_mutex = AGS_RECYCLING_GET_OBJ_MUTEX(recycling);
1694 
1695   /* get audio signal */
1696   g_object_get(recycling,
1697 	       "audio-signal", &audio_signal,
1698 	       NULL);
1699 
1700   /* get template */
1701   template = ags_audio_signal_get_template(audio_signal);
1702 
1703   if(template != NULL){
1704     g_object_set(template,
1705 		 "samplerate", samplerate,
1706 		 NULL);
1707   }
1708 
1709   g_object_unref(template);
1710 
1711   /* get rt-template */
1712   rt_template =
1713     start_rt_template = ags_audio_signal_get_rt_template(audio_signal);
1714 
1715   while(rt_template != NULL){
1716     g_object_set(rt_template->data,
1717 		 "samplerate", samplerate,
1718 		 NULL);
1719 
1720     rt_template = rt_template->next;
1721   }
1722 
1723   g_list_free_full(start_rt_template,
1724 		   g_object_unref);
1725 
1726   /* free list */
1727   g_list_free_full(audio_signal,
1728 		   g_object_unref);
1729 }
1730 
1731 /**
1732  * ags_recycling_get_buffer_size:
1733  * @recycling: the #AgsRecycling
1734  *
1735  * Gets buffer size.
1736  *
1737  * Returns: the buffer size
1738  *
1739  * Since: 3.1.0
1740  */
1741 guint
ags_recycling_get_buffer_size(AgsRecycling * recycling)1742 ags_recycling_get_buffer_size(AgsRecycling *recycling)
1743 {
1744   guint buffer_size;
1745 
1746   if(!AGS_IS_RECYCLING(recycling)){
1747     return(0);
1748   }
1749 
1750   g_object_get(recycling,
1751 	       "buffer-size", &buffer_size,
1752 	       NULL);
1753 
1754   return(buffer_size);
1755 }
1756 
1757 /**
1758  * ags_recycling_set_buffer_size:
1759  * @recycling: the #AgsRecycling
1760  * @buffer_size: the buffer size
1761  *
1762  * Set buffer size.
1763  *
1764  * Since: 3.0.0
1765  */
1766 void
ags_recycling_set_buffer_size(AgsRecycling * recycling,guint buffer_size)1767 ags_recycling_set_buffer_size(AgsRecycling *recycling, guint buffer_size)
1768 {
1769   AgsAudioSignal *template;
1770 
1771   GList *audio_signal;
1772   GList *start_rt_template, *rt_template;
1773 
1774   GRecMutex *recycling_mutex;
1775 
1776   if(!AGS_IS_RECYCLING(recycling)){
1777     return;
1778   }
1779 
1780   /* get recycling mutex */
1781   recycling_mutex = AGS_RECYCLING_GET_OBJ_MUTEX(recycling);
1782 
1783   /* get audio signal */
1784   g_object_get(recycling,
1785 	       "audio-signal", &audio_signal,
1786 	       NULL);
1787 
1788   /* get template */
1789   template = ags_audio_signal_get_template(audio_signal);
1790 
1791   if(template != NULL){
1792     g_object_set(template,
1793 		 "buffer-size", buffer_size,
1794 		 NULL);
1795   }
1796 
1797   g_object_unref(template);
1798 
1799   /* get rt-template */
1800   rt_template =
1801     start_rt_template = ags_audio_signal_get_rt_template(audio_signal);
1802 
1803   while(rt_template != NULL){
1804     g_object_set(rt_template->data,
1805 		 "buffer-size", buffer_size,
1806 		 NULL);
1807 
1808     rt_template = rt_template->next;
1809   }
1810 
1811   g_list_free_full(start_rt_template,
1812 		   g_object_unref);
1813 
1814   /* free list */
1815   g_list_free_full(audio_signal,
1816 		   g_object_unref);
1817 }
1818 
1819 /**
1820  * ags_recycling_get_format:
1821  * @recycling: the #AgsRecycling
1822  *
1823  * Gets format.
1824  *
1825  * Returns: the format
1826  *
1827  * Since: 3.1.0
1828  */
1829 guint
ags_recycling_get_format(AgsRecycling * recycling)1830 ags_recycling_get_format(AgsRecycling *recycling)
1831 {
1832   guint format;
1833 
1834   if(!AGS_IS_RECYCLING(recycling)){
1835     return(0);
1836   }
1837 
1838   g_object_get(recycling,
1839 	       "format", &format,
1840 	       NULL);
1841 
1842   return(format);
1843 }
1844 
1845 /**
1846  * ags_recycling_set_format:
1847  * @recycling: the #AgsRecycling
1848  * @format: the format
1849  *
1850  * Set format.
1851  *
1852  * Since: 3.0.0
1853  */
1854 void
ags_recycling_set_format(AgsRecycling * recycling,guint format)1855 ags_recycling_set_format(AgsRecycling *recycling, guint format)
1856 {
1857   AgsAudioSignal *template;
1858 
1859   GList *audio_signal;
1860   GList *start_rt_template, *rt_template;
1861 
1862   GRecMutex *recycling_mutex;
1863 
1864   if(!AGS_IS_RECYCLING(recycling)){
1865     return;
1866   }
1867 
1868   /* get recycling mutex */
1869   recycling_mutex = AGS_RECYCLING_GET_OBJ_MUTEX(recycling);
1870 
1871   /* get audio signal */
1872   g_object_get(recycling,
1873 	       "audio-signal", &audio_signal,
1874 	       NULL);
1875 
1876   /* get template */
1877   template = ags_audio_signal_get_template(audio_signal);
1878 
1879   if(template != NULL){
1880     g_object_set(template,
1881 		 "format", format,
1882 		 NULL);
1883   }
1884 
1885   g_object_unref(template);
1886 
1887   /* get rt-template */
1888   rt_template =
1889     start_rt_template = ags_audio_signal_get_rt_template(audio_signal);
1890 
1891   while(rt_template != NULL){
1892     g_object_set(rt_template->data,
1893 		 "format", format,
1894 		 NULL);
1895 
1896     rt_template = rt_template->next;
1897   }
1898 
1899   g_list_free_full(start_rt_template,
1900 		   g_object_unref);
1901 
1902   /* free list */
1903   g_list_free_full(audio_signal,
1904 		   g_object_unref);
1905 }
1906 
1907 /**
1908  * ags_recycling_get_audio_signal:
1909  * @recycling: the #AgsRecycling
1910  *
1911  * Get recall id.
1912  *
1913  * Returns: (element-type AgsAudio.AudioSignal) (transfer full): the #GList-struct containig #AgsAudioSignal
1914  *
1915  * Since: 3.1.0
1916  */
1917 GList*
ags_recycling_get_audio_signal(AgsRecycling * recycling)1918 ags_recycling_get_audio_signal(AgsRecycling *recycling)
1919 {
1920   GList *audio_signal;
1921 
1922   if(!AGS_IS_RECYCLING(recycling)){
1923     return(NULL);
1924   }
1925 
1926   g_object_get(recycling,
1927 	       "audio-signal", &audio_signal,
1928 	       NULL);
1929 
1930   return(audio_signal);
1931 }
1932 
1933 /**
1934  * ags_recycling_set_audio_signal:
1935  * @recycling: the #AgsRecycling
1936  * @audio_signal: (element-type AgsAudio.AudioSignal) (transfer full): the #GList-struct containing #AgsAudioSignal
1937  *
1938  * Set recall id by replacing existing.
1939  *
1940  * Since: 3.1.0
1941  */
1942 void
ags_recycling_set_audio_signal(AgsRecycling * recycling,GList * audio_signal)1943 ags_recycling_set_audio_signal(AgsRecycling *recycling, GList *audio_signal)
1944 {
1945   GList *start_audio_signal;
1946 
1947   GRecMutex *recycling_mutex;
1948 
1949   if(!AGS_IS_RECYCLING(recycling)){
1950     return;
1951   }
1952 
1953   /* get recycling mutex */
1954   recycling_mutex = AGS_RECYCLING_GET_OBJ_MUTEX(recycling);
1955 
1956   g_rec_mutex_lock(recycling_mutex);
1957 
1958   start_audio_signal = recycling->audio_signal;
1959 
1960   recycling->audio_signal = audio_signal;
1961 
1962   g_rec_mutex_unlock(recycling_mutex);
1963 
1964   g_list_free_full(start_audio_signal,
1965 		   (GDestroyNotify) g_object_unref);
1966 }
1967 
1968 void
ags_recycling_real_add_audio_signal(AgsRecycling * recycling,AgsAudioSignal * audio_signal)1969 ags_recycling_real_add_audio_signal(AgsRecycling *recycling,
1970 				    AgsAudioSignal *audio_signal)
1971 {
1972   AgsAudioSignal *old_template;
1973 
1974   GObject *output_soundcard;
1975 
1976   GHashTable *hash_table;
1977   GList *start_list, *list;
1978 
1979   GRecMutex *recycling_mutex;
1980 
1981   /* get recycling mutex */
1982   recycling_mutex = AGS_RECYCLING_GET_OBJ_MUTEX(recycling);
1983 
1984   /* get audio signal */
1985   g_rec_mutex_lock(recycling_mutex);
1986 
1987   if(g_list_find(recycling->audio_signal,
1988 		 audio_signal) != NULL){
1989     g_rec_mutex_unlock(recycling_mutex);
1990 
1991     return;
1992   }
1993 
1994   output_soundcard = recycling->output_soundcard;
1995 
1996   g_rec_mutex_unlock(recycling_mutex);
1997 
1998   g_object_get(recycling,
1999 	       "audio-signal", &start_list,
2000 	       NULL);
2001 
2002   /* get some fields */
2003   if(ags_audio_signal_test_flags(audio_signal, AGS_AUDIO_SIGNAL_TEMPLATE)){
2004     /* old template */
2005     old_template = ags_audio_signal_get_template(start_list);
2006 
2007     /* remove old template */
2008     ags_recycling_remove_audio_signal(recycling,
2009 				      old_template);
2010 
2011     g_object_unref(old_template);
2012 
2013     /* add new template */
2014     g_rec_mutex_lock(recycling_mutex);
2015 
2016     recycling->audio_signal = g_list_prepend(recycling->audio_signal,
2017 					     audio_signal);
2018     g_object_ref(audio_signal);
2019 
2020     g_rec_mutex_unlock(recycling_mutex);
2021 
2022     /* add/remove */
2023     list = start_list;
2024     hash_table = g_hash_table_new_full(g_direct_hash, g_direct_equal,
2025 				       NULL,
2026 				       NULL);
2027 
2028     while(list != NULL){
2029       AgsAudioSignal *current_audio_signal;
2030       AgsAudioSignal *rt_template, *old_rt_template;
2031       AgsRecallID *current_recall_id;
2032 
2033       current_audio_signal = list->data;
2034 
2035       /* get some fields */
2036       if(ags_audio_signal_test_flags(current_audio_signal, AGS_AUDIO_SIGNAL_RT_TEMPLATE)){
2037 	current_recall_id = (AgsRecallID *) current_audio_signal->recall_id;
2038 
2039 	/* create rt-template */
2040 	rt_template = ags_audio_signal_new(output_soundcard,
2041 					   (GObject *) recycling,
2042 					   (GObject *) current_recall_id);
2043 	ags_audio_signal_set_flags(rt_template, AGS_AUDIO_SIGNAL_RT_TEMPLATE);
2044 
2045 	g_hash_table_insert(hash_table,
2046 			    current_audio_signal, rt_template);
2047 
2048 	/* remove old rt-template */
2049 	ags_recycling_remove_audio_signal(recycling,
2050 					  current_audio_signal);
2051 
2052 	/* add new rt-template */
2053 	ags_recycling_add_audio_signal(recycling,
2054 				       rt_template);
2055       }
2056 
2057       list = list->next;
2058     }
2059 
2060     /* update */
2061     list = start_list;
2062 
2063     while(list != NULL){
2064       AgsAudioSignal *current_audio_signal;
2065       AgsAudioSignal *rt_template;
2066 
2067       current_audio_signal = list->data;
2068 
2069       /* get some fields */
2070       g_object_get(current_audio_signal,
2071 		   "rt-template", &rt_template,
2072 		   NULL);
2073 
2074       if(rt_template != NULL){
2075 	g_object_set(list->data,
2076 		     "rt-template", g_hash_table_lookup(hash_table,
2077 							rt_template),
2078 		     NULL);
2079 
2080 	g_object_unref(rt_template);
2081       }
2082 
2083       list = list->next;
2084     }
2085 
2086     g_hash_table_destroy(hash_table);
2087   }else{
2088     /* add new audio signal */
2089     g_rec_mutex_lock(recycling_mutex);
2090 
2091     recycling->audio_signal = g_list_prepend(recycling->audio_signal,
2092 					     audio_signal);
2093     g_object_ref(audio_signal);
2094 
2095     g_rec_mutex_unlock(recycling_mutex);
2096   }
2097 
2098   g_list_free_full(start_list,
2099 		   g_object_unref);
2100 
2101   g_object_set(audio_signal,
2102 	       "recycling", recycling,
2103 	       NULL);
2104 }
2105 
2106 /**
2107  * ags_recycling_add_audio_signal:
2108  * @recycling: the #AgsRecycling
2109  * @audio_signal: the #AgsAudioSignal to add
2110  *
2111  * Add @audio_signal to @recycling.
2112  *
2113  * Since: 3.0.0
2114  */
2115 void
ags_recycling_add_audio_signal(AgsRecycling * recycling,AgsAudioSignal * audio_signal)2116 ags_recycling_add_audio_signal(AgsRecycling *recycling,
2117 			       AgsAudioSignal *audio_signal)
2118 {
2119   GRecMutex *recycling_mutex;
2120 
2121   g_return_if_fail(AGS_IS_RECYCLING(recycling) &&
2122 		   AGS_IS_AUDIO_SIGNAL(audio_signal));
2123 
2124   /* get recycling mutex */
2125   recycling_mutex = AGS_RECYCLING_GET_OBJ_MUTEX(recycling);
2126 
2127   /* get audio signal */
2128   g_rec_mutex_lock(recycling_mutex);
2129 
2130   if(g_list_find(recycling->audio_signal,
2131 		 audio_signal) != NULL){
2132     g_rec_mutex_unlock(recycling_mutex);
2133 
2134     return;
2135   }
2136 
2137   g_rec_mutex_unlock(recycling_mutex);
2138 
2139   /* emit signal */
2140   g_object_ref(G_OBJECT(recycling));
2141   g_object_ref(G_OBJECT(audio_signal));
2142   g_signal_emit(G_OBJECT(recycling),
2143 		recycling_signals[ADD_AUDIO_SIGNAL], 0,
2144 		audio_signal);
2145   g_object_unref(G_OBJECT(audio_signal));
2146   g_object_unref(G_OBJECT(recycling));
2147 }
2148 
2149 void
ags_recycling_real_remove_audio_signal(AgsRecycling * recycling,AgsAudioSignal * audio_signal)2150 ags_recycling_real_remove_audio_signal(AgsRecycling *recycling,
2151 				       AgsAudioSignal *audio_signal)
2152 {
2153   GRecMutex *recycling_mutex;
2154 
2155   /* get recycling mutex */
2156   recycling_mutex = AGS_RECYCLING_GET_OBJ_MUTEX(recycling);
2157 
2158   /* check audio signal */
2159   g_rec_mutex_lock(recycling_mutex);
2160 
2161   if(g_list_find(recycling->audio_signal,
2162 		 audio_signal) == NULL){
2163     g_rec_mutex_unlock(recycling_mutex);
2164 
2165     return;
2166   }
2167 
2168   recycling->audio_signal = g_list_remove(recycling->audio_signal,
2169 					  audio_signal);
2170 
2171   g_rec_mutex_unlock(recycling_mutex);
2172 
2173   g_object_set(audio_signal,
2174 	       "recycling", NULL,
2175 	       NULL);
2176 
2177   g_object_unref(audio_signal);
2178 }
2179 
2180 /**
2181  * ags_recycling_remove_audio_signal:
2182  * @recycling: the #AgsRecycling
2183  * @audio_signal: the #AgsAudioSignal to remove
2184  *
2185  * Remove @audio_signal of @recycling.
2186  *
2187  * Since: 3.0.0
2188  */
2189 void
ags_recycling_remove_audio_signal(AgsRecycling * recycling,AgsAudioSignal * audio_signal)2190 ags_recycling_remove_audio_signal(AgsRecycling *recycling,
2191 				  AgsAudioSignal *audio_signal)
2192 {
2193   g_return_if_fail(AGS_IS_RECYCLING(recycling) && AGS_IS_AUDIO_SIGNAL(audio_signal));
2194 
2195   /* emit signal */
2196   g_object_ref((GObject *) recycling);
2197   g_object_ref((GObject *) audio_signal);
2198   g_signal_emit(G_OBJECT(recycling),
2199 		recycling_signals[REMOVE_AUDIO_SIGNAL], 0,
2200 		audio_signal);
2201   g_object_unref((GObject *) audio_signal);
2202   g_object_unref((GObject *) recycling);
2203 }
2204 
2205 /**
2206  * ags_recycling_data_request:
2207  * @recycling: the #AgsRecycling
2208  * @audio_signal: the #AgsAudioSignal
2209  *
2210  * Request data of @audio_signal.
2211  *
2212  * Since: 3.0.0
2213  */
2214 void
ags_recycling_data_request(AgsRecycling * recycling,AgsAudioSignal * audio_signal)2215 ags_recycling_data_request(AgsRecycling *recycling,
2216 			   AgsAudioSignal *audio_signal)
2217 {
2218   g_return_if_fail(AGS_IS_RECYCLING(recycling));
2219 
2220   /* emit signal */
2221   g_object_ref((GObject *) recycling);
2222   g_signal_emit(G_OBJECT(recycling),
2223 		recycling_signals[DATA_REQUEST], 0,
2224 		audio_signal);
2225   g_object_unref((GObject *) recycling);
2226 }
2227 
2228 /**
2229  * ags_recycling_create_audio_signal_with_defaults:
2230  * @recycling: the #AgsRecycling
2231  * @audio_signal: the #AgsAudioSignal to apply defaults
2232  * @delay: the delay
2233  * @attack: the attack
2234  *
2235  * Create audio signal with defaults.
2236  *
2237  * Since: 3.0.0
2238  */
2239 void
ags_recycling_create_audio_signal_with_defaults(AgsRecycling * recycling,AgsAudioSignal * audio_signal,gdouble delay,guint attack)2240 ags_recycling_create_audio_signal_with_defaults(AgsRecycling *recycling,
2241 						AgsAudioSignal *audio_signal,
2242 						gdouble delay, guint attack)
2243 {
2244   AgsAudioSignal *template;
2245 
2246   GObject *output_soundcard;
2247 
2248   GList *start_list, *list;
2249 
2250   guint samplerate;
2251   guint buffer_size;
2252   guint format;
2253   guint last_frame;
2254   guint loop_start, loop_end;
2255   guint length;
2256   guint frame_count;
2257 
2258   GRecMutex *recycling_mutex;
2259   GRecMutex *audio_signal_mutex;
2260   GRecMutex *template_mutex;
2261 
2262   if(!AGS_IS_RECYCLING(recycling) ||
2263      !AGS_IS_AUDIO_SIGNAL(audio_signal)){
2264     return;
2265   }
2266 
2267   /* get recycling mutex */
2268   recycling_mutex = AGS_RECYCLING_GET_OBJ_MUTEX(recycling);
2269 
2270   /* get audio signal mutex */
2271   audio_signal_mutex = AGS_AUDIO_SIGNAL_GET_OBJ_MUTEX(audio_signal);
2272 
2273   /* get audio signal list */
2274   g_object_get(recycling,
2275 	       "audio-signal", &start_list,
2276 	       NULL);
2277 
2278   /* get template */
2279   template = ags_audio_signal_get_template(start_list);
2280 
2281   g_list_free_full(start_list,
2282 		   g_object_unref);
2283 
2284   /* set delay and attack */
2285   g_object_set(audio_signal,
2286 	       "delay", delay,
2287 	       "attack", attack,
2288 	       NULL);
2289 
2290   if(template == NULL){
2291     ags_audio_signal_stream_resize(audio_signal,
2292 				   0);
2293 
2294     return;
2295   }
2296 
2297   /* get template mutex */
2298   template_mutex = AGS_AUDIO_SIGNAL_GET_OBJ_MUTEX(template);
2299 
2300   /* get some fields */
2301   g_rec_mutex_lock(template_mutex);
2302 
2303   output_soundcard = template->output_soundcard;
2304 
2305   samplerate = template->samplerate;
2306   buffer_size = template->buffer_size;
2307   format = template->format;
2308 
2309   length = template->length;
2310   frame_count = template->frame_count;
2311 
2312   last_frame = template->last_frame;
2313   loop_start = template->loop_start;
2314   loop_end = template->loop_end;
2315 
2316   g_rec_mutex_unlock(template_mutex);
2317 
2318   /* apply delay and attack */
2319   last_frame = (((guint)(delay *
2320 			 buffer_size) +
2321 		 attack +
2322 		 last_frame) %
2323 		buffer_size);
2324   loop_start = (((guint) (delay *
2325 			  buffer_size) +
2326 		 attack +
2327 		 loop_start) %
2328 		buffer_size);
2329   loop_end = (((guint)(delay *
2330 		       buffer_size) +
2331 	       attack +
2332 	       loop_end) %
2333 	      buffer_size);
2334 
2335   /* apply defaults */
2336   g_object_set(audio_signal,
2337 	       "recycling", recycling,
2338 	       "output-soundcard", output_soundcard,
2339 	       "samplerate", samplerate,
2340 	       "buffer-size", buffer_size,
2341 	       "format", format,
2342 	       "frame-count", frame_count,
2343 	       "last-frame", last_frame,
2344 	       "loop-start", loop_start,
2345 	       "loop-end", loop_end,
2346 	       NULL);
2347 
2348   /* resize and duplicate */
2349   ags_audio_signal_stream_resize(audio_signal,
2350 				 length);
2351   ags_audio_signal_duplicate_stream(audio_signal,
2352 				    template);
2353 
2354   g_object_unref(template);
2355 }
2356 
2357 /**
2358  * ags_recycling_create_audio_signal_with_frame_count:
2359  * @recycling: the #AgsRecycling
2360  * @audio_signal: the #AgsAudioSignal to apply defaults
2361  * @frame_count: the audio data size
2362  * @delay: the delay
2363  * @attack: the attack
2364  *
2365  * Create audio signal with frame count.
2366  *
2367  * Since: 3.0.0
2368  */
2369 void
ags_recycling_create_audio_signal_with_frame_count(AgsRecycling * recycling,AgsAudioSignal * audio_signal,guint frame_count,gdouble delay,guint attack)2370 ags_recycling_create_audio_signal_with_frame_count(AgsRecycling *recycling,
2371 						   AgsAudioSignal *audio_signal,
2372 						   guint frame_count,
2373 						   gdouble delay, guint attack)
2374 {
2375   AgsAudioSignal *template;
2376 
2377   GObject *output_soundcard;
2378 
2379   GList *start_list, *list;
2380   GList *stream, *template_stream;
2381 
2382   guint samplerate;
2383   guint buffer_size;
2384   guint format;
2385   guint last_frame;
2386   guint loop_start, loop_end;
2387   guint new_last_frame;
2388   guint new_loop_start, new_loop_end;
2389   guint template_length;
2390   guint loop_length;
2391   guint loop_frame_count;
2392   guint n_frames;
2393   guint copy_n_frames;
2394   guint nth_loop;
2395   guint i, j;
2396   guint copy_mode;
2397 
2398   GRecMutex *recycling_mutex;
2399   GRecMutex *audio_signal_mutex;
2400   GRecMutex *template_mutex;
2401   GRecMutex *template_stream_mutex;
2402 
2403   if(!AGS_IS_RECYCLING(recycling) ||
2404      !AGS_IS_AUDIO_SIGNAL(audio_signal)){
2405     return;
2406   }
2407 
2408   /* get recycling mutex */
2409   recycling_mutex = AGS_RECYCLING_GET_OBJ_MUTEX(recycling);
2410 
2411   /* get audio signal mutex */
2412   audio_signal_mutex = AGS_AUDIO_SIGNAL_GET_OBJ_MUTEX(audio_signal);
2413 
2414   /* get audio signal list */
2415   g_object_get(recycling,
2416 	       "audio-signal", &start_list,
2417 	       NULL);
2418 
2419   /* get template */
2420   template = ags_audio_signal_get_template(start_list);
2421 
2422   g_list_free_full(start_list,
2423 		   g_object_unref);
2424 
2425   /* set delay and attack */
2426   g_object_set(audio_signal,
2427 	       "delay", delay,
2428 	       "attack", attack,
2429 	       NULL);
2430 
2431   if(template == NULL){
2432     g_rec_mutex_lock(audio_signal_mutex);
2433 
2434     buffer_size = audio_signal->buffer_size;
2435 
2436     g_rec_mutex_unlock(audio_signal_mutex);
2437 
2438     ags_audio_signal_stream_resize(audio_signal,
2439 				   (guint) ceil((attack + frame_count) / buffer_size) + 1);
2440 
2441     return;
2442   }
2443 
2444   /* get template mutex */
2445   template_mutex = AGS_AUDIO_SIGNAL_GET_OBJ_MUTEX(template);
2446 
2447   /* get some fields */
2448   g_rec_mutex_lock(template_mutex);
2449 
2450   output_soundcard = template->output_soundcard;
2451 
2452   samplerate = template->samplerate;
2453   buffer_size = template->buffer_size;
2454   format = template->format;
2455 
2456   last_frame = template->last_frame;
2457   loop_start = template->loop_start;
2458   loop_end = template->loop_end;
2459 
2460   template_length = template->length;
2461 
2462   g_rec_mutex_unlock(template_mutex);
2463 
2464   /* apply delay and attack */
2465   new_last_frame = (((guint)(delay *
2466 			     buffer_size) +
2467 		     attack +
2468 		     last_frame) %
2469 		    buffer_size);
2470   new_loop_start = ((guint) (delay *
2471 			     buffer_size) +
2472 		    attack +
2473 		    loop_start);
2474   new_loop_end = ((guint)(delay *
2475 			  buffer_size) +
2476 		  attack +
2477 		  loop_end);
2478 
2479   /* apply defaults */
2480   g_object_set(audio_signal,
2481 	       "recycling", recycling,
2482 	       "output-soundcard", output_soundcard,
2483 	       "samplerate", samplerate,
2484 	       "buffer-size", buffer_size,
2485 	       "format", format,
2486 	       NULL);
2487 
2488   /* resize */
2489   if(loop_end > loop_start){
2490     loop_length = loop_end - loop_start;
2491 
2492     if((frame_count - loop_start) > (last_frame - loop_end) &&
2493        last_frame >= loop_end){
2494       loop_frame_count = (frame_count - loop_start) - (last_frame - loop_end);
2495     }else{
2496       loop_frame_count = loop_length;
2497     }
2498 
2499     ags_audio_signal_stream_resize(audio_signal,
2500 				   (guint) ceil(frame_count / buffer_size) + 1);
2501   }else{
2502     ags_audio_signal_duplicate_stream(audio_signal,
2503 				      template);
2504     ags_audio_signal_stream_resize(audio_signal,
2505 				   (guint) ceil(frame_count / buffer_size) + 1);
2506 
2507     return;
2508   }
2509 
2510   new_last_frame = ((guint) (delay * buffer_size) + frame_count + attack) % buffer_size;
2511 
2512   g_object_set(audio_signal,
2513 	       "last-frame", new_last_frame,
2514 	       NULL);
2515 
2516   if(template_length == 0){
2517     g_object_unref(template);
2518 
2519     return;
2520   }
2521 
2522   /* get template stream mutex */
2523   template_stream_mutex = AGS_AUDIO_SIGNAL_GET_STREAM_MUTEX(template);
2524 
2525   /* loop related copying */
2526   copy_mode = ags_audio_buffer_util_get_copy_mode(ags_audio_buffer_util_format_from_soundcard(format),
2527  						  ags_audio_buffer_util_format_from_soundcard(format));
2528 
2529   /* generic copying */
2530   stream = g_list_nth(audio_signal->stream,
2531 		      (guint) ((delay * buffer_size) + attack) / buffer_size);
2532 
2533   g_rec_mutex_lock(template_stream_mutex);
2534 
2535   template_stream = template->stream;
2536 
2537   for(i = 0, j = attack, nth_loop = 0; i < frame_count && stream != NULL && template_stream != NULL;){
2538     /* compute count of frames to copy */
2539     copy_n_frames = buffer_size;
2540 
2541     if(loop_start < loop_end &&
2542        i + copy_n_frames < loop_start + loop_frame_count){
2543       if(j + copy_n_frames > loop_end){
2544 	copy_n_frames = loop_end - j;
2545       }
2546     }
2547 
2548     if((i % buffer_size) + copy_n_frames > buffer_size){
2549       copy_n_frames = buffer_size - (i % buffer_size);
2550     }
2551 
2552     if((j % buffer_size) + copy_n_frames > buffer_size){
2553       copy_n_frames = buffer_size - (j % buffer_size);
2554     }
2555 
2556     if(i + copy_n_frames > frame_count){
2557       copy_n_frames = frame_count - i;
2558     }
2559 
2560     /* copy */
2561     ags_audio_buffer_util_copy_buffer_to_buffer(stream->data, 1, i % buffer_size,
2562 						template_stream->data, 1, j % buffer_size,
2563 						copy_n_frames, copy_mode);
2564 
2565     if((i + copy_n_frames) % buffer_size == 0){
2566       stream = stream->next;
2567     }
2568 
2569     if((j + copy_n_frames) % buffer_size == 0){
2570       template_stream = template_stream->next;
2571     }
2572 
2573     i += copy_n_frames;
2574 
2575     if(loop_start < loop_end){
2576       if(j + copy_n_frames == loop_end &&
2577 	 i + copy_n_frames < loop_start + loop_frame_count){
2578 	template_stream = g_list_nth(template->stream,
2579 				     floor(loop_start / buffer_size));
2580 
2581 	j = loop_start;
2582 
2583 	nth_loop++;
2584       }else{
2585 	j += copy_n_frames;
2586       }
2587     }else{
2588       j += copy_n_frames;
2589     }
2590   }
2591 
2592   g_rec_mutex_unlock(template_stream_mutex);
2593 
2594   g_object_unref(template);
2595 }
2596 
2597 /**
2598  * ags_recycling_find_next_channel:
2599  * @start_region: boundary start
2600  * @end_region: boundary end
2601  * @prev_channel: previous channel
2602  *
2603  * Retrieve next recycling with different channel.
2604  *
2605  * Returns: (transfer full): Matching recycling.
2606  *
2607  * Since: 3.0.0
2608  */
2609 AgsRecycling*
ags_recycling_find_next_channel(AgsRecycling * start_region,AgsRecycling * end_region,GObject * prev_channel)2610 ags_recycling_find_next_channel(AgsRecycling *start_region, AgsRecycling *end_region,
2611 				GObject *prev_channel)
2612 {
2613   AgsRecycling *recycling, *next_recycling;
2614 
2615   /* verify objects and get pointer for safe access */
2616   if(!AGS_IS_RECYCLING(start_region)){
2617     return(NULL);
2618   }
2619 
2620   /* find */
2621   recycling = start_region;
2622 
2623   if(recycling != NULL){
2624     g_object_ref(recycling);
2625   }
2626 
2627   while(recycling != NULL &&
2628 	recycling != end_region){
2629     GObject *current_channel;
2630 
2631     gboolean success;
2632 
2633     g_object_get(recycling,
2634 		 "channel", &current_channel,
2635 		 NULL);
2636 
2637     /* check if new match */
2638     success = (current_channel != prev_channel) ? TRUE: FALSE;
2639     g_object_unref(current_channel);
2640 
2641     if(success){
2642       return(recycling);
2643     }
2644 
2645     /* iterate */
2646     next_recycling = ags_recycling_next(recycling);
2647 
2648     g_object_unref(recycling);
2649 
2650     recycling = next_recycling;
2651   }
2652 
2653   if(recycling != NULL){
2654     g_object_unref(recycling);
2655   }
2656 
2657   /* no new channel within region */
2658   return(NULL);
2659 }
2660 
2661 /**
2662  * ags_recycling_position:
2663  * @start_region: boundary start
2664  * @end_region: boundary end
2665  * @recycling: matching recycling
2666  *
2667  * Retrieve position of recycling.
2668  *
2669  * Returns: position within boundary.
2670  *
2671  * Since: 3.0.0
2672  */
2673 gint
ags_recycling_position(AgsRecycling * start_region,AgsRecycling * end_region,AgsRecycling * recycling)2674 ags_recycling_position(AgsRecycling *start_region, AgsRecycling *end_region,
2675 		       AgsRecycling *recycling)
2676 {
2677   AgsRecycling *current, *next;
2678 
2679   gint position;
2680 
2681   if(!AGS_IS_RECYCLING(start_region)){
2682     return(-1);
2683   }
2684 
2685   /* determine position */
2686   current = start_region;
2687   g_object_ref(current);
2688 
2689   position = -1;
2690 
2691   while(current != NULL && current != end_region){
2692     position++;
2693 
2694     /* check if new match */
2695     if(current == recycling){
2696       break;
2697     }
2698 
2699     /* iterate */
2700     next = ags_recycling_next(current);
2701 
2702     g_object_unref(current);
2703 
2704     current = next;
2705   }
2706 
2707   if(current != NULL){
2708     g_object_unref(current);
2709   }
2710 
2711   return(position);
2712 }
2713 
2714 /**
2715  * ags_recycling_is_active:
2716  * @start_region: boundary start
2717  * @end_region: boundary end
2718  * @recall_id: the #AgsRecallID
2719  *
2720  * Check if is active.
2721  *
2722  * Returns: %TRUE if related audio signal to recall id is available, otherwise %FALSE
2723  *
2724  * Since: 3.0.0
2725  */
2726 gboolean
ags_recycling_is_active(AgsRecycling * start_region,AgsRecycling * end_region,GObject * recall_id)2727 ags_recycling_is_active(AgsRecycling *start_region, AgsRecycling *end_region,
2728 			GObject *recall_id)
2729 {
2730   AgsRecycling *current, *next;
2731   AgsRecyclingContext *recycling_context;
2732 
2733   GList *start_list, *list;
2734 
2735   gboolean is_active;
2736   gboolean success;
2737 
2738   if(!AGS_IS_RECYCLING(start_region) ||
2739      !AGS_IS_RECALL_ID(recall_id)){
2740     return(FALSE);
2741   }
2742 
2743   current = start_region;
2744   g_object_ref(current);
2745 
2746   success = FALSE;
2747 
2748   while(current != end_region){
2749     /* get audio signal */
2750     g_object_get(current,
2751 		 "audio-signal", &start_list,
2752 		 NULL);
2753 
2754     /* is active */
2755     is_active = (ags_audio_signal_is_active(start_list,
2756 					    recall_id)) ? TRUE: FALSE;
2757 
2758     g_list_free_full(start_list,
2759 		     g_object_unref);
2760 
2761     if(is_active){
2762       success = TRUE;
2763 
2764       break;
2765     }
2766 
2767     /* iterate */
2768     next = ags_recycling_next(current);
2769 
2770     g_object_unref(next);
2771 
2772     current = next;
2773   }
2774 
2775   if(current != NULL){
2776     g_object_unref(current);
2777   }
2778 
2779   return(success);
2780 }
2781 
2782 /**
2783  * ags_recycling_new:
2784  * @output_soundcard: the #GObject implementing #AgsSoundcard
2785  *
2786  * Creates a #AgsRecycling, with defaults of @soundcard.
2787  *
2788  * Returns: a new #AgsRecycling
2789  *
2790  * Since: 3.0.0
2791  */
2792 AgsRecycling*
ags_recycling_new(GObject * output_soundcard)2793 ags_recycling_new(GObject *output_soundcard)
2794 {
2795   AgsRecycling *recycling;
2796 
2797   recycling = (AgsRecycling *) g_object_new(AGS_TYPE_RECYCLING,
2798 					    "output-soundcard", output_soundcard,
2799 					    NULL);
2800 
2801   return(recycling);
2802 }
2803