1 /* GSequencer - Advanced GTK Sequencer
2 * Copyright (C) 2005-2021 Joël Krähemann
3 *
4 * This file is part of GSequencer.
5 *
6 * GSequencer is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * GSequencer is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with GSequencer. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include <ags/audio/thread/ags_channel_thread.h>
21
22 #include <ags/audio/ags_channel.h>
23 #include <ags/audio/ags_playback.h>
24
25 #include <ags/audio/thread/ags_audio_loop.h>
26
27 #include <math.h>
28
29 #include <ags/i18n.h>
30
31 void ags_channel_thread_class_init(AgsChannelThreadClass *channel_thread);
32 void ags_channel_thread_connectable_interface_init(AgsConnectableInterface *connectable);
33 void ags_channel_thread_init(AgsChannelThread *channel_thread);
34 void ags_channel_thread_set_property(GObject *gobject,
35 guint prop_id,
36 const GValue *value,
37 GParamSpec *param_spec);
38 void ags_channel_thread_get_property(GObject *gobject,
39 guint prop_id,
40 GValue *value,
41 GParamSpec *param_spec);
42 void ags_channel_thread_dispose(GObject *gobject);
43 void ags_channel_thread_finalize(GObject *gobject);
44
45 void ags_channel_thread_start(AgsThread *thread);
46 void ags_channel_thread_run(AgsThread *thread);
47 void ags_channel_thread_stop(AgsThread *thread);
48
49 /**
50 * SECTION:ags_channel_thread
51 * @short_description: channel thread
52 * @title: AgsChannelThread
53 * @section_id:
54 * @include: ags/audio/thread/ags_channel_thread.h
55 *
56 * The #AgsChannelThread acts as channel output thread to soundcard.
57 */
58
59 enum{
60 PROP_0,
61 PROP_DEFAULT_OUTPUT_SOUNDCARD,
62 PROP_CHANNEL,
63 PROP_SOUND_SCOPE,
64 };
65
66 static gpointer ags_channel_thread_parent_class = NULL;
67 static AgsConnectableInterface *ags_channel_thread_parent_connectable_interface;
68
69 GType
ags_channel_thread_get_type()70 ags_channel_thread_get_type()
71 {
72 static volatile gsize g_define_type_id__volatile = 0;
73
74 if(g_once_init_enter (&g_define_type_id__volatile)){
75 GType ags_type_channel_thread = 0;
76
77 static const GTypeInfo ags_channel_thread_info = {
78 sizeof (AgsChannelThreadClass),
79 NULL, /* base_init */
80 NULL, /* base_finalize */
81 (GClassInitFunc) ags_channel_thread_class_init,
82 NULL, /* class_finalize */
83 NULL, /* class_data */
84 sizeof (AgsChannelThread),
85 0, /* n_preallocs */
86 (GInstanceInitFunc) ags_channel_thread_init,
87 };
88
89 static const GInterfaceInfo ags_connectable_interface_info = {
90 (GInterfaceInitFunc) ags_channel_thread_connectable_interface_init,
91 NULL, /* interface_finalize */
92 NULL, /* interface_data */
93 };
94
95 ags_type_channel_thread = g_type_register_static(AGS_TYPE_THREAD,
96 "AgsChannelThread",
97 &ags_channel_thread_info,
98 0);
99
100 g_type_add_interface_static(ags_type_channel_thread,
101 AGS_TYPE_CONNECTABLE,
102 &ags_connectable_interface_info);
103
104 g_once_init_leave(&g_define_type_id__volatile, ags_type_channel_thread);
105 }
106
107 return g_define_type_id__volatile;
108 }
109
110 void
ags_channel_thread_class_init(AgsChannelThreadClass * channel_thread)111 ags_channel_thread_class_init(AgsChannelThreadClass *channel_thread)
112 {
113 GObjectClass *gobject;
114 AgsThreadClass *thread;
115 GParamSpec *param_spec;
116
117 ags_channel_thread_parent_class = g_type_class_peek_parent(channel_thread);
118
119 /* GObject */
120 gobject = (GObjectClass *) channel_thread;
121
122 gobject->set_property = ags_channel_thread_set_property;
123 gobject->get_property = ags_channel_thread_get_property;
124
125 gobject->dispose = ags_channel_thread_dispose;
126 gobject->finalize = ags_channel_thread_finalize;
127
128 /* properties */
129 /**
130 * AgsChannelThread:default-output-soundcard:
131 *
132 * The assigned default soundcard.
133 *
134 * Since: 3.0.0
135 */
136 param_spec = g_param_spec_object("default-output-soundcard",
137 i18n_pspec("default output soundcard assigned to"),
138 i18n_pspec("The default output AgsSoundcard it is assigned to"),
139 G_TYPE_OBJECT,
140 G_PARAM_READABLE | G_PARAM_WRITABLE);
141 g_object_class_install_property(gobject,
142 PROP_DEFAULT_OUTPUT_SOUNDCARD,
143 param_spec);
144
145 /**
146 * AgsChannelThread:channel:
147 *
148 * The assigned #AgsChannel.
149 *
150 * Since: 3.0.0
151 */
152 param_spec = g_param_spec_object("channel",
153 i18n_pspec("channel assigned to"),
154 i18n_pspec("The AgsChannel it is assigned to."),
155 AGS_TYPE_CHANNEL,
156 G_PARAM_READABLE | G_PARAM_WRITABLE);
157 g_object_class_install_property(gobject,
158 PROP_CHANNEL,
159 param_spec);
160
161
162 /* AgsThread */
163 thread = (AgsThreadClass *) channel_thread;
164
165 thread->start = ags_channel_thread_start;
166 thread->run = ags_channel_thread_run;
167 thread->stop = ags_channel_thread_stop;
168 }
169
170 void
ags_channel_thread_connectable_interface_init(AgsConnectableInterface * connectable)171 ags_channel_thread_connectable_interface_init(AgsConnectableInterface *connectable)
172 {
173 ags_channel_thread_parent_connectable_interface = g_type_interface_peek_parent(connectable);
174 }
175
176 void
ags_channel_thread_init(AgsChannelThread * channel_thread)177 ags_channel_thread_init(AgsChannelThread *channel_thread)
178 {
179 AgsThread *thread;
180
181 AgsConfig *config;
182
183 gdouble frequency;
184 guint samplerate;
185 guint buffer_size;
186
187 thread = (AgsThread *) channel_thread;
188
189 ags_thread_set_flags(thread, (AGS_THREAD_START_SYNCED_FREQ |
190 AGS_THREAD_IMMEDIATE_SYNC));
191
192 config = ags_config_get_instance();
193
194 samplerate = ags_soundcard_helper_config_get_samplerate(config);
195 buffer_size = ags_soundcard_helper_config_get_buffer_size(config);
196
197 frequency = ((gdouble) samplerate / (gdouble) buffer_size) + AGS_SOUNDCARD_DEFAULT_OVERCLOCK;
198
199 g_object_set(thread,
200 "frequency", frequency,
201 NULL);
202
203 g_atomic_int_set(&(channel_thread->status_flags),
204 0);
205
206 channel_thread->default_output_soundcard = NULL;
207
208 /* start */
209 g_mutex_init(&(channel_thread->wakeup_mutex));
210
211 g_cond_init(&(channel_thread->wakeup_cond));
212
213 /* sync */
214 g_mutex_init(&(channel_thread->done_mutex));
215
216 g_cond_init(&(channel_thread->done_cond));
217
218 /* channel and sound scope */
219 channel_thread->channel = NULL;
220 channel_thread->sound_scope = -1;
221
222 /* staging program */
223 channel_thread->do_fx_staging = FALSE;
224
225 channel_thread->staging_program = g_malloc(3 * sizeof(guint));
226
227 channel_thread->staging_program[0] = (AGS_SOUND_STAGING_FEED_INPUT_QUEUE |
228 AGS_SOUND_STAGING_AUTOMATE |
229 AGS_SOUND_STAGING_RUN_PRE);
230 channel_thread->staging_program[1] = (AGS_SOUND_STAGING_RUN_INTER);
231 channel_thread->staging_program[2] = (AGS_SOUND_STAGING_RUN_POST);
232
233 channel_thread->staging_program_count = 3;
234 }
235
236 void
ags_channel_thread_set_property(GObject * gobject,guint prop_id,const GValue * value,GParamSpec * param_spec)237 ags_channel_thread_set_property(GObject *gobject,
238 guint prop_id,
239 const GValue *value,
240 GParamSpec *param_spec)
241 {
242 AgsChannelThread *channel_thread;
243
244 GRecMutex *thread_mutex;
245
246 channel_thread = AGS_CHANNEL_THREAD(gobject);
247
248 /* get thread mutex */
249 thread_mutex = AGS_THREAD_GET_OBJ_MUTEX(channel_thread);
250
251 switch(prop_id){
252 case PROP_DEFAULT_OUTPUT_SOUNDCARD:
253 {
254 GObject *default_output_soundcard;
255
256 default_output_soundcard = g_value_get_object(value);
257
258 g_rec_mutex_lock(thread_mutex);
259
260 if(channel_thread->default_output_soundcard == default_output_soundcard){
261 g_rec_mutex_unlock(thread_mutex);
262
263 return;
264 }
265
266 if(channel_thread->default_output_soundcard != NULL){
267 g_object_unref(channel_thread->default_output_soundcard);
268 }
269
270 if(default_output_soundcard != NULL){
271 g_object_ref(default_output_soundcard);
272 }
273
274 channel_thread->default_output_soundcard = default_output_soundcard;
275
276 g_rec_mutex_unlock(thread_mutex);
277 }
278 break;
279 case PROP_CHANNEL:
280 {
281 AgsChannel *channel;
282
283 channel = (AgsChannel *) g_value_get_object(value);
284
285 g_rec_mutex_lock(thread_mutex);
286
287 if(channel_thread->channel == (GObject *) channel){
288 g_rec_mutex_unlock(thread_mutex);
289
290 return;
291 }
292
293 if(channel_thread->channel != NULL){
294 g_object_unref(G_OBJECT(channel_thread->channel));
295 }
296
297 if(channel != NULL){
298 g_object_ref(G_OBJECT(channel));
299 }
300
301 channel_thread->channel = (GObject *) channel;
302
303 g_rec_mutex_unlock(thread_mutex);
304 }
305 break;
306 default:
307 G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, param_spec);
308 break;
309 }
310 }
311
312 void
ags_channel_thread_get_property(GObject * gobject,guint prop_id,GValue * value,GParamSpec * param_spec)313 ags_channel_thread_get_property(GObject *gobject,
314 guint prop_id,
315 GValue *value,
316 GParamSpec *param_spec)
317 {
318 AgsChannelThread *channel_thread;
319
320 GRecMutex *thread_mutex;
321
322 channel_thread = AGS_CHANNEL_THREAD(gobject);
323
324 /* get thread mutex */
325 thread_mutex = AGS_THREAD_GET_OBJ_MUTEX(channel_thread);
326
327 switch(prop_id){
328 case PROP_DEFAULT_OUTPUT_SOUNDCARD:
329 {
330 g_rec_mutex_lock(thread_mutex);
331
332 g_value_set_object(value, channel_thread->default_output_soundcard);
333
334 g_rec_mutex_unlock(thread_mutex);
335 }
336 break;
337 case PROP_CHANNEL:
338 {
339 g_rec_mutex_lock(thread_mutex);
340
341 g_value_set_object(value, G_OBJECT(channel_thread->channel));
342
343 g_rec_mutex_unlock(thread_mutex);
344 }
345 break;
346 default:
347 G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, param_spec);
348 break;
349 }
350 }
351
352 void
ags_channel_thread_dispose(GObject * gobject)353 ags_channel_thread_dispose(GObject *gobject)
354 {
355 AgsChannelThread *channel_thread;
356
357 channel_thread = AGS_CHANNEL_THREAD(gobject);
358
359 /* soundcard */
360 if(channel_thread->default_output_soundcard != NULL){
361 gpointer tmp;
362
363 tmp = channel_thread->default_output_soundcard;
364
365 channel_thread->default_output_soundcard = NULL;
366
367 g_object_unref(tmp);
368 }
369
370 /* channel */
371 if(channel_thread->channel != NULL){
372 gpointer tmp;
373
374 tmp = channel_thread->channel;
375
376 channel_thread->channel = NULL;
377
378 g_object_unref(tmp);
379 }
380
381 /* call parent */
382 G_OBJECT_CLASS(ags_channel_thread_parent_class)->dispose(gobject);
383 }
384
385 void
ags_channel_thread_finalize(GObject * gobject)386 ags_channel_thread_finalize(GObject *gobject)
387 {
388 AgsChannelThread *channel_thread;
389
390 channel_thread = AGS_CHANNEL_THREAD(gobject);
391
392 /* soundcard */
393 if(channel_thread->default_output_soundcard != NULL){
394 gpointer tmp;
395
396 tmp = channel_thread->default_output_soundcard;
397
398 channel_thread->default_output_soundcard = NULL;
399
400 g_object_unref(tmp);
401 }
402
403 /* channel */
404 if(channel_thread->channel != NULL){
405 gpointer tmp;
406
407 tmp = channel_thread->channel;
408
409 channel_thread->channel = NULL;
410
411 g_object_unref(tmp);
412 }
413
414 /* call parent */
415 G_OBJECT_CLASS(ags_channel_thread_parent_class)->finalize(gobject);
416 }
417
418 void
ags_channel_thread_start(AgsThread * thread)419 ags_channel_thread_start(AgsThread *thread)
420 {
421 #ifdef AGS_DEBUG
422 g_message("channel thread start");
423 #endif
424
425 /* reset status */
426 ags_channel_thread_set_status_flags(thread, (AGS_CHANNEL_THREAD_STATUS_WAIT |
427 AGS_CHANNEL_THREAD_STATUS_DONE |
428 AGS_CHANNEL_THREAD_STATUS_WAIT_SYNC |
429 AGS_CHANNEL_THREAD_STATUS_DONE_SYNC));
430
431 AGS_THREAD_CLASS(ags_channel_thread_parent_class)->start(thread);
432 }
433
434 void
ags_channel_thread_run(AgsThread * thread)435 ags_channel_thread_run(AgsThread *thread)
436 {
437 AgsChannel *channel;
438 AgsPlayback *playback;
439
440 AgsAudioLoop *audio_loop;
441 AgsChannelThread *channel_thread;
442
443 GList *recall_id;
444
445 gint sound_scope;
446
447 GRecMutex *thread_mutex;
448
449 /* real-time setup */
450 #ifdef AGS_WITH_RT
451 if(!ags_thread_test_status_flags(thread, AGS_THREAD_STATUS_RT_SETUP)){
452 AgsPriority *priority;
453
454 struct sched_param param;
455
456 gchar *str;
457
458 priority = ags_priority_get_instance();
459
460 /* Declare ourself as a real time task */
461 param.sched_priority = 45;
462
463 str = ags_priority_get_value(priority,
464 AGS_PRIORITY_RT_THREAD,
465 AGS_PRIORITY_KEY_AUDIO);
466
467 if(str != NULL){
468 param.sched_priority = (int) g_ascii_strtoull(str,
469 NULL,
470 10);
471 }
472
473 if(str == NULL ||
474 ((!g_ascii_strncasecmp(str,
475 "0",
476 2)) != TRUE)){
477 if(sched_setscheduler(0, SCHED_FIFO, ¶m) == -1) {
478 perror("sched_setscheduler failed");
479 }
480 }
481
482 g_free(str);
483
484 ags_thread_set_status_flags(thread, AGS_THREAD_STATUS_RT_SETUP);
485 }
486 #endif
487
488 audio_loop = thread->parent;
489
490 if(!ags_thread_test_status_flags(thread, AGS_THREAD_STATUS_SYNCED)){
491 return;
492 }
493
494 channel_thread = AGS_CHANNEL_THREAD(thread);
495
496 channel = NULL;
497
498 g_object_get(channel_thread,
499 "channel", &channel,
500 NULL);
501
502 playback = NULL;
503
504 g_object_get(channel,
505 "playback", &playback,
506 NULL);
507
508 /* start - wait until signaled */
509 g_mutex_lock(&(channel_thread->wakeup_mutex));
510
511 if(!ags_channel_thread_test_status_flags(channel_thread, AGS_CHANNEL_THREAD_STATUS_DONE) &&
512 ags_channel_thread_test_status_flags(channel_thread, AGS_CHANNEL_THREAD_STATUS_WAIT)){
513 ags_channel_thread_unset_status_flags(channel_thread, AGS_CHANNEL_THREAD_STATUS_DONE);
514
515 while(!ags_channel_thread_test_status_flags(channel_thread, AGS_CHANNEL_THREAD_STATUS_DONE) &&
516 ags_channel_thread_test_status_flags(channel_thread, AGS_CHANNEL_THREAD_STATUS_WAIT)){
517 g_cond_wait(&(channel_thread->wakeup_cond),
518 &(channel_thread->wakeup_mutex));
519 }
520 }
521
522 ags_channel_thread_set_status_flags(channel_thread, (AGS_CHANNEL_THREAD_STATUS_WAIT |
523 AGS_CHANNEL_THREAD_STATUS_DONE));
524
525 g_mutex_unlock(&(channel_thread->wakeup_mutex));
526
527 /*
528 * do audio processing
529 */
530 if(channel_thread->sound_scope >= 0){
531 sound_scope = channel_thread->sound_scope;
532
533 if(sound_scope != AGS_SOUND_SCOPE_PLAYBACK ||
534 ags_playback_get_recall_id(playback, sound_scope) != NULL){
535 if((recall_id = ags_channel_check_scope(channel, sound_scope)) != NULL){
536 guint *staging_program;
537
538 guint staging_program_count;
539 guint nth;
540
541 staging_program = ags_channel_thread_get_staging_program(channel_thread,
542 &staging_program_count);
543
544 for(nth = 0; nth < staging_program_count; nth++){
545 ags_channel_recursive_run_stage(channel,
546 sound_scope, staging_program[nth]);
547 }
548
549 g_free(staging_program);
550
551 g_list_free_full(recall_id,
552 g_object_unref);
553 }
554 }
555 }else{
556 gint nth_sound_scope;
557
558 for(nth_sound_scope = 0; nth_sound_scope < AGS_SOUND_SCOPE_LAST; nth_sound_scope++){
559 if(nth_sound_scope == AGS_SOUND_SCOPE_PLAYBACK ||
560 ags_playback_get_recall_id(playback, nth_sound_scope) == NULL){
561 continue;
562 }
563
564 if((recall_id = ags_channel_check_scope(channel, nth_sound_scope)) != NULL){
565 guint *staging_program;
566
567 guint staging_program_count;
568 guint nth;
569
570 staging_program = ags_channel_thread_get_staging_program(channel_thread,
571 &staging_program_count);
572
573 for(nth = 0; nth < staging_program_count; nth++){
574 ags_channel_recursive_run_stage(channel,
575 nth_sound_scope, staging_program[nth]);
576 }
577
578 g_free(staging_program);
579
580 g_list_free_full(recall_id,
581 g_object_unref);
582 }
583 }
584 }
585
586 /* sync */
587 g_mutex_lock(&(channel_thread->done_mutex));
588
589 ags_channel_thread_unset_status_flags(channel_thread, AGS_CHANNEL_THREAD_STATUS_WAIT_SYNC);
590
591 if(!ags_channel_thread_test_status_flags(channel_thread, AGS_CHANNEL_THREAD_STATUS_DONE_SYNC)){
592 g_cond_signal(&(channel_thread->done_cond));
593 }
594
595 g_mutex_unlock(&(channel_thread->done_mutex));
596
597 /* unref */
598 if(channel != NULL){
599 g_object_unref(channel);
600 }
601
602 if(playback != NULL){
603 g_object_unref(playback);
604 }
605 }
606
607 void
ags_channel_thread_stop(AgsThread * thread)608 ags_channel_thread_stop(AgsThread *thread)
609 {
610 AgsChannelThread *channel_thread;
611
612 if(!ags_thread_test_status_flags(thread, AGS_THREAD_STATUS_RUNNING)){
613 return;
614 }
615
616 /* */
617 channel_thread = AGS_CHANNEL_THREAD(thread);
618
619 #ifdef AGS_DEBUG
620 g_message("channel thread stop");
621 #endif
622
623 /* call parent */
624 AGS_THREAD_CLASS(ags_channel_thread_parent_class)->stop(thread);
625
626 /* ensure synced */
627 g_mutex_lock(&(channel_thread->done_mutex));
628
629 ags_channel_thread_unset_status_flags(channel_thread, AGS_CHANNEL_THREAD_STATUS_WAIT_SYNC);
630
631 if(!ags_channel_thread_test_status_flags(channel_thread, AGS_CHANNEL_THREAD_STATUS_DONE_SYNC)){
632 g_cond_signal(&(channel_thread->done_cond));
633 }
634
635 g_mutex_unlock(&(channel_thread->done_mutex));
636 }
637
638 /**
639 * ags_channel_thread_test_status_flags:
640 * @channel_thread: the #AgsChannelThread
641 * @status_flags: status flags
642 *
643 * Test @status_flags of @channel_thread.
644 *
645 * Returns: %TRUE if status flags set, otherwise %FALSE
646 *
647 * Since: 3.0.0
648 */
649 gboolean
ags_channel_thread_test_status_flags(AgsChannelThread * channel_thread,guint status_flags)650 ags_channel_thread_test_status_flags(AgsChannelThread *channel_thread, guint status_flags)
651 {
652 gboolean retval;
653
654 if(!AGS_IS_CHANNEL_THREAD(channel_thread)){
655 return(FALSE);
656 }
657
658 retval = ((status_flags & (g_atomic_int_get(&(channel_thread->status_flags)))) != 0) ? TRUE: FALSE;
659
660 return(retval);
661 }
662
663 /**
664 * ags_channel_thread_set_status_flags:
665 * @channel_thread: the #AgsChannelThread
666 * @status_flags: status flags
667 *
668 * Set status flags.
669 *
670 * Since: 3.0.0
671 */
672 void
ags_channel_thread_set_status_flags(AgsChannelThread * channel_thread,guint status_flags)673 ags_channel_thread_set_status_flags(AgsChannelThread *channel_thread, guint status_flags)
674 {
675 if(!AGS_IS_CHANNEL_THREAD(channel_thread)){
676 return;
677 }
678
679 g_atomic_int_or(&(channel_thread->status_flags),
680 status_flags);
681 }
682
683 /**
684 * ags_channel_thread_unset_status_flags:
685 * @channel_thread: the #AgsChannelThread
686 * @status_flags: status flags
687 *
688 * Unset status flags.
689 *
690 * Since: 3.0.0
691 */
692 void
ags_channel_thread_unset_status_flags(AgsChannelThread * channel_thread,guint status_flags)693 ags_channel_thread_unset_status_flags(AgsChannelThread *channel_thread, guint status_flags)
694 {
695 if(!AGS_IS_CHANNEL_THREAD(channel_thread)){
696 return;
697 }
698
699 g_atomic_int_and(&(channel_thread->status_flags),
700 (~status_flags));
701 }
702
703 /**
704 * ags_channel_thread_set_sound_scope:
705 * @channel_thread: the #AgsChannelThread
706 * @sound_scope: the sound scope
707 *
708 * Set sound scope.
709 *
710 * Since: 3.0.0
711 */
712 void
ags_channel_thread_set_sound_scope(AgsChannelThread * channel_thread,gint sound_scope)713 ags_channel_thread_set_sound_scope(AgsChannelThread *channel_thread,
714 gint sound_scope)
715 {
716 GRecMutex *thread_mutex;
717
718 if(!AGS_IS_CHANNEL_THREAD(channel_thread)){
719 return;
720 }
721
722 thread_mutex = AGS_THREAD_GET_OBJ_MUTEX(channel_thread);
723
724 /* set scope */
725 g_rec_mutex_lock(thread_mutex);
726
727 channel_thread->sound_scope = sound_scope;
728
729 g_rec_mutex_unlock(thread_mutex);
730 }
731
732 /**
733 * ags_channel_thread_get_do_fx_staging:
734 * @channel_thread: the #AgsChannelThread
735 *
736 * Get do fx staging.
737 *
738 * Returns: %TRUE if set, otherwise %FALSE
739 *
740 * Since: 3.3.0
741 */
742 gboolean
ags_channel_thread_get_do_fx_staging(AgsChannelThread * channel_thread)743 ags_channel_thread_get_do_fx_staging(AgsChannelThread *channel_thread)
744 {
745 gboolean do_fx_staging;
746
747 GRecMutex *thread_mutex;
748
749 if(!AGS_IS_CHANNEL_THREAD(channel_thread)){
750 return(FALSE);
751 }
752
753 thread_mutex = AGS_THREAD_GET_OBJ_MUTEX(channel_thread);
754
755 /* get do fx staging */
756 g_rec_mutex_lock(thread_mutex);
757
758 do_fx_staging = channel_thread->do_fx_staging;
759
760 g_rec_mutex_unlock(thread_mutex);
761
762 return(do_fx_staging);
763 }
764
765 /**
766 * ags_channel_thread_set_do_fx_staging:
767 * @channel_thread: the #AgsChannelThread
768 * @do_fx_staging: %TRUE if do fx staging, else %FALSe
769 *
770 * Set do fx staging.
771 *
772 * Since: 3.3.0
773 */
774 void
ags_channel_thread_set_do_fx_staging(AgsChannelThread * channel_thread,gboolean do_fx_staging)775 ags_channel_thread_set_do_fx_staging(AgsChannelThread *channel_thread, gboolean do_fx_staging)
776 {
777 GRecMutex *thread_mutex;
778
779 if(!AGS_IS_CHANNEL_THREAD(channel_thread)){
780 return;
781 }
782
783 thread_mutex = AGS_THREAD_GET_OBJ_MUTEX(channel_thread);
784
785 /* get do fx staging */
786 g_rec_mutex_lock(thread_mutex);
787
788 channel_thread->do_fx_staging = do_fx_staging;
789
790 g_rec_mutex_unlock(thread_mutex);
791 }
792
793 /**
794 * ags_channel_thread_get_staging_program:
795 * @channel_thread: the #AgsChannelThread
796 * @staging_program_count: (out): the staging program count
797 *
798 * Get staging program.
799 *
800 * Returns: (transfer full): the staging program
801 *
802 * Since: 3.3.0
803 */
804 guint*
ags_channel_thread_get_staging_program(AgsChannelThread * channel_thread,guint * staging_program_count)805 ags_channel_thread_get_staging_program(AgsChannelThread *channel_thread,
806 guint *staging_program_count)
807 {
808 guint *staging_program;
809
810 GRecMutex *thread_mutex;
811
812 if(!AGS_IS_CHANNEL_THREAD(channel_thread)){
813 if(staging_program_count != NULL){
814 staging_program_count[0] = 0;
815 }
816
817 return(NULL);
818 }
819
820 thread_mutex = AGS_THREAD_GET_OBJ_MUTEX(channel_thread);
821
822 /* get staging program */
823 staging_program = NULL;
824
825 g_rec_mutex_lock(thread_mutex);
826
827 if(channel_thread->staging_program_count > 0){
828 staging_program = (guint *) g_malloc(channel_thread->staging_program_count * sizeof(guint));
829
830 memcpy(staging_program, channel_thread->staging_program, channel_thread->staging_program_count * sizeof(guint));
831 }
832
833 if(staging_program_count != NULL){
834 staging_program_count[0] = channel_thread->staging_program_count;
835 }
836
837 g_rec_mutex_unlock(thread_mutex);
838
839 return(staging_program);
840 }
841
842 /**
843 * ags_channel_thread_set_staging_program:
844 * @channel_thread: the #AgsChannelThread
845 * @staging_program: (transfer none): the staging program
846 * @staging_program_count: the staging program count
847 *
848 * Set staging program.
849 *
850 * Since: 3.3.0
851 */
852 void
ags_channel_thread_set_staging_program(AgsChannelThread * channel_thread,guint * staging_program,guint staging_program_count)853 ags_channel_thread_set_staging_program(AgsChannelThread *channel_thread,
854 guint *staging_program,
855 guint staging_program_count)
856 {
857 GRecMutex *thread_mutex;
858
859 if(!AGS_IS_CHANNEL_THREAD(channel_thread)){
860 return;
861 }
862
863 thread_mutex = AGS_THREAD_GET_OBJ_MUTEX(channel_thread);
864
865 /* set staging program */
866 g_rec_mutex_lock(thread_mutex);
867
868 g_free(channel_thread->staging_program);
869
870 if(staging_program_count > 0){
871 channel_thread->staging_program = (guint *) g_malloc(staging_program_count * sizeof(guint));
872
873 memcpy(channel_thread->staging_program, staging_program, staging_program_count * sizeof(guint));
874 }else{
875 channel_thread->staging_program = NULL;
876 }
877
878 channel_thread->staging_program_count = staging_program_count;
879
880 g_rec_mutex_unlock(thread_mutex);
881 }
882
883 /**
884 * ags_channel_thread_new:
885 * @default_output_soundcard: the #GObject
886 * @channel: the #AgsChannel
887 *
888 * Create a new #AgsChannelThread.
889 *
890 * Returns: the new #AgsChannelThread
891 *
892 * Since: 3.0.0
893 */
894 AgsChannelThread*
ags_channel_thread_new(GObject * default_output_soundcard,GObject * channel)895 ags_channel_thread_new(GObject *default_output_soundcard,
896 GObject *channel)
897 {
898 AgsChannelThread *channel_thread;
899
900 channel_thread = (AgsChannelThread *) g_object_new(AGS_TYPE_CHANNEL_THREAD,
901 "default-output-soundcard", default_output_soundcard,
902 "channel", channel,
903 NULL);
904
905
906 return(channel_thread);
907 }
908