1 /* GSequencer - Advanced GTK Sequencer
2  * Copyright (C) 2005-2019 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  * Yuri Victorovich <yuri@FreeBSD.org> (tiny change) - provided FreeBSD and
20  *   DragonFly macros.
21  */
22 
23 #include <ags/audio/ags_recall_dssi.h>
24 
25 #include <ags/plugin/ags_dssi_manager.h>
26 #include <ags/plugin/ags_dssi_plugin.h>
27 #include <ags/plugin/ags_plugin_port.h>
28 #include <ags/plugin/ags_ladspa_conversion.h>
29 
30 #include <ags/audio/ags_port.h>
31 #include <ags/audio/ags_port_util.h>
32 
33 #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__)
34 #include <machine/endian.h>
35 #else
36 #ifndef AGS_W32API
37 #include <endian.h>
38 #endif
39 #endif
40 
41 #if defined(AGS_W32API)
42 #include <windows.h>
43 #else
44 #include <dlfcn.h>
45 #endif
46 
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <sys/types.h>
51 #include <unistd.h>
52 
53 #include <libxml/tree.h>
54 
55 #include <ags/i18n.h>
56 
57 void ags_recall_dssi_class_init(AgsRecallDssiClass *recall_dssi_class);
58 void ags_recall_dssi_connectable_interface_init(AgsConnectableInterface *connectable);
59 void ags_recall_dssi_init(AgsRecallDssi *recall_dssi);
60 void ags_recall_dssi_set_property(GObject *gobject,
61 				  guint prop_id,
62 				  const GValue *value,
63 				  GParamSpec *param_spec);
64 void ags_recall_dssi_get_property(GObject *gobject,
65 				  guint prop_id,
66 				  GValue *value,
67 				  GParamSpec *param_spec);
68 void ags_recall_dssi_finalize(GObject *gobject);
69 
70 /**
71  * SECTION:ags_recall_dssi
72  * @short_description: The object interfacing with DSSI
73  * @title: AgsRecallDssi
74  * @section_id:
75  * @include: ags/audio/ags_recall_dssi.h
76  *
77  * #AgsRecallDssi provides DSSI support.
78  */
79 
80 enum{
81   PROP_0,
82   PROP_PLUGIN,
83   PROP_BANK,
84   PROP_PROGRAM,
85 };
86 
87 static gpointer ags_recall_dssi_parent_class = NULL;
88 static AgsConnectableInterface* ags_recall_dssi_parent_connectable_interface;
89 
90 GType
ags_recall_dssi_get_type(void)91 ags_recall_dssi_get_type (void)
92 {
93   static volatile gsize g_define_type_id__volatile = 0;
94 
95   if(g_once_init_enter (&g_define_type_id__volatile)){
96     GType ags_type_recall_dssi = 0;
97 
98     static const GTypeInfo ags_recall_dssi_info = {
99       sizeof (AgsRecallDssiClass),
100       NULL, /* base_init */
101       NULL, /* base_finalize */
102       (GClassInitFunc) ags_recall_dssi_class_init,
103       NULL, /* class_finalize */
104       NULL, /* class_data */
105       sizeof (AgsRecallDssi),
106       0,    /* n_preallocs */
107       (GInstanceInitFunc) ags_recall_dssi_init,
108     };
109 
110     static const GInterfaceInfo ags_connectable_interface_info = {
111       (GInterfaceInitFunc) ags_recall_dssi_connectable_interface_init,
112       NULL, /* interface_finalize */
113       NULL, /* interface_data */
114     };
115 
116     ags_type_recall_dssi = g_type_register_static(AGS_TYPE_RECALL_CHANNEL,
117 						  "AgsRecallDssi",
118 						  &ags_recall_dssi_info,
119 						  0);
120 
121     g_type_add_interface_static(ags_type_recall_dssi,
122 				AGS_TYPE_CONNECTABLE,
123 				&ags_connectable_interface_info);
124 
125     g_once_init_leave(&g_define_type_id__volatile, ags_type_recall_dssi);
126   }
127 
128   return g_define_type_id__volatile;
129 }
130 
131 void
ags_recall_dssi_class_init(AgsRecallDssiClass * recall_dssi)132 ags_recall_dssi_class_init(AgsRecallDssiClass *recall_dssi)
133 {
134   GObjectClass *gobject;
135 
136   GParamSpec *param_spec;
137 
138   ags_recall_dssi_parent_class = g_type_class_peek_parent(recall_dssi);
139 
140   /* GObjectClass */
141   gobject = (GObjectClass *) recall_dssi;
142 
143   gobject->set_property = ags_recall_dssi_set_property;
144   gobject->get_property = ags_recall_dssi_get_property;
145 
146   gobject->finalize = ags_recall_dssi_finalize;
147 
148   /* properties */
149   /**
150    * AgsRecallDssi:plugin:
151    *
152    * The assigned plugin.
153    *
154    * Since: 3.0.0
155    */
156   param_spec = g_param_spec_object("plugin",
157 				   i18n_pspec("plugin of recall dssi"),
158 				   i18n_pspec("The plugin which this recall dssi does run"),
159 				   AGS_TYPE_DSSI_PLUGIN,
160 				   G_PARAM_READABLE | G_PARAM_WRITABLE);
161   g_object_class_install_property(gobject,
162 				  PROP_PLUGIN,
163 				  param_spec);
164 
165   /**
166    * AgsRecallDssi:bank:
167    *
168    * The selected bank.
169    *
170    * Since: 3.0.0
171    */
172   param_spec = g_param_spec_uint("bank",
173 				 i18n_pspec("bank"),
174 				 i18n_pspec("The selected bank"),
175 				 0,
176 				 G_MAXUINT32,
177 				 0,
178 				 G_PARAM_READABLE | G_PARAM_WRITABLE);
179   g_object_class_install_property(gobject,
180 				  PROP_BANK,
181 				  param_spec);
182 
183   /**
184    * AgsRecallDssi:program:
185    *
186    * The selected program.
187    *
188    * Since: 3.0.0
189    */
190   param_spec = g_param_spec_uint("program",
191 				 i18n_pspec("program"),
192 				 i18n_pspec("The selected program"),
193 				 0,
194 				 G_MAXUINT32,
195 				 0,
196 				 G_PARAM_READABLE | G_PARAM_WRITABLE);
197   g_object_class_install_property(gobject,
198 				  PROP_PROGRAM,
199 				  param_spec);
200 }
201 
202 void
ags_recall_dssi_connectable_interface_init(AgsConnectableInterface * connectable)203 ags_recall_dssi_connectable_interface_init(AgsConnectableInterface *connectable)
204 {
205   ags_recall_dssi_parent_connectable_interface = g_type_interface_peek_parent(connectable);
206 }
207 
208 void
ags_recall_dssi_init(AgsRecallDssi * recall_dssi)209 ags_recall_dssi_init(AgsRecallDssi *recall_dssi)
210 {
211   AGS_RECALL(recall_dssi)->name = "ags-dssi";
212   AGS_RECALL(recall_dssi)->version = AGS_RECALL_DEFAULT_VERSION;
213   AGS_RECALL(recall_dssi)->build_id = AGS_RECALL_DEFAULT_BUILD_ID;
214   AGS_RECALL(recall_dssi)->xml_type = "ags-recall-dssi";
215   AGS_RECALL(recall_dssi)->port = NULL;
216 
217   recall_dssi->bank = 0;
218   recall_dssi->program = 0;
219 
220   recall_dssi->plugin = NULL;
221   recall_dssi->plugin_descriptor = NULL;
222 
223   recall_dssi->input_port = NULL;
224   recall_dssi->input_lines = 0;
225 
226   recall_dssi->output_port = NULL;
227   recall_dssi->output_lines = 0;
228 }
229 
230 void
ags_recall_dssi_set_property(GObject * gobject,guint prop_id,const GValue * value,GParamSpec * param_spec)231 ags_recall_dssi_set_property(GObject *gobject,
232 			     guint prop_id,
233 			     const GValue *value,
234 			     GParamSpec *param_spec)
235 {
236   AgsRecallDssi *recall_dssi;
237 
238   GRecMutex *recall_mutex;
239 
240   recall_dssi = AGS_RECALL_DSSI(gobject);
241 
242   /* get recall mutex */
243   recall_mutex = AGS_RECALL_GET_OBJ_MUTEX(recall_dssi);
244 
245   switch(prop_id){
246   case PROP_PLUGIN:
247     {
248       AgsDssiPlugin *plugin;
249 
250       plugin = (AgsDssiPlugin *) g_value_get_object(value);
251 
252       g_rec_mutex_lock(recall_mutex);
253 
254       if(recall_dssi->plugin == plugin){
255 	g_rec_mutex_unlock(recall_mutex);
256 
257 	return;
258       }
259 
260       if(recall_dssi->plugin != NULL){
261 	g_object_unref(recall_dssi->plugin);
262       }
263 
264       if(plugin != NULL){
265 	g_object_ref(plugin);
266       }
267 
268       recall_dssi->plugin = plugin;
269 
270       g_rec_mutex_unlock(recall_mutex);
271     }
272     break;
273   case PROP_BANK:
274     {
275       g_rec_mutex_lock(recall_mutex);
276 
277       recall_dssi->bank = g_value_get_uint(value);
278 
279       g_rec_mutex_unlock(recall_mutex);
280     }
281     break;
282   case PROP_PROGRAM:
283     {
284       g_rec_mutex_lock(recall_mutex);
285 
286       recall_dssi->program = g_value_get_uint(value);
287 
288       g_rec_mutex_unlock(recall_mutex);
289     }
290     break;
291   default:
292     G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, param_spec);
293     break;
294   };
295 }
296 
297 void
ags_recall_dssi_get_property(GObject * gobject,guint prop_id,GValue * value,GParamSpec * param_spec)298 ags_recall_dssi_get_property(GObject *gobject,
299 			     guint prop_id,
300 			     GValue *value,
301 			     GParamSpec *param_spec)
302 {
303   AgsRecallDssi *recall_dssi;
304 
305   GRecMutex *recall_mutex;
306 
307   recall_dssi = AGS_RECALL_DSSI(gobject);
308 
309   /* get recall mutex */
310   recall_mutex = AGS_RECALL_GET_OBJ_MUTEX(recall_dssi);
311 
312   switch(prop_id){
313   case PROP_PLUGIN:
314     {
315       g_rec_mutex_lock(recall_mutex);
316 
317       g_value_set_object(value, recall_dssi->plugin);
318 
319       g_rec_mutex_unlock(recall_mutex);
320     }
321     break;
322   case PROP_BANK:
323     {
324       g_rec_mutex_lock(recall_mutex);
325 
326       g_value_set_uint(value, recall_dssi->bank);
327 
328       g_rec_mutex_unlock(recall_mutex);
329     }
330     break;
331   case PROP_PROGRAM:
332     {
333       g_rec_mutex_lock(recall_mutex);
334 
335       g_value_set_uint(value, recall_dssi->program);
336 
337       g_rec_mutex_unlock(recall_mutex);
338     }
339     break;
340   default:
341     G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, param_spec);
342     break;
343   }
344 }
345 
346 void
ags_recall_dssi_finalize(GObject * gobject)347 ags_recall_dssi_finalize(GObject *gobject)
348 {
349   AgsRecallDssi *recall_dssi;
350 
351   recall_dssi = AGS_RECALL_DSSI(gobject);
352 
353   if(recall_dssi->input_port != NULL){
354     free(recall_dssi->input_port);
355   }
356 
357   if(recall_dssi->output_port != NULL){
358     free(recall_dssi->output_port);
359   }
360 
361   /* call parent */
362   G_OBJECT_CLASS(ags_recall_dssi_parent_class)->finalize(gobject);
363 }
364 
365 /**
366  * ags_recall_dssi_load:
367  * @recall_dssi: the #AgsRecallDssi
368  *
369  * Set up DSSI handle.
370  *
371  * Since: 3.0.0
372  */
373 void
ags_recall_dssi_load(AgsRecallDssi * recall_dssi)374 ags_recall_dssi_load(AgsRecallDssi *recall_dssi)
375 {
376   AgsDssiPlugin *dssi_plugin;
377 
378   gchar *filename;
379   gchar *effect;
380 
381   guint effect_index;
382 
383   void *plugin_so;
384   DSSI_Descriptor_Function dssi_descriptor;
385   DSSI_Descriptor *plugin_descriptor;
386 
387   GRecMutex *recall_mutex;
388 
389   if(!AGS_IS_RECALL_DSSI(recall_dssi)){
390     return;
391   }
392 
393   /* get recall mutex */
394   recall_mutex = AGS_RECALL_GET_OBJ_MUTEX(recall_dssi);
395 
396   /* get some fields */
397   g_rec_mutex_lock(recall_mutex);
398 
399   filename = g_strdup(AGS_RECALL(recall_dssi)->filename);
400   effect = g_strdup(AGS_RECALL(recall_dssi)->effect);
401 
402   effect_index = AGS_RECALL(recall_dssi)->effect_index;
403 
404   g_rec_mutex_unlock(recall_mutex);
405 
406   /* find dssi plugin */
407   dssi_plugin = ags_dssi_manager_find_dssi_plugin(ags_dssi_manager_get_instance(),
408 						  filename, effect);
409   g_free(filename);
410   g_free(effect);
411 
412   g_object_get(dssi_plugin,
413 	       "plugin-so", &plugin_so,
414 	       NULL);
415 
416   if(plugin_so){
417     gboolean success;
418 
419     success = FALSE;
420 
421 #ifdef AGS_W32API
422     dssi_descriptor = (DSSI_Descriptor_Function) GetProcAddress(plugin_so,
423 								"dssi_descriptor");
424 
425     success = (!dssi_descriptor) ? FALSE: TRUE;
426 #else
427     dssi_descriptor = (DSSI_Descriptor_Function) dlsym(plugin_so,
428 						       "dssi_descriptor");
429 
430     success = (dlerror() == NULL) ? TRUE: FALSE;
431 #endif
432 
433     if(success && dssi_descriptor){
434       g_rec_mutex_lock(recall_mutex);
435 
436       recall_dssi->plugin_descriptor =
437 	plugin_descriptor = dssi_descriptor((unsigned long) effect_index);
438 
439       g_rec_mutex_unlock(recall_mutex);
440     }
441   }
442 }
443 
444 /**
445  * ags_recall_dssi_load_ports:
446  * @recall_dssi: the #AgsRecallDssi
447  *
448  * Set up DSSI ports.
449  *
450  * Returns: (element-type AgsAudio.Port) (transfer full): the #GList-struct containing #AgsPort
451  *
452  * Since: 3.0.0
453  */
454 GList*
ags_recall_dssi_load_ports(AgsRecallDssi * recall_dssi)455 ags_recall_dssi_load_ports(AgsRecallDssi *recall_dssi)
456 {
457   AgsPort *current_port;
458 
459   AgsDssiPlugin *dssi_plugin;
460 
461   GList *port, *retval;
462   GList *plugin_port_start, *plugin_port;
463 
464   gchar *filename;
465   gchar *effect;
466 
467   guint effect_index;
468 
469   guint port_count;
470   guint i;
471 
472   GRecMutex *recall_mutex;
473   GRecMutex *base_plugin_mutex;
474 
475   if(!AGS_IS_RECALL_DSSI(recall_dssi)){
476     return(NULL);
477   }
478 
479   /* get recall mutex */
480   recall_mutex = AGS_RECALL_GET_OBJ_MUTEX(recall_dssi);
481 
482   /* get some fields */
483   g_rec_mutex_lock(recall_mutex);
484 
485   filename = g_strdup(AGS_RECALL(recall_dssi)->filename);
486   effect = g_strdup(AGS_RECALL(recall_dssi)->effect);
487 
488   effect_index = AGS_RECALL(recall_dssi)->effect_index;
489 
490   g_rec_mutex_unlock(recall_mutex);
491 
492   /* find dssi plugin */
493   dssi_plugin = ags_dssi_manager_find_dssi_plugin(ags_dssi_manager_get_instance(),
494 						  filename, effect);
495   g_free(filename);
496   g_free(effect);
497 
498   /* set dssi plugin */
499   g_rec_mutex_lock(recall_mutex);
500 
501   recall_dssi->plugin = dssi_plugin;
502 
503   g_rec_mutex_unlock(recall_mutex);
504 
505   /* get base plugin mutex */
506   base_plugin_mutex = AGS_BASE_PLUGIN_GET_OBJ_MUTEX(dssi_plugin);
507 
508   /* get port descriptor */
509   g_rec_mutex_lock(base_plugin_mutex);
510 
511   plugin_port =
512     plugin_port_start = g_list_copy(AGS_BASE_PLUGIN(dssi_plugin)->plugin_port);
513 
514   g_rec_mutex_unlock(base_plugin_mutex);
515 
516   port = NULL;
517   retval = NULL;
518 
519   if(plugin_port != NULL){
520     port_count = g_list_length(plugin_port_start);
521 
522     for(i = 0; i < port_count; i++){
523       AgsPluginPort *current_plugin_port;
524 
525       GRecMutex *plugin_port_mutex;
526 
527       current_plugin_port = AGS_PLUGIN_PORT(plugin_port->data);
528 
529       /* get plugin port mutex */
530       plugin_port_mutex = AGS_PLUGIN_PORT_GET_OBJ_MUTEX(current_plugin_port);
531 
532       if(ags_plugin_port_test_flags(current_plugin_port,
533 				    AGS_PLUGIN_PORT_CONTROL)){
534 	gchar *plugin_name;
535 	gchar *specifier;
536 	gchar *control_port;
537 
538 	GValue *default_value;
539 
540 	plugin_name = g_strdup_printf("dssi-%u", dssi_plugin->unique_id);
541 	control_port = g_strdup_printf("%u/%u",
542 				       i,
543 				       port_count);
544 
545 	default_value = g_new0(GValue,
546 			       1);
547 
548 	g_rec_mutex_lock(plugin_port_mutex);
549 
550 	specifier = g_strdup(current_plugin_port->port_name);
551 
552 	g_value_init(default_value,
553 		     G_TYPE_FLOAT);
554 	g_value_copy(current_plugin_port->default_value,
555 		     default_value);
556 
557 	g_rec_mutex_unlock(plugin_port_mutex);
558 
559 	current_port = g_object_new(AGS_TYPE_PORT,
560 				    "plugin-name", plugin_name,
561 				    "specifier", specifier,
562 				    "control-port", control_port,
563 				    "port-value-is-pointer", FALSE,
564 				    "port-value-type", G_TYPE_FLOAT,
565 				    NULL);
566 	current_port->flags |= AGS_PORT_USE_LADSPA_FLOAT;
567 	g_object_ref(current_port);
568 
569 	if(ags_plugin_port_test_flags(current_plugin_port,
570 				      AGS_PLUGIN_PORT_OUTPUT)){
571 	  current_port->flags |= AGS_PORT_IS_OUTPUT;
572 
573 	  ags_recall_set_flags((AgsRecall *) recall_dssi,
574 			       AGS_RECALL_HAS_OUTPUT_PORT);
575 
576 	}else{
577 	  if(!ags_plugin_port_test_flags(current_plugin_port,
578 					 AGS_PLUGIN_PORT_INTEGER) &&
579 	     !ags_plugin_port_test_flags(current_plugin_port,
580 					 AGS_PLUGIN_PORT_TOGGLED)){
581 	    current_port->flags |= AGS_PORT_INFINITE_RANGE;
582 	  }
583 	}
584 
585 	g_object_set(current_port,
586 		     "plugin-port", current_plugin_port,
587 		     NULL);
588 
589 	ags_recall_dssi_load_conversion(recall_dssi,
590 					(GObject *) current_port,
591 					current_plugin_port);
592 
593 	ags_port_safe_write_raw(current_port,
594 				default_value);
595 
596 	port = g_list_prepend(port,
597 			      current_port);
598 
599 	g_value_unset(default_value);
600 	g_free(default_value);
601 
602 	g_free(plugin_name);
603 	g_free(control_port);
604 	g_free(specifier);
605       }else if(ags_plugin_port_test_flags(current_plugin_port,
606 					  AGS_PLUGIN_PORT_AUDIO)){
607 	g_rec_mutex_lock(recall_mutex);
608 
609 	if(ags_plugin_port_test_flags(current_plugin_port,
610 				      AGS_PLUGIN_PORT_INPUT)){
611 	  if(recall_dssi->input_port == NULL){
612 	    recall_dssi->input_port = (guint *) malloc(sizeof(guint));
613 	    recall_dssi->input_port[0] = i;
614 	  }else{
615 	    recall_dssi->input_port = (guint *) realloc(recall_dssi->input_port,
616 							(recall_dssi->input_lines + 1) * sizeof(guint));
617 	    recall_dssi->input_port[recall_dssi->input_lines] = i;
618 	  }
619 
620 	  recall_dssi->input_lines += 1;
621 	}else if(ags_plugin_port_test_flags(current_plugin_port,
622 					    AGS_PLUGIN_PORT_OUTPUT)){
623 	  if(recall_dssi->output_port == NULL){
624 	    recall_dssi->output_port = (guint *) malloc(sizeof(guint));
625 	    recall_dssi->output_port[0] = i;
626 	  }else{
627 	    recall_dssi->output_port = (guint *) realloc(recall_dssi->output_port,
628 							 (recall_dssi->output_lines + 1) * sizeof(guint));
629 	    recall_dssi->output_port[recall_dssi->output_lines] = i;
630 	  }
631 
632 	  recall_dssi->output_lines += 1;
633 	}
634 
635 	g_rec_mutex_unlock(recall_mutex);
636       }
637 
638       /* iterate plugin port */
639       plugin_port = plugin_port->next;
640     }
641 
642     /* reverse port */
643     g_rec_mutex_lock(recall_mutex);
644 
645     AGS_RECALL(recall_dssi)->port = g_list_reverse(port);
646 
647     retval = g_list_copy(AGS_RECALL(recall_dssi)->port);
648 
649     g_rec_mutex_unlock(recall_mutex);
650   }
651 
652   g_list_free(plugin_port_start);
653 
654   return(retval);
655 }
656 
657 /**
658  * ags_recall_dssi_load_conversion:
659  * @recall_dssi: the #AgsRecallDssi
660  * @port: the #AgsPort
661  * @plugin_port: the #AgsPluginPort
662  *
663  * Loads conversion object by using @plugin_port and sets in on @port.
664  *
665  * Since: 3.0.0
666  */
667 void
ags_recall_dssi_load_conversion(AgsRecallDssi * recall_dssi,GObject * port,gpointer plugin_port)668 ags_recall_dssi_load_conversion(AgsRecallDssi *recall_dssi,
669 				GObject *port,
670 				gpointer plugin_port)
671 {
672   AgsLadspaConversion *ladspa_conversion;
673 
674   if(!AGS_IS_RECALL_DSSI(recall_dssi) ||
675      !AGS_IS_PORT(port) ||
676      !AGS_IS_PLUGIN_PORT(plugin_port)){
677     return;
678   }
679 
680   ags_port_util_load_ladspa_conversion(port,
681 				       plugin_port);
682 }
683 
684 /**
685  * ags_recall_dssi_find:
686  * @recall: (element-type AgsAudio.Recall) (transfer none): the #GList-struct containing #AgsRecall
687  * @filename: plugin filename
688  * @effect: effect's name
689  *
690  * Retrieve DSSI recall.
691  *
692  * Returns: (element-type AgsAudio.Recall) (transfer none): Next matching #GList-struct or %NULL
693  *
694  * Since: 3.0.0
695  */
696 GList*
ags_recall_dssi_find(GList * recall,gchar * filename,gchar * effect)697 ags_recall_dssi_find(GList *recall,
698 		     gchar *filename, gchar *effect)
699 {
700   gboolean success;
701 
702   GRecMutex *recall_mutex;
703 
704   while(recall != NULL){
705     if(AGS_IS_RECALL_DSSI(recall->data)){
706       /* get recall mutex */
707       recall_mutex = AGS_RECALL_GET_OBJ_MUTEX(recall->data);
708 
709       /* check filename and effect */
710       g_rec_mutex_lock(recall_mutex);
711 
712       success = (!g_strcmp0(AGS_RECALL(recall->data)->filename,
713 			    filename) &&
714 		 !g_strcmp0(AGS_RECALL(recall->data)->effect,
715 			    effect)) ? TRUE: FALSE;
716 
717       g_rec_mutex_unlock(recall_mutex);
718 
719       if(success){
720 	return(recall);
721       }
722     }
723 
724     recall = recall->next;
725   }
726 
727   return(NULL);
728 }
729 
730 /**
731  * ags_recall_dssi_new:
732  * @source: the #AgsChannel as source
733  * @filename: the DSSI plugin filename
734  * @effect: effect's name
735  * @effect_index: effect's index
736  *
737  * Creates a new instance of #AgsRecallDssi
738  *
739  * Returns: the new #AgsRecallDssi
740  *
741  * Since: 3.0.0
742  */
743 AgsRecallDssi*
ags_recall_dssi_new(AgsChannel * source,gchar * filename,gchar * effect,guint effect_index)744 ags_recall_dssi_new(AgsChannel *source,
745 		    gchar *filename,
746 		    gchar *effect,
747 		    guint effect_index)
748 {
749   AgsRecallDssi *recall_dssi;
750 
751   GObject *output_soundcard;
752 
753   output_soundcard = NULL;
754 
755   if(source != NULL){
756     g_object_get(source,
757 		 "output-soundcard", &output_soundcard,
758 		 NULL);
759   }
760 
761   recall_dssi = (AgsRecallDssi *) g_object_new(AGS_TYPE_RECALL_DSSI,
762 					       "output-soundcard", output_soundcard,
763 					       "source", source,
764 					       "filename", filename,
765 					       "effect", effect,
766 					       "effect-index", effect_index,
767 					       NULL);
768 
769   if(output_soundcard != NULL){
770     g_object_unref(output_soundcard);
771   }
772 
773   return(recall_dssi);
774 }
775