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/thread/ags_audio_loop.h>
21 
22 #include <ags/audio/ags_playback_domain.h>
23 #include <ags/audio/ags_playback.h>
24 #include <ags/audio/ags_audio.h>
25 #include <ags/audio/ags_channel.h>
26 
27 #include <ags/audio/thread/ags_soundcard_thread.h>
28 #include <ags/audio/thread/ags_sequencer_thread.h>
29 #include <ags/audio/thread/ags_export_thread.h>
30 #include <ags/audio/thread/ags_audio_thread.h>
31 #include <ags/audio/thread/ags_channel_thread.h>
32 
33 #include <ags/i18n.h>
34 
35 void ags_audio_loop_class_init(AgsAudioLoopClass *audio_loop);
36 void ags_audio_loop_connectable_interface_init(AgsConnectableInterface *connectable);
37 void ags_audio_loop_main_loop_interface_init(AgsMainLoopInterface *main_loop);
38 void ags_audio_loop_init(AgsAudioLoop *audio_loop);
39 void ags_audio_loop_set_property(GObject *gobject,
40 				 guint prop_id,
41 				 const GValue *value,
42 				 GParamSpec *param_spec);
43 void ags_audio_loop_get_property(GObject *gobject,
44 				 guint prop_id,
45 				 GValue *value,
46 				 GParamSpec *param_spec);
47 void ags_audio_loop_dispose(GObject *gobject);
48 void ags_audio_loop_finalize(GObject *gobject);
49 
50 GRecMutex* ags_audio_loop_get_tree_lock(AgsMainLoop *main_loop);
51 void ags_audio_loop_set_syncing(AgsMainLoop *main_loop, gboolean is_syncing);
52 gboolean ags_audio_loop_is_syncing(AgsMainLoop *main_loop);
53 void ags_audio_loop_set_critical_region(AgsMainLoop *main_loop, gboolean is_critical_region);
54 gboolean ags_audio_loop_is_critical_region(AgsMainLoop *main_loop);
55 void ags_audio_loop_inc_queued_critical_region(AgsMainLoop *main_loop);
56 void ags_audio_loop_dec_queued_critical_region(AgsMainLoop *main_loop);
57 guint ags_audio_loop_test_queued_critical_region(AgsMainLoop *main_loop);
58 void ags_audio_loop_change_frequency(AgsMainLoop *main_loop,
59 				     gdouble frequency);
60 
61 void ags_audio_loop_start(AgsThread *thread);
62 void ags_audio_loop_run(AgsThread *thread);
63 
64 void ags_audio_loop_play_channel(AgsAudioLoop *audio_loop);
65 void ags_audio_loop_play_channel_super_threaded(AgsAudioLoop *audio_loop,
66 						AgsPlayback *playback);
67 void ags_audio_loop_sync_channel_super_threaded(AgsAudioLoop *audio_loop,
68 						AgsPlayback *playback);
69 void ags_audio_loop_play_audio(AgsAudioLoop *audio_loop);
70 void ags_audio_loop_play_audio_super_threaded(AgsAudioLoop *audio_loop,
71 					      AgsPlaybackDomain *playback_domain);
72 void ags_audio_loop_sync_audio_super_threaded(AgsAudioLoop *audio_loop,
73 					      AgsPlaybackDomain *playback_domain);
74 
75 /**
76  * SECTION:ags_audio_loop
77  * @short_description: audio loop
78  * @title: AgsAudioLoop
79  * @section_id:
80  * @include: ags/audio/thread/ags_audio_loop.h
81  *
82  * The #AgsAudioLoop is suitable as #AgsMainLoop and does
83  * audio processing.
84  */
85 
86 enum{
87   PROP_0,
88   PROP_PLAY_CHANNEL,
89   PROP_PLAY_AUDIO,
90 };
91 
92 static gpointer ags_audio_loop_parent_class = NULL;
93 static AgsConnectableInterface *ags_audio_loop_parent_connectable_interface;
94 
95 GType
ags_audio_loop_get_type()96 ags_audio_loop_get_type()
97 {
98   static volatile gsize g_define_type_id__volatile = 0;
99 
100   if(g_once_init_enter (&g_define_type_id__volatile)){
101     GType ags_type_audio_loop = 0;
102 
103     static const GTypeInfo ags_audio_loop_info = {
104       sizeof (AgsAudioLoopClass),
105       NULL, /* base_init */
106       NULL, /* base_finalize */
107       (GClassInitFunc) ags_audio_loop_class_init,
108       NULL, /* class_finalize */
109       NULL, /* class_data */
110       sizeof (AgsAudioLoop),
111       0,    /* n_preallocs */
112       (GInstanceInitFunc) ags_audio_loop_init,
113     };
114 
115     static const GInterfaceInfo ags_connectable_interface_info = {
116       (GInterfaceInitFunc) ags_audio_loop_connectable_interface_init,
117       NULL, /* interface_finalize */
118       NULL, /* interface_data */
119     };
120 
121     static const GInterfaceInfo ags_main_loop_interface_info = {
122       (GInterfaceInitFunc) ags_audio_loop_main_loop_interface_init,
123       NULL, /* interface_finalize */
124       NULL, /* interface_data */
125     };
126 
127     ags_type_audio_loop = g_type_register_static(AGS_TYPE_THREAD,
128 						 "AgsAudioLoop",
129 						 &ags_audio_loop_info,
130 						 0);
131 
132     g_type_add_interface_static(ags_type_audio_loop,
133 				AGS_TYPE_CONNECTABLE,
134 				&ags_connectable_interface_info);
135 
136     g_type_add_interface_static(ags_type_audio_loop,
137 				AGS_TYPE_MAIN_LOOP,
138 				&ags_main_loop_interface_info);
139 
140     g_once_init_leave(&g_define_type_id__volatile, ags_type_audio_loop);
141   }
142 
143   return g_define_type_id__volatile;
144 }
145 
146 void
ags_audio_loop_class_init(AgsAudioLoopClass * audio_loop)147 ags_audio_loop_class_init(AgsAudioLoopClass *audio_loop)
148 {
149   GObjectClass *gobject;
150   AgsThreadClass *thread;
151   GParamSpec *param_spec;
152 
153   ags_audio_loop_parent_class = g_type_class_peek_parent(audio_loop);
154 
155   /* GObject */
156   gobject = (GObjectClass *) audio_loop;
157 
158   gobject->set_property = ags_audio_loop_set_property;
159   gobject->get_property = ags_audio_loop_get_property;
160 
161   gobject->dispose = ags_audio_loop_dispose;
162   gobject->finalize = ags_audio_loop_finalize;
163 
164   /* properties */
165   /**
166    * AgsAudioLoop:play-channel: (type GList(AgsPlayback)) (transfer full)
167    *
168    * An #AgsChannel to add for playback.
169    *
170    * Since: 3.0.0
171    */
172   param_spec = g_param_spec_pointer("play-channel",
173 				    i18n_pspec("channel to run"),
174 				    i18n_pspec("A channel to run"),
175 				    G_PARAM_WRITABLE);
176   g_object_class_install_property(gobject,
177 				  PROP_PLAY_CHANNEL,
178 				  param_spec);
179 
180   /**
181    * AgsAudioLoop:play-audio: (type GList(AgsPlaybackDomain)) (transfer full)
182    *
183    * An #AgsAudio to add for playback.
184    *
185    * Since: 3.0.0
186    */
187   param_spec = g_param_spec_pointer("play-audio",
188 				    i18n_pspec("audio to run"),
189 				    i18n_pspec("A audio to run"),
190 				    G_PARAM_WRITABLE);
191   g_object_class_install_property(gobject,
192 				  PROP_PLAY_AUDIO,
193 				  param_spec);
194 
195   /* AgsThread */
196   thread = (AgsThreadClass *) audio_loop;
197 
198   thread->start = ags_audio_loop_start;
199   thread->run = ags_audio_loop_run;
200 
201   /* AgsAudioLoop */
202 }
203 
204 void
ags_audio_loop_connectable_interface_init(AgsConnectableInterface * connectable)205 ags_audio_loop_connectable_interface_init(AgsConnectableInterface *connectable)
206 {
207   ags_audio_loop_parent_connectable_interface = g_type_interface_peek_parent(connectable);
208 }
209 
210 void
ags_audio_loop_main_loop_interface_init(AgsMainLoopInterface * main_loop)211 ags_audio_loop_main_loop_interface_init(AgsMainLoopInterface *main_loop)
212 {
213   main_loop->get_tree_lock = ags_audio_loop_get_tree_lock;
214 
215   main_loop->set_syncing = ags_audio_loop_set_syncing;
216   main_loop->is_syncing = ags_audio_loop_is_syncing;
217 
218   main_loop->set_critical_region = ags_audio_loop_set_critical_region;
219   main_loop->is_critical_region = ags_audio_loop_is_critical_region;
220 
221   main_loop->inc_queued_critical_region = ags_audio_loop_inc_queued_critical_region;
222   main_loop->dec_queued_critical_region = ags_audio_loop_dec_queued_critical_region;
223   main_loop->test_queued_critical_region = ags_audio_loop_test_queued_critical_region;
224 
225   main_loop->change_frequency = ags_audio_loop_change_frequency;
226 }
227 
228 void
ags_audio_loop_init(AgsAudioLoop * audio_loop)229 ags_audio_loop_init(AgsAudioLoop *audio_loop)
230 {
231   AgsThread *thread;
232 
233   AgsConfig *config;
234 
235   gdouble frequency;
236   guint samplerate;
237   guint buffer_size;
238   guint i;
239 
240   thread = (AgsThread *) audio_loop;
241 
242   ags_thread_set_flags(thread, AGS_THREAD_TIME_ACCOUNTING);
243 
244   /* calculate frequency */
245   config = ags_config_get_instance();
246 
247   samplerate = ags_soundcard_helper_config_get_samplerate(config);
248   buffer_size = ags_soundcard_helper_config_get_buffer_size(config);
249 
250   frequency = ((gdouble) samplerate / (gdouble) buffer_size) + AGS_SOUNDCARD_DEFAULT_OVERCLOCK;
251   g_object_set(thread,
252 	       "frequency", frequency,
253 	       NULL);
254 
255   audio_loop->flags = 0;
256 
257   /* tree lock mutex */
258   g_rec_mutex_init(&(audio_loop->tree_lock));
259 
260   ags_main_loop_set_syncing(AGS_MAIN_LOOP(audio_loop), FALSE);
261 
262   ags_main_loop_set_critical_region(AGS_MAIN_LOOP(audio_loop), FALSE);
263   g_atomic_int_set(&(audio_loop->critical_region_ref), 0);
264 
265   /* recall related lists */
266   audio_loop->play_channel_ref = 0;
267   audio_loop->play_channel = NULL;
268 
269   audio_loop->play_audio_ref = 0;
270   audio_loop->play_audio = NULL;
271 
272   audio_loop->sync_thread = NULL;
273 
274   /* staging program */
275   audio_loop->do_fx_staging = FALSE;
276 
277   audio_loop->staging_program = g_malloc(3 * sizeof(guint));
278 
279   audio_loop->staging_program[0] = (AGS_SOUND_STAGING_FEED_INPUT_QUEUE |
280 				    AGS_SOUND_STAGING_AUTOMATE |
281 				    AGS_SOUND_STAGING_RUN_PRE);
282   audio_loop->staging_program[1] = (AGS_SOUND_STAGING_RUN_INTER);
283   audio_loop->staging_program[2] = (AGS_SOUND_STAGING_RUN_POST);
284 
285   audio_loop->staging_program_count = 3;
286 }
287 
288 void
ags_audio_loop_set_property(GObject * gobject,guint prop_id,const GValue * value,GParamSpec * param_spec)289 ags_audio_loop_set_property(GObject *gobject,
290 			    guint prop_id,
291 			    const GValue *value,
292 			    GParamSpec *param_spec)
293 {
294   AgsAudioLoop *audio_loop;
295 
296   GRecMutex *thread_mutex;
297 
298   audio_loop = AGS_AUDIO_LOOP(gobject);
299 
300   /* get thread mutex */
301   thread_mutex = AGS_THREAD_GET_OBJ_MUTEX(audio_loop);
302 
303   switch(prop_id){
304   case PROP_PLAY_CHANNEL:
305     {
306       AgsPlayback *playback;
307 
308       playback = (AgsPlayback *) g_value_get_pointer(value);
309 
310       g_rec_mutex_lock(thread_mutex);
311 
312       if(AGS_IS_PLAYBACK(playback) &&
313 	 g_list_find(audio_loop->play_channel, playback) == NULL){
314 	g_object_ref(playback);
315 	audio_loop->play_channel = g_list_prepend(audio_loop->play_channel,
316 						  playback);
317 	audio_loop->play_channel_ref = audio_loop->play_channel_ref + 1;
318       }
319 
320       g_rec_mutex_unlock(thread_mutex);
321     }
322     break;
323   case PROP_PLAY_AUDIO:
324     {
325       AgsPlaybackDomain *playback_domain;
326 
327       playback_domain = (AgsPlaybackDomain *) g_value_get_pointer(value);
328 
329       g_rec_mutex_lock(thread_mutex);
330 
331       if(AGS_IS_PLAYBACK_DOMAIN(playback_domain) &&
332 	 g_list_find(audio_loop->play_audio, playback_domain) == NULL){
333 	audio_loop->play_audio = g_list_prepend(audio_loop->play_audio,
334 						playback_domain);
335 	g_object_ref(playback_domain);
336 	audio_loop->play_audio_ref = audio_loop->play_audio_ref + 1;
337       }
338 
339       g_rec_mutex_unlock(thread_mutex);
340     }
341     break;
342   default:
343     G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, param_spec);
344     break;
345   }
346 }
347 
348 void
ags_audio_loop_get_property(GObject * gobject,guint prop_id,GValue * value,GParamSpec * param_spec)349 ags_audio_loop_get_property(GObject *gobject,
350 			    guint prop_id,
351 			    GValue *value,
352 			    GParamSpec *param_spec)
353 {
354   AgsAudioLoop *audio_loop;
355 
356   GRecMutex *thread_mutex;
357 
358   audio_loop = AGS_AUDIO_LOOP(gobject);
359 
360   /* get thread mutex */
361   thread_mutex = AGS_THREAD_GET_OBJ_MUTEX(audio_loop);
362 
363   switch(prop_id){
364   case PROP_PLAY_CHANNEL:
365     {
366       g_rec_mutex_lock(thread_mutex);
367 
368       g_value_set_pointer(value, g_list_copy_deep(audio_loop->play_channel,
369 						  (GCopyFunc) g_object_ref,
370 						  NULL));
371 
372       g_rec_mutex_unlock(thread_mutex);
373     }
374     break;
375   case PROP_PLAY_AUDIO:
376     {
377       g_rec_mutex_lock(thread_mutex);
378 
379       g_value_set_pointer(value, g_list_copy_deep(audio_loop->play_audio,
380 						  (GCopyFunc) g_object_ref,
381 						  NULL));
382 
383       g_rec_mutex_unlock(thread_mutex);
384     }
385     break;
386   default:
387     G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, param_spec);
388     break;
389   }
390 }
391 
392 void
ags_audio_loop_dispose(GObject * gobject)393 ags_audio_loop_dispose(GObject *gobject)
394 {
395   AgsAudioLoop *audio_loop;
396 
397   audio_loop = AGS_AUDIO_LOOP(gobject);
398 
399   /* unref AgsPlayback lists */
400   if(audio_loop->play_channel != NULL){
401     g_list_free_full(audio_loop->play_channel,
402 		     g_object_unref);
403 
404     audio_loop->play_channel = NULL;
405   }
406 
407   if(audio_loop->play_audio != NULL){
408     g_list_free_full(audio_loop->play_audio,
409 		     g_object_unref);
410 
411     audio_loop->play_audio = NULL;
412   }
413 
414   /* call parent */
415   G_OBJECT_CLASS(ags_audio_loop_parent_class)->dispose(gobject);
416 }
417 
418 void
ags_audio_loop_finalize(GObject * gobject)419 ags_audio_loop_finalize(GObject *gobject)
420 {
421   AgsAudioLoop *audio_loop;
422 
423   audio_loop = AGS_AUDIO_LOOP(gobject);
424 
425   /* unref AgsPlayback lists */
426   g_list_free_full(audio_loop->play_channel,
427 		   g_object_unref);
428 
429   g_list_free_full(audio_loop->play_audio,
430 		   g_object_unref);
431 
432   /* call parent */
433   G_OBJECT_CLASS(ags_audio_loop_parent_class)->finalize(gobject);
434 }
435 
436 GRecMutex*
ags_audio_loop_get_tree_lock(AgsMainLoop * main_loop)437 ags_audio_loop_get_tree_lock(AgsMainLoop *main_loop)
438 {
439   GRecMutex *tree_lock;
440 
441   /* get tree lock mutex */
442   tree_lock = &(AGS_AUDIO_LOOP(main_loop)->tree_lock);
443 
444   return(tree_lock);
445 }
446 
447 void
ags_audio_loop_set_syncing(AgsMainLoop * main_loop,gboolean is_syncing)448 ags_audio_loop_set_syncing(AgsMainLoop *main_loop, gboolean is_syncing)
449 {
450   AgsAudioLoop *audio_loop;
451 
452   audio_loop = AGS_AUDIO_LOOP(main_loop);
453 
454   /* set syncing */
455   g_atomic_int_set(&(audio_loop->is_syncing), is_syncing);
456 }
457 
458 gboolean
ags_audio_loop_is_syncing(AgsMainLoop * main_loop)459 ags_audio_loop_is_syncing(AgsMainLoop *main_loop)
460 {
461   AgsAudioLoop *audio_loop;
462 
463   gboolean is_syncing;
464 
465   audio_loop = AGS_AUDIO_LOOP(main_loop);
466 
467   /* is syncing */
468   is_syncing = g_atomic_int_get(&(audio_loop->is_syncing));
469 
470   return(is_syncing);
471 }
472 
473 void
ags_audio_loop_set_critical_region(AgsMainLoop * main_loop,gboolean is_critical_region)474 ags_audio_loop_set_critical_region(AgsMainLoop *main_loop, gboolean is_critical_region)
475 {
476   AgsAudioLoop *audio_loop;
477 
478   audio_loop = AGS_AUDIO_LOOP(main_loop);
479 
480   /* set critical region */
481   g_atomic_int_set(&(audio_loop->is_critical_region), is_critical_region);
482 }
483 
484 gboolean
ags_audio_loop_is_critical_region(AgsMainLoop * main_loop)485 ags_audio_loop_is_critical_region(AgsMainLoop *main_loop)
486 {
487   AgsAudioLoop *audio_loop;
488 
489   gboolean is_critical_region;
490 
491   audio_loop = AGS_AUDIO_LOOP(main_loop);
492 
493   /* is critical region */
494   is_critical_region = g_atomic_int_get(&(audio_loop->is_critical_region));
495 
496   return(is_critical_region);
497 }
498 
499 void
ags_audio_loop_inc_queued_critical_region(AgsMainLoop * main_loop)500 ags_audio_loop_inc_queued_critical_region(AgsMainLoop *main_loop)
501 {
502   AgsAudioLoop *audio_loop;
503 
504   audio_loop = AGS_AUDIO_LOOP(main_loop);
505 
506   /* increment critical region */
507   g_atomic_int_inc(&(audio_loop->critical_region_ref));
508 }
509 
510 void
ags_audio_loop_dec_queued_critical_region(AgsMainLoop * main_loop)511 ags_audio_loop_dec_queued_critical_region(AgsMainLoop *main_loop)
512 {
513   AgsAudioLoop *audio_loop;
514 
515   audio_loop = AGS_AUDIO_LOOP(main_loop);
516 
517   /* decrement critical region */
518   g_atomic_int_dec_and_test(&(audio_loop->critical_region_ref));
519 }
520 
521 guint
ags_audio_loop_test_queued_critical_region(AgsMainLoop * main_loop)522 ags_audio_loop_test_queued_critical_region(AgsMainLoop *main_loop)
523 {
524   AgsAudioLoop *audio_loop;
525 
526   guint critical_region_ref;
527 
528   audio_loop = AGS_AUDIO_LOOP(main_loop);
529 
530   /* set critical region */
531   critical_region_ref = g_atomic_int_get(&(audio_loop->is_critical_region));
532 
533   return(critical_region_ref);
534 }
535 
536 void
ags_audio_loop_change_frequency(AgsMainLoop * main_loop,gdouble frequency)537 ags_audio_loop_change_frequency(AgsMainLoop *main_loop,
538 				gdouble frequency)
539 {
540   AgsThread *audio_loop;
541   AgsThread *thread, *next_thread;
542 
543   audio_loop = AGS_THREAD(main_loop);
544 
545   g_object_set(audio_loop,
546 	       "frequency", frequency,
547 	       NULL);
548 
549   /* reset soundcard thread */
550   thread = ags_thread_find_type(audio_loop, AGS_TYPE_SOUNDCARD_THREAD);
551 
552   while(thread != NULL){
553     if(AGS_IS_SOUNDCARD_THREAD(thread)){
554       g_object_set(thread,
555 		   "frequency", frequency,
556 		   NULL);
557     }
558 
559     /* iterate */
560     next_thread = ags_thread_next(thread);
561 
562     g_object_unref(thread);
563 
564     thread = next_thread;
565   }
566 
567   /* reset sequencer thread */
568   thread = ags_thread_find_type(audio_loop, AGS_TYPE_SEQUENCER_THREAD);
569 
570   while(thread != NULL){
571     if(AGS_IS_SEQUENCER_THREAD(thread)){
572       g_object_set(thread,
573 		   "frequency", frequency,
574 		   NULL);
575     }
576 
577     /* iterate */
578     next_thread = ags_thread_next(thread);
579 
580     g_object_unref(thread);
581 
582     thread = next_thread;
583   }
584 
585   /* reset export thread */
586   thread = ags_thread_find_type(audio_loop, AGS_TYPE_EXPORT_THREAD);
587 
588   while(thread != NULL){
589     if(AGS_IS_EXPORT_THREAD(thread)){
590       g_object_set(thread,
591 		   "frequency", frequency,
592 		   NULL);
593     }
594 
595     /* iterate */
596     next_thread = ags_thread_next(thread);
597 
598     g_object_unref(thread);
599 
600     thread = next_thread;
601   }
602 
603   /* reset audio thread */
604   thread = ags_thread_find_type(audio_loop, AGS_TYPE_AUDIO_THREAD);
605 
606   while(thread != NULL){
607     if(AGS_IS_AUDIO_THREAD(thread)){
608       AgsThread *child, *next_child;
609 
610       g_object_set(thread,
611 		   "frequency", frequency,
612 		   NULL);
613 
614       /* reset channel thread */
615       child = ags_thread_find_type(audio_loop, AGS_TYPE_CHANNEL_THREAD);
616 
617       while(child != NULL){
618 	g_object_set(child,
619 		     "frequency", frequency,
620 		     NULL);
621 
622 	/* iterate */
623 	next_child = ags_thread_next(child);
624 
625 	g_object_unref(child);
626 
627 	child = next_child;
628       }
629     }
630 
631     /* iterate */
632     next_thread = ags_thread_next(thread);
633 
634     g_object_unref(thread);
635 
636     thread = next_thread;
637   }
638 }
639 
640 void
ags_audio_loop_start(AgsThread * thread)641 ags_audio_loop_start(AgsThread *thread)
642 {
643   AgsAudioLoop *audio_loop;
644 
645   GRecMutex *thread_mutex;
646 
647   audio_loop = AGS_AUDIO_LOOP(thread);
648 
649   /* set status synced */
650   ags_thread_set_status_flags(thread, AGS_THREAD_STATUS_SYNCED);
651 
652   /* call parent */
653   AGS_THREAD_CLASS(ags_audio_loop_parent_class)->start(thread);
654 }
655 
656 void
ags_audio_loop_run(AgsThread * thread)657 ags_audio_loop_run(AgsThread *thread)
658 {
659   AgsAudioLoop *audio_loop;
660 
661   GList *start_queue;
662 
663   guint play_audio_ref, play_channel_ref;
664 
665   GRecMutex *thread_mutex;
666 
667 //  g_message("do: audio loop %f", thread->tic_delay);
668 
669   audio_loop = AGS_AUDIO_LOOP(thread);
670 
671   thread_mutex = AGS_THREAD_GET_OBJ_MUTEX(thread);
672 
673   /* real-time setup */
674 #ifdef AGS_WITH_RT
675   if(!ags_thread_test_status_flags(thread, AGS_THREAD_STATUS_RT_SETUP)){
676     AgsPriority *priority;
677 
678     struct sched_param param;
679 
680     gchar *str;
681 
682     priority = ags_priority_get_instance();
683 
684     /* Declare ourself as a real time task */
685     param.sched_priority = 45;
686 
687     str = ags_priority_get_value(priority,
688 				 AGS_PRIORITY_RT_THREAD,
689 				 AGS_PRIORITY_KEY_AUDIO);
690 
691     if(str != NULL){
692       param.sched_priority = (int) g_ascii_strtoull(str,
693 						    NULL,
694 						    10);
695     }
696 
697     if(str == NULL ||
698        ((!g_ascii_strncasecmp(str,
699 			      "0",
700 			      2)) != TRUE)){
701       if(sched_setscheduler(0, SCHED_FIFO, &param) == -1) {
702 	perror("sched_setscheduler failed");
703       }
704     }
705 
706     g_free(str);
707 
708     ags_thread_set_status_flags(thread, AGS_THREAD_STATUS_RT_SETUP);
709   }
710 #endif
711 
712   /* get some fields */
713   g_rec_mutex_lock(thread_mutex);
714 
715   start_queue = thread->start_queue;
716 
717   play_audio_ref = audio_loop->play_audio_ref;
718   play_channel_ref = audio_loop->play_channel_ref;
719 
720   g_rec_mutex_unlock(thread_mutex);
721 
722   /* play channel */
723   if(ags_audio_loop_test_flags(audio_loop, AGS_AUDIO_LOOP_PLAY_CHANNEL)){
724     ags_audio_loop_play_channel(audio_loop);
725 
726     if(play_channel_ref == 0 &&
727        start_queue == NULL){
728       ags_audio_loop_unset_flags(audio_loop, AGS_AUDIO_LOOP_PLAY_CHANNEL);
729     }
730   }
731 
732   /* play audio */
733   if(ags_audio_loop_test_flags(audio_loop, AGS_AUDIO_LOOP_PLAY_AUDIO)){
734     ags_audio_loop_play_audio(audio_loop);
735 
736     if(play_audio_ref == 0 &&
737        start_queue == NULL){
738       ags_audio_loop_unset_flags(audio_loop, AGS_AUDIO_LOOP_PLAY_AUDIO);
739     }
740   }
741 
742   /* decide if we stop */
743   if(play_channel_ref == 0 &&
744      play_audio_ref == 0){
745     AgsThread *soundcard_thread, *next_soundcard_thread;
746     AgsThread *sequencer_thread, *next_sequencer_thread;
747     AgsThread *export_thread, *next_export_thread;
748 
749     gdouble frequency;
750 
751     /* soundcard thread */
752     soundcard_thread = ags_thread_find_type(thread,
753 					    AGS_TYPE_SOUNDCARD_THREAD);
754 
755     while(soundcard_thread != NULL){
756       if(AGS_IS_SOUNDCARD_THREAD(soundcard_thread)){
757 	if(ags_thread_test_status_flags(soundcard_thread, AGS_THREAD_STATUS_RUNNING)){
758 	  ags_thread_stop(soundcard_thread);
759 	}
760       }
761 
762       /* iterate */
763       next_soundcard_thread = ags_thread_next(soundcard_thread);
764 
765       g_object_unref(soundcard_thread);
766 
767       soundcard_thread = next_soundcard_thread;
768     }
769 
770     /* sequencer thread */
771     sequencer_thread = ags_thread_find_type(thread,
772 					    AGS_TYPE_SEQUENCER_THREAD);
773 
774     while(sequencer_thread != NULL){
775       if(AGS_IS_SEQUENCER_THREAD(sequencer_thread)){
776 	if(ags_thread_test_status_flags(sequencer_thread, AGS_THREAD_STATUS_RUNNING)){
777 	  ags_thread_stop(sequencer_thread);
778 	}
779       }
780 
781       /* iterate */
782       next_sequencer_thread = ags_thread_next(sequencer_thread);
783 
784       g_object_unref(sequencer_thread);
785 
786       sequencer_thread = next_sequencer_thread;
787     }
788 
789     /* export thread */
790     export_thread = ags_thread_find_type(thread,
791 					 AGS_TYPE_EXPORT_THREAD);
792 
793     while(export_thread != NULL){
794       if(AGS_IS_EXPORT_THREAD(export_thread)){
795 	if(ags_thread_test_status_flags(export_thread, AGS_THREAD_STATUS_RUNNING)){
796 	  ags_thread_stop(export_thread);
797 	}
798       }
799 
800       /* iterate */
801       next_export_thread = ags_thread_next(export_thread);
802 
803       g_object_unref(export_thread);
804 
805       export_thread = next_export_thread;
806     }
807 
808     g_object_get(audio_loop,
809 		 "frequency", &frequency,
810 		 NULL);
811 
812     if(!ags_thread_test_flags(audio_loop, AGS_THREAD_TIME_ACCOUNTING)){
813       g_usleep((guint) (G_USEC_PER_SEC / frequency) - 4);
814     }
815   }
816 }
817 
818 /**
819  * ags_audio_loop_play_channel:
820  * @audio_loop: an #AgsAudioLoop
821  *
822  * Invokes ags_channel_recursive_run_stage() for all scopes containing #AgsRecallID.
823  *
824  * Since: 3.0.0
825  */
826 void
ags_audio_loop_play_channel(AgsAudioLoop * audio_loop)827 ags_audio_loop_play_channel(AgsAudioLoop *audio_loop)
828 {
829   AgsPlayback *playback;
830   AgsChannel *channel;
831 
832   GList *start_play_channel, *play_channel;
833   GList *recall_id;
834 
835   gint sound_scope;
836 
837   GRecMutex *thread_mutex;
838 
839   thread_mutex = AGS_THREAD_GET_OBJ_MUTEX(audio_loop);
840 
841   /* get play channel */
842   g_rec_mutex_lock(thread_mutex);
843 
844   start_play_channel = g_list_copy_deep(audio_loop->play_channel,
845 					(GCopyFunc) g_object_ref,
846 					NULL);
847 
848   g_rec_mutex_unlock(thread_mutex);
849 
850   if(start_play_channel == NULL){
851     if(ags_audio_loop_test_flags(audio_loop, AGS_AUDIO_LOOP_PLAY_CHANNEL_TERMINATING)){
852       ags_audio_loop_unset_flags(audio_loop, (AGS_AUDIO_LOOP_PLAY_CHANNEL |
853 					      AGS_AUDIO_LOOP_PLAY_CHANNEL_TERMINATING));
854     }else{
855       ags_audio_loop_set_flags(audio_loop, AGS_AUDIO_LOOP_PLAY_CHANNEL_TERMINATING);
856     }
857   }
858 
859   //FIXME:JK: missing else
860   ags_audio_loop_unset_flags(audio_loop, AGS_AUDIO_LOOP_PLAY_CHANNEL_TERMINATING);
861   ags_audio_loop_set_flags(audio_loop, AGS_AUDIO_LOOP_PLAYING_CHANNEL);
862 
863   /* run the 3 stages */
864   play_channel = start_play_channel;
865 
866   while(play_channel != NULL){
867     playback = (AgsPlayback *) play_channel->data;
868 
869     channel = NULL;
870     g_object_get(playback,
871 		 "channel", &channel,
872 		 NULL);
873 
874     /* play */
875 #if 0
876     if(ags_playback_test_flags(playback, AGS_PLAYBACK_SUPER_THREADED_CHANNEL)){
877       /* super threaded */
878       ags_audio_loop_play_channel_super_threaded(audio_loop,
879 						 playback);
880     }else{
881 #endif
882       /* not super threaded */
883       sound_scope = AGS_SOUND_SCOPE_PLAYBACK;
884 
885       if(ags_playback_get_recall_id(playback, sound_scope) == NULL){
886 	goto ags_audio_loop_play_channel_NO_PLAYBACK;
887       }
888 
889       if((recall_id = ags_channel_check_scope(channel, sound_scope)) != NULL){
890 	guint *staging_program;
891 
892 	guint staging_program_count;
893 	guint nth;
894 
895 	staging_program = ags_audio_loop_get_staging_program(audio_loop,
896 							     &staging_program_count);
897 
898 	for(nth = 0; nth < staging_program_count; nth++){
899 	  ags_channel_recursive_run_stage(channel,
900 					  sound_scope, staging_program[nth]);
901 	}
902 
903 	g_free(staging_program);
904 
905 	g_list_free_full(recall_id,
906 			 g_object_unref);
907       }
908 #if 0
909     }
910 #endif
911   ags_audio_loop_play_channel_NO_PLAYBACK:
912 
913     if(channel != NULL){
914       g_object_unref(channel);
915     }
916 
917     /* iterate */
918     play_channel = play_channel->next;
919   }
920 
921   /* sync channel */
922 #if 0
923   play_channel = start_play_channel;
924 
925   while(play_channel != NULL){
926     playback = (AgsPlayback *) play_channel->data;
927 
928     /* sync */
929     if(ags_playback_test_flags(playback, AGS_PLAYBACK_SUPER_THREADED_CHANNEL)){
930       /* super threaded */
931       ags_audio_loop_sync_channel_super_threaded(audio_loop,
932 						 playback);
933     }
934 
935     /* iterate */
936     play_channel = play_channel->next;
937   }
938 #endif
939 
940   g_list_free_full(start_play_channel,
941 		   g_object_unref);
942 }
943 
944 void
ags_audio_loop_play_channel_super_threaded(AgsAudioLoop * audio_loop,AgsPlayback * playback)945 ags_audio_loop_play_channel_super_threaded(AgsAudioLoop *audio_loop,
946 					   AgsPlayback *playback)
947 {
948   AgsChannel *channel;
949   AgsChannelThread *channel_thread;
950 
951   AgsThread *thread;
952 
953   GList *recall_id;
954 
955   gint sound_scope;
956 
957   channel = NULL;
958   g_object_get(playback,
959 	       "channel", &channel,
960 	       NULL);
961 
962   sound_scope = AGS_SOUND_SCOPE_PLAYBACK;
963 
964   if((recall_id = ags_channel_check_scope(channel, sound_scope)) != NULL){
965     thread = ags_playback_get_channel_thread(playback,
966 					     sound_scope);
967 
968     if(thread != NULL){
969       channel_thread = (AgsChannelThread *) thread;
970 
971       if(ags_thread_test_status_flags(thread, AGS_THREAD_STATUS_RUNNING) &&
972 	 ags_thread_test_status_flags(thread, AGS_THREAD_STATUS_SYNCED)){
973 	/* wakeup wait */
974 	g_mutex_lock(&(channel_thread->wakeup_mutex));
975 
976 	ags_channel_thread_unset_status_flags(channel_thread,
977 					      AGS_CHANNEL_THREAD_STATUS_WAIT);
978 
979 	if(!ags_channel_thread_test_status_flags(channel_thread, AGS_CHANNEL_THREAD_STATUS_DONE)){
980 	  g_cond_signal(&(channel_thread->wakeup_cond));
981 	}
982 
983 	g_mutex_unlock(&(channel_thread->wakeup_mutex));
984 
985 	audio_loop->sync_thread = g_list_prepend(audio_loop->sync_thread,
986 						 channel_thread);
987       }
988     }
989 
990     g_list_free_full(recall_id,
991 		     g_object_unref);
992   }
993 
994   if(channel != NULL){
995     g_object_unref(channel);
996   }
997 }
998 
999 void
ags_audio_loop_sync_channel_super_threaded(AgsAudioLoop * audio_loop,AgsPlayback * playback)1000 ags_audio_loop_sync_channel_super_threaded(AgsAudioLoop *audio_loop,
1001 					   AgsPlayback *playback)
1002 {
1003   AgsChannel *channel;
1004   AgsChannelThread *channel_thread;
1005 
1006   AgsThread *thread;
1007 
1008   GList *recall_id;
1009 
1010   gint sound_scope;
1011 
1012   channel = NULL;
1013   g_object_get(playback,
1014 	       "channel", &channel,
1015 	       NULL);
1016 
1017   sound_scope = AGS_SOUND_SCOPE_PLAYBACK;
1018 
1019   if((recall_id = ags_channel_check_scope(channel, sound_scope)) != NULL){
1020     thread = ags_playback_get_channel_thread(playback,
1021 					     sound_scope);
1022 
1023     if(thread != NULL){
1024       channel_thread = (AgsChannelThread *) thread;
1025 
1026       g_mutex_lock(&(channel_thread->done_mutex));
1027 
1028       if(g_list_find(audio_loop->sync_thread, channel_thread) != NULL &&
1029 	 ags_thread_test_status_flags(channel_thread, AGS_THREAD_STATUS_RUNNING) &&
1030 	 ags_thread_test_status_flags(channel_thread, AGS_THREAD_STATUS_SYNCED)){
1031 	if(ags_channel_thread_test_status_flags(channel_thread, AGS_CHANNEL_THREAD_STATUS_WAIT_SYNC)){
1032 	  ags_channel_thread_unset_status_flags(channel_thread, AGS_CHANNEL_THREAD_STATUS_DONE_SYNC);
1033 
1034 	  while(!ags_channel_thread_test_status_flags(channel_thread, AGS_CHANNEL_THREAD_STATUS_DONE_SYNC) &&
1035 		ags_channel_thread_test_status_flags(channel_thread, AGS_CHANNEL_THREAD_STATUS_WAIT_SYNC)){
1036 	    g_cond_wait(&(channel_thread->done_cond),
1037 			&(channel_thread->done_mutex));
1038 	  }
1039 	}
1040       }
1041 
1042       ags_channel_thread_set_status_flags(channel_thread, (AGS_CHANNEL_THREAD_STATUS_WAIT_SYNC |
1043 							   AGS_CHANNEL_THREAD_STATUS_DONE_SYNC));
1044 
1045       g_mutex_unlock(&(channel_thread->done_mutex));
1046     }
1047 
1048     g_list_free_full(recall_id,
1049 		     g_object_unref);
1050   }
1051 
1052   if(channel != NULL){
1053     g_object_unref(channel);
1054   }
1055 }
1056 
1057 /**
1058  * ags_audio_loop_play_audio:
1059  * @audio_loop: an #AgsAudioLoop
1060  *
1061  * Invokes ags_audio_recursive_run_stage() for all scopes containing #AgsRecallID.
1062  *
1063  * Since: 3.0.0
1064  */
1065 void
ags_audio_loop_play_audio(AgsAudioLoop * audio_loop)1066 ags_audio_loop_play_audio(AgsAudioLoop *audio_loop)
1067 {
1068   AgsPlaybackDomain *playback_domain;
1069   AgsAudio *audio;
1070 
1071   GList *start_play_audio, *play_audio;
1072   GList *recall_id;
1073 
1074   gint sound_scope;
1075 
1076   GRecMutex *thread_mutex;
1077 
1078   thread_mutex = AGS_THREAD_GET_OBJ_MUTEX(audio_loop);
1079 
1080   /* get play audio */
1081   g_rec_mutex_lock(thread_mutex);
1082 
1083   start_play_audio = g_list_copy_deep(audio_loop->play_audio,
1084 				      (GCopyFunc) g_object_ref,
1085 				      NULL);
1086 
1087   g_rec_mutex_unlock(thread_mutex);
1088 
1089   if(start_play_audio == NULL){
1090     if(ags_audio_loop_test_flags(audio_loop, AGS_AUDIO_LOOP_PLAY_AUDIO_TERMINATING)){
1091       ags_audio_loop_unset_flags(audio_loop, (AGS_AUDIO_LOOP_PLAY_AUDIO |
1092 					      AGS_AUDIO_LOOP_PLAY_AUDIO_TERMINATING));
1093     }else{
1094       ags_audio_loop_set_flags(audio_loop, AGS_AUDIO_LOOP_PLAY_AUDIO_TERMINATING);
1095     }
1096   }
1097 
1098   //FIXME:JK: missing else
1099   ags_audio_loop_unset_flags(audio_loop, AGS_AUDIO_LOOP_PLAY_AUDIO_TERMINATING);
1100   ags_audio_loop_set_flags(audio_loop, AGS_AUDIO_LOOP_PLAYING_AUDIO);
1101 
1102   /* playing */
1103   play_audio = start_play_audio;
1104 
1105   while(play_audio != NULL){
1106     AgsChannel *start_output;
1107     AgsChannel *output, *next;
1108 
1109     playback_domain = (AgsPlaybackDomain *) play_audio->data;
1110 
1111     audio = NULL;
1112 
1113     g_object_get(playback_domain,
1114 		 "audio", &audio,
1115 		 NULL);
1116 
1117     /* play */
1118     start_output = NULL;
1119 
1120     g_object_get(audio,
1121 		 "output", &start_output,
1122 		 NULL);
1123 
1124 
1125     if(start_output != NULL){
1126       output = start_output;
1127       g_object_ref(output);
1128 
1129       while(output != NULL){
1130 	ags_channel_unset_staging_completed(output, -1);
1131 
1132 	/* iterate */
1133 	next = ags_channel_next(output);
1134 
1135 	g_object_unref(output);
1136 
1137 	output = next;
1138       }
1139     }
1140 
1141     if(ags_playback_domain_test_flags(playback_domain, AGS_PLAYBACK_DOMAIN_SUPER_THREADED_AUDIO)){
1142       /* super threaded */
1143       ags_audio_loop_play_audio_super_threaded(audio_loop,
1144 					       playback_domain);
1145     }else{
1146       /* not super threaded */
1147       for(sound_scope = 0; sound_scope < AGS_SOUND_SCOPE_LAST; sound_scope++){
1148 	if(sound_scope != AGS_SOUND_SCOPE_PLAYBACK){
1149 	  if((recall_id = ags_audio_check_scope(audio, sound_scope)) != NULL){
1150 	    guint *staging_program;
1151 
1152 	    guint staging_program_count;
1153 	    guint nth;
1154 
1155 	    staging_program = ags_audio_loop_get_staging_program(audio_loop,
1156 								 &staging_program_count);
1157 
1158 	    for(nth = 0; nth < staging_program_count; nth++){
1159 	      ags_audio_recursive_run_stage(audio,
1160 					    sound_scope, staging_program[nth]);
1161 	    }
1162 
1163 	    g_free(staging_program);
1164 
1165 	    g_list_free_full(recall_id,
1166 			     g_object_unref);
1167 	  }
1168 	}
1169       }
1170     }
1171 
1172     if(audio != NULL){
1173       g_object_unref(audio);
1174     }
1175 
1176     if(start_output != NULL){
1177       g_object_unref(start_output);
1178     }
1179 
1180     /* iterate */
1181     play_audio = play_audio->next;
1182   }
1183 
1184   /* sync audio */
1185   play_audio = start_play_audio;
1186 
1187   while(play_audio != NULL){
1188     playback_domain = (AgsPlaybackDomain *) play_audio->data;
1189 
1190     /* sync */
1191     if(ags_playback_domain_test_flags(playback_domain, AGS_PLAYBACK_DOMAIN_SUPER_THREADED_AUDIO)){
1192       ags_audio_loop_sync_audio_super_threaded(audio_loop,
1193 					       playback_domain);
1194     }
1195 
1196     /* iterate */
1197     play_audio = play_audio->next;
1198   }
1199 
1200   g_list_free_full(start_play_audio,
1201 		   g_object_unref);
1202 
1203   g_list_free(audio_loop->sync_thread);
1204   audio_loop->sync_thread = NULL;
1205 }
1206 
1207 /**
1208  * ags_audio_loop_play_audio_super_threaded:
1209  * @audio_loop: the #AgsAudioLoop
1210  * @playback_domain: an #AgsPlaybackDomain
1211  *
1212  * Play audio super-threaded.
1213  *
1214  * Since: 3.0.0
1215  */
1216 void
ags_audio_loop_play_audio_super_threaded(AgsAudioLoop * audio_loop,AgsPlaybackDomain * playback_domain)1217 ags_audio_loop_play_audio_super_threaded(AgsAudioLoop *audio_loop, AgsPlaybackDomain *playback_domain)
1218 {
1219   AgsAudio *audio;
1220   AgsAudioThread *audio_thread;
1221 
1222   AgsThread *thread;
1223 
1224   GList *recall_id;
1225 
1226   gint sound_scope;
1227 
1228   audio = NULL;
1229   g_object_get(playback_domain,
1230 	       "audio", &audio,
1231 	       NULL);
1232 
1233   for(sound_scope = 0; sound_scope < AGS_SOUND_SCOPE_LAST; sound_scope++){
1234     if(sound_scope == AGS_SOUND_SCOPE_PLAYBACK){
1235       continue;
1236     }
1237 
1238     if((recall_id = ags_audio_check_scope(audio, sound_scope)) != NULL){
1239       thread = ags_playback_domain_get_audio_thread(playback_domain,
1240 						    sound_scope);
1241 
1242       if(thread != NULL){
1243 	audio_thread = (AgsAudioThread *) thread;
1244 
1245 	if(ags_thread_test_status_flags(audio_thread, AGS_THREAD_STATUS_RUNNING) &&
1246 	   ags_thread_test_status_flags(audio_thread, AGS_THREAD_STATUS_SYNCED)){
1247 	  /* wakeup wait */
1248 	  g_mutex_lock(&(audio_thread->wakeup_mutex));
1249 
1250 	  ags_audio_thread_unset_status_flags(audio_thread, AGS_AUDIO_THREAD_STATUS_WAIT);
1251 
1252 	  if(!ags_audio_thread_test_status_flags(audio_thread, AGS_AUDIO_THREAD_STATUS_DONE)){
1253 	    g_cond_signal(&(audio_thread->wakeup_cond));
1254 	  }
1255 
1256 	  g_mutex_unlock(&(audio_thread->wakeup_mutex));
1257 
1258 	  audio_loop->sync_thread = g_list_prepend(audio_loop->sync_thread,
1259 						   audio_thread);
1260 	}
1261       }
1262 
1263       g_list_free_full(recall_id,
1264 		       g_object_unref);
1265     }
1266   }
1267 
1268   if(audio != NULL){
1269     g_object_unref(audio);
1270   }
1271 }
1272 
1273 /**
1274  * ags_audio_loop_sync_audio_super_threaded:
1275  * @audio_loop: the #AgsAudioLoop
1276  * @playback_domain: an #AgsPlaybackDomain
1277  *
1278  * Sync audio super-threaded.
1279  *
1280  * Since: 3.0.0
1281  */
1282 void
ags_audio_loop_sync_audio_super_threaded(AgsAudioLoop * audio_loop,AgsPlaybackDomain * playback_domain)1283 ags_audio_loop_sync_audio_super_threaded(AgsAudioLoop *audio_loop, AgsPlaybackDomain *playback_domain)
1284 {
1285   AgsAudio *audio;
1286   AgsAudioThread *audio_thread;
1287 
1288   AgsThread *thread;
1289 
1290   GList *recall_id;
1291 
1292   gint sound_scope;
1293 
1294   audio = NULL;
1295   g_object_get(playback_domain,
1296 	       "audio", &audio,
1297 	       NULL);
1298 
1299   for(sound_scope = 0; sound_scope < AGS_SOUND_SCOPE_LAST; sound_scope++){
1300     if(sound_scope == AGS_SOUND_SCOPE_PLAYBACK){
1301       continue;
1302     }
1303 
1304     if((recall_id = ags_audio_check_scope(audio, sound_scope)) != NULL){
1305       thread = ags_playback_domain_get_audio_thread(playback_domain,
1306 						    sound_scope);
1307 
1308       if(thread != NULL){
1309 	audio_thread = (AgsAudioThread *) thread;
1310 
1311 	if(g_list_find(audio_loop->sync_thread, audio_thread) != NULL &&
1312 	   ags_thread_test_status_flags(audio_thread, AGS_THREAD_STATUS_RUNNING) &&
1313 	   ags_thread_test_status_flags(audio_thread, AGS_THREAD_STATUS_SYNCED)){
1314 	  g_mutex_lock(&(audio_thread->done_mutex));
1315 
1316 	  if(ags_audio_thread_test_status_flags(audio_thread, AGS_AUDIO_THREAD_STATUS_WAIT_SYNC)){
1317 	    ags_audio_thread_unset_status_flags(audio_thread, AGS_AUDIO_THREAD_STATUS_DONE_SYNC);
1318 
1319 	    while(!ags_audio_thread_test_status_flags(audio_thread, AGS_AUDIO_THREAD_STATUS_DONE_SYNC) &&
1320 		  ags_audio_thread_test_status_flags(audio_thread, AGS_AUDIO_THREAD_STATUS_WAIT_SYNC)){
1321 	      g_cond_wait(&(audio_thread->done_cond),
1322 			  &(audio_thread->done_mutex));
1323 	    }
1324 	  }
1325 
1326 	  ags_audio_thread_set_status_flags(audio_thread, (AGS_AUDIO_THREAD_STATUS_WAIT_SYNC |
1327 							   AGS_AUDIO_THREAD_STATUS_DONE_SYNC));
1328 
1329 	  g_mutex_unlock(&(audio_thread->done_mutex));
1330 	}
1331       }
1332 
1333       g_list_free_full(recall_id,
1334 		       g_object_unref);
1335     }
1336   }
1337 
1338   g_object_unref(audio);
1339 }
1340 
1341 /**
1342  * ags_audio_loop_test_flags:
1343  * @audio_loop: the #AgsAudioLoop
1344  * @flags: the flags
1345  *
1346  * Test @flags to be set on @audio_loop.
1347  *
1348  * Returns: %TRUE if flags are set, else %FALSE
1349  *
1350  * Since: 3.0.0
1351  */
1352 gboolean
ags_audio_loop_test_flags(AgsAudioLoop * audio_loop,guint flags)1353 ags_audio_loop_test_flags(AgsAudioLoop *audio_loop, guint flags)
1354 {
1355   gboolean retval;
1356 
1357   GRecMutex *audio_loop_mutex;
1358 
1359   if(!AGS_IS_AUDIO_LOOP(audio_loop)){
1360     return(FALSE);
1361   }
1362 
1363   /* get audio loop mutex */
1364   audio_loop_mutex = AGS_THREAD_GET_OBJ_MUTEX(audio_loop);
1365 
1366   /* test flags */
1367   g_rec_mutex_lock(audio_loop_mutex);
1368 
1369   retval = ((flags & (audio_loop->flags)) != 0) ? TRUE: FALSE;
1370 
1371   g_rec_mutex_unlock(audio_loop_mutex);
1372 
1373   return(retval);
1374 }
1375 
1376 /**
1377  * ags_audio_loop_set_flags:
1378  * @audio_loop: the #AgsAudioLoop
1379  * @flags: the flags
1380  *
1381  * Set flags.
1382  *
1383  * Since: 3.0.0
1384  */
1385 void
ags_audio_loop_set_flags(AgsAudioLoop * audio_loop,guint flags)1386 ags_audio_loop_set_flags(AgsAudioLoop *audio_loop, guint flags)
1387 {
1388   GRecMutex *audio_loop_mutex;
1389 
1390   if(!AGS_IS_AUDIO_LOOP(audio_loop)){
1391     return;
1392   }
1393 
1394   /* get audio loop mutex */
1395   audio_loop_mutex = AGS_THREAD_GET_OBJ_MUTEX(audio_loop);
1396 
1397   /* set flags */
1398   g_rec_mutex_lock(audio_loop_mutex);
1399 
1400   audio_loop->flags |= flags;
1401 
1402   g_rec_mutex_unlock(audio_loop_mutex);
1403 }
1404 
1405 /**
1406  * ags_audio_loop_unset_flags:
1407  * @audio_loop: the #AgsAudioLoop
1408  * @flags: the flags
1409  *
1410  * Unset flags.
1411  *
1412  * Since: 3.0.0
1413  */
1414 void
ags_audio_loop_unset_flags(AgsAudioLoop * audio_loop,guint flags)1415 ags_audio_loop_unset_flags(AgsAudioLoop *audio_loop, guint flags)
1416 {
1417   GRecMutex *audio_loop_mutex;
1418 
1419   if(!AGS_IS_AUDIO_LOOP(audio_loop)){
1420     return;
1421   }
1422 
1423   /* get audio loop mutex */
1424   audio_loop_mutex = AGS_THREAD_GET_OBJ_MUTEX(audio_loop);
1425 
1426   /* unset flags */
1427   g_rec_mutex_lock(audio_loop_mutex);
1428 
1429   audio_loop->flags &= (~flags);
1430 
1431   g_rec_mutex_unlock(audio_loop_mutex);
1432 }
1433 
1434 /**
1435  * ags_audio_loop_add_audio:
1436  * @audio_loop: the #AgsAudioLoop
1437  * @audio: an #AgsAudio
1438  *
1439  * Add audio for playback.
1440  *
1441  * Since: 3.0.0
1442  */
1443 void
ags_audio_loop_add_audio(AgsAudioLoop * audio_loop,GObject * audio)1444 ags_audio_loop_add_audio(AgsAudioLoop *audio_loop, GObject *audio)
1445 {
1446   AgsPlaybackDomain *playback_domain;
1447 
1448   GRecMutex *thread_mutex;
1449 
1450   if(!AGS_IS_AUDIO_LOOP(audio_loop) ||
1451      !AGS_IS_AUDIO(audio)){
1452     return;
1453   }
1454 
1455   thread_mutex = AGS_THREAD_GET_OBJ_MUTEX(audio_loop);
1456 
1457   g_object_get(audio,
1458 	       "playback-domain", &playback_domain,
1459 	       NULL);
1460 
1461   g_rec_mutex_lock(thread_mutex);
1462 
1463   if(g_list_find(audio_loop->play_audio,
1464 		 playback_domain) == NULL){
1465     audio_loop->play_audio = g_list_prepend(audio_loop->play_audio,
1466 					    playback_domain);
1467 
1468     audio_loop->play_audio_ref = audio_loop->play_audio_ref + 1;
1469   }else{
1470     if(playback_domain != NULL){
1471       g_object_unref(playback_domain);
1472     }
1473   }
1474 
1475   g_rec_mutex_unlock(thread_mutex);
1476 }
1477 
1478 /**
1479  * ags_audio_loop_remove_audio:
1480  * @audio_loop: the #AgsAudioLoop
1481  * @audio: an #AgsAudio
1482  *
1483  * Remove audio of playback.
1484  *
1485  * Since: 3.0.0
1486  */
1487 void
ags_audio_loop_remove_audio(AgsAudioLoop * audio_loop,GObject * audio)1488 ags_audio_loop_remove_audio(AgsAudioLoop *audio_loop, GObject *audio)
1489 {
1490   AgsPlaybackDomain *playback_domain;
1491 
1492   GRecMutex *thread_mutex;
1493 
1494   if(!AGS_IS_AUDIO_LOOP(audio_loop) ||
1495      !AGS_IS_AUDIO(audio)){
1496     return;
1497   }
1498 
1499   thread_mutex = AGS_THREAD_GET_OBJ_MUTEX(audio_loop);
1500 
1501   g_object_get(audio,
1502 	       "playback-domain", &playback_domain,
1503 	       NULL);
1504 
1505   g_rec_mutex_lock(thread_mutex);
1506 
1507   if(g_list_find(audio_loop->play_audio,
1508 		 playback_domain) != NULL){
1509     audio_loop->play_audio = g_list_remove(audio_loop->play_audio,
1510 					   playback_domain);
1511     audio_loop->play_audio_ref = audio_loop->play_audio_ref - 1;
1512 
1513     g_object_unref(playback_domain);
1514   }
1515 
1516   g_rec_mutex_unlock(thread_mutex);
1517 
1518   if(playback_domain != NULL){
1519     g_object_unref(playback_domain);
1520   }
1521 }
1522 
1523 /**
1524  * ags_audio_loop_add_channel:
1525  * @audio_loop: the #AgsAudioLoop
1526  * @channel: an #AgsChannel
1527  *
1528  * Add channel for playback.
1529  *
1530  * Since: 3.0.0
1531  */
1532 void
ags_audio_loop_add_channel(AgsAudioLoop * audio_loop,GObject * channel)1533 ags_audio_loop_add_channel(AgsAudioLoop *audio_loop, GObject *channel)
1534 {
1535   AgsPlayback *playback;
1536 
1537   GRecMutex *thread_mutex;
1538 
1539   if(!AGS_IS_AUDIO_LOOP(audio_loop) ||
1540      !AGS_IS_CHANNEL(channel)){
1541     return;
1542   }
1543 
1544   thread_mutex = AGS_THREAD_GET_OBJ_MUTEX(audio_loop);
1545 
1546   g_object_get(channel,
1547 	       "playback", &playback,
1548 	       NULL);
1549 
1550   g_rec_mutex_lock(thread_mutex);
1551 
1552   if(g_list_find(audio_loop->play_channel,
1553 		 playback) == NULL){
1554     audio_loop->play_channel = g_list_prepend(audio_loop->play_channel,
1555 					      playback);
1556 
1557     audio_loop->play_channel_ref = audio_loop->play_channel_ref + 1;
1558   }else{
1559     if(playback != NULL){
1560       g_object_unref(playback);
1561     }
1562   }
1563 
1564   g_rec_mutex_unlock(thread_mutex);
1565 }
1566 
1567 /**
1568  * ags_audio_loop_remove_channel:
1569  * @audio_loop: the #AgsAudioLoop
1570  * @channel: an #AgsChannel
1571  *
1572  * Remove channel of playback.
1573  *
1574  * Since: 3.0.0
1575  */
1576 void
ags_audio_loop_remove_channel(AgsAudioLoop * audio_loop,GObject * channel)1577 ags_audio_loop_remove_channel(AgsAudioLoop *audio_loop, GObject *channel)
1578 {
1579   AgsPlayback *playback;
1580 
1581   GRecMutex *thread_mutex;
1582 
1583   if(!AGS_IS_AUDIO_LOOP(audio_loop) ||
1584      !AGS_IS_CHANNEL(channel)){
1585     return;
1586   }
1587 
1588   thread_mutex = AGS_THREAD_GET_OBJ_MUTEX(audio_loop);
1589 
1590   g_object_get(channel,
1591 	       "playback", &playback,
1592 	       NULL);
1593 
1594   g_rec_mutex_lock(thread_mutex);
1595 
1596   if(g_list_find(audio_loop->play_channel,
1597 		 playback) != NULL){
1598     audio_loop->play_channel = g_list_remove(audio_loop->play_channel,
1599 					     playback);
1600     audio_loop->play_channel_ref = audio_loop->play_channel_ref - 1;
1601 
1602     g_object_unref(playback);
1603   }
1604 
1605   g_rec_mutex_unlock(thread_mutex);
1606 
1607   if(playback != NULL){
1608     g_object_unref(playback);
1609   }
1610 }
1611 
1612 /**
1613  * ags_audio_loop_get_do_fx_staging:
1614  * @audio_loop: the #AgsAudioLoop
1615  *
1616  * Get do fx staging.
1617  *
1618  * Returns: %TRUE if set, otherwise %FALSE
1619  *
1620  * Since: 3.3.0
1621  */
1622 gboolean
ags_audio_loop_get_do_fx_staging(AgsAudioLoop * audio_loop)1623 ags_audio_loop_get_do_fx_staging(AgsAudioLoop *audio_loop)
1624 {
1625   gboolean do_fx_staging;
1626 
1627   GRecMutex *thread_mutex;
1628 
1629   if(!AGS_IS_AUDIO_LOOP(audio_loop)){
1630     return(FALSE);
1631   }
1632 
1633   thread_mutex = AGS_THREAD_GET_OBJ_MUTEX(audio_loop);
1634 
1635   /* get do fx staging */
1636   g_rec_mutex_lock(thread_mutex);
1637 
1638   do_fx_staging = audio_loop->do_fx_staging;
1639 
1640   g_rec_mutex_unlock(thread_mutex);
1641 
1642   return(do_fx_staging);
1643 }
1644 
1645 /**
1646  * ags_audio_loop_set_do_fx_staging:
1647  * @audio_loop: the #AgsAudioLoop
1648  * @do_fx_staging: %TRUE if do fx staging, else %FALSE
1649  *
1650  * Set do fx staging.
1651  *
1652  * Since: 3.3.0
1653  */
1654 void
ags_audio_loop_set_do_fx_staging(AgsAudioLoop * audio_loop,gboolean do_fx_staging)1655 ags_audio_loop_set_do_fx_staging(AgsAudioLoop *audio_loop, gboolean do_fx_staging)
1656 {
1657   GRecMutex *thread_mutex;
1658 
1659   if(!AGS_IS_AUDIO_LOOP(audio_loop)){
1660     return;
1661   }
1662 
1663   thread_mutex = AGS_THREAD_GET_OBJ_MUTEX(audio_loop);
1664 
1665   /* get do fx staging */
1666   g_rec_mutex_lock(thread_mutex);
1667 
1668   audio_loop->do_fx_staging = do_fx_staging;
1669 
1670   g_rec_mutex_unlock(thread_mutex);
1671 }
1672 
1673 /**
1674  * ags_audio_loop_get_staging_program:
1675  * @audio_loop: the #AgsAudioLoop
1676  * @staging_program_count: (out): the staging program count
1677  *
1678  * Get staging program.
1679  *
1680  * Returns: (transfer full): the staging program
1681  *
1682  * Since: 3.3.0
1683  */
1684 guint*
ags_audio_loop_get_staging_program(AgsAudioLoop * audio_loop,guint * staging_program_count)1685 ags_audio_loop_get_staging_program(AgsAudioLoop *audio_loop,
1686 				   guint *staging_program_count)
1687 {
1688   guint *staging_program;
1689 
1690   GRecMutex *thread_mutex;
1691 
1692   if(!AGS_IS_AUDIO_LOOP(audio_loop)){
1693     if(staging_program_count != NULL){
1694       staging_program_count[0] = 0;
1695     }
1696 
1697     return(NULL);
1698   }
1699 
1700   thread_mutex = AGS_THREAD_GET_OBJ_MUTEX(audio_loop);
1701 
1702   /* get staging program */
1703   staging_program = NULL;
1704 
1705   g_rec_mutex_lock(thread_mutex);
1706 
1707   if(audio_loop->staging_program_count > 0){
1708     staging_program = (guint *) g_malloc(audio_loop->staging_program_count * sizeof(guint));
1709 
1710     memcpy(staging_program, audio_loop->staging_program, audio_loop->staging_program_count * sizeof(guint));
1711   }
1712 
1713   if(staging_program_count != NULL){
1714     staging_program_count[0] = audio_loop->staging_program_count;
1715   }
1716 
1717   g_rec_mutex_unlock(thread_mutex);
1718 
1719   return(staging_program);
1720 }
1721 
1722 /**
1723  * ags_audio_loop_set_staging_program:
1724  * @audio_loop: the #AgsAudioLoop
1725  * @staging_program: (transfer none): the staging program
1726  * @staging_program_count: the staging program count
1727  *
1728  * Set staging program.
1729  *
1730  * Since: 3.3.0
1731  */
1732 void
ags_audio_loop_set_staging_program(AgsAudioLoop * audio_loop,guint * staging_program,guint staging_program_count)1733 ags_audio_loop_set_staging_program(AgsAudioLoop *audio_loop,
1734 				   guint *staging_program,
1735 				   guint staging_program_count)
1736 {
1737   GRecMutex *thread_mutex;
1738 
1739   if(!AGS_IS_AUDIO_LOOP(audio_loop)){
1740     return;
1741   }
1742 
1743   thread_mutex = AGS_THREAD_GET_OBJ_MUTEX(audio_loop);
1744 
1745   /* set staging program */
1746   g_rec_mutex_lock(thread_mutex);
1747 
1748   g_free(audio_loop->staging_program);
1749 
1750   if(staging_program_count > 0){
1751     audio_loop->staging_program = (guint *) g_malloc(staging_program_count * sizeof(guint));
1752 
1753     memcpy(audio_loop->staging_program, staging_program, staging_program_count * sizeof(guint));
1754   }else{
1755     audio_loop->staging_program = NULL;
1756   }
1757 
1758   audio_loop->staging_program_count = staging_program_count;
1759 
1760   g_rec_mutex_unlock(thread_mutex);
1761 }
1762 
1763 /**
1764  * ags_audio_loop_new:
1765  *
1766  * Create a new #AgsAudioLoop.
1767  *
1768  * Returns: the new #AgsAudioLoop
1769  *
1770  * Since: 3.0.0
1771  */
1772 AgsAudioLoop*
ags_audio_loop_new()1773 ags_audio_loop_new()
1774 {
1775   AgsAudioLoop *audio_loop;
1776 
1777   audio_loop = (AgsAudioLoop *) g_object_new(AGS_TYPE_AUDIO_LOOP,
1778 					     NULL);
1779 
1780   return(audio_loop);
1781 }
1782