1 /* GSequencer - Advanced GTK Sequencer
2  * Copyright (C) 2005-2020 Joël Krähemann
3  *
4  * This file is part of GSequencer.
5  *
6  * GSequencer is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * GSequencer is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with GSequencer.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include <ags/audio/ags_automation.h>
21 
22 #include <ags/plugin/ags_base_plugin.h>
23 #include <ags/plugin/ags_plugin_port.h>
24 
25 #include <ags/audio/ags_audio.h>
26 #include <ags/audio/ags_port.h>
27 
28 #include <ags/config.h>
29 
30 #include <stdlib.h>
31 
32 #include <math.h>
33 #include <errno.h>
34 
35 #include <ags/i18n.h>
36 
37 void ags_automation_class_init(AgsAutomationClass *automation);
38 void ags_automation_init(AgsAutomation *automation);
39 void ags_automation_set_property(GObject *gobject,
40 				 guint prop_id,
41 				 const GValue *value,
42 				 GParamSpec *param_spec);
43 void ags_automation_get_property(GObject *gobject,
44 				 guint prop_id,
45 				 GValue *value,
46 				 GParamSpec *param_spec);
47 void ags_automation_dispose(GObject *gobject);
48 void ags_automation_finalize(GObject *gobject);
49 
50 void ags_automation_insert_from_clipboard_version_0_4_3(AgsAutomation *automation,
51 							xmlNode *root_node, char *version,
52 							char *x_boundary, char *y_boundary,
53 							gboolean from_x_offset, guint x_offset,
54 							gboolean from_y_offset, guint y_offset,
55 							gboolean match_line, gboolean no_duplicates,
56 							guint current_line,
57 							gboolean match_timestamp);
58 
59 void ags_automation_insert_native_scale_from_clipboard(AgsAutomation *automation,
60 						       xmlNode *root_node, char *version,
61 						       char *x_boundary, char *y_boundary,
62 						       gboolean from_x_offset, guint x_offset,
63 						       gboolean from_y_offset, guint y_offset,
64 						       gboolean match_line, gboolean no_duplicates);
65 
66 /**
67  * SECTION:ags_automation
68  * @short_description: Automation class supporting selection and clipboard.
69  * @title: AgsAutomation
70  * @section_id:
71  * @include: ags/audio/ags_automation.h
72  *
73  * #AgsAutomation acts as a container of #AgsAcceleration.
74  */
75 
76 enum{
77   PROP_0,
78   PROP_AUDIO,
79   PROP_CHANNEL_TYPE,
80   PROP_LINE,
81   PROP_TIMESTAMP,
82   PROP_CONTROL_NAME,
83   PROP_STEPS,
84   PROP_UPPER,
85   PROP_LOWER,
86   PROP_DEFAULT_VALUE,
87   PROP_PORT,
88   PROP_ACCELERATION,
89 };
90 
91 static gpointer ags_automation_parent_class = NULL;
92 
93 GType
ags_automation_get_type()94 ags_automation_get_type()
95 {
96   static volatile gsize g_define_type_id__volatile = 0;
97 
98   if(g_once_init_enter (&g_define_type_id__volatile)){
99     GType ags_type_automation = 0;
100 
101     static const GTypeInfo ags_automation_info = {
102       sizeof(AgsAutomationClass),
103       NULL,
104       NULL,
105       (GClassInitFunc) ags_automation_class_init,
106       NULL,
107       NULL,
108       sizeof(AgsAutomation),
109       0,
110       (GInstanceInitFunc) ags_automation_init,
111     };
112 
113     ags_type_automation = g_type_register_static(G_TYPE_OBJECT,
114 						 "AgsAutomation",
115 						 &ags_automation_info,
116 						 0);
117 
118     g_once_init_leave(&g_define_type_id__volatile, ags_type_automation);
119   }
120 
121   return g_define_type_id__volatile;
122 }
123 
124 GType
ags_automation_flags_get_type()125 ags_automation_flags_get_type()
126 {
127   static volatile gsize g_flags_type_id__volatile;
128 
129   if(g_once_init_enter (&g_flags_type_id__volatile)){
130     static const GFlagsValue values[] = {
131       { AGS_AUTOMATION_BYPASS, "AGS_AUTOMATION_BYPASS", "automation-bypass" },
132       { 0, NULL, NULL }
133     };
134 
135     GType g_flags_type_id = g_flags_register_static(g_intern_static_string("AgsAutomationFlags"), values);
136 
137     g_once_init_leave (&g_flags_type_id__volatile, g_flags_type_id);
138   }
139 
140   return g_flags_type_id__volatile;
141 }
142 
143 void
ags_automation_class_init(AgsAutomationClass * automation)144 ags_automation_class_init(AgsAutomationClass *automation)
145 {
146   GObjectClass *gobject;
147   GParamSpec *param_spec;
148 
149   ags_automation_parent_class = g_type_class_peek_parent(automation);
150 
151   gobject = (GObjectClass *) automation;
152 
153   gobject->set_property = ags_automation_set_property;
154   gobject->get_property = ags_automation_get_property;
155 
156   gobject->dispose = ags_automation_dispose;
157   gobject->finalize = ags_automation_finalize;
158 
159   /* properties */
160   /**
161    * AgsAutomation:audio:
162    *
163    * The assigned #AgsAudio
164    *
165    * Since: 3.0.0
166    */
167   param_spec = g_param_spec_object("audio",
168 				   i18n_pspec("audio of automation"),
169 				   i18n_pspec("The audio of automation"),
170 				   AGS_TYPE_AUDIO,
171 				   G_PARAM_READABLE | G_PARAM_WRITABLE);
172   g_object_class_install_property(gobject,
173 				  PROP_AUDIO,
174 				  param_spec);
175 
176   /**
177    * AgsAutomation:channel-type:
178    *
179    * The effect's assigned channel type.
180    *
181    * Since: 3.0.0
182    */
183   param_spec =  g_param_spec_gtype("channel-type",
184 				   i18n_pspec("channel type to apply"),
185 				   i18n_pspec("The channel type to apply"),
186 				   G_TYPE_NONE,
187 				   G_PARAM_READABLE | G_PARAM_WRITABLE);
188   g_object_class_install_property(gobject,
189 				  PROP_CHANNEL_TYPE,
190 				  param_spec);
191 
192   /**
193    * AgsAutomation:line:
194    *
195    * The effect's line.
196    *
197    * Since: 3.0.0
198    */
199   param_spec =  g_param_spec_uint("line",
200 				  i18n_pspec("line of effect"),
201 				  i18n_pspec("The numerical line of effect"),
202 				  0,
203 				  65535,
204 				  0,
205 				  G_PARAM_READABLE | G_PARAM_WRITABLE);
206   g_object_class_install_property(gobject,
207 				  PROP_LINE,
208 				  param_spec);
209 
210   /**
211    * AgsAutomation:timestamp:
212    *
213    * The automation's timestamp.
214    *
215    * Since: 3.0.0
216    */
217   param_spec = g_param_spec_object("timestamp",
218 				   i18n_pspec("timestamp of automation"),
219 				   i18n_pspec("The timestamp of automation"),
220 				   AGS_TYPE_TIMESTAMP,
221 				   G_PARAM_READABLE | G_PARAM_WRITABLE);
222   g_object_class_install_property(gobject,
223 				  PROP_TIMESTAMP,
224 				  param_spec);
225 
226   /**
227    * AgsAutomation:control-name:
228    *
229    * The effect's assigned control name.
230    *
231    * Since: 3.0.0
232    */
233   param_spec =  g_param_spec_string("control-name",
234 				    i18n_pspec("control name"),
235 				    i18n_pspec("The control name"),
236 				    NULL,
237 				    G_PARAM_READABLE | G_PARAM_WRITABLE);
238   g_object_class_install_property(gobject,
239 				  PROP_CONTROL_NAME,
240 				  param_spec);
241 
242   /**
243    * AgsAutomation:steps:
244    *
245    * The effect's steps.
246    *
247    * Since: 3.0.0
248    */
249   param_spec =  g_param_spec_uint("steps",
250 				  i18n_pspec("steps of effect"),
251 				  i18n_pspec("The steps of effect"),
252 				  0,
253 				  G_MAXUINT32,
254 				  0,
255 				  G_PARAM_READABLE | G_PARAM_WRITABLE);
256   g_object_class_install_property(gobject,
257 				  PROP_STEPS,
258 				  param_spec);
259 
260   /**
261    * AgsAutomation:upper:
262    *
263    * The effect's upper.
264    *
265    * Since: 3.0.0
266    */
267   param_spec =  g_param_spec_double("upper",
268 				    i18n_pspec("upper of effect"),
269 				    i18n_pspec("The upper of effect"),
270 				    -1.0 * G_MAXDOUBLE,
271 				    G_MAXDOUBLE,
272 				    0.0,
273 				    G_PARAM_READABLE | G_PARAM_WRITABLE);
274   g_object_class_install_property(gobject,
275 				  PROP_UPPER,
276 				  param_spec);
277 
278   /**
279    * AgsAutomation:lower:
280    *
281    * The effect's lower.
282    *
283    * Since: 3.0.0
284    */
285   param_spec =  g_param_spec_double("lower",
286 				    i18n_pspec("lower of effect"),
287 				    i18n_pspec("The lower of effect"),
288 				    -1.0 * G_MAXDOUBLE,
289 				    G_MAXDOUBLE,
290 				    0.0,
291 				    G_PARAM_READABLE | G_PARAM_WRITABLE);
292   g_object_class_install_property(gobject,
293 				  PROP_LOWER,
294 				  param_spec);
295 
296 
297   /**
298    * AgsAutomation:default-value:
299    *
300    * The effect's default-value.
301    *
302    * Since: 3.0.0
303    */
304   param_spec =  g_param_spec_double("default-value",
305 				    i18n_pspec("default value of effect"),
306 				    i18n_pspec("The default value of effect"),
307 				    -1.0 * G_MAXDOUBLE,
308 				    G_MAXDOUBLE,
309 				    0.0,
310 				    G_PARAM_READABLE | G_PARAM_WRITABLE);
311   g_object_class_install_property(gobject,
312 				  PROP_DEFAULT_VALUE,
313 				  param_spec);
314 
315   /**
316    * AgsAutomation:port:
317    *
318    * The assigned #AgsPort
319    *
320    * Since: 3.0.0
321    */
322   param_spec = g_param_spec_object("port",
323 				   i18n_pspec("port of automation"),
324 				   i18n_pspec("The port of automation"),
325 				   AGS_TYPE_PORT,
326 				   G_PARAM_READABLE | G_PARAM_WRITABLE);
327   g_object_class_install_property(gobject,
328 				  PROP_PORT,
329 				  param_spec);
330 
331   /**
332    * AgsAutomation:acceleration: (type GList(AgsAcceleration)) (transfer full)
333    *
334    * The acceleration list.
335    *
336    * Since: 3.0.0
337    */
338   param_spec = g_param_spec_pointer("acceleration",
339 				    i18n_pspec("acceleration"),
340 				    i18n_pspec("The acceleration"),
341 				    G_PARAM_READABLE | G_PARAM_WRITABLE);
342   g_object_class_install_property(gobject,
343 				  PROP_ACCELERATION,
344 				  param_spec);
345 }
346 
347 void
ags_automation_init(AgsAutomation * automation)348 ags_automation_init(AgsAutomation *automation)
349 {
350   automation->flags = 0; // AGS_AUTOMATION_BYPASS
351 
352   /* add automation mutex */
353   g_rec_mutex_init(&(automation->obj_mutex));
354 
355   /*  */
356   automation->audio = NULL;
357   automation->channel_type = G_TYPE_NONE;
358   automation->line = 0;
359 
360   automation->timestamp = ags_timestamp_new();
361 
362   automation->timestamp->flags &= (~AGS_TIMESTAMP_UNIX);
363   automation->timestamp->flags |= AGS_TIMESTAMP_OFFSET;
364 
365   automation->timestamp->timer.ags_offset.offset = 0;
366 
367   g_object_ref(automation->timestamp);
368 
369   automation->control_name = NULL;
370 
371   automation->steps = 8;
372   automation->upper = 1.0;
373   automation->lower = 0.0;
374   automation->default_value = 0.0;
375 
376   automation->source_function = NULL;
377 
378   automation->port = NULL;
379 
380   automation->acceleration = NULL;
381   automation->selection = NULL;
382 }
383 
384 void
ags_automation_set_property(GObject * gobject,guint prop_id,const GValue * value,GParamSpec * param_spec)385 ags_automation_set_property(GObject *gobject,
386 			    guint prop_id,
387 			    const GValue *value,
388 			    GParamSpec *param_spec)
389 {
390   AgsAutomation *automation;
391 
392   GRecMutex *automation_mutex;
393 
394   automation = AGS_AUTOMATION(gobject);
395 
396   /* get automation mutex */
397   automation_mutex = AGS_AUTOMATION_GET_OBJ_MUTEX(automation);
398 
399   switch(prop_id){
400   case PROP_AUDIO:
401     {
402       AgsAudio *audio;
403 
404       audio = (AgsAudio *) g_value_get_object(value);
405 
406       g_rec_mutex_lock(automation_mutex);
407 
408       if(automation->audio == (GObject *) audio){
409 	g_rec_mutex_unlock(automation_mutex);
410 
411 	return;
412       }
413 
414       if(automation->audio != NULL){
415 	g_object_unref(automation->audio);
416       }
417 
418       if(audio != NULL){
419 	g_object_ref(audio);
420       }
421 
422       automation->audio = (GObject *) audio;
423 
424       g_rec_mutex_unlock(automation_mutex);
425     }
426     break;
427   case PROP_LINE:
428     {
429       guint line;
430 
431       line = g_value_get_uint(value);
432 
433       g_rec_mutex_lock(automation_mutex);
434 
435       automation->line = line;
436 
437       g_rec_mutex_unlock(automation_mutex);
438     }
439     break;
440   case PROP_CHANNEL_TYPE:
441     {
442       GType channel_type;
443 
444       channel_type = (GType) g_value_get_gtype(value);
445 
446       g_rec_mutex_lock(automation_mutex);
447 
448       automation->channel_type = channel_type;
449 
450       g_rec_mutex_unlock(automation_mutex);
451     }
452     break;
453   case PROP_CONTROL_NAME:
454     {
455       gchar *control_name;
456 
457       control_name = g_value_get_string(value);
458 
459       g_rec_mutex_lock(automation_mutex);
460 
461       if(automation->control_name != NULL){
462 	g_free(automation->control_name);
463       }
464 
465       automation->control_name = g_strdup(control_name);
466 
467       g_rec_mutex_unlock(automation_mutex);
468     }
469     break;
470   case PROP_PORT:
471     {
472       AgsPort *port;
473       AgsPluginPort *plugin_port;
474 
475       port = g_value_get_object(value);
476 
477       g_rec_mutex_lock(automation_mutex);
478 
479       if(automation->port == (GObject *) port){
480 	g_rec_mutex_unlock(automation_mutex);
481 
482 	return;
483       }
484 
485       if(automation->port != NULL){
486 	g_object_unref(automation->port);
487       }
488 
489       plugin_port = NULL;
490 
491       if(port != NULL){
492 	g_object_ref(port);
493 
494 	g_object_get(port,
495 		     "plugin-port", &plugin_port,
496 		     NULL);
497 
498 	if((AGS_PORT_INFINITE_RANGE & (port->flags)) != 0){
499 	  automation->steps = AGS_AUTOMATION_MAXIMUM_STEPS;
500 	}
501       }
502 
503       automation->port = (GObject *) port;
504 
505       g_rec_mutex_unlock(automation_mutex);
506 
507       if(plugin_port != NULL){
508 	gfloat upper, lower;
509 	guint steps;
510 	guint plugin_port_flags;
511 
512 	GRecMutex *plugin_port_mutex;
513 
514 	/* get plugin port mutex */
515 	plugin_port_mutex = AGS_PLUGIN_PORT_GET_OBJ_MUTEX(plugin_port);
516 
517 	/* get some fields */
518 	g_rec_mutex_lock(plugin_port_mutex);
519 
520 	plugin_port_flags = plugin_port->flags;
521 
522 	lower = g_value_get_float(plugin_port->lower_value);
523 	upper = g_value_get_float(plugin_port->upper_value);
524 
525 	steps = plugin_port->scale_steps;
526 
527 	g_rec_mutex_unlock(plugin_port_mutex);
528 
529 	g_object_set(automation,
530 		     "upper", upper,
531 		     "lower", lower,
532 		     NULL);
533 
534 	if((AGS_PLUGIN_PORT_TOGGLED & (plugin_port_flags)) != 0){
535 	  automation->lower = 0.0;
536 	  automation->upper = 1.0;
537 	  automation->steps = 1;
538 	}else if((AGS_PLUGIN_PORT_INTEGER & (plugin_port_flags)) != 0){
539 	  automation->steps = steps;
540 	}else{
541 	  automation->steps = AGS_AUTOMATION_DEFAULT_PRECISION;
542 	}
543 
544 	g_object_unref(plugin_port);
545       }
546     }
547     break;
548   case PROP_TIMESTAMP:
549     {
550       AgsTimestamp *timestamp;
551 
552       timestamp = (AgsTimestamp *) g_value_get_object(value);
553 
554       g_rec_mutex_lock(automation_mutex);
555 
556       if(automation->timestamp == timestamp){
557 	g_rec_mutex_unlock(automation_mutex);
558 
559 	return;
560       }
561 
562       if(automation->timestamp != NULL){
563 	g_object_unref(G_OBJECT(automation->timestamp));
564       }
565 
566       if(timestamp != NULL){
567 	g_object_ref(G_OBJECT(timestamp));
568       }
569 
570       automation->timestamp = timestamp;
571 
572       g_rec_mutex_unlock(automation_mutex);
573     }
574     break;
575   case PROP_STEPS:
576     {
577       guint steps;
578 
579       steps = g_value_get_uint(value);
580 
581       g_rec_mutex_lock(automation_mutex);
582 
583       automation->steps = steps;
584 
585       g_rec_mutex_unlock(automation_mutex);
586     }
587     break;
588   case PROP_UPPER:
589     {
590       gdouble upper;
591 
592       upper = g_value_get_double(value);
593 
594       g_rec_mutex_lock(automation_mutex);
595 
596       automation->upper = upper;
597 
598       g_rec_mutex_unlock(automation_mutex);
599     }
600     break;
601   case PROP_LOWER:
602     {
603       gdouble lower;
604 
605       lower = g_value_get_double(value);
606 
607       g_rec_mutex_lock(automation_mutex);
608 
609       automation->lower = lower;
610 
611       g_rec_mutex_unlock(automation_mutex);
612     }
613     break;
614   case PROP_DEFAULT_VALUE:
615     {
616       gdouble default_value;
617 
618       default_value = g_value_get_double(value);
619 
620       g_rec_mutex_lock(automation_mutex);
621 
622       automation->default_value = default_value;
623 
624       g_rec_mutex_unlock(automation_mutex);
625     }
626     break;
627   case PROP_ACCELERATION:
628     {
629       AgsAcceleration *acceleration;
630 
631       acceleration = (AgsAcceleration *) g_value_get_object(value);
632 
633       g_rec_mutex_lock(automation_mutex);
634 
635       if(!AGS_IS_ACCELERATION(acceleration) ||
636 	 g_list_find(automation->acceleration, acceleration) != NULL){
637 	g_rec_mutex_unlock(automation_mutex);
638 
639 	return;
640       }
641 
642       g_rec_mutex_unlock(automation_mutex);
643 
644       ags_automation_add_acceleration(automation,
645 				      acceleration,
646 				      FALSE);
647     }
648     break;
649   default:
650     G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, param_spec);
651     break;
652   }
653 }
654 
655 void
ags_automation_get_property(GObject * gobject,guint prop_id,GValue * value,GParamSpec * param_spec)656 ags_automation_get_property(GObject *gobject,
657 			    guint prop_id,
658 			    GValue *value,
659 			    GParamSpec *param_spec)
660 {
661   AgsAutomation *automation;
662 
663   GRecMutex *automation_mutex;
664 
665   automation = AGS_AUTOMATION(gobject);
666 
667   /* get automation mutex */
668   automation_mutex = AGS_AUTOMATION_GET_OBJ_MUTEX(automation);
669 
670   switch(prop_id){
671   case PROP_AUDIO:
672     {
673       g_rec_mutex_lock(automation_mutex);
674 
675       g_value_set_object(value, automation->audio);
676 
677       g_rec_mutex_unlock(automation_mutex);
678     }
679     break;
680   case PROP_LINE:
681     {
682       g_rec_mutex_lock(automation_mutex);
683 
684       g_value_set_uint(value, automation->line);
685 
686       g_rec_mutex_unlock(automation_mutex);
687     }
688     break;
689   case PROP_CHANNEL_TYPE:
690     {
691       g_rec_mutex_lock(automation_mutex);
692 
693       g_value_set_gtype(value, automation->channel_type);
694 
695       g_rec_mutex_unlock(automation_mutex);
696     }
697     break;
698   case PROP_TIMESTAMP:
699     {
700       g_rec_mutex_lock(automation_mutex);
701 
702       g_value_set_object(value, automation->timestamp);
703 
704       g_rec_mutex_unlock(automation_mutex);
705     }
706     break;
707   case PROP_CONTROL_NAME:
708     {
709       g_rec_mutex_lock(automation_mutex);
710 
711       g_value_set_string(value, automation->control_name);
712 
713       g_rec_mutex_unlock(automation_mutex);
714     }
715     break;
716   case PROP_STEPS:
717     {
718       g_rec_mutex_lock(automation_mutex);
719 
720       g_value_set_uint(value, automation->steps);
721 
722       g_rec_mutex_unlock(automation_mutex);
723     }
724     break;
725   case PROP_UPPER:
726     {
727       g_rec_mutex_lock(automation_mutex);
728 
729       g_value_set_double(value, automation->upper);
730 
731       g_rec_mutex_unlock(automation_mutex);
732     }
733     break;
734   case PROP_LOWER:
735     {
736       g_rec_mutex_lock(automation_mutex);
737 
738       g_value_set_double(value, automation->lower);
739 
740       g_rec_mutex_unlock(automation_mutex);
741     }
742     break;
743   case PROP_DEFAULT_VALUE:
744     {
745       g_rec_mutex_lock(automation_mutex);
746 
747       g_value_set_double(value, automation->default_value);
748 
749       g_rec_mutex_unlock(automation_mutex);
750     }
751     break;
752   case PROP_PORT:
753     {
754       g_rec_mutex_lock(automation_mutex);
755 
756       g_value_set_object(value, automation->port);
757 
758       g_rec_mutex_unlock(automation_mutex);
759     }
760     break;
761   case PROP_ACCELERATION:
762     {
763       g_rec_mutex_lock(automation_mutex);
764 
765       g_value_set_pointer(value, g_list_copy_deep(automation->acceleration,
766 						  (GCopyFunc) g_object_ref,
767 						  NULL));
768 
769       g_rec_mutex_unlock(automation_mutex);
770     }
771     break;
772   default:
773     G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, param_spec);
774     break;
775   }
776 }
777 
778 void
ags_automation_dispose(GObject * gobject)779 ags_automation_dispose(GObject *gobject)
780 {
781   AgsAutomation *automation;
782 
783   GList *list;
784 
785   automation = AGS_AUTOMATION(gobject);
786 
787   /* audio */
788   if(automation->audio != NULL){
789     g_object_unref(automation->audio);
790 
791     automation->audio = NULL;
792   }
793 
794   /* timestamp */
795   if(automation->timestamp != NULL){
796     g_object_unref(automation->timestamp);
797 
798     automation->timestamp = NULL;
799   }
800 
801   /* source function */
802   if(automation->source_function != NULL){
803     g_object_run_dispose((GObject *) automation->source_function);
804 
805     g_object_unref(automation->source_function);
806 
807     automation->source_function = NULL;
808   }
809 
810   /* port */
811   if(automation->port != NULL){
812     g_object_unref(automation->port);
813 
814     automation->port = NULL;
815   }
816 
817   /* acceleration */
818   list = automation->acceleration;
819 
820   while(list != NULL){
821     g_object_run_dispose(G_OBJECT(list->data));
822 
823     list = list->next;
824   }
825 
826   g_list_free_full(automation->acceleration,
827 		   g_object_unref);
828   g_list_free_full(automation->selection,
829 		   g_object_unref);
830 
831   automation->acceleration = NULL;
832   automation->selection = NULL;
833 
834   /* call parent */
835   G_OBJECT_CLASS(ags_automation_parent_class)->dispose(gobject);
836 }
837 
838 void
ags_automation_finalize(GObject * gobject)839 ags_automation_finalize(GObject *gobject)
840 {
841   AgsAutomation *automation;
842 
843   automation = AGS_AUTOMATION(gobject);
844 
845   /* audio */
846   if(automation->audio != NULL){
847     g_object_unref(automation->audio);
848   }
849 
850   /* timestamp */
851   if(automation->timestamp != NULL){
852     g_object_unref(automation->timestamp);
853   }
854 
855   /* control name */
856   if(automation->control_name != NULL){
857     free(automation->control_name);
858   }
859 
860   /* source function */
861   if(automation->source_function != NULL){
862     g_object_unref(automation->source_function);
863   }
864 
865   /* port */
866   if(automation->port != NULL){
867     g_object_unref(automation->port);
868   }
869 
870   /* acceleration */
871   g_list_free_full(automation->acceleration,
872 		   g_object_unref);
873 
874   g_list_free_full(automation->selection,
875 		   g_object_unref);
876 
877   /* call parent */
878   G_OBJECT_CLASS(ags_automation_parent_class)->finalize(gobject);
879 }
880 
881 /**
882  * ags_automation_test_flags:
883  * @automation: the #AgsAutomation
884  * @flags: the flags
885  *
886  * Test @flags to be set on @automation.
887  *
888  * Returns: %TRUE if flags are set, else %FALSE
889  *
890  * Since: 3.0.0
891  */
892 gboolean
ags_automation_test_flags(AgsAutomation * automation,guint flags)893 ags_automation_test_flags(AgsAutomation *automation, guint flags)
894 {
895   gboolean retval;
896 
897   GRecMutex *automation_mutex;
898 
899   if(!AGS_IS_AUTOMATION(automation)){
900     return(FALSE);
901   }
902 
903   /* get automation mutex */
904   automation_mutex = AGS_AUTOMATION_GET_OBJ_MUTEX(automation);
905 
906   /* test */
907   g_rec_mutex_lock(automation_mutex);
908 
909   retval = (flags & (automation->flags)) ? TRUE: FALSE;
910 
911   g_rec_mutex_unlock(automation_mutex);
912 
913   return(retval);
914 }
915 
916 /**
917  * ags_automation_set_flags:
918  * @automation: the #AgsAutomation
919  * @flags: the flags
920  *
921  * Set @flags on @automation.
922  *
923  * Since: 3.0.0
924  */
925 void
ags_automation_set_flags(AgsAutomation * automation,guint flags)926 ags_automation_set_flags(AgsAutomation *automation, guint flags)
927 {
928   GRecMutex *automation_mutex;
929 
930   if(!AGS_IS_AUTOMATION(automation)){
931     return;
932   }
933 
934   /* get automation mutex */
935   automation_mutex = AGS_AUTOMATION_GET_OBJ_MUTEX(automation);
936 
937   /* set */
938   g_rec_mutex_lock(automation_mutex);
939 
940   automation->flags |= flags;
941 
942   g_rec_mutex_unlock(automation_mutex);
943 }
944 
945 /**
946  * ags_automation_unset_flags:
947  * @automation: the #AgsAutomation
948  * @flags: the flags
949  *
950  * Unset @flags on @automation.
951  *
952  * Since: 3.0.0
953  */
954 void
ags_automation_unset_flags(AgsAutomation * automation,guint flags)955 ags_automation_unset_flags(AgsAutomation *automation, guint flags)
956 {
957   GRecMutex *automation_mutex;
958 
959   if(!AGS_IS_AUTOMATION(automation)){
960     return;
961   }
962 
963   /* get automation mutex */
964   automation_mutex = AGS_AUTOMATION_GET_OBJ_MUTEX(automation);
965 
966   /* set */
967   g_rec_mutex_lock(automation_mutex);
968 
969   automation->flags &= (~flags);
970 
971   g_rec_mutex_unlock(automation_mutex);
972 }
973 
974 /**
975  * ags_automation_get_obj_mutex:
976  * @automation: the #AgsAutomation
977  *
978  * Get object mutex.
979  *
980  * Returns: the #GRecMutex to lock @automation
981  *
982  * Since: 3.1.0
983  */
984 GRecMutex*
ags_automation_get_obj_mutex(AgsAutomation * automation)985 ags_automation_get_obj_mutex(AgsAutomation *automation)
986 {
987   if(!AGS_IS_AUTOMATION(automation)){
988     return(NULL);
989   }
990 
991   return(AGS_AUTOMATION_GET_OBJ_MUTEX(automation));
992 }
993 
994 /**
995  * ags_automation_find_port:
996  * @automation: (element-type AgsAudio.Automation) (transfer none): the #GList-struct containing #AgsAutomation
997  * @port: the #AgsPort to match
998  *
999  * Find automation by port.
1000  *
1001  * Returns: (element-type AgsAudio.Automation) (transfer none): next matching automation as #GList-struct or %NULL if not found
1002  *
1003  * Since: 3.0.0
1004  */
1005 GList*
ags_automation_find_port(GList * automation,GObject * port)1006 ags_automation_find_port(GList *automation,
1007 			 GObject *port)
1008 {
1009   if(automation == NULL ||
1010      !AGS_IS_PORT(port)){
1011     return(NULL);
1012   }
1013 
1014   while(automation != NULL){
1015     GObject *current_port;
1016 
1017     gboolean success;
1018 
1019     g_object_get(automation->data,
1020 		 "port", &current_port,
1021 		 NULL);
1022 
1023     success = (current_port == port) ? TRUE: FALSE;
1024 
1025     g_object_unref(current_port);
1026 
1027     if(success){
1028       break;
1029     }
1030 
1031     automation = automation->next;
1032   }
1033 
1034   return(automation);
1035 }
1036 
1037 /**
1038  * ags_automation_find_near_timestamp:
1039  * @automation: (element-type AgsAudio.Automation) (transfer none): the #GList-struct containing #AgsAutomation
1040  * @line: the matching audio channel
1041  * @timestamp: the matching timestamp
1042  *
1043  * Retrieve appropriate automation for timestamp.
1044  *
1045  * Returns: (element-type AgsAudio.Automation) (transfer none): Next matching #GList-struct or %NULL if not found
1046  *
1047  * Since: 3.0.0
1048  */
1049 GList*
ags_automation_find_near_timestamp(GList * automation,guint line,AgsTimestamp * timestamp)1050 ags_automation_find_near_timestamp(GList *automation, guint line,
1051 				   AgsTimestamp *timestamp)
1052 {
1053   AgsTimestamp *current_timestamp;
1054 
1055   GList *retval;
1056   GList *current_start, *current_end, *current;
1057 
1058   guint current_line;
1059   guint64 current_x, x;
1060   guint length, position;
1061   gboolean use_ags_offset;
1062   gboolean success;
1063 
1064   if(automation == NULL){
1065     return(NULL);
1066   }
1067 
1068   current_start = automation;
1069   current_end = g_list_last(automation);
1070 
1071   length = g_list_length(automation);
1072   position = (length - 1) / 2;
1073 
1074   current = g_list_nth(current_start,
1075 		       position);
1076 
1077   x = 0;
1078   use_ags_offset = FALSE;
1079 
1080   if(ags_timestamp_test_flags(timestamp,
1081 			      AGS_TIMESTAMP_OFFSET)){
1082     x = ags_timestamp_get_ags_offset(timestamp);
1083 
1084     use_ags_offset = TRUE;
1085   }else if(ags_timestamp_test_flags(timestamp,
1086 				    AGS_TIMESTAMP_UNIX)){
1087     x = ags_timestamp_get_unix_time(timestamp);
1088 
1089     use_ags_offset = FALSE;
1090   }
1091 
1092   retval = NULL;
1093   success = FALSE;
1094 
1095   while(!success && current != NULL){
1096     current_x = 0;
1097 
1098     /* check current - start */
1099     g_object_get(current_start->data,
1100 		 "line", &current_line,
1101 		 NULL);
1102 
1103     if(current_line == line){
1104       if(timestamp == NULL){
1105 	retval = current_start;
1106 
1107 	break;
1108       }
1109 
1110       g_object_get(current_start->data,
1111 		   "timestamp", &current_timestamp,
1112 		   NULL);
1113 
1114       if(current_timestamp != NULL){
1115 	if(use_ags_offset){
1116 	  current_x = ags_timestamp_get_ags_offset(current_timestamp);
1117 
1118 	  g_object_unref(current_timestamp);
1119 
1120 	  if(current_x > x){
1121 	    break;
1122 	  }
1123 	}else{
1124 	  current_x = ags_timestamp_get_unix_time(current_timestamp);
1125 
1126 	  g_object_unref(current_timestamp);
1127 
1128 	  if(current_x > x){
1129 	    break;
1130 	  }
1131 	}
1132 
1133 	if(use_ags_offset){
1134 	  if(current_x >= x &&
1135 	     current_x < x + AGS_AUTOMATION_DEFAULT_OFFSET){
1136 	    retval = current_start;
1137 
1138 	    break;
1139 	  }
1140 	}else{
1141 	  if(current_x >= x &&
1142 	     current_x < x + AGS_AUTOMATION_DEFAULT_DURATION){
1143 	    retval = current_start;
1144 
1145 	    break;
1146 	  }
1147 	}
1148       }else{
1149 	g_warning("inconsistent data");
1150       }
1151     }
1152 
1153     /* check current - end */
1154     g_object_get(current_end->data,
1155 		 "line", &current_line,
1156 		 NULL);
1157 
1158     if(current_line == line){
1159       if(timestamp == NULL){
1160 	retval = current_end;
1161 
1162 	break;
1163       }
1164 
1165       g_object_get(current_end->data,
1166 		   "timestamp", &current_timestamp,
1167 		   NULL);
1168 
1169       if(current_timestamp != NULL){
1170 	if(use_ags_offset){
1171 	  current_x = ags_timestamp_get_ags_offset(current_timestamp);
1172 
1173 	  g_object_unref(current_timestamp);
1174 
1175 	  if(current_x < x){
1176 	    break;
1177 	  }
1178 	}else{
1179 	  current_x = ags_timestamp_get_unix_time(current_timestamp);
1180 
1181 	  g_object_unref(current_timestamp);
1182 
1183 	  if(current_x < x){
1184 	    break;
1185 	  }
1186 	}
1187 
1188 	if(use_ags_offset){
1189 	  if(current_x >= x &&
1190 	     current_x < x + AGS_AUTOMATION_DEFAULT_OFFSET){
1191 	    retval = current_end;
1192 
1193 	    break;
1194 	  }
1195 	}else{
1196 	  if(current_x >= x &&
1197 	     current_x < x + AGS_AUTOMATION_DEFAULT_DURATION){
1198 	    retval = current_end;
1199 
1200 	    break;
1201 	  }
1202 	}
1203       }else{
1204 	g_warning("inconsistent data");
1205       }
1206     }
1207 
1208     /* check current - center */
1209     g_object_get(current->data,
1210 		 "line", &current_line,
1211 		 NULL);
1212 
1213     if(current_line == line){
1214       if(timestamp == NULL){
1215 	retval = current;
1216 
1217 	break;
1218       }
1219     }
1220 
1221     g_object_get(current->data,
1222 		 "timestamp", &current_timestamp,
1223 		 NULL);
1224 
1225     if(current_timestamp != NULL){
1226       if(use_ags_offset){
1227 	current_x = ags_timestamp_get_ags_offset(current_timestamp);
1228 
1229 	g_object_unref(current_timestamp);
1230 
1231 	if(current_x >= x &&
1232 	   current_x < x + AGS_AUTOMATION_DEFAULT_OFFSET &&
1233 	  current_line == line){
1234 	  retval = current;
1235 
1236 	  break;
1237 	}
1238       }else{
1239 	current_x = ags_timestamp_get_unix_time(current_timestamp);
1240 
1241 	g_object_unref(current_timestamp);
1242 
1243 	if(current_x >= x &&
1244 	   current_x < x + AGS_AUTOMATION_DEFAULT_DURATION &&
1245 	   current_line == line){
1246 	  retval = current;
1247 
1248 	  break;
1249 	}
1250       }
1251     }else{
1252       g_warning("inconsistent data");
1253     }
1254 
1255     if(length <= 3){
1256       break;
1257     }
1258 
1259     if(current_x < x){
1260       current_start = current->next;
1261       current_end = current_end->prev;
1262     }else if(current_x > x){
1263       current_start = current_start->next;
1264       current_end = current->prev;
1265     }else{
1266       current_start = current_start->next;
1267       //NOTE:JK: we want progression
1268       //current_end = current_end->prev;
1269     }
1270 
1271     length = g_list_position(current_start,
1272 			     current_end) + 1;
1273     position = (length - 1) / 2;
1274 
1275     current = g_list_nth(current_start,
1276 			 position);
1277   }
1278 
1279   return(retval);
1280 }
1281 
1282 /**
1283  * ags_automation_find_near_timestamp_extended:
1284  * @automation: (element-type AgsAudio.Automation) (transfer none): the #GList-struct containing #AgsAutomation
1285  * @line: the matching audio channel
1286  * @channel_type: the matching channel type
1287  * @control_name: the matching control name
1288  * @timestamp: the matching timestamp
1289  *
1290  * Retrieve appropriate automation for timestamp.
1291  *
1292  * Returns: (element-type AgsAudio.Automation) (transfer none): Next matching #GList-struct or %NULL if not found
1293  *
1294  * Since: 3.0.0
1295  */
1296 GList*
ags_automation_find_near_timestamp_extended(GList * automation,guint line,GType channel_type,gchar * control_name,AgsTimestamp * timestamp)1297 ags_automation_find_near_timestamp_extended(GList *automation, guint line,
1298 					    GType channel_type, gchar *control_name,
1299 					    AgsTimestamp *timestamp)
1300 {
1301   AgsTimestamp *current_timestamp;
1302 
1303   GList *retval;
1304   GList *current_start, *current_end, *current;
1305 
1306   GType current_channel_type;
1307 
1308   gchar *current_control_name;
1309 
1310   guint current_line;
1311   guint64 current_x, x;
1312   guint length, position;
1313   gboolean use_ags_offset;
1314   gboolean success;
1315 
1316   if(automation == NULL){
1317     return(NULL);
1318   }
1319 
1320   current_start = automation;
1321   current_end = g_list_last(automation);
1322 
1323   length = g_list_length(automation);
1324   position = (length - 1) / 2;
1325 
1326   current = g_list_nth(current_start,
1327 		       position);
1328 
1329   x = 0;
1330   use_ags_offset = FALSE;
1331 
1332   if(ags_timestamp_test_flags(timestamp,
1333 			      AGS_TIMESTAMP_OFFSET)){
1334     x = ags_timestamp_get_ags_offset(timestamp);
1335 
1336     use_ags_offset = TRUE;
1337   }else if(ags_timestamp_test_flags(timestamp,
1338 				    AGS_TIMESTAMP_UNIX)){
1339     x = ags_timestamp_get_unix_time(timestamp);
1340 
1341     use_ags_offset = FALSE;
1342   }
1343 
1344   retval = NULL;
1345   success = FALSE;
1346 
1347   while(!success && current != NULL){
1348     current_x = 0;
1349 
1350     /* check current - start */
1351     g_object_get(current_start->data,
1352 		 "line", &current_line,
1353 		 "channel-type", &current_channel_type,
1354 		 "control-name", &current_control_name,
1355 		 NULL);
1356 
1357     if(current_line == line &&
1358        g_type_is_a(current_channel_type,
1359 		   channel_type) &&
1360        !g_strcmp0(current_control_name,
1361 		  control_name)){
1362       g_free(current_control_name);
1363 
1364       if(timestamp == NULL){
1365 	retval = current_start;
1366 
1367 	break;
1368       }
1369 
1370       g_object_get(current_start->data,
1371 		   "timestamp", &current_timestamp,
1372 		   NULL);
1373 
1374       if(current_timestamp != NULL){
1375 	if(use_ags_offset){
1376 	  current_x = ags_timestamp_get_ags_offset(current_timestamp);
1377 
1378 	  g_object_unref(current_timestamp);
1379 
1380 	  if(current_x > x){
1381 	    break;
1382 	  }
1383 	}else{
1384 	  current_x = ags_timestamp_get_unix_time(current_timestamp);
1385 
1386 	  g_object_unref(current_timestamp);
1387 
1388 	  if(current_x > x){
1389 	    break;
1390 	  }
1391 	}
1392 
1393 	if(use_ags_offset){
1394 	  if(current_x >= x &&
1395 	     current_x < x + AGS_AUTOMATION_DEFAULT_OFFSET){
1396 	    retval = current_start;
1397 
1398 	    break;
1399 	  }
1400 	}else{
1401 	  if(current_x >= x &&
1402 	     current_x < x + AGS_AUTOMATION_DEFAULT_DURATION){
1403 	    retval = current_start;
1404 
1405 	    break;
1406 	  }
1407 	}
1408       }else{
1409 	g_warning("inconsistent data");
1410       }
1411     }else{
1412       g_free(current_control_name);
1413     }
1414 
1415     /* check current - end */
1416     g_object_get(current_end->data,
1417 		 "line", &current_line,
1418 		 "channel-type", &current_channel_type,
1419 		 "control-name", &current_control_name,
1420 		 NULL);
1421 
1422     if(current_line == line &&
1423        g_type_is_a(current_channel_type,
1424 		   channel_type) &&
1425        !g_strcmp0(current_control_name,
1426 		  control_name)){
1427       g_free(current_control_name);
1428 
1429       if(timestamp == NULL){
1430 	retval = current_end;
1431 
1432 	break;
1433       }
1434 
1435       g_object_get(current_end->data,
1436 		   "timestamp", &current_timestamp,
1437 		   NULL);
1438 
1439       if(current_timestamp != NULL){
1440 	if(use_ags_offset){
1441 	  current_x = ags_timestamp_get_ags_offset(current_timestamp);
1442 
1443 	  g_object_unref(current_timestamp);
1444 
1445 	  if(current_x < x){
1446 	    break;
1447 	  }
1448 	}else{
1449 	  current_x = ags_timestamp_get_unix_time(current_timestamp);
1450 
1451 	  g_object_unref(current_timestamp);
1452 
1453 	  if(current_x < x){
1454 	    break;
1455 	  }
1456 	}
1457 
1458 	if(use_ags_offset){
1459 	  if(current_x >= x &&
1460 	     current_x < x + AGS_AUTOMATION_DEFAULT_OFFSET){
1461 	    retval = current_end;
1462 
1463 	    break;
1464 	  }
1465 	}else{
1466 	  if(current_x >= x &&
1467 	     current_x < x + AGS_AUTOMATION_DEFAULT_DURATION){
1468 	    retval = current_end;
1469 
1470 	    break;
1471 	  }
1472 	}
1473       }else{
1474 	g_warning("inconsistent data");
1475       }
1476     }else{
1477       g_free(current_control_name);
1478     }
1479 
1480     /* check current - center */
1481     current_x = 0;
1482 
1483     g_object_get(current->data,
1484 		 "line", &current_line,
1485 		 "channel-type", &current_channel_type,
1486 		 "control-name", &current_control_name,
1487 		 NULL);
1488 
1489     if(current_line == line &&
1490        g_type_is_a(current_channel_type,
1491 		   channel_type) &&
1492        !g_strcmp0(current_control_name,
1493 		  control_name)){
1494       if(timestamp == NULL){
1495 	retval = current;
1496 
1497 	g_free(current_control_name);
1498 
1499 	break;
1500       }
1501     }
1502 
1503     g_object_get(current->data,
1504 		 "timestamp", &current_timestamp,
1505 		 NULL);
1506 
1507     if(current_timestamp != NULL){
1508       if(use_ags_offset){
1509 	current_x = ags_timestamp_get_ags_offset(current_timestamp);
1510 
1511 	g_object_unref(current_timestamp);
1512 
1513 	if(current_x >= x &&
1514 	   current_x < x + AGS_AUTOMATION_DEFAULT_OFFSET &&
1515 	   current_line == line &&
1516 	   g_type_is_a(current_channel_type,
1517 		       channel_type) &&
1518 	   !g_strcmp0(current_control_name,
1519 		      control_name)){
1520 	  retval = current;
1521 
1522 	  break;
1523 	}
1524       }else{
1525 	current_x = ags_timestamp_get_unix_time(current_timestamp);
1526 
1527 	g_object_unref(current_timestamp);
1528 
1529 	if(current_x >= x &&
1530 	   current_x < x + AGS_AUTOMATION_DEFAULT_DURATION &&
1531 	   current_line == line &&
1532 	   g_type_is_a(current_channel_type,
1533 		       channel_type) &&
1534 	   !g_strcmp0(current_control_name,
1535 		      control_name)){
1536 	  retval = current;
1537 
1538 	  break;
1539 	}
1540       }
1541     }else{
1542       g_warning("inconsistent data");
1543     }
1544 
1545     g_free(current_control_name);
1546 
1547     if(position == 0){
1548       break;
1549     }
1550 
1551     if(current_x < x){
1552       current_start = current->next;
1553       current_end = current_end->prev;
1554     }else if(current_x > x){
1555       current_start = current_start->next;
1556       current_end = current->prev;
1557     }else{
1558       current_start = current_start->next;
1559       //NOTE:JK: we want progression
1560       //current_end = current_end->prev;
1561     }
1562 
1563     length = g_list_position(current_start,
1564 			     current_end) + 1;
1565     position = (length - 1) / 2;
1566 
1567     current = g_list_nth(current_start,
1568 			 position);
1569   }
1570 
1571   return(retval);
1572 }
1573 
1574 /**
1575  * ags_automation_sort_func:
1576  * @a: an #AgsAutomation
1577  * @b: an other #AgsAutomation
1578  *
1579  * Compare @a to @b.
1580  *
1581  * Returns: 0 if equal, -1 if smaller and 1 if bigger offset
1582  *
1583  * Since: 3.0.0
1584  */
1585 gint
ags_automation_sort_func(gconstpointer a,gconstpointer b)1586 ags_automation_sort_func(gconstpointer a,
1587 			 gconstpointer b)
1588 {
1589   AgsTimestamp *timestamp_a, *timestamp_b;
1590 
1591   guint64 offset_a, offset_b;
1592 
1593   g_object_get(a,
1594 	       "timestamp", &timestamp_a,
1595 	       NULL);
1596 
1597   g_object_get(b,
1598 	       "timestamp", &timestamp_b,
1599 	       NULL);
1600 
1601   offset_a = ags_timestamp_get_ags_offset(timestamp_a);
1602   offset_b = ags_timestamp_get_ags_offset(timestamp_b);
1603 
1604   g_object_unref(timestamp_a);
1605   g_object_unref(timestamp_b);
1606 
1607   if(offset_a == offset_b){
1608     return(0);
1609   }else if(offset_a < offset_b){
1610     return(-1);
1611   }else if(offset_a > offset_b){
1612     return(1);
1613   }
1614 
1615   return(0);
1616 }
1617 
1618 /**
1619  * ags_automation_add:
1620  * @automation: (element-type AgsAudio.Automation) (transfer none): the #GList-struct containing #AgsAutomation
1621  * @new_automation: the #AgsAutomation to add
1622  *
1623  * Add @new_automation sorted to @automation
1624  *
1625  * Returns: (element-type AgsAudio.Automation) (transfer none): the new beginning of @automation
1626  *
1627  * Since: 3.0.0
1628  */
1629 GList*
ags_automation_add(GList * automation,AgsAutomation * new_automation)1630 ags_automation_add(GList *automation,
1631 		   AgsAutomation *new_automation)
1632 {
1633   if(!AGS_IS_AUTOMATION(new_automation)){
1634     return(automation);
1635   }
1636 
1637   automation = g_list_insert_sorted(automation,
1638 				    new_automation,
1639 				    ags_automation_sort_func);
1640 
1641   return(automation);
1642 }
1643 
1644 /**
1645  * ags_automation_get_audio:
1646  * @automation: the #AgsAutomation
1647  *
1648  * Get audio.
1649  *
1650  * Returns: (transfer full): the #AgsAudio
1651  *
1652  * Since: 3.1.0
1653  */
1654 GObject*
ags_automation_get_audio(AgsAutomation * automation)1655 ags_automation_get_audio(AgsAutomation *automation)
1656 {
1657   GObject *audio;
1658 
1659   if(!AGS_IS_AUTOMATION(automation)){
1660     return(NULL);
1661   }
1662 
1663   g_object_get(automation,
1664 	       "audio", &audio,
1665 	       NULL);
1666 
1667   return(audio);
1668 }
1669 
1670 /**
1671  * ags_automation_set_audio:
1672  * @automation: the #AgsAutomation
1673  * @audio: the #AgsAudio
1674  *
1675  * Set audio.
1676  *
1677  * Since: 3.1.0
1678  */
1679 void
ags_automation_set_audio(AgsAutomation * automation,GObject * audio)1680 ags_automation_set_audio(AgsAutomation *automation, GObject *audio)
1681 {
1682   if(!AGS_IS_AUTOMATION(automation)){
1683     return;
1684   }
1685 
1686   g_object_set(automation,
1687 	       "audio", audio,
1688 	       NULL);
1689 }
1690 
1691 /**
1692  * ags_automation_get_channel_type:
1693  * @automation: the #AgsAutomation
1694  *
1695  * Gets channel type.
1696  *
1697  * Returns: the channel type
1698  *
1699  * Since: 3.1.0
1700  */
1701 GType
ags_automation_get_channel_type(AgsAutomation * automation)1702 ags_automation_get_channel_type(AgsAutomation *automation)
1703 {
1704   GType channel_type;
1705 
1706   if(!AGS_IS_AUTOMATION(automation)){
1707     return(0);
1708   }
1709 
1710   g_object_get(automation,
1711 	       "channel_type", &channel_type,
1712 	       NULL);
1713 
1714   return(channel_type);
1715 }
1716 
1717 /**
1718  * ags_automation_set_channel_type:
1719  * @automation: the #AgsAutomation
1720  * @channel_type: the channel type
1721  *
1722  * Sets channel type.
1723  *
1724  * Since: 3.1.0
1725  */
1726 void
ags_automation_set_channel_type(AgsAutomation * automation,GType channel_type)1727 ags_automation_set_channel_type(AgsAutomation *automation, GType channel_type)
1728 {
1729   if(!AGS_IS_AUTOMATION(automation)){
1730     return;
1731   }
1732 
1733   g_object_set(automation,
1734 	       "channel-type", channel_type,
1735 	       NULL);
1736 }
1737 
1738 /**
1739  * ags_automation_get_line:
1740  * @automation: the #AgsAutomation
1741  *
1742  * Gets line.
1743  *
1744  * Returns: the line
1745  *
1746  * Since: 3.1.0
1747  */
1748 guint
ags_automation_get_line(AgsAutomation * automation)1749 ags_automation_get_line(AgsAutomation *automation)
1750 {
1751   guint line;
1752 
1753   if(!AGS_IS_AUTOMATION(automation)){
1754     return(0);
1755   }
1756 
1757   g_object_get(automation,
1758 	       "line", &line,
1759 	       NULL);
1760 
1761   return(line);
1762 }
1763 
1764 /**
1765  * ags_automation_set_line:
1766  * @automation: the #AgsAutomation
1767  * @line: the line
1768  *
1769  * Sets line.
1770  *
1771  * Since: 3.1.0
1772  */
1773 void
ags_automation_set_line(AgsAutomation * automation,guint line)1774 ags_automation_set_line(AgsAutomation *automation, guint line)
1775 {
1776   if(!AGS_IS_AUTOMATION(automation)){
1777     return;
1778   }
1779 
1780   g_object_set(automation,
1781 	       "line", line,
1782 	       NULL);
1783 }
1784 
1785 /**
1786  * ags_automation_get_timestamp:
1787  * @automation: the #AgsAutomation
1788  *
1789  * Get timestamp.
1790  *
1791  * Returns: (transfer full): the #AgsTimestamp
1792  *
1793  * Since: 3.1.0
1794  */
1795 AgsTimestamp*
ags_automation_get_timestamp(AgsAutomation * automation)1796 ags_automation_get_timestamp(AgsAutomation *automation)
1797 {
1798   AgsTimestamp *timestamp;
1799 
1800   if(!AGS_IS_AUTOMATION(automation)){
1801     return(NULL);
1802   }
1803 
1804   g_object_get(automation,
1805 	       "timestamp", &timestamp,
1806 	       NULL);
1807 
1808   return(timestamp);
1809 }
1810 
1811 /**
1812  * ags_automation_set_timestamp:
1813  * @automation: the #AgsAutomation
1814  * @timestamp: the #AgsTimestamp
1815  *
1816  * Set timestamp.
1817  *
1818  * Since: 3.1.0
1819  */
1820 void
ags_automation_set_timestamp(AgsAutomation * automation,AgsTimestamp * timestamp)1821 ags_automation_set_timestamp(AgsAutomation *automation, AgsTimestamp *timestamp)
1822 {
1823   if(!AGS_IS_AUTOMATION(automation)){
1824     return;
1825   }
1826 
1827   g_object_set(automation,
1828 	       "timestamp", timestamp,
1829 	       NULL);
1830 }
1831 
1832 /**
1833  * ags_automation_get_control_name:
1834  * @automation: the #AgsAutomation
1835  *
1836  * Get control name.
1837  *
1838  * Returns: (transfer full): the control name
1839  *
1840  * Since: 3.1.0
1841  */
1842 gchar*
ags_automation_get_control_name(AgsAutomation * automation)1843 ags_automation_get_control_name(AgsAutomation *automation)
1844 {
1845   gchar *control_name;
1846 
1847   if(!AGS_IS_AUTOMATION(automation)){
1848     return(NULL);
1849   }
1850 
1851   g_object_get(automation,
1852 	       "control-name", &control_name,
1853 	       NULL);
1854 
1855   return(control_name);
1856 }
1857 
1858 /**
1859  * ags_automation_set_control_name:
1860  * @automation: the #AgsAutomation
1861  * @control_name: the control name
1862  *
1863  * Set control name.
1864  *
1865  * Since: 3.1.0
1866  */
1867 void
ags_automation_set_control_name(AgsAutomation * automation,gchar * control_name)1868 ags_automation_set_control_name(AgsAutomation *automation, gchar *control_name)
1869 {
1870   if(!AGS_IS_AUTOMATION(automation)){
1871     return;
1872   }
1873 
1874   g_object_set(automation,
1875 	       "control-name", control_name,
1876 	       NULL);
1877 }
1878 
1879 /**
1880  * ags_automation_get_steps:
1881  * @automation: the #AgsAutomation
1882  *
1883  * Gets steps.
1884  *
1885  * Returns: the steps
1886  *
1887  * Since: 3.1.0
1888  */
1889 guint
ags_automation_get_steps(AgsAutomation * automation)1890 ags_automation_get_steps(AgsAutomation *automation)
1891 {
1892   guint steps;
1893 
1894   if(!AGS_IS_AUTOMATION(automation)){
1895     return(0);
1896   }
1897 
1898   g_object_get(automation,
1899 	       "steps", &steps,
1900 	       NULL);
1901 
1902   return(steps);
1903 }
1904 
1905 /**
1906  * ags_automation_set_steps:
1907  * @automation: the #AgsAutomation
1908  * @steps: the steps
1909  *
1910  * Sets steps.
1911  *
1912  * Since: 3.1.0
1913  */
1914 void
ags_automation_set_steps(AgsAutomation * automation,guint steps)1915 ags_automation_set_steps(AgsAutomation *automation, guint steps)
1916 {
1917   if(!AGS_IS_AUTOMATION(automation)){
1918     return;
1919   }
1920 
1921   g_object_set(automation,
1922 	       "steps", steps,
1923 	       NULL);
1924 }
1925 
1926 /**
1927  * ags_automation_get_upper:
1928  * @automation: the #AgsAutomation
1929  *
1930  * Gets upper.
1931  *
1932  * Returns: the upper
1933  *
1934  * Since: 3.1.0
1935  */
1936 gdouble
ags_automation_get_upper(AgsAutomation * automation)1937 ags_automation_get_upper(AgsAutomation *automation)
1938 {
1939   gdouble upper;
1940 
1941   if(!AGS_IS_AUTOMATION(automation)){
1942     return(0.0);
1943   }
1944 
1945   g_object_get(automation,
1946 	       "upper", &upper,
1947 	       NULL);
1948 
1949   return(upper);
1950 }
1951 
1952 /**
1953  * ags_automation_set_upper:
1954  * @automation: the #AgsAutomation
1955  * @upper: the upper
1956  *
1957  * Sets upper.
1958  *
1959  * Since: 3.1.0
1960  */
1961 void
ags_automation_set_upper(AgsAutomation * automation,gdouble upper)1962 ags_automation_set_upper(AgsAutomation *automation, gdouble upper)
1963 {
1964   if(!AGS_IS_AUTOMATION(automation)){
1965     return;
1966   }
1967 
1968   g_object_set(automation,
1969 	       "upper", upper,
1970 	       NULL);
1971 }
1972 
1973 /**
1974  * ags_automation_get_lower:
1975  * @automation: the #AgsAutomation
1976  *
1977  * Gets lower.
1978  *
1979  * Returns: the lower
1980  *
1981  * Since: 3.1.0
1982  */
1983 gdouble
ags_automation_get_lower(AgsAutomation * automation)1984 ags_automation_get_lower(AgsAutomation *automation)
1985 {
1986   gdouble lower;
1987 
1988   if(!AGS_IS_AUTOMATION(automation)){
1989     return(0.0);
1990   }
1991 
1992   g_object_get(automation,
1993 	       "lower", &lower,
1994 	       NULL);
1995 
1996   return(lower);
1997 }
1998 
1999 /**
2000  * ags_automation_set_lower:
2001  * @automation: the #AgsAutomation
2002  * @lower: the lower
2003  *
2004  * Sets lower.
2005  *
2006  * Since: 3.1.0
2007  */
2008 void
ags_automation_set_lower(AgsAutomation * automation,gdouble lower)2009 ags_automation_set_lower(AgsAutomation *automation, gdouble lower)
2010 {
2011   if(!AGS_IS_AUTOMATION(automation)){
2012     return;
2013   }
2014 
2015   g_object_set(automation,
2016 	       "lower", lower,
2017 	       NULL);
2018 }
2019 
2020 /**
2021  * ags_automation_get_default_value:
2022  * @automation: the #AgsAutomation
2023  *
2024  * Gets default value.
2025  *
2026  * Returns: the default value
2027  *
2028  * Since: 3.1.0
2029  */
2030 gdouble
ags_automation_get_default_value(AgsAutomation * automation)2031 ags_automation_get_default_value(AgsAutomation *automation)
2032 {
2033   gdouble default_value;
2034 
2035   if(!AGS_IS_AUTOMATION(automation)){
2036     return(0.0);
2037   }
2038 
2039   g_object_get(automation,
2040 	       "default-value", &default_value,
2041 	       NULL);
2042 
2043   return(default_value);
2044 }
2045 
2046 /**
2047  * ags_automation_set_default_value:
2048  * @automation: the #AgsAutomation
2049  * @default_value: the default value
2050  *
2051  * Sets default value.
2052  *
2053  * Since: 3.1.0
2054  */
2055 void
ags_automation_set_default_value(AgsAutomation * automation,gdouble default_value)2056 ags_automation_set_default_value(AgsAutomation *automation, gdouble default_value)
2057 {
2058   if(!AGS_IS_AUTOMATION(automation)){
2059     return;
2060   }
2061 
2062   g_object_set(automation,
2063 	       "default-value", default_value,
2064 	       NULL);
2065 }
2066 
2067 /**
2068  * ags_automation_get_port:
2069  * @automation: the #AgsAutomation
2070  *
2071  * Get port.
2072  *
2073  * Returns: (transfer full): the #AgsPort
2074  *
2075  * Since: 3.1.0
2076  */
2077 GObject*
ags_automation_get_port(AgsAutomation * automation)2078 ags_automation_get_port(AgsAutomation *automation)
2079 {
2080   GObject *port;
2081 
2082   if(!AGS_IS_AUTOMATION(automation)){
2083     return(NULL);
2084   }
2085 
2086   g_object_get(automation,
2087 	       "port", &port,
2088 	       NULL);
2089 
2090   return(port);
2091 }
2092 
2093 /**
2094  * ags_automation_set_port:
2095  * @automation: the #AgsAutomation
2096  * @port: the #AgsPort
2097  *
2098  * Set port.
2099  *
2100  * Since: 3.1.0
2101  */
2102 void
ags_automation_set_port(AgsAutomation * automation,GObject * port)2103 ags_automation_set_port(AgsAutomation *automation, GObject *port)
2104 {
2105   if(!AGS_IS_AUTOMATION(automation)){
2106     return;
2107   }
2108 
2109   g_object_set(automation,
2110 	       "port", port,
2111 	       NULL);
2112 }
2113 
2114 
2115 /**
2116  * ags_automation_get_acceleration:
2117  * @automation: the #AgsAutomation
2118  *
2119  * Get acceleration.
2120  *
2121  * Returns: (element-type AgsAudio.Acceleration) (transfer full): the #GList-struct containig #AgsAcceleration
2122  *
2123  * Since: 3.1.0
2124  */
2125 GList*
ags_automation_get_acceleration(AgsAutomation * automation)2126 ags_automation_get_acceleration(AgsAutomation *automation)
2127 {
2128   GList *acceleration;
2129 
2130   if(!AGS_IS_AUTOMATION(automation)){
2131     return(NULL);
2132   }
2133 
2134   g_object_get(automation,
2135 	       "acceleration", &acceleration,
2136 	       NULL);
2137 
2138   return(acceleration);
2139 }
2140 
2141 /**
2142  * ags_automation_set_acceleration:
2143  * @automation: the #AgsAutomation
2144  * @acceleration: (element-type AgsAudio.Acceleration) (transfer full): the #GList-struct containing #AgsAcceleration
2145  *
2146  * Set acceleration by replacing existing.
2147  *
2148  * Since: 3.1.0
2149  */
2150 void
ags_automation_set_acceleration(AgsAutomation * automation,GList * acceleration)2151 ags_automation_set_acceleration(AgsAutomation *automation, GList *acceleration)
2152 {
2153   GList *start_acceleration;
2154 
2155   GRecMutex *automation_mutex;
2156 
2157   if(!AGS_IS_AUTOMATION(automation)){
2158     return;
2159   }
2160 
2161   /* get automation mutex */
2162   automation_mutex = AGS_AUTOMATION_GET_OBJ_MUTEX(automation);
2163 
2164   g_rec_mutex_lock(automation_mutex);
2165 
2166   start_acceleration = automation->acceleration;
2167   automation->acceleration = acceleration;
2168 
2169   g_rec_mutex_unlock(automation_mutex);
2170 
2171   g_list_free_full(start_acceleration,
2172 		   (GDestroyNotify) g_object_unref);
2173 }
2174 
2175 /**
2176  * ags_automation_add_acceleration:
2177  * @automation: the #AgsAutomation
2178  * @acceleration: the #AgsAcceleration to add
2179  * @use_selection_list: if %TRUE add to selection, else to default automation
2180  *
2181  * Adds @acceleration to @automation.
2182  *
2183  * Since: 3.0.0
2184  */
2185 void
ags_automation_add_acceleration(AgsAutomation * automation,AgsAcceleration * acceleration,gboolean use_selection_list)2186 ags_automation_add_acceleration(AgsAutomation *automation,
2187 				AgsAcceleration *acceleration,
2188 				gboolean use_selection_list)
2189 {
2190   GRecMutex *automation_mutex;
2191 
2192   if(!AGS_IS_AUTOMATION(automation) ||
2193      !AGS_IS_ACCELERATION(acceleration)){
2194     return;
2195   }
2196 
2197   /* get automation mutex */
2198   automation_mutex = AGS_AUTOMATION_GET_OBJ_MUTEX(automation);
2199 
2200   /* insert sorted */
2201   g_object_ref(acceleration);
2202 
2203   g_rec_mutex_lock(automation_mutex);
2204 
2205   if(use_selection_list){
2206     automation->selection = g_list_insert_sorted(automation->selection,
2207 						 acceleration,
2208 						 (GCompareFunc) ags_acceleration_sort_func);
2209     ags_acceleration_set_flags(acceleration,
2210 			       AGS_ACCELERATION_IS_SELECTED);
2211   }else{
2212     automation->acceleration = g_list_insert_sorted(automation->acceleration,
2213 						    acceleration,
2214 						    (GCompareFunc) ags_acceleration_sort_func);
2215   }
2216 
2217   g_rec_mutex_unlock(automation_mutex);
2218 }
2219 
2220 /**
2221  * ags_automation_remove_acceleration:
2222  * @automation: the #AgsAutomation
2223  * @acceleration: the #AgsAcceleration to remove
2224  * @use_selection_list: if %TRUE remove from selection, else from default automation
2225  *
2226  * Removes @acceleration from @automation.
2227  *
2228  * Since: 3.0.0
2229  */
2230 void
ags_automation_remove_acceleration(AgsAutomation * automation,AgsAcceleration * acceleration,gboolean use_selection_list)2231 ags_automation_remove_acceleration(AgsAutomation *automation,
2232 				   AgsAcceleration *acceleration,
2233 				   gboolean use_selection_list)
2234 {
2235   GRecMutex *automation_mutex;
2236 
2237   if(!AGS_IS_AUTOMATION(automation) ||
2238      !AGS_IS_ACCELERATION(acceleration)){
2239     return;
2240   }
2241 
2242   /* get automation mutex */
2243   automation_mutex = AGS_AUTOMATION_GET_OBJ_MUTEX(automation);
2244 
2245   /* remove if found */
2246   g_rec_mutex_lock(automation_mutex);
2247 
2248   if(!use_selection_list){
2249     if(g_list_find(automation->acceleration,
2250 		   acceleration) != NULL){
2251       automation->acceleration = g_list_remove(automation->acceleration,
2252 					       acceleration);
2253       g_object_unref(acceleration);
2254     }
2255   }else{
2256     if(g_list_find(automation->selection,
2257 		   acceleration) != NULL){
2258       automation->selection = g_list_remove(automation->selection,
2259 					    acceleration);
2260       g_object_unref(acceleration);
2261     }
2262   }
2263 
2264   g_rec_mutex_unlock(automation_mutex);
2265 }
2266 
2267 /**
2268  * ags_automation_remove_acceleration_at_position:
2269  * @automation: the #AgsAutomation
2270  * @x: x offset
2271  * @y: y value of acceleration
2272  *
2273  * Removes one #AgsAcceleration of automation.
2274  *
2275  * Returns: %TRUE if successfully removed acceleration, else %FALSE
2276  *
2277  * Since: 3.0.0
2278  */
2279 gboolean
ags_automation_remove_acceleration_at_position(AgsAutomation * automation,guint x,gdouble y)2280 ags_automation_remove_acceleration_at_position(AgsAutomation *automation,
2281 					       guint x, gdouble y)
2282 {
2283   AgsAcceleration *acceleration;
2284 
2285   GList *start_list, *list;
2286 
2287   gdouble upper, lower;
2288   guint current_x;
2289   gdouble current_y;
2290   gboolean retval;
2291 
2292   GRecMutex *automation_mutex;
2293 
2294   if(!AGS_IS_AUTOMATION(automation)){
2295     return(FALSE);
2296   }
2297 
2298   /* get automation mutex */
2299   automation_mutex = AGS_AUTOMATION_GET_OBJ_MUTEX(automation);
2300 
2301   /* find acceleration */
2302   g_rec_mutex_lock(automation_mutex);
2303 
2304   upper = automation->upper;
2305   lower = automation->lower;
2306 
2307   list =
2308     start_list = g_list_copy_deep(automation->acceleration,
2309 				  (GCopyFunc) g_object_ref,
2310 				  NULL);
2311 
2312   g_rec_mutex_unlock(automation_mutex);
2313 
2314   acceleration = NULL;
2315 
2316   retval = FALSE;
2317 
2318   while(list != NULL){
2319     g_object_get(list->data,
2320 		 "x", &current_x,
2321 		 "y", &current_y,
2322 		 NULL);
2323 
2324     if(current_x == x &&
2325        (current_y - ((upper - lower) / AGS_AUTOMATION_MAXIMUM_STEPS) <= y &&
2326 	current_y + ((upper - lower) / AGS_AUTOMATION_MAXIMUM_STEPS) >= y)){
2327       acceleration = list->data;
2328 
2329       retval = TRUE;
2330 
2331       break;
2332     }
2333 
2334     if(current_x > x){
2335       break;
2336     }
2337 
2338     list = list->next;
2339   }
2340 
2341   /* delete link and unref */
2342   if(retval){
2343     g_rec_mutex_lock(automation_mutex);
2344 
2345     automation->acceleration = g_list_remove(automation->acceleration,
2346 					     acceleration);
2347     g_object_unref(acceleration);
2348 
2349     g_rec_mutex_unlock(automation_mutex);
2350   }
2351 
2352   g_list_free_full(start_list,
2353 		   g_object_unref);
2354 
2355   return(retval);
2356 }
2357 
2358 /**
2359  * ags_automation_get_selection:
2360  * @automation: the #AgsAutomation
2361  *
2362  * Retrieve selection.
2363  *
2364  * Returns: (element-type AgsAudio.Acceleration) (transfer none): the selection.
2365  *
2366  * Since: 3.0.0
2367  */
2368 GList*
ags_automation_get_selection(AgsAutomation * automation)2369 ags_automation_get_selection(AgsAutomation *automation)
2370 {
2371   GList *selection;
2372 
2373   GRecMutex *automation_mutex;
2374 
2375   if(!AGS_IS_AUTOMATION(automation)){
2376     return(NULL);
2377   }
2378 
2379   /* get automation mutex */
2380   automation_mutex = AGS_AUTOMATION_GET_OBJ_MUTEX(automation);
2381 
2382   /* selection */
2383   g_rec_mutex_lock(automation_mutex);
2384 
2385   selection = automation->selection;
2386 
2387   g_rec_mutex_unlock(automation_mutex);
2388 
2389   return(selection);
2390 }
2391 
2392 /**
2393  * ags_automation_is_acceleration_selected:
2394  * @automation: the #AgsAutomation
2395  * @acceleration: the #AgsAcceleration to check for
2396  *
2397  * Check selection for acceleration.
2398  *
2399  * Returns: %TRUE if selected, else %FALSE
2400  *
2401  * Since: 3.0.0
2402  */
2403 gboolean
ags_automation_is_acceleration_selected(AgsAutomation * automation,AgsAcceleration * acceleration)2404 ags_automation_is_acceleration_selected(AgsAutomation *automation, AgsAcceleration *acceleration)
2405 {
2406   GList *selection;
2407 
2408   guint x, current_x;
2409   gboolean retval;
2410 
2411   GRecMutex *automation_mutex;
2412 
2413   if(!AGS_IS_AUTOMATION(automation) ||
2414      !AGS_IS_ACCELERATION(acceleration)){
2415     return(FALSE);
2416   }
2417 
2418   /* get automation mutex */
2419   automation_mutex = AGS_AUTOMATION_GET_OBJ_MUTEX(automation);
2420 
2421   /* get x */
2422   g_object_get(acceleration,
2423 	       "x", &x,
2424 	       NULL);
2425 
2426   /* match acceleration */
2427   g_rec_mutex_lock(automation_mutex);
2428 
2429   selection = automation->selection;
2430   retval = FALSE;
2431 
2432   while(selection != NULL){
2433     /* get current x */
2434     g_object_get(selection->data,
2435 		 "x", &current_x,
2436 		 NULL);
2437 
2438     if(current_x > x){
2439       break;
2440     }
2441 
2442     if(selection->data == acceleration){
2443       retval = TRUE;
2444 
2445       break;
2446     }
2447 
2448     selection = selection->next;
2449   }
2450 
2451   g_rec_mutex_unlock(automation_mutex);
2452 
2453   return(retval);
2454 }
2455 
2456 /**
2457  * ags_automation_find_point:
2458  * @automation: the #AgsAutomation
2459  * @x: x offset
2460  * @y: y value acceleration, will be ignored
2461  * @use_selection_list: if %TRUE selection is searched
2462  *
2463  * Find acceleration by offset and acceleration.
2464  *
2465  * Returns: (transfer none): the matching acceleration.
2466  *
2467  * Since: 3.0.0
2468  */
2469 AgsAcceleration*
ags_automation_find_point(AgsAutomation * automation,guint x,gdouble y,gboolean use_selection_list)2470 ags_automation_find_point(AgsAutomation *automation,
2471 			  guint x, gdouble y,
2472 			  gboolean use_selection_list)
2473 {
2474   AgsAcceleration *retval;
2475 
2476   GList *acceleration;
2477 
2478   guint current_x;
2479 
2480   GRecMutex *automation_mutex;
2481 
2482   if(!AGS_IS_AUTOMATION(automation)){
2483     return(NULL);
2484   }
2485 
2486   /* get automation mutex */
2487   automation_mutex = AGS_AUTOMATION_GET_OBJ_MUTEX(automation);
2488 
2489   /* find acceleration */
2490   g_rec_mutex_lock(automation_mutex);
2491 
2492   if(use_selection_list){
2493     acceleration = automation->selection;
2494   }else{
2495     acceleration = automation->acceleration;
2496   }
2497 
2498   retval = NULL;
2499 
2500   while(acceleration != NULL){
2501     g_object_get(acceleration->data,
2502 		 "x", &current_x,
2503 		 NULL);
2504 
2505     if(current_x > x){
2506       break;
2507     }
2508 
2509     if(current_x == x){
2510       retval = acceleration->data;
2511 
2512       break;
2513     }
2514 
2515     acceleration = acceleration->next;
2516   }
2517 
2518   g_rec_mutex_unlock(automation_mutex);
2519 
2520   return(retval);
2521 }
2522 
2523 /**
2524  * ags_automation_find_region:
2525  * @automation: the #AgsAutomation
2526  * @x0: start offset
2527  * @y0: value start
2528  * @x1: end offset
2529  * @y1: value end
2530  * @use_selection_list: if %TRUE selection is searched
2531  *
2532  * Find acceleration by offset and value region.
2533  *
2534  * Returns: (element-type AgsAudio.Acceleration) (transfer full): the matching accelerations as #GList-struct
2535  *
2536  * Since: 3.0.0
2537  */
2538 GList*
ags_automation_find_region(AgsAutomation * automation,guint x0,gdouble y0,guint x1,gdouble y1,gboolean use_selection_list)2539 ags_automation_find_region(AgsAutomation *automation,
2540 			   guint x0, gdouble y0,
2541 			   guint x1, gdouble y1,
2542 			   gboolean use_selection_list)
2543 {
2544   GList *acceleration;
2545   GList *region;
2546 
2547   guint current_x;
2548   gdouble current_y;
2549 
2550   GRecMutex *automation_mutex;
2551 
2552   if(!AGS_IS_AUTOMATION(automation)){
2553     return(NULL);
2554   }
2555 
2556   /* get automation mutex */
2557   automation_mutex = AGS_AUTOMATION_GET_OBJ_MUTEX(automation);
2558 
2559   if(x0 > x1){
2560     guint tmp;
2561 
2562     tmp = x1;
2563     x1 = x0;
2564     x0 = x1;
2565   }
2566 
2567   if(y0 > y1){
2568     gdouble tmp_y;
2569 
2570     tmp_y = y0;
2571     y0 = y1;
2572     y1 = tmp_y;
2573   }
2574 
2575   /* find acceleration */
2576   g_rec_mutex_lock(automation_mutex);
2577 
2578   if(use_selection_list){
2579     acceleration = automation->selection;
2580   }else{
2581     acceleration = automation->acceleration;
2582   }
2583 
2584   while(acceleration != NULL){
2585     g_object_get(acceleration->data,
2586 		 "x", &current_x,
2587 		 NULL);
2588 
2589     if(current_x >= x0){
2590       break;
2591     }
2592 
2593     acceleration = acceleration->next;
2594   }
2595 
2596   region = NULL;
2597 
2598   while(acceleration != NULL){
2599     g_object_get(acceleration->data,
2600 		 "x", &current_x,
2601 		 "y", &current_y,
2602 		 NULL);
2603 
2604     if(current_x > x1){
2605       break;
2606     }
2607 
2608     if(current_y >= y0 && current_y < y1){
2609       region = g_list_prepend(region,
2610 			      acceleration->data);
2611     }
2612 
2613     acceleration = acceleration->next;
2614   }
2615 
2616   g_rec_mutex_unlock(automation_mutex);
2617 
2618   region = g_list_reverse(region);
2619 
2620   return(region);
2621 }
2622 
2623 /**
2624  * ags_automation_free_selection:
2625  * @automation: the #AgsAutomation
2626  *
2627  * Clear selection.
2628  *
2629  * Since: 3.0.0
2630  */
2631 void
ags_automation_free_selection(AgsAutomation * automation)2632 ags_automation_free_selection(AgsAutomation *automation)
2633 {
2634   AgsAcceleration *acceleration;
2635 
2636   GList *list_start, *list;
2637 
2638   GRecMutex *automation_mutex;
2639 
2640   if(!AGS_IS_AUTOMATION(automation)){
2641     return;
2642   }
2643 
2644   /* get automation mutex */
2645   automation_mutex = AGS_AUTOMATION_GET_OBJ_MUTEX(automation);
2646 
2647   /* free selection */
2648   g_rec_mutex_lock(automation_mutex);
2649 
2650   list =
2651     list_start = automation->selection;
2652 
2653   while(list != NULL){
2654     ags_acceleration_unset_flags(list->data,
2655 				 AGS_ACCELERATION_IS_SELECTED);
2656 
2657     list = list->next;
2658   }
2659 
2660   automation->selection = NULL;
2661 
2662   g_rec_mutex_unlock(automation_mutex);
2663 
2664   g_list_free_full(list_start,
2665 		   g_object_unref);
2666 }
2667 
2668 /**
2669  * ags_automation_add_point_to_selection:
2670  * @automation: the #AgsAutomation
2671  * @x: x offset
2672  * @y: y acceleration value
2673  * @replace_current_selection: if %TRUE selection is replaced
2674  *
2675  * Select acceleration at position.
2676  *
2677  * Since: 3.0.0
2678  */
2679 void
ags_automation_add_point_to_selection(AgsAutomation * automation,guint x,gdouble y,gboolean replace_current_selection)2680 ags_automation_add_point_to_selection(AgsAutomation *automation,
2681 				      guint x, gdouble y,
2682 				      gboolean replace_current_selection)
2683 {
2684   AgsAcceleration *acceleration;
2685 
2686   GRecMutex *automation_mutex;
2687 
2688   if(!AGS_IS_AUTOMATION(automation)){
2689     return;
2690   }
2691 
2692   /* get automation mutex */
2693   automation_mutex = AGS_AUTOMATION_GET_OBJ_MUTEX(automation);
2694 
2695   /* find acceleration */
2696   acceleration = ags_automation_find_point(automation,
2697 					   x, y,
2698 					   FALSE);
2699 
2700   if(acceleration == NULL){
2701     /* there is nothing to be selected */
2702     if(replace_current_selection){
2703       ags_automation_free_selection(automation);
2704     }
2705   }else{
2706     /* add to or replace selection */
2707     ags_acceleration_set_flags(acceleration, AGS_ACCELERATION_IS_SELECTED);
2708 
2709     if(replace_current_selection){
2710       GList *list;
2711 
2712       list = g_list_alloc();
2713       list->data = acceleration;
2714       g_object_ref(acceleration);
2715 
2716       ags_automation_free_selection(automation);
2717 
2718       /* replace */
2719       g_rec_mutex_lock(automation_mutex);
2720 
2721       automation->selection = list;
2722 
2723       g_rec_mutex_unlock(automation_mutex);
2724     }else{
2725       if(!ags_automation_is_acceleration_selected(automation,
2726 						  acceleration)){
2727 	/* add */
2728 	ags_automation_add_acceleration(automation,
2729 					acceleration, TRUE);
2730       }
2731     }
2732   }
2733 }
2734 
2735 /**
2736  * ags_automation_remove_point_from_selection:
2737  * @automation: the #AgsAutomation
2738  * @x: x offset
2739  * @y: y acceleration value
2740  *
2741  * Remove acceleration at position of selection.
2742  *
2743  * Since: 3.0.0
2744  */
2745 void
ags_automation_remove_point_from_selection(AgsAutomation * automation,guint x,gdouble y)2746 ags_automation_remove_point_from_selection(AgsAutomation *automation,
2747 					   guint x, gdouble y)
2748 {
2749   AgsAcceleration *acceleration;
2750 
2751   GRecMutex *automation_mutex;
2752 
2753   if(!AGS_IS_AUTOMATION(automation)){
2754     return;
2755   }
2756 
2757   /* get automation mutex */
2758   automation_mutex = AGS_AUTOMATION_GET_OBJ_MUTEX(automation);
2759 
2760   /* find point */
2761   acceleration = ags_automation_find_point(automation,
2762 					   x, y,
2763 					   TRUE);
2764 
2765   if(acceleration != NULL){
2766     ags_acceleration_unset_flags(acceleration,
2767 				 AGS_ACCELERATION_IS_SELECTED);
2768 
2769     /* remove acceleration from selection */
2770     g_rec_mutex_lock(automation_mutex);
2771 
2772     automation->selection = g_list_remove(automation->selection,
2773 					  acceleration);
2774     g_object_unref(acceleration);
2775 
2776     g_rec_mutex_unlock(automation_mutex);
2777   }
2778 }
2779 
2780 /**
2781  * ags_automation_add_region_from_selection:
2782  * @automation: the #AgsAutomation
2783  * @x0: x start offset
2784  * @y0: y start acceleration
2785  * @x1: x end offset
2786  * @y1: y end acceleration
2787  * @replace_current_selection: if %TRUE current selection is replaced, else if %FALSE
2788  * it is added to current selection.
2789  *
2790  * Add acceleration within region of selection.
2791  *
2792  * Since: 3.0.0
2793  */
2794 void
ags_automation_add_region_to_selection(AgsAutomation * automation,guint x0,gdouble y0,guint x1,gdouble y1,gboolean replace_current_selection)2795 ags_automation_add_region_to_selection(AgsAutomation *automation,
2796 				       guint x0, gdouble y0,
2797 				       guint x1, gdouble y1,
2798 				       gboolean replace_current_selection)
2799 {
2800   AgsAcceleration *acceleration;
2801 
2802   GList *region, *list;
2803 
2804   GRecMutex *automation_mutex;
2805 
2806   if(!AGS_IS_AUTOMATION(automation)){
2807     return;
2808   }
2809 
2810   /* get automation mutex */
2811   automation_mutex = AGS_AUTOMATION_GET_OBJ_MUTEX(automation);
2812 
2813   /* find region */
2814   region = ags_automation_find_region(automation,
2815 				      x0, y0,
2816 				      x1, y1,
2817 				      FALSE);
2818 
2819   if(replace_current_selection){
2820     ags_automation_free_selection(automation);
2821 
2822     list = region;
2823 
2824     while(list != NULL){
2825       ags_acceleration_set_flags(list->data,
2826 				 AGS_ACCELERATION_IS_SELECTED);
2827       g_object_ref(list->data);
2828 
2829       list = list->next;
2830     }
2831 
2832     /* replace */
2833     g_rec_mutex_lock(automation_mutex);
2834 
2835     automation->selection = region;
2836 
2837     g_rec_mutex_unlock(automation_mutex);
2838   }else{
2839     list = region;
2840 
2841     while(list != NULL){
2842       if(!ags_automation_is_acceleration_selected(automation, list->data)){
2843 	/* add */
2844 	ags_automation_add_acceleration(automation,
2845 					list->data,
2846 					TRUE);
2847       }
2848 
2849       list = list->next;
2850     }
2851 
2852     g_list_free(region);
2853   }
2854 }
2855 
2856 /**
2857  * ags_automation_remove_region_from_selection:
2858  * @automation: the #AgsAutomation
2859  * @x0: x start offset
2860  * @y0: y start acceleration
2861  * @x1: x end offset
2862  * @y1: y end acceleration
2863  *
2864  * Remove acceleration within region of selection.
2865  *
2866  * Since: 3.0.0
2867  */
2868 void
ags_automation_remove_region_from_selection(AgsAutomation * automation,guint x0,gdouble y0,guint x1,gdouble y1)2869 ags_automation_remove_region_from_selection(AgsAutomation *automation,
2870 					    guint x0, gdouble y0,
2871 					    guint x1, gdouble y1)
2872 {
2873   AgsAcceleration *acceleration;
2874 
2875   GList *region;
2876   GList *list;
2877 
2878   GRecMutex *automation_mutex;
2879 
2880   if(!AGS_IS_AUTOMATION(automation)){
2881     return;
2882   }
2883 
2884   /* get automation mutex */
2885   automation_mutex = AGS_AUTOMATION_GET_OBJ_MUTEX(automation);
2886 
2887   /* find region */
2888   region = ags_automation_find_region(automation,
2889 				      x0, y0,
2890 				      x1, y1,
2891 				      TRUE);
2892 
2893   list = region;
2894 
2895   while(list != NULL){
2896     ags_acceleration_unset_flags(list->data,
2897 				 AGS_ACCELERATION_IS_SELECTED);
2898 
2899     /* remove */
2900     g_rec_mutex_lock(automation_mutex);
2901 
2902     automation->selection = g_list_remove(automation->selection,
2903 					  list->data);
2904 
2905     g_rec_mutex_unlock(automation_mutex);
2906 
2907     g_object_unref(list->data);
2908 
2909     /* iterate */
2910     list = list->next;
2911   }
2912 
2913   g_list_free(region);
2914 }
2915 
2916 /**
2917  * ags_automation_add_all_to_selection:
2918  * @automation: the #AgsAutomation
2919  *
2920  * Add all acceleration to selection.
2921  *
2922  * Since: 3.0.0
2923  */
2924 void
ags_automation_add_all_to_selection(AgsAutomation * automation)2925 ags_automation_add_all_to_selection(AgsAutomation *automation)
2926 {
2927   GList *list;
2928 
2929   GRecMutex *automation_mutex;
2930 
2931   if(!AGS_IS_AUTOMATION(automation)){
2932     return;
2933   }
2934 
2935   /* get automation mutex */
2936   automation_mutex = AGS_AUTOMATION_GET_OBJ_MUTEX(automation);
2937 
2938   /* select all */
2939   g_rec_mutex_lock(automation_mutex);
2940 
2941   list = automation->acceleration;
2942 
2943   while(list != NULL){
2944     ags_automation_add_acceleration(automation,
2945 				    list->data, TRUE);
2946 
2947     list = list->next;
2948   }
2949 
2950   g_rec_mutex_unlock(automation_mutex);
2951 }
2952 
2953 /**
2954  * ags_automation_copy_selection:
2955  * @automation: the #AgsAutomation
2956  *
2957  * Copy selection to clipboard.
2958  *
2959  * Returns: (transfer none): the selection as xmlNode
2960  *
2961  * Since: 3.0.0
2962  */
2963 xmlNode*
ags_automation_copy_selection(AgsAutomation * automation)2964 ags_automation_copy_selection(AgsAutomation *automation)
2965 {
2966   AgsTimestamp *timestamp;
2967 
2968   xmlNode *automation_node, *current_acceleration;
2969   xmlNode *timestamp_node;
2970 
2971   GList *selection;
2972 
2973   guint current_x;
2974   gdouble current_y;
2975   guint x_boundary;
2976   gdouble y_boundary;
2977 
2978   GRecMutex *automation_mutex;
2979 
2980   if(!AGS_IS_AUTOMATION(automation)){
2981     return(NULL);
2982   }
2983 
2984   /* get automation mutex */
2985   automation_mutex = AGS_AUTOMATION_GET_OBJ_MUTEX(automation);
2986 
2987   /* create root node */
2988   g_rec_mutex_lock(automation_mutex);
2989 
2990   automation_node = xmlNewNode(NULL, BAD_CAST "automation");
2991 
2992   xmlNewProp(automation_node, BAD_CAST "program", BAD_CAST "ags");
2993   xmlNewProp(automation_node, BAD_CAST "type", BAD_CAST AGS_AUTOMATION_CLIPBOARD_TYPE);
2994   xmlNewProp(automation_node, BAD_CAST "version", BAD_CAST AGS_AUTOMATION_CLIPBOARD_VERSION);
2995   xmlNewProp(automation_node, BAD_CAST "format", BAD_CAST AGS_AUTOMATION_CLIPBOARD_FORMAT);
2996   xmlNewProp(automation_node, "control-name", automation->control_name);
2997   xmlNewProp(automation_node, "line", g_strdup_printf("%u", automation->line));
2998 
2999   /* timestamp */
3000   timestamp = automation->timestamp;
3001 
3002   if(timestamp != NULL){
3003     timestamp_node = xmlNewNode(NULL,
3004 				BAD_CAST "timestamp");
3005     xmlAddChild(automation_node,
3006 		timestamp_node);
3007 
3008     xmlNewProp(timestamp_node,
3009 	       BAD_CAST "offset",
3010 	       BAD_CAST (g_strdup_printf("%lu", ags_timestamp_get_ags_offset(timestamp))));
3011   }
3012 
3013   /* selection */
3014   selection = automation->selection;
3015 
3016   if(selection != NULL){
3017     g_object_get(selection->data,
3018 		 "x", &current_x,
3019 		 NULL);
3020 
3021     x_boundary = current_x;
3022     y_boundary = G_MAXDOUBLE;
3023   }else{
3024     x_boundary = 0;
3025     y_boundary = 0.0;
3026   }
3027 
3028   while(selection != NULL){
3029     g_object_get(selection->data,
3030 		 "x", &current_x,
3031 		 "y", &current_y,
3032 		 NULL);
3033 
3034     current_acceleration = xmlNewChild(automation_node, NULL, BAD_CAST "acceleration", NULL);
3035 
3036     xmlNewProp(current_acceleration, BAD_CAST "x", BAD_CAST g_strdup_printf("%u", current_x));
3037     xmlNewProp(current_acceleration, BAD_CAST "y", BAD_CAST g_strdup_printf("%f", current_y));
3038 
3039     if(y_boundary > current_y){
3040       y_boundary = current_y;
3041     }
3042 
3043     selection = selection->next;
3044   }
3045 
3046   g_rec_mutex_unlock(automation_mutex);
3047 
3048   xmlNewProp(automation_node, BAD_CAST "x-boundary", BAD_CAST g_strdup_printf("%u", x_boundary));
3049   xmlNewProp(automation_node, BAD_CAST "y-boundary", BAD_CAST g_strdup_printf("%f", y_boundary));
3050 
3051   return(automation_node);
3052 }
3053 
3054 /**
3055  * ags_automation_cut_selection:
3056  * @automation: the #AgsAutomation
3057  *
3058  * Cut selection to clipboard.
3059  *
3060  * Returns: (transfer none): the selection as xmlNod
3061  *
3062  * Since: 3.0.0
3063  */
3064 xmlNode*
ags_automation_cut_selection(AgsAutomation * automation)3065 ags_automation_cut_selection(AgsAutomation *automation)
3066 {
3067   xmlNode *automation_node;
3068 
3069   GList *selection, *acceleration;
3070 
3071   GRecMutex *automation_mutex;
3072 
3073   if(!AGS_IS_AUTOMATION(automation)){
3074     return(NULL);
3075   }
3076 
3077   /* get automation mutex */
3078   automation_mutex = AGS_AUTOMATION_GET_OBJ_MUTEX(automation);
3079 
3080   /* copy selection */
3081   automation_node = ags_automation_copy_selection(automation);
3082 
3083   /* cut */
3084   g_rec_mutex_lock(automation_mutex);
3085 
3086   selection = automation->selection;
3087 
3088   while(selection != NULL){
3089     automation->acceleration = g_list_remove(automation->acceleration,
3090 					     selection->data);
3091     g_object_unref(selection->data);
3092 
3093     selection = selection->next;
3094   }
3095 
3096   g_rec_mutex_unlock(automation_mutex);
3097 
3098   /* free selection */
3099   ags_automation_free_selection(automation);
3100 
3101   return(automation_node);
3102 }
3103 
3104 void
ags_automation_insert_from_clipboard_version_0_4_3(AgsAutomation * automation,xmlNode * root_node,char * version,char * x_boundary,char * y_boundary,gboolean from_x_offset,guint x_offset,gboolean from_y_offset,guint y_offset,gboolean match_line,gboolean no_duplicates,guint current_line,gboolean match_timestamp)3105 ags_automation_insert_from_clipboard_version_0_4_3(AgsAutomation *automation,
3106 						   xmlNode *root_node, char *version,
3107 						   char *x_boundary, char *y_boundary,
3108 						   gboolean from_x_offset, guint x_offset,
3109 						   gboolean from_y_offset, guint y_offset,
3110 						   gboolean match_line, gboolean no_duplicates,
3111 						   guint current_line,
3112 						   gboolean match_timestamp)
3113 {
3114   AgsAcceleration *acceleration;
3115 
3116   AgsTimestamp *timestamp;
3117 
3118   xmlNode *node;
3119 
3120   char *endptr;
3121   char *x, *y;
3122 
3123   guint64 timestamp_offset;
3124   guint x_boundary_val, y_boundary_val;
3125   guint x_val;
3126   gdouble y_val;
3127   guint base_x_difference, base_y_difference;
3128   gboolean subtract_x, subtract_y;
3129 
3130   node = root_node->children;
3131 
3132   /* retrieve x values for resetting */
3133   base_x_difference = 0;
3134   subtract_x = FALSE;
3135 
3136   if(from_x_offset){
3137     if(x_boundary != NULL){
3138       errno = 0;
3139       x_boundary_val = strtoul(x_boundary, &endptr, 10);
3140 
3141       if(errno == ERANGE){
3142 	goto dont_reset_x_offset;
3143       }
3144 
3145       if(x_boundary == endptr){
3146 	goto dont_reset_x_offset;
3147       }
3148 
3149       if(x_boundary_val < x_offset){
3150 	base_x_difference = x_offset - x_boundary_val;
3151 	subtract_x = FALSE;
3152       }else{
3153 	base_x_difference = x_boundary_val - x_offset;
3154 	subtract_x = TRUE;
3155       }
3156     }else{
3157     dont_reset_x_offset:
3158       from_x_offset = FALSE;
3159     }
3160   }
3161 
3162   /* retrieve y values for resetting */
3163   base_y_difference = 0;
3164   subtract_y = FALSE;
3165 
3166   if(from_y_offset){
3167     if(y_boundary != NULL){
3168       errno = 0;
3169       y_boundary_val = strtoul(y_boundary, &endptr, 10);
3170 
3171       if(errno == ERANGE){
3172 	goto dont_reset_y_offset;
3173       }
3174 
3175       if(y_boundary == endptr){
3176 	goto dont_reset_y_offset;
3177       }
3178 
3179       if(y_boundary_val < y_offset){
3180 	base_y_difference = y_offset - y_boundary_val;
3181 	subtract_y = FALSE;
3182       }else{
3183 	base_y_difference = y_boundary_val - y_offset;
3184 	subtract_y = TRUE;
3185       }
3186     }else{
3187     dont_reset_y_offset:
3188       from_y_offset = FALSE;
3189     }
3190   }
3191 
3192   for(; node != NULL; node = node->next){
3193     if(node->type == XML_ELEMENT_NODE && !xmlStrncmp("acceleration", node->name, 5)){
3194       /* retrieve x0 offset */
3195       x = xmlGetProp(node, "x");
3196 
3197       if(x == NULL){
3198 	continue;
3199       }
3200 
3201       errno = 0;
3202       x_val = strtoul(x, &endptr, 10);
3203 
3204       if(errno == ERANGE){
3205 	continue;
3206       }
3207 
3208       if(x == endptr){
3209 	continue;
3210       }
3211 
3212       /* retrieve y offset */
3213       y = xmlGetProp(node, "y");
3214 
3215       if(y == NULL){
3216 	continue;
3217       }
3218 
3219       errno = 0;
3220       y_val = strtod(y,
3221 		     &endptr);
3222 
3223       if(errno == ERANGE){
3224 	continue;
3225       }
3226 
3227       if(y == endptr){
3228 	continue;
3229       }
3230 
3231       /* calculate new offset */
3232       if(from_x_offset){
3233 	errno = 0;
3234 
3235 	if(subtract_x){
3236 	  x_val -= base_x_difference;
3237 
3238 	  if(errno != 0){
3239 	    continue;
3240 	  }
3241 	}else{
3242 	  x_val += base_x_difference;
3243 
3244 	  if(errno != 0){
3245 	    continue;
3246 	  }
3247 	}
3248       }
3249 
3250       if(from_y_offset){
3251 	errno = 0;
3252 
3253 	if(subtract_y){
3254 	  y_val -= base_y_difference;
3255 	}else{
3256 	  y_val += base_y_difference;
3257 	}
3258 
3259 	if(errno != 0){
3260 	  continue;
3261 	}
3262       }
3263 
3264       /* check duplicate */
3265       if(no_duplicates &&
3266 	 ags_automation_find_point(automation,
3267 				   x_val, y_val,
3268 				   FALSE) != NULL){
3269 	continue;
3270       }
3271 
3272       /* add acceleration */
3273       g_object_get(automation,
3274 		   "timestamp", &timestamp,
3275 		   NULL);
3276 
3277       timestamp_offset = ags_timestamp_get_ags_offset(timestamp);
3278       g_object_unref(timestamp);
3279 
3280       if(!match_timestamp ||
3281 	 (x_val >= timestamp_offset &&
3282 	  x_val < timestamp_offset + AGS_AUTOMATION_DEFAULT_OFFSET)){
3283 	acceleration = ags_acceleration_new();
3284 
3285 	acceleration->x = x_val;
3286 	acceleration->y = y_val;
3287 
3288 #ifdef AGS_DEBUG
3289 	g_message("adding acceleration at: [%u|%f]\n", x_val, y_val);
3290 #endif
3291 
3292 	ags_automation_add_acceleration(automation,
3293 					acceleration,
3294 					FALSE);
3295       }
3296     }
3297   }
3298 }
3299 
3300 void
ags_automation_insert_native_scale_from_clipboard(AgsAutomation * automation,xmlNode * root_node,char * version,char * x_boundary,char * y_boundary,gboolean from_x_offset,guint x_offset,gboolean from_y_offset,guint y_offset,gboolean match_line,gboolean no_duplicates)3301 ags_automation_insert_native_scale_from_clipboard(AgsAutomation *automation,
3302 						  xmlNode *root_node, char *version,
3303 						  char *x_boundary, char *y_boundary,
3304 						  gboolean from_x_offset, guint x_offset,
3305 						  gboolean from_y_offset, guint y_offset,
3306 						  gboolean match_line, gboolean no_duplicates)
3307 {
3308   guint current_line;
3309 
3310   gboolean match_timestamp;
3311 
3312   if(!AGS_IS_AUTOMATION(automation)){
3313     return;
3314   }
3315 
3316   g_object_get(automation,
3317 	       "line", &current_line,
3318 	       NULL);
3319 
3320   match_timestamp = TRUE;
3321 
3322   if(!xmlStrncmp("0.4.3", version, 6)){
3323     ags_automation_insert_from_clipboard_version_0_4_3(automation,
3324 						       root_node, version,
3325 						       x_boundary, y_boundary,
3326 						       from_x_offset, x_offset,
3327 						       from_y_offset, y_offset,
3328 						       match_line, no_duplicates,
3329 						       current_line,
3330 						       match_timestamp);
3331   }else if(!xmlStrncmp("1.3.0", version, 6)){
3332     match_timestamp = TRUE;
3333 
3334     if(match_line &&
3335        current_line != g_ascii_strtoull(xmlGetProp(root_node,
3336 						   "line"),
3337 					NULL,
3338 					10)){
3339       return;
3340     }
3341 
3342     ags_automation_insert_from_clipboard_version_0_4_3(automation,
3343 						       root_node, version,
3344 						       x_boundary, y_boundary,
3345 						       from_x_offset, x_offset,
3346 						       from_y_offset, y_offset,
3347 						       match_line, no_duplicates,
3348 						       current_line,
3349 						       match_timestamp);
3350   }
3351 }
3352 
3353 /**
3354  * ags_automation_insert_from_clipboard:
3355  * @automation: the #AgsAutomation
3356  * @automation_node: the xmlNode
3357  * @reset_x_offset: if %TRUE reset x offset
3358  * @x_offset: the x offset to use
3359  * @reset_y_offset: if %TRUE reset y offset
3360  * @y_offset: the y offset to use
3361  *
3362  * Insert clipboard @automation_node to @automation.
3363  *
3364  * Since: 3.0.0
3365  */
3366 void
ags_automation_insert_from_clipboard(AgsAutomation * automation,xmlNode * automation_node,gboolean reset_x_offset,guint x_offset,gboolean reset_y_offset,gdouble y_offset)3367 ags_automation_insert_from_clipboard(AgsAutomation *automation,
3368 				     xmlNode *automation_node,
3369 				     gboolean reset_x_offset, guint x_offset,
3370 				     gboolean reset_y_offset, gdouble y_offset)
3371 {
3372   ags_automation_insert_from_clipboard_extended(automation,
3373 						automation_node,
3374 						reset_x_offset, x_offset,
3375 						reset_y_offset, y_offset,
3376 						FALSE, FALSE);
3377 }
3378 
3379 /**
3380  * ags_automation_insert_from_clipboard_extended:
3381  * @automation: the #AgsAutomation
3382  * @automation_node: the xmlNode
3383  * @reset_x_offset: if %TRUE reset x offset
3384  * @x_offset: the x offset to use
3385  * @reset_y_offset: if %TRUE reset y offset
3386  * @y_offset: the y offset to use
3387  * @match_line: if %TRUE match line or discard
3388  * @no_duplicates: if %TRUE eliminate duplicates
3389  *
3390  * Insert clipboard @automation_node to @automation.
3391  *
3392  * Since: 3.0.0
3393  */
3394 void
ags_automation_insert_from_clipboard_extended(AgsAutomation * automation,xmlNode * automation_node,gboolean reset_x_offset,guint x_offset,gboolean reset_y_offset,gdouble y_offset,gboolean match_line,gboolean no_duplicates)3395 ags_automation_insert_from_clipboard_extended(AgsAutomation *automation,
3396 					      xmlNode *automation_node,
3397 					      gboolean reset_x_offset, guint x_offset,
3398 					      gboolean reset_y_offset, gdouble y_offset,
3399 					      gboolean match_line, gboolean no_duplicates)
3400 {
3401   char *program, *version, *type, *format;
3402   char *base_frequency;
3403   char *x_boundary, *y_boundary;
3404 
3405   if(!AGS_IS_AUTOMATION(automation)){
3406     return;
3407   }
3408 
3409   while(automation_node != NULL){
3410     if(automation_node->type == XML_ELEMENT_NODE && !xmlStrncmp("automation", automation_node->name, 11)){
3411       break;
3412     }
3413 
3414     automation_node = automation_node->next;
3415   }
3416 
3417   if(automation_node != NULL){
3418     program = xmlGetProp(automation_node, "program");
3419 
3420     if(!xmlStrncmp("ags", program, 4)){
3421       version = xmlGetProp(automation_node, "version");
3422       type = xmlGetProp(automation_node, "type");
3423       format = xmlGetProp(automation_node, "format");
3424 
3425       if(!xmlStrcmp(AGS_AUTOMATION_CLIPBOARD_FORMAT,
3426 		    format) ||
3427 	 !xmlStrcmp(AGS_AUTOMATION_CLIPBOARD_LEGACY_FORMAT,
3428 		    format)){
3429 	x_boundary = xmlGetProp(automation_node, "x_boundary");
3430 	y_boundary = xmlGetProp(automation_node, "y_boundary");
3431 
3432 	ags_automation_insert_native_scale_from_clipboard(automation,
3433 							  automation_node, version,
3434 							  x_boundary, y_boundary,
3435 							  reset_x_offset, x_offset,
3436 							  reset_y_offset, y_offset,
3437 							  match_line, no_duplicates);
3438       }
3439     }
3440   }
3441 }
3442 
3443 /**
3444  * ags_automation_get_specifier_unique:
3445  * @automation: (element-type AgsAudio.Automation) (transfer none): the #GList-struct containing #AgsAutomation
3446  *
3447  * Retrieve automation port specifier.
3448  *
3449  * Returns: (element-type utf8) (array zero-terminated=1) (transfer full): a %NULL terminated string array
3450  *
3451  * Since: 3.0.0
3452  */
3453 gchar**
ags_automation_get_specifier_unique(GList * automation)3454 ags_automation_get_specifier_unique(GList *automation)
3455 {
3456   AgsAutomation *current_automation;
3457 
3458   gchar **specifier;
3459   gchar *current_control_name;
3460 
3461   guint length, i;
3462   gboolean contains_control_name;
3463 
3464   GRecMutex *automation_mutex;
3465 
3466   if(automation == NULL){
3467     return(NULL);
3468   }
3469 
3470   specifier = (gchar **) malloc(sizeof(gchar*));
3471   specifier[0] = NULL;
3472   length = 1;
3473 
3474   while(automation != NULL){
3475     current_automation = automation->data;
3476 
3477     /* get automation mutex */
3478     automation_mutex = AGS_AUTOMATION_GET_OBJ_MUTEX(current_automation);
3479 
3480     /* duplicate control name */
3481     g_rec_mutex_lock(automation_mutex);
3482 
3483     current_control_name = g_strdup(current_automation->control_name);
3484 
3485     g_rec_mutex_unlock(automation_mutex);
3486 
3487 #ifdef HAVE_GLIB_2_44
3488     contains_control_name = g_strv_contains(specifier,
3489 					    current_control_name);
3490 #else
3491     contains_control_name = ags_strv_contains(specifier,
3492 					      current_control_name);
3493 #endif
3494 
3495     if(!contains_control_name){
3496       specifier = (gchar **) realloc(specifier,
3497 				     (length + 1) * sizeof(gchar *));
3498       specifier[length - 1] = current_control_name;
3499       specifier[length] = NULL;
3500 
3501       length++;
3502     }else{
3503       g_free(current_control_name);
3504     }
3505 
3506     /* iterate */
3507     automation = automation->next;
3508   }
3509 
3510   return(specifier);
3511 }
3512 
3513 /**
3514  * ags_automation_get_specifier_unique_with_channel_type:
3515  * @automation: (element-type AgsAudio.Automation) (transfer none): the #GList-struct containing #AgsAutomation
3516  * @channel_type: the channel's #GType
3517  *
3518  * Retrieve automation port specifier.
3519  *
3520  * Returns: (element-type utf8) (array zero-terminated=1) (transfer full): a %NULL terminated string array
3521  *
3522  * Since: 3.0.0
3523  */
3524 gchar**
ags_automation_get_specifier_unique_with_channel_type(GList * automation,GType channel_type)3525 ags_automation_get_specifier_unique_with_channel_type(GList *automation,
3526 						      GType channel_type)
3527 {
3528   AgsAutomation *current_automation;
3529 
3530   GType current_channel_type;
3531 
3532   gchar **specifier;
3533   gchar *current_control_name;
3534 
3535   guint length, i;
3536   gboolean contains_control_name;
3537 
3538   GRecMutex *automation_mutex;
3539 
3540   if(automation == NULL){
3541     return(NULL);
3542   }
3543 
3544   specifier = (gchar **) malloc(sizeof(gchar*));
3545   specifier[0] = NULL;
3546   length = 1;
3547 
3548   while(automation != NULL){
3549     current_automation = automation->data;
3550 
3551     /* get automation mutex */
3552     automation_mutex = AGS_AUTOMATION_GET_OBJ_MUTEX(current_automation);
3553 
3554     /* get channel type */
3555     g_rec_mutex_lock(automation_mutex);
3556 
3557     current_channel_type = current_automation->channel_type;
3558 
3559     g_rec_mutex_unlock(automation_mutex);
3560 
3561     if(current_channel_type != channel_type){
3562       automation = automation->next;
3563 
3564       continue;
3565     }
3566 
3567     /* duplicate control name */
3568     g_rec_mutex_lock(automation_mutex);
3569 
3570     current_control_name = g_strdup(current_automation->control_name);
3571 
3572     g_rec_mutex_unlock(automation_mutex);
3573 
3574 #ifdef HAVE_GLIB_2_44
3575     contains_control_name = g_strv_contains(specifier,
3576 					    current_control_name);
3577 #else
3578     contains_control_name = ags_strv_contains(specifier,
3579 					      current_control_name);
3580 #endif
3581 
3582     if(!contains_control_name){
3583       specifier = (gchar **) realloc(specifier,
3584 				     (length + 1) * sizeof(gchar *));
3585       specifier[length - 1] = current_control_name;
3586       specifier[length] = NULL;
3587 
3588       length++;
3589     }else{
3590       g_free(current_control_name);
3591     }
3592 
3593     /* iterate */
3594     automation = automation->next;
3595   }
3596 
3597   return(specifier);
3598 }
3599 
3600 /**
3601  * ags_automation_find_specifier:
3602  * @automation: (element-type AgsAudio.Automation) (transfer none): the #GList-struct containing #AgsAutomation
3603  * @specifier: the string specifier to find
3604  *
3605  * Find port specifier.
3606  *
3607  * Returns: (element-type AgsAudio.Automation) (transfer none): Next matching #GList
3608  *
3609  * Since: 3.0.0
3610  */
3611 GList*
ags_automation_find_specifier(GList * automation,gchar * specifier)3612 ags_automation_find_specifier(GList *automation,
3613 			      gchar *specifier)
3614 {
3615   AgsAutomation *current_automation;
3616 
3617   gchar *current_control_name;
3618 
3619   gboolean success;
3620 
3621   GRecMutex *automation_mutex;
3622 
3623   while(automation != NULL){
3624     current_automation = automation->data;
3625 
3626     /* get automation mutex */
3627     automation_mutex = AGS_AUTOMATION_GET_OBJ_MUTEX(current_automation);
3628 
3629     /* duplicate control name */
3630     g_rec_mutex_lock(automation_mutex);
3631 
3632     current_control_name = g_strdup(current_automation->control_name);
3633 
3634     g_rec_mutex_unlock(automation_mutex);
3635 
3636     /* check control name */
3637     success = (!g_ascii_strcasecmp(current_control_name,
3638 				   specifier)) ? TRUE: FALSE;
3639     g_free(current_control_name);
3640 
3641     if(success){
3642       break;
3643     }
3644 
3645     automation = automation->next;
3646   }
3647 
3648   return(automation);
3649 }
3650 
3651 /**
3652  * ags_automation_find_channel_type_with_control_name:
3653  * @automation: (element-type AgsAudio.Automation) (transfer none): the #GList-struct containing #AgsAutomation
3654  * @channel_type: the #GType to match
3655  * @specifier: the control name
3656  *
3657  * Find automation by @channel_type.
3658  *
3659  * Returns: (element-type AgsAudio.Automation) (transfer none): next matching automation as #GList-struct or %NULL if not found
3660  *
3661  * Since: 3.0.0
3662  */
3663 GList*
ags_automation_find_channel_type_with_control_name(GList * automation,GType channel_type,gchar * specifier)3664 ags_automation_find_channel_type_with_control_name(GList *automation,
3665 						   GType channel_type, gchar *specifier)
3666 {
3667   AgsAutomation *current_automation;
3668 
3669   GType current_channel_type;
3670 
3671   gchar *current_control_name;
3672 
3673   gboolean success;
3674 
3675   GRecMutex *automation_mutex;
3676 
3677   if(automation == NULL){
3678     return(NULL);
3679   }
3680 
3681   while(automation != NULL){
3682     current_automation = automation->data;
3683 
3684     /* get automation mutex */
3685     automation_mutex = AGS_AUTOMATION_GET_OBJ_MUTEX(current_automation);
3686 
3687     /* duplicate control name */
3688     g_rec_mutex_lock(automation_mutex);
3689 
3690     current_channel_type = current_automation->channel_type;
3691 
3692     current_control_name = g_strdup(current_automation->control_name);
3693 
3694     g_rec_mutex_unlock(automation_mutex);
3695 
3696     /* check channel type and control name */
3697     success = (current_channel_type == channel_type &&
3698 	       !g_strcmp0(current_control_name,
3699 			  specifier)) ? TRUE: FALSE;
3700 
3701     g_free(current_control_name);
3702 
3703     if(success){
3704       break;
3705     }
3706 
3707     automation = automation->next;
3708   }
3709 
3710   return(automation);
3711 }
3712 
3713 /**
3714  * ags_automation_find_specifier_with_type_and_line:
3715  * @automation: (element-type AgsAudio.Automation) (transfer none): the #GList-struct containing #AgsAutomation
3716  * @specifier: the string specifier to find
3717  * @channel_type: the channel #GType
3718  * @line: the line
3719  *
3720  * Find port specifier with channel type and line.
3721  *
3722  * Returns: (element-type AgsAudio.Automation) (transfer none): Next matching #GList-struct
3723  *
3724  * Since: 3.0.0
3725  */
3726 GList*
ags_automation_find_specifier_with_type_and_line(GList * automation,gchar * specifier,GType channel_type,guint line)3727 ags_automation_find_specifier_with_type_and_line(GList *automation,
3728 						 gchar *specifier,
3729 						 GType channel_type,
3730 						 guint line)
3731 {
3732   AgsAutomation *current_automation;
3733 
3734   GType current_channel_type;
3735 
3736   gchar *current_control_name;
3737 
3738   guint current_line;
3739   gboolean success;
3740 
3741   GRecMutex *automation_mutex;
3742 
3743   if(automation == NULL){
3744     return(NULL);
3745   }
3746 
3747   while(automation != NULL){
3748     current_automation = automation->data;
3749 
3750     /* get automation mutex */
3751     automation_mutex = AGS_AUTOMATION_GET_OBJ_MUTEX(current_automation);
3752 
3753     /* duplicate control name */
3754     g_rec_mutex_lock(automation_mutex);
3755 
3756     current_channel_type = current_automation->channel_type;
3757     current_line = current_automation->line;
3758 
3759     current_control_name = g_strdup(current_automation->control_name);
3760 
3761     g_rec_mutex_unlock(automation_mutex);
3762 
3763     /* check channel type, line and control name */
3764     success = (!g_ascii_strcasecmp(current_control_name,
3765 				   specifier) &&
3766 	       current_channel_type == channel_type &&
3767 	       current_line == line) ? TRUE: FALSE;
3768 
3769     g_free(current_control_name);
3770 
3771     if(success){
3772       break;
3773     }
3774 
3775     automation = automation->next;
3776   }
3777 
3778   return(automation);
3779 }
3780 
3781 /**
3782  * ags_automation_get_value:
3783  * @automation: the #AgsAutomation
3784  * @x: the x-offset
3785  * @x_end: the x-end-offset
3786  * @use_prev_on_failure: if %TRUE use previous value, else return G_MAXUINT
3787  * @value: (out caller-allocates): the return location of value
3788  *
3789  * Get automation value.
3790  *
3791  * Returns: the x_offset
3792  *
3793  * Since: 3.0.0
3794  */
3795 guint
ags_automation_get_value(AgsAutomation * automation,guint x,guint x_end,gboolean use_prev_on_failure,GValue * value)3796 ags_automation_get_value(AgsAutomation *automation,
3797 			 guint x, guint x_end,
3798 			 gboolean use_prev_on_failure,
3799 			 GValue *value)
3800 {
3801   AgsAcceleration *matching_acceleration;
3802   AgsPort *port;
3803 
3804   GType port_value_type;
3805 
3806   GList *acceleration;
3807   GList *next, *prev;
3808   GList *current_start, *current_end, *current;
3809 
3810   guint current_start_x, current_end_x, current_x;
3811   guint length, position;
3812   gboolean success;
3813   guint ret_x;
3814   gdouble default_value;
3815   gdouble y;
3816   gboolean port_value_is_pointer;
3817 
3818   GRecMutex *automation_mutex;
3819 
3820   if(!AGS_IS_AUTOMATION(automation)){
3821     return(G_MAXUINT);
3822   }
3823 
3824   /* get automation mutex */
3825   automation_mutex = AGS_AUTOMATION_GET_OBJ_MUTEX(automation);
3826 
3827   /*  */
3828   g_rec_mutex_lock(automation_mutex);
3829 
3830   port = (AgsPort *) automation->port;
3831   acceleration = automation->acceleration;
3832 
3833   current_start = acceleration;
3834   current_end = g_list_last(acceleration);
3835 
3836   length = g_list_length(acceleration);
3837   position = (length - 1) / 2;
3838 
3839   current = g_list_nth(current_start,
3840 		       position);
3841 
3842   if(acceleration == NULL){
3843     g_rec_mutex_unlock(automation_mutex);
3844 
3845     return(G_MAXUINT);
3846   }
3847 
3848   matching_acceleration = NULL;
3849 
3850   ret_x = 0;
3851   success = FALSE;
3852 
3853   while(!success && current != NULL){
3854     g_object_get(current_start->data,
3855 		 "x", &current_start_x,
3856 		 NULL);
3857 
3858     if(current_start_x > x){
3859       break;
3860     }
3861 
3862     if(current_start_x >= x &&
3863        current_start_x < x_end){
3864       matching_acceleration = current_start->data;
3865 
3866       break;
3867     }
3868 
3869     g_object_get(current_end->data,
3870 		 "x", &current_end_x,
3871 		 NULL);
3872 
3873     if(current_end_x < x){
3874       break;
3875     }
3876 
3877     if(current_end_x >= x &&
3878        current_end_x < x_end){
3879       matching_acceleration = current_end->data;
3880 
3881       break;
3882     }
3883 
3884     g_object_get(current->data,
3885 		 "x", &current_x,
3886 		 NULL);
3887 
3888     if(current_x >= x &&
3889        current_x < x_end){
3890       matching_acceleration = current->data;
3891 
3892       break;
3893     }
3894 
3895     if(length <= 3){
3896       break;
3897     }
3898 
3899     if(current_x < x){
3900       current_start = current->next;
3901       current_end = current_end->prev;
3902     }else if(current_x > x){
3903       current_start = current_start->next;
3904       current_end = current->prev;
3905     }else{
3906       current_start = current_start->next;
3907       //NOTE:JK: we want progression
3908       //current_end = current_end->prev;
3909     }
3910 
3911     length = g_list_position(current_start,
3912 			     current_end) + 1;
3913     position = (length - 1) / 2;
3914 
3915     current = g_list_nth(current_start,
3916 			 position);
3917   }
3918 
3919   if(matching_acceleration != NULL){
3920     guint tmp_x;
3921 
3922     next = NULL;
3923 
3924     if(current_start->data == matching_acceleration){
3925       next = current_start->next;
3926 
3927       ret_x = current_start_x;
3928     }else if(current_end->data == matching_acceleration){
3929       next = current_end->next;
3930 
3931       ret_x = current_end_x;
3932     }else if(current->data == matching_acceleration){
3933       next = current->next;
3934 
3935       ret_x = current_x;
3936     }
3937 
3938     while(next != NULL){
3939       g_object_get(next->data,
3940 		   "x", &tmp_x,
3941 		   NULL);
3942 
3943       if(tmp_x > x_end){
3944 	break;
3945       }
3946 
3947       matching_acceleration = next->data;
3948 
3949       next = next->next;
3950     }
3951   }else{
3952     if(use_prev_on_failure){
3953       guint tmp_x;
3954 
3955       prev = current_end;
3956 
3957       while(prev != NULL){
3958 	g_object_get(prev->data,
3959 		     "x", &tmp_x,
3960 		     NULL);
3961 
3962 
3963 	if(tmp_x < x){
3964 	  matching_acceleration = prev->data;
3965 
3966 	  break;
3967 	}
3968 
3969 	prev = prev->prev;
3970       }
3971     }
3972   }
3973 
3974   g_rec_mutex_unlock(automation_mutex);
3975 
3976   if(matching_acceleration == NULL){
3977     return(G_MAXUINT);
3978   }
3979 
3980   /* apply port */
3981   g_object_get(automation,
3982 	       "default-value", &default_value,
3983 	       NULL);
3984 
3985   g_object_get(port,
3986 	       "port-value-is-pointer", &port_value_is_pointer,
3987 	       "port-value-type", &port_value_type,
3988 	       NULL);
3989 
3990   g_object_get(matching_acceleration,
3991 	       "y", &y,
3992 	       NULL);
3993 
3994   if(!port_value_is_pointer){
3995     if(port_value_type == G_TYPE_BOOLEAN){
3996       gboolean current;
3997 
3998       current = FALSE;
3999 
4000       if(acceleration == NULL){
4001 	if(default_value != 0.0){
4002 	  current = TRUE;
4003 	}
4004       }else{
4005 	if(y != 0.0){
4006 	  current = TRUE;
4007 	}
4008       }
4009 
4010       g_value_init(value,
4011 		   G_TYPE_BOOLEAN);
4012       g_value_set_boolean(value,
4013 			  current);
4014     }else if(port_value_type == G_TYPE_INT64){
4015       gint64 current;
4016 
4017       current = floor(y);
4018 
4019       g_value_init(value,
4020 		   G_TYPE_INT64);
4021       g_value_set_int64(value,
4022 			current);
4023     }else if(port_value_type == G_TYPE_UINT64){
4024       guint64 current;
4025 
4026       current = floor(y);
4027 
4028       g_value_init(value,
4029 		   G_TYPE_UINT64);
4030       g_value_set_uint64(value,
4031 			 current);
4032     }else if(port_value_type == G_TYPE_FLOAT){
4033       gfloat current;
4034 
4035       current = y;
4036 
4037       g_value_init(value,
4038 		   G_TYPE_FLOAT);
4039       g_value_set_float(value,
4040 			current);
4041     }else if(port_value_type == G_TYPE_DOUBLE){
4042       gdouble current;
4043 
4044       current = y;
4045 
4046       g_value_init(value,
4047 		   G_TYPE_DOUBLE);
4048       g_value_set_double(value,
4049 			 current);
4050     }else if(port_value_type == G_TYPE_POINTER){
4051       g_warning("ags_automation.c - unsupported value type pointer");
4052     }else if(port_value_type == G_TYPE_OBJECT){
4053       g_warning("ags_automation.c - unsupported value type object");
4054     }else{
4055       g_warning("ags_automation.c - unknown type");
4056     }
4057   }else{
4058     g_warning("ags_automation.c - unsupported value type pointer");
4059   }
4060 
4061   return(ret_x);
4062 }
4063 
4064 /**
4065  * ags_automation_new:
4066  * @audio: the #AgsAudio
4067  * @line: the line to apply
4068  * @channel_type: the channel type
4069  * @control_name: the control name
4070  *
4071  * Creates a new instance of #AgsAutomation.
4072  *
4073  * Returns: the new #AgsAutomation
4074  *
4075  * Since: 3.0.0
4076  */
4077 AgsAutomation*
ags_automation_new(GObject * audio,guint line,GType channel_type,gchar * control_name)4078 ags_automation_new(GObject *audio,
4079 		   guint line,
4080 		   GType channel_type,
4081 		   gchar *control_name)
4082 {
4083   AgsAutomation *automation;
4084 
4085   automation = (AgsAutomation *) g_object_new(AGS_TYPE_AUTOMATION,
4086 					      "audio", audio,
4087 					      "line", line,
4088 					      "channel-type", channel_type,
4089 					      "control-name", control_name,
4090 					      NULL);
4091 
4092   return(automation);
4093 }
4094