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, &param) == -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