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, ¶m) == -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