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