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", ¤t_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", ¤t_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", ¤t_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", ¤t_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", ¤t_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", ¤t_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", ¤t_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", ¤t_line,
1353 "channel-type", ¤t_channel_type,
1354 "control-name", ¤t_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", ¤t_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", ¤t_line,
1418 "channel-type", ¤t_channel_type,
1419 "control-name", ¤t_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", ¤t_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", ¤t_line,
1485 "channel-type", ¤t_channel_type,
1486 "control-name", ¤t_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", ¤t_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", ×tamp_a,
1595 NULL);
1596
1597 g_object_get(b,
1598 "timestamp", ×tamp_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", ×tamp,
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", ¤t_x,
2321 "y", ¤t_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", ¤t_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", ¤t_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", ¤t_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", ¤t_x,
2601 "y", ¤t_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", ¤t_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", ¤t_x,
3031 "y", ¤t_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", ×tamp,
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", ¤t_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", ¤t_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", ¤t_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", ¤t_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