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_notation.h>
21
22 #include <ags/audio/ags_audio.h>
23 #include <ags/audio/ags_port.h>
24
25 #include <ags/audio/midi/ags_midi_util.h>
26 #include <ags/audio/midi/ags_midi_parser.h>
27 #include <ags/audio/midi/ags_midi_builder.h>
28
29 #include <stdlib.h>
30
31 #include <ags/i18n.h>
32
33 #include <errno.h>
34
35 void ags_notation_class_init(AgsNotationClass *notation);
36 void ags_notation_init(AgsNotation *notation);
37 void ags_notation_set_property(GObject *gobject,
38 guint prop_id,
39 const GValue *value,
40 GParamSpec *param_spec);
41 void ags_notation_get_property(GObject *gobject,
42 guint prop_id,
43 GValue *value,
44 GParamSpec *param_spec);
45 void ags_notation_dispose(GObject *gobject);
46 void ags_notation_finalize(GObject *gobject);
47
48 void ags_notation_insert_native_piano_from_clipboard_version_0_3_12(AgsNotation *notation,
49 xmlNode *root_node, char *version,
50 char *base_frequency,
51 char *x_boundary, char *y_boundary,
52 gboolean reset_x_offset, guint x_offset,
53 gboolean reset_y_offset, guint y_offset,
54 gboolean match_channel, gboolean no_duplicates,
55 guint current_audio_channel,
56 gboolean match_timestamp);
57
58 void ags_notation_insert_native_piano_from_clipboard(AgsNotation *notation,
59 xmlNode *root_node, char *version,
60 char *base_frequency,
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_channel, gboolean no_duplicates);
65
66 /**
67 * SECTION:ags_notation
68 * @short_description: Notation class supporting selection and clipboard.
69 * @title: AgsNotation
70 * @section_id:
71 * @include: ags/audio/ags_notation.h
72 *
73 * #AgsNotation acts as a container of #AgsNote.
74 */
75
76 enum{
77 PROP_0,
78 PROP_AUDIO,
79 PROP_AUDIO_CHANNEL,
80 PROP_TIMESTAMP,
81 PROP_NOTE,
82 };
83
84 static gpointer ags_notation_parent_class = NULL;
85
86 GType
ags_notation_get_type()87 ags_notation_get_type()
88 {
89 static volatile gsize g_define_type_id__volatile = 0;
90
91 if(g_once_init_enter (&g_define_type_id__volatile)){
92 GType ags_type_notation = 0;
93
94 static const GTypeInfo ags_notation_info = {
95 sizeof(AgsNotationClass),
96 NULL,
97 NULL,
98 (GClassInitFunc) ags_notation_class_init,
99 NULL,
100 NULL,
101 sizeof(AgsNotation),
102 0,
103 (GInstanceInitFunc) ags_notation_init,
104 };
105
106 ags_type_notation = g_type_register_static(G_TYPE_OBJECT,
107 "AgsNotation",
108 &ags_notation_info,
109 0);
110
111 g_once_init_leave(&g_define_type_id__volatile, ags_type_notation);
112 }
113
114 return g_define_type_id__volatile;
115 }
116
117 void
ags_notation_class_init(AgsNotationClass * notation)118 ags_notation_class_init(AgsNotationClass *notation)
119 {
120 GObjectClass *gobject;
121 GParamSpec *param_spec;
122
123 ags_notation_parent_class = g_type_class_peek_parent(notation);
124
125 gobject = (GObjectClass *) notation;
126
127 gobject->set_property = ags_notation_set_property;
128 gobject->get_property = ags_notation_get_property;
129
130 gobject->dispose = ags_notation_dispose;
131 gobject->finalize = ags_notation_finalize;
132
133 /* properties */
134 /**
135 * AgsNotation:audio:
136 *
137 * The assigned #AgsAudio
138 *
139 * Since: 3.0.0
140 */
141 param_spec = g_param_spec_object("audio",
142 i18n_pspec("audio of notation"),
143 i18n_pspec("The audio of notation"),
144 AGS_TYPE_AUDIO,
145 G_PARAM_READABLE | G_PARAM_WRITABLE);
146 g_object_class_install_property(gobject,
147 PROP_AUDIO,
148 param_spec);
149
150
151 /**
152 * AgsNotation:audio-channel:
153 *
154 * The effect's audio-channel.
155 *
156 * Since: 3.0.0
157 */
158 param_spec = g_param_spec_uint("audio-channel",
159 i18n_pspec("audio-channel of effect"),
160 i18n_pspec("The numerical audio-channel of effect"),
161 0,
162 65535,
163 0,
164 G_PARAM_READABLE | G_PARAM_WRITABLE);
165 g_object_class_install_property(gobject,
166 PROP_AUDIO_CHANNEL,
167 param_spec);
168
169 /**
170 * AgsNotation:timestamp:
171 *
172 * The notation's timestamp.
173 *
174 * Since: 3.0.0
175 */
176 param_spec = g_param_spec_object("timestamp",
177 i18n_pspec("timestamp of notation"),
178 i18n_pspec("The timestamp of notation"),
179 AGS_TYPE_TIMESTAMP,
180 G_PARAM_READABLE | G_PARAM_WRITABLE);
181 g_object_class_install_property(gobject,
182 PROP_TIMESTAMP,
183 param_spec);
184
185 /**
186 * AgsNotation:note: (type GList(AgsNote)) (transfer full)
187 *
188 * The assigned #AgsNote
189 *
190 * Since: 3.0.0
191 */
192 param_spec = g_param_spec_pointer("note",
193 i18n_pspec("note of notation"),
194 i18n_pspec("The note of notation"),
195 G_PARAM_READABLE | G_PARAM_WRITABLE);
196 g_object_class_install_property(gobject,
197 PROP_NOTE,
198 param_spec);
199 }
200
201 void
ags_notation_init(AgsNotation * notation)202 ags_notation_init(AgsNotation *notation)
203 {
204 notation->flags = 0;
205
206 /* add notation mutex */
207 g_rec_mutex_init(&(notation->obj_mutex));
208
209 /* fields */
210 notation->audio = NULL;
211 notation->audio_channel = 0;
212
213 notation->timestamp = ags_timestamp_new();
214
215 notation->timestamp->flags &= (~AGS_TIMESTAMP_UNIX);
216 notation->timestamp->flags |= AGS_TIMESTAMP_OFFSET;
217
218 notation->timestamp->timer.ags_offset.offset = 0;
219
220 g_object_ref(notation->timestamp);
221
222 notation->maximum_note_length = AGS_NOTATION_MAXIMUM_NOTE_LENGTH;
223
224 notation->note = NULL;
225 notation->selection = NULL;
226 }
227
228 void
ags_notation_set_property(GObject * gobject,guint prop_id,const GValue * value,GParamSpec * param_spec)229 ags_notation_set_property(GObject *gobject,
230 guint prop_id,
231 const GValue *value,
232 GParamSpec *param_spec)
233 {
234 AgsNotation *notation;
235
236 GRecMutex *notation_mutex;
237
238 notation = AGS_NOTATION(gobject);
239
240 /* get notation mutex */
241 notation_mutex = AGS_NOTATION_GET_OBJ_MUTEX(notation);
242
243 switch(prop_id){
244 case PROP_AUDIO:
245 {
246 AgsAudio *audio;
247
248 audio = (AgsAudio *) g_value_get_object(value);
249
250 g_rec_mutex_lock(notation_mutex);
251
252 if(notation->audio == (GObject *) audio){
253 g_rec_mutex_unlock(notation_mutex);
254
255 return;
256 }
257
258 if(notation->audio != NULL){
259 g_object_unref(notation->audio);
260 }
261
262 if(audio != NULL){
263 g_object_ref(audio);
264 }
265
266 notation->audio = (GObject *) audio;
267
268 g_rec_mutex_unlock(notation_mutex);
269 }
270 break;
271 case PROP_AUDIO_CHANNEL:
272 {
273 guint audio_channel;
274
275 audio_channel = g_value_get_uint(value);
276
277 g_rec_mutex_lock(notation_mutex);
278
279 notation->audio_channel = audio_channel;
280
281 g_rec_mutex_unlock(notation_mutex);
282 }
283 break;
284 case PROP_TIMESTAMP:
285 {
286 AgsTimestamp *timestamp;
287
288 timestamp = (AgsTimestamp *) g_value_get_object(value);
289
290 g_rec_mutex_lock(notation_mutex);
291
292 if(timestamp == notation->timestamp){
293 g_rec_mutex_unlock(notation_mutex);
294
295 return;
296 }
297
298 if(notation->timestamp != NULL){
299 g_object_unref(G_OBJECT(notation->timestamp));
300 }
301
302 if(timestamp != NULL){
303 g_object_ref(G_OBJECT(timestamp));
304 }
305
306 notation->timestamp = timestamp;
307
308 g_rec_mutex_unlock(notation_mutex);
309 }
310 break;
311 case PROP_NOTE:
312 {
313 AgsNote *note;
314
315 note = (AgsNote *) g_value_get_object(value);
316
317 g_rec_mutex_lock(notation_mutex);
318
319 if(note == NULL ||
320 g_list_find(notation->note, note) != NULL){
321 g_rec_mutex_unlock(notation_mutex);
322
323 return;
324 }
325
326 g_rec_mutex_unlock(notation_mutex);
327
328 ags_notation_add_note(notation,
329 note,
330 FALSE);
331 }
332 break;
333 default:
334 G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, param_spec);
335 break;
336 }
337 }
338
339 void
ags_notation_get_property(GObject * gobject,guint prop_id,GValue * value,GParamSpec * param_spec)340 ags_notation_get_property(GObject *gobject,
341 guint prop_id,
342 GValue *value,
343 GParamSpec *param_spec)
344 {
345 AgsNotation *notation;
346
347 GRecMutex *notation_mutex;
348
349 notation = AGS_NOTATION(gobject);
350
351 /* get notation mutex */
352 notation_mutex = AGS_NOTATION_GET_OBJ_MUTEX(notation);
353
354 switch(prop_id){
355 case PROP_AUDIO:
356 {
357 g_rec_mutex_lock(notation_mutex);
358
359 g_value_set_object(value, notation->audio);
360
361 g_rec_mutex_unlock(notation_mutex);
362 }
363 break;
364 case PROP_AUDIO_CHANNEL:
365 {
366 g_rec_mutex_lock(notation_mutex);
367
368 g_value_set_uint(value, notation->audio_channel);
369
370 g_rec_mutex_unlock(notation_mutex);
371 }
372 break;
373 case PROP_TIMESTAMP:
374 {
375 g_rec_mutex_lock(notation_mutex);
376
377 g_value_set_object(value, notation->timestamp);
378
379 g_rec_mutex_unlock(notation_mutex);
380 }
381 break;
382 case PROP_NOTE:
383 {
384 g_rec_mutex_lock(notation_mutex);
385
386 g_value_set_pointer(value, g_list_copy_deep(notation->note,
387 (GCopyFunc) g_object_ref,
388 NULL));
389
390 g_rec_mutex_unlock(notation_mutex);
391 }
392 break;
393 default:
394 G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, param_spec);
395 break;
396 }
397 }
398
399 void
ags_notation_dispose(GObject * gobject)400 ags_notation_dispose(GObject *gobject)
401 {
402 AgsNotation *notation;
403
404 GList *list;
405
406 notation = AGS_NOTATION(gobject);
407
408 /* audio */
409 if(notation->audio != NULL){
410 g_object_unref(notation->audio);
411
412 notation->audio = NULL;
413 }
414
415 /* timestamp */
416 if(notation->timestamp != NULL){
417 g_object_unref(notation->timestamp);
418
419 notation->timestamp = NULL;
420 }
421
422 /* note and selection */
423 list = notation->note;
424
425 while(list != NULL){
426 g_object_run_dispose(G_OBJECT(list->data));
427
428 list = list->next;
429 }
430
431 g_list_free_full(notation->note,
432 g_object_unref);
433 g_list_free_full(notation->selection,
434 g_object_unref);
435
436 notation->note = NULL;
437 notation->selection = NULL;
438
439 /* call parent */
440 G_OBJECT_CLASS(ags_notation_parent_class)->dispose(gobject);
441 }
442
443 void
ags_notation_finalize(GObject * gobject)444 ags_notation_finalize(GObject *gobject)
445 {
446 AgsNotation *notation;
447
448 notation = AGS_NOTATION(gobject);
449
450 /* audio */
451 if(notation->audio != NULL){
452 g_object_unref(notation->audio);
453 }
454
455 /* timestamp */
456 if(notation->timestamp != NULL){
457 g_object_unref(notation->timestamp);
458 }
459
460 /* note and selection */
461 g_list_free_full(notation->note,
462 g_object_unref);
463
464 g_list_free_full(notation->selection,
465 g_object_unref);
466
467 /* call parent */
468 G_OBJECT_CLASS(ags_notation_parent_class)->finalize(gobject);
469 }
470
471 /**
472 * ags_notation_get_obj_mutex:
473 * @notation: the #AgsNotation
474 *
475 * Get object mutex.
476 *
477 * Returns: the #GRecMutex to lock @notation
478 *
479 * Since: 3.1.0
480 */
481 GRecMutex*
ags_notation_get_obj_mutex(AgsNotation * notation)482 ags_notation_get_obj_mutex(AgsNotation *notation)
483 {
484 if(!AGS_IS_NOTATION(notation)){
485 return(NULL);
486 }
487
488 return(AGS_NOTATION_GET_OBJ_MUTEX(notation));
489 }
490
491 /**
492 * ags_notation_test_flags:
493 * @notation: the #AgsNotation
494 * @flags: the flags
495 *
496 * Test @flags to be set on @notation.
497 *
498 * Returns: %TRUE if flags are set, else %FALSE
499 *
500 * Since: 3.0.0
501 */
502 gboolean
ags_notation_test_flags(AgsNotation * notation,guint flags)503 ags_notation_test_flags(AgsNotation *notation, guint flags)
504 {
505 gboolean retval;
506
507 GRecMutex *notation_mutex;
508
509 if(!AGS_IS_NOTATION(notation)){
510 return(FALSE);
511 }
512
513 /* get notation mutex */
514 notation_mutex = AGS_NOTATION_GET_OBJ_MUTEX(notation);
515
516 /* test */
517 g_rec_mutex_lock(notation_mutex);
518
519 retval = (flags & (notation->flags)) ? TRUE: FALSE;
520
521 g_rec_mutex_unlock(notation_mutex);
522
523 return(retval);
524 }
525
526 /**
527 * ags_notation_set_flags:
528 * @notation: the #AgsNotation
529 * @flags: the flags
530 *
531 * Set @flags on @notation.
532 *
533 * Since: 3.0.0
534 */
535 void
ags_notation_set_flags(AgsNotation * notation,guint flags)536 ags_notation_set_flags(AgsNotation *notation, guint flags)
537 {
538 GRecMutex *notation_mutex;
539
540 if(!AGS_IS_NOTATION(notation)){
541 return;
542 }
543
544 /* get notation mutex */
545 notation_mutex = AGS_NOTATION_GET_OBJ_MUTEX(notation);
546
547 /* set */
548 g_rec_mutex_lock(notation_mutex);
549
550 notation->flags |= flags;
551
552 g_rec_mutex_unlock(notation_mutex);
553 }
554
555 /**
556 * ags_notation_unset_flags:
557 * @notation: the #AgsNotation
558 * @flags: the flags
559 *
560 * Unset @flags on @notation.
561 *
562 * Since: 3.0.0
563 */
564 void
ags_notation_unset_flags(AgsNotation * notation,guint flags)565 ags_notation_unset_flags(AgsNotation *notation, guint flags)
566 {
567 GRecMutex *notation_mutex;
568
569 if(!AGS_IS_NOTATION(notation)){
570 return;
571 }
572
573 /* get notation mutex */
574 notation_mutex = AGS_NOTATION_GET_OBJ_MUTEX(notation);
575
576 /* set */
577 g_rec_mutex_lock(notation_mutex);
578
579 notation->flags &= (~flags);
580
581 g_rec_mutex_unlock(notation_mutex);
582 }
583
584 /**
585 * ags_notation_find_near_timestamp:
586 * @notation: (element-type AgsAudio.Notation) (transfer none): the #GList-struct containing #AgsNotation
587 * @audio_channel: the matching audio channel
588 * @timestamp: (allow-none): the matching timestamp, or %NULL to match any timestamp
589 *
590 * Retrieve appropriate notation for timestamp.
591 *
592 * Returns: (element-type AgsAudio.Notation) (transfer none): Next matching #GList-struct or %NULL if not found
593 *
594 * Since: 3.0.0
595 */
596 GList*
ags_notation_find_near_timestamp(GList * notation,guint audio_channel,AgsTimestamp * timestamp)597 ags_notation_find_near_timestamp(GList *notation, guint audio_channel,
598 AgsTimestamp *timestamp)
599 {
600 AgsTimestamp *current_timestamp;
601
602 GList *retval;
603 GList *current_start, *current_end, *current;
604
605 guint current_audio_channel;
606 guint64 current_x, x;
607 guint length, position;
608 gboolean use_ags_offset;
609 gboolean success;
610
611 if(notation == NULL){
612 return(NULL);
613 }
614
615 current_start = notation;
616 current_end = g_list_last(notation);
617
618 length = g_list_length(notation);
619 position = (length - 1) / 2;
620
621 current = g_list_nth(current_start,
622 position);
623
624 if(ags_timestamp_test_flags(timestamp,
625 AGS_TIMESTAMP_OFFSET)){
626 x = ags_timestamp_get_ags_offset(timestamp);
627
628 use_ags_offset = TRUE;
629 }else if(ags_timestamp_test_flags(timestamp,
630 AGS_TIMESTAMP_UNIX)){
631 x = ags_timestamp_get_unix_time(timestamp);
632
633 use_ags_offset = FALSE;
634 }else{
635 return(NULL);
636 }
637
638 retval = NULL;
639 success = FALSE;
640
641 while(!success && current != NULL){
642 current_x = 0;
643
644 /* check current - start */
645 g_object_get(current_start->data,
646 "audio-channel", ¤t_audio_channel,
647 NULL);
648
649 if(current_audio_channel == audio_channel){
650 if(timestamp == NULL){
651 retval = current_start;
652
653 break;
654 }
655
656 g_object_get(current_start->data,
657 "timestamp", ¤t_timestamp,
658 NULL);
659
660 if(current_timestamp != NULL){
661 if(use_ags_offset){
662 current_x = ags_timestamp_get_ags_offset(current_timestamp);
663
664 g_object_unref(current_timestamp);
665
666 if(current_x > x){
667 break;
668 }
669 }else{
670 current_x = ags_timestamp_get_unix_time(current_timestamp);
671
672 g_object_unref(current_timestamp);
673
674 if(current_x > x){
675 break;
676 }
677 }
678
679 if(use_ags_offset){
680 if(current_x >= x &&
681 current_x < x + AGS_NOTATION_DEFAULT_OFFSET){
682 retval = current_start;
683
684 break;
685 }
686 }else{
687 if(current_x >= x &&
688 current_x < x + AGS_NOTATION_DEFAULT_DURATION){
689 retval = current_start;
690
691 break;
692 }
693 }
694 }else{
695 g_warning("inconsistent data");
696 }
697 }
698
699 /* check current - end */
700 g_object_get(current_end->data,
701 "audio-channel", ¤t_audio_channel,
702 NULL);
703
704 if(current_audio_channel == audio_channel){
705 if(timestamp == NULL){
706 retval = current_end;
707
708 break;
709 }
710
711 g_object_get(current_end->data,
712 "timestamp", ¤t_timestamp,
713 NULL);
714
715 if(current_timestamp != NULL){
716 if(use_ags_offset){
717 current_x = ags_timestamp_get_ags_offset(current_timestamp);
718
719 g_object_unref(current_timestamp);
720
721 if(current_x < x){
722 break;
723 }
724 }else{
725 current_x = ags_timestamp_get_unix_time(current_timestamp);
726
727 g_object_unref(current_timestamp);
728
729 if(current_x < x){
730 break;
731 }
732 }
733
734 if(use_ags_offset){
735 if(current_x >= x &&
736 current_x < x + AGS_NOTATION_DEFAULT_OFFSET){
737 retval = current_end;
738
739 break;
740 }
741 }else{
742 if(current_x >= x &&
743 current_x < x + AGS_NOTATION_DEFAULT_DURATION){
744 retval = current_end;
745
746 break;
747 }
748 }
749 }else{
750 g_warning("inconsistent data");
751 }
752 }
753
754 /* check current - center */
755 g_object_get(current->data,
756 "audio-channel", ¤t_audio_channel,
757 NULL);
758
759 if(current_audio_channel == audio_channel){
760 if(timestamp == NULL){
761 retval = current;
762
763 break;
764 }
765 }
766
767 g_object_get(current->data,
768 "timestamp", ¤t_timestamp,
769 NULL);
770
771 if(current_timestamp != NULL){
772 if(use_ags_offset){
773 current_x = ags_timestamp_get_ags_offset(current_timestamp);
774
775 g_object_unref(current_timestamp);
776
777 if(current_x >= x &&
778 current_x < x + AGS_NOTATION_DEFAULT_OFFSET &&
779 current_audio_channel == audio_channel){
780 retval = current;
781
782 break;
783 }
784 }else{
785 current_x = ags_timestamp_get_unix_time(current_timestamp);
786
787 g_object_unref(current_timestamp);
788
789 if(current_x >= x &&
790 current_x < x + AGS_NOTATION_DEFAULT_DURATION &&
791 current_audio_channel == audio_channel){
792 retval = current;
793
794 break;
795 }
796 }
797 }else{
798 g_warning("inconsistent data");
799 }
800
801 if(length <= 3){
802 break;
803 }
804
805 if(current_x < x){
806 current_start = current->next;
807 current_end = current_end->prev;
808 }else if(current_x > x){
809 current_start = current_start->next;
810 current_end = current->prev;
811 }else{
812 current_start = current_start->next;
813 //NOTE:JK: we want progression
814 //current_end = current_end->prev;
815 }
816
817 length = g_list_position(current_start,
818 current_end) + 1;
819 position = (length - 1) / 2;
820
821 current = g_list_nth(current_start,
822 position);
823 }
824
825 return(retval);
826 }
827
828 /**
829 * ags_notation_sort_func:
830 * @a: the #AgsNotation
831 * @b: another #AgsNotation
832 *
833 * Compare @a and @b.
834 *
835 * Returns: 0 if equal, -1 if smaller and 1 if bigger timestamp
836 *
837 * Since: 3.0.0
838 */
839 gint
ags_notation_sort_func(gconstpointer a,gconstpointer b)840 ags_notation_sort_func(gconstpointer a,
841 gconstpointer b)
842 {
843 AgsTimestamp *timestamp_a, *timestamp_b;
844
845 guint64 offset_a, offset_b;
846
847 g_object_get(a,
848 "timestamp", ×tamp_a,
849 NULL);
850
851 g_object_get(b,
852 "timestamp", ×tamp_b,
853 NULL);
854
855 offset_a = ags_timestamp_get_ags_offset(timestamp_a);
856 offset_b = ags_timestamp_get_ags_offset(timestamp_b);
857
858 g_object_unref(timestamp_a);
859 g_object_unref(timestamp_b);
860
861 if(offset_a == offset_b){
862 return(0);
863 }else if(offset_a < offset_b){
864 return(-1);
865 }else if(offset_a > offset_b){
866 return(1);
867 }
868
869 return(0);
870 }
871
872 /**
873 * ags_notation_get_audio:
874 * @notation: the #AgsNotation
875 *
876 * Get audio.
877 *
878 * Returns: (transfer full): the #AgsAudio
879 *
880 * Since: 3.1.0
881 */
882 GObject*
ags_notation_get_audio(AgsNotation * notation)883 ags_notation_get_audio(AgsNotation *notation)
884 {
885 GObject *audio;
886
887 if(!AGS_IS_NOTATION(notation)){
888 return(NULL);
889 }
890
891 g_object_get(notation,
892 "audio", &audio,
893 NULL);
894
895 return(audio);
896 }
897
898 /**
899 * ags_notation_set_audio:
900 * @notation: the #AgsNotation
901 * @audio: the #AgsAudio
902 *
903 * Set audio.
904 *
905 * Since: 3.1.0
906 */
907 void
ags_notation_set_audio(AgsNotation * notation,GObject * audio)908 ags_notation_set_audio(AgsNotation *notation, GObject *audio)
909 {
910 if(!AGS_IS_NOTATION(notation)){
911 return;
912 }
913
914 g_object_set(notation,
915 "audio", audio,
916 NULL);
917 }
918
919 /**
920 * ags_notation_get_audio_channel:
921 * @notation: the #AgsNotation
922 *
923 * Gets audio channel.
924 *
925 * Returns: the audio channel
926 *
927 * Since: 3.1.0
928 */
929 guint
ags_notation_get_audio_channel(AgsNotation * notation)930 ags_notation_get_audio_channel(AgsNotation *notation)
931 {
932 guint audio_channel;
933
934 if(!AGS_IS_NOTATION(notation)){
935 return(0);
936 }
937
938 g_object_get(notation,
939 "audio-channel", &audio_channel,
940 NULL);
941
942 return(audio_channel);
943 }
944
945 /**
946 * ags_notation_set_audio_channel:
947 * @notation: the #AgsNotation
948 * @audio_channel: the audio channel
949 *
950 * Sets audio channel.
951 *
952 * Since: 3.1.0
953 */
954 void
ags_notation_set_audio_channel(AgsNotation * notation,guint audio_channel)955 ags_notation_set_audio_channel(AgsNotation *notation, guint audio_channel)
956 {
957 if(!AGS_IS_NOTATION(notation)){
958 return;
959 }
960
961 g_object_set(notation,
962 "audio-channel", audio_channel,
963 NULL);
964 }
965
966 /**
967 * ags_notation_get_is_minor:
968 * @notation: the #AgsNotation
969 *
970 * Gets is minor.
971 *
972 * Returns: is minor
973 *
974 * Since: 3.1.0
975 */
976 gboolean
ags_notation_get_is_minor(AgsNotation * notation)977 ags_notation_get_is_minor(AgsNotation *notation)
978 {
979 gboolean is_minor;
980
981 if(!AGS_IS_NOTATION(notation)){
982 return(FALSE);
983 }
984
985 g_object_get(notation,
986 "is-minor", &is_minor,
987 NULL);
988
989 return(is_minor);
990 }
991
992 /**
993 * ags_notation_set_is_minor:
994 * @notation: the #AgsNotation
995 * @is_minor: is minor
996 *
997 * Sets is minor.
998 *
999 * Since: 3.1.0
1000 */
1001 void
ags_notation_set_is_minor(AgsNotation * notation,gboolean is_minor)1002 ags_notation_set_is_minor(AgsNotation *notation, gboolean is_minor)
1003 {
1004 if(!AGS_IS_NOTATION(notation)){
1005 return;
1006 }
1007
1008 g_object_set(notation,
1009 "is-minor", is_minor,
1010 NULL);
1011 }
1012
1013 /**
1014 * ags_notation_get_sharp_flats:
1015 * @notation: the #AgsNotation
1016 *
1017 * Gets sharp flats.
1018 *
1019 * Returns: the sharp flats
1020 *
1021 * Since: 3.1.0
1022 */
1023 guint
ags_notation_get_sharp_flats(AgsNotation * notation)1024 ags_notation_get_sharp_flats(AgsNotation *notation)
1025 {
1026 guint sharp_flats;
1027
1028 if(!AGS_IS_NOTATION(notation)){
1029 return(0);
1030 }
1031
1032 g_object_get(notation,
1033 "sharp-flats", &sharp_flats,
1034 NULL);
1035
1036 return(sharp_flats);
1037 }
1038
1039 /**
1040 * ags_notation_set_sharp_flats:
1041 * @notation: the #AgsNotation
1042 * @sharp_flats: the sharp flats
1043 *
1044 * Sets sharp flats.
1045 *
1046 * Since: 3.1.0
1047 */
1048 void
ags_notation_set_sharp_flats(AgsNotation * notation,guint sharp_flats)1049 ags_notation_set_sharp_flats(AgsNotation *notation, guint sharp_flats)
1050 {
1051 if(!AGS_IS_NOTATION(notation)){
1052 return;
1053 }
1054
1055 g_object_set(notation,
1056 "sharp-flats", sharp_flats,
1057 NULL);
1058 }
1059
1060 /**
1061 * ags_notation_get_timestamp:
1062 * @notation: the #AgsNotation
1063 *
1064 * Get timestamp.
1065 *
1066 * Returns: (transfer full): the #AgsTimestamp
1067 *
1068 * Since: 3.1.0
1069 */
1070 AgsTimestamp*
ags_notation_get_timestamp(AgsNotation * notation)1071 ags_notation_get_timestamp(AgsNotation *notation)
1072 {
1073 AgsTimestamp *timestamp;
1074
1075 if(!AGS_IS_NOTATION(notation)){
1076 return(NULL);
1077 }
1078
1079 g_object_get(notation,
1080 "timestamp", ×tamp,
1081 NULL);
1082
1083 return(timestamp);
1084 }
1085
1086 /**
1087 * ags_notation_set_timestamp:
1088 * @notation: the #AgsNotation
1089 * @timestamp: the #AgsTimestamp
1090 *
1091 * Set timestamp.
1092 *
1093 * Since: 3.1.0
1094 */
1095 void
ags_notation_set_timestamp(AgsNotation * notation,AgsTimestamp * timestamp)1096 ags_notation_set_timestamp(AgsNotation *notation, AgsTimestamp *timestamp)
1097 {
1098 if(!AGS_IS_NOTATION(notation)){
1099 return;
1100 }
1101
1102 g_object_set(notation,
1103 "timestamp", timestamp,
1104 NULL);
1105 }
1106
1107 /**
1108 * ags_notation_get_note:
1109 * @notation: the #AgsNotation
1110 *
1111 * Get note.
1112 *
1113 * Returns: (element-type AgsAudio.Note) (transfer full): the #GList-struct containig #AgsNote
1114 *
1115 * Since: 3.1.0
1116 */
1117 GList*
ags_notation_get_note(AgsNotation * notation)1118 ags_notation_get_note(AgsNotation *notation)
1119 {
1120 GList *note;
1121
1122 if(!AGS_IS_NOTATION(notation)){
1123 return(NULL);
1124 }
1125
1126 g_object_get(notation,
1127 "note", ¬e,
1128 NULL);
1129
1130 return(note);
1131 }
1132
1133 /**
1134 * ags_notation_set_note:
1135 * @notation: the #AgsNotation
1136 * @note: (element-type AgsAudio.Note) (transfer full): the #GList-struct containing #AgsNote
1137 *
1138 * Set note by replacing existing.
1139 *
1140 * Since: 3.1.0
1141 */
1142 void
ags_notation_set_note(AgsNotation * notation,GList * note)1143 ags_notation_set_note(AgsNotation *notation, GList *note)
1144 {
1145 GList *start_note;
1146
1147 GRecMutex *notation_mutex;
1148
1149 if(!AGS_IS_NOTATION(notation)){
1150 return;
1151 }
1152
1153 /* get notation mutex */
1154 notation_mutex = AGS_NOTATION_GET_OBJ_MUTEX(notation);
1155
1156 g_rec_mutex_lock(notation_mutex);
1157
1158 start_note = notation->note;
1159 notation->note = note;
1160
1161 g_rec_mutex_unlock(notation_mutex);
1162
1163 g_list_free_full(start_note,
1164 (GDestroyNotify) g_object_unref);
1165 }
1166
1167 /**
1168 * ags_notation_add:
1169 * @notation: (element-type AgsAudio.Notation) (transfer none): the #GList-struct containing #AgsNotation
1170 * @new_notation: the #AgsNotation to add
1171 *
1172 * Add @new_notation sorted to @notation
1173 *
1174 * Returns: (element-type AgsAudio.Notation) (transfer none): the new beginning of @notation
1175 *
1176 * Since: 3.0.0
1177 */
1178 GList*
ags_notation_add(GList * notation,AgsNotation * new_notation)1179 ags_notation_add(GList *notation,
1180 AgsNotation *new_notation)
1181 {
1182 AgsTimestamp *timestamp;
1183
1184 GList *list;
1185
1186 guint audio_channel;
1187
1188 if(!AGS_IS_NOTATION(new_notation)){
1189 return(notation);
1190 }
1191
1192 g_object_get(new_notation,
1193 "timestamp", ×tamp,
1194 "audio-channel", &audio_channel,
1195 NULL);
1196
1197 list = ags_notation_find_near_timestamp(notation, audio_channel,
1198 timestamp);
1199 g_object_unref(timestamp);
1200
1201 if(list != NULL){
1202 g_critical("timestamp already preset");
1203
1204 return(notation);
1205 }
1206
1207 notation = g_list_insert_sorted(notation,
1208 new_notation,
1209 ags_notation_sort_func);
1210
1211 return(notation);
1212 }
1213
1214 /**
1215 * ags_notation_add_note:
1216 * @notation: the #AgsNotation
1217 * @note: the #AgsNote to add
1218 * @use_selection_list: if %TRUE add to selection, else to default notation
1219 *
1220 * Adds @note to @notation.
1221 *
1222 * Since: 3.0.0
1223 */
1224 void
ags_notation_add_note(AgsNotation * notation,AgsNote * note,gboolean use_selection_list)1225 ags_notation_add_note(AgsNotation *notation,
1226 AgsNote *note,
1227 gboolean use_selection_list)
1228 {
1229 AgsTimestamp *timestamp;
1230
1231 guint64 timestamp_x;
1232 guint x0;
1233
1234 GRecMutex *notation_mutex;
1235
1236 if(!AGS_IS_NOTATION(notation) ||
1237 !AGS_IS_NOTE(note)){
1238 return;
1239 }
1240
1241 /* get notation mutex */
1242 notation_mutex = AGS_NOTATION_GET_OBJ_MUTEX(notation);
1243
1244 g_object_get(notation,
1245 "timestamp", ×tamp,
1246 NULL);
1247
1248 timestamp_x = ags_timestamp_get_ags_offset(timestamp);
1249 g_object_unref(timestamp);
1250
1251 g_object_get(note,
1252 "x0", &x0,
1253 NULL);
1254
1255 if(x0 < timestamp_x ||
1256 x0 >= timestamp_x + AGS_NOTATION_DEFAULT_OFFSET){
1257 g_critical("timestamp not matching note:x0");
1258
1259 return;
1260 }
1261
1262 /* insert sorted */
1263 g_object_ref(note);
1264
1265 #ifdef AGS_DEBUG
1266 g_message("add note[%d,%d|%d]", note->x[0], note->x[1], note->y);
1267 #endif
1268
1269 g_rec_mutex_lock(notation_mutex);
1270
1271 if(use_selection_list){
1272 notation->selection = g_list_insert_sorted(notation->selection,
1273 note,
1274 (GCompareFunc) ags_note_sort_func);
1275 ags_note_set_flags(note,
1276 AGS_NOTE_IS_SELECTED);
1277 }else{
1278 notation->note = g_list_insert_sorted(notation->note,
1279 note,
1280 (GCompareFunc) ags_note_sort_func);
1281 }
1282
1283 g_rec_mutex_unlock(notation_mutex);
1284 }
1285
1286 /**
1287 * ags_notation_remove_note:
1288 * @notation: the #AgsNotation
1289 * @note: the #AgsNote to remove
1290 * @use_selection_list: if %TRUE remove from selection, else from default notation
1291 *
1292 * Removes @note from @notation.
1293 *
1294 * Since: 3.0.0
1295 */
1296 void
ags_notation_remove_note(AgsNotation * notation,AgsNote * note,gboolean use_selection_list)1297 ags_notation_remove_note(AgsNotation *notation,
1298 AgsNote *note,
1299 gboolean use_selection_list)
1300 {
1301 GRecMutex *notation_mutex;
1302
1303 if(!AGS_IS_NOTATION(notation) ||
1304 !AGS_IS_NOTE(note)){
1305 return;
1306 }
1307
1308 /* get notation mutex */
1309 notation_mutex = AGS_NOTATION_GET_OBJ_MUTEX(notation);
1310
1311 /* remove if found */
1312 g_rec_mutex_lock(notation_mutex);
1313
1314 if(!use_selection_list){
1315 if(g_list_find(notation->note,
1316 note) != NULL){
1317 notation->note = g_list_remove(notation->note,
1318 note);
1319 g_object_unref(note);
1320 }
1321 }else{
1322 if(g_list_find(notation->selection,
1323 note) != NULL){
1324 notation->selection = g_list_remove(notation->selection,
1325 note);
1326 g_object_unref(note);
1327 }
1328 }
1329
1330 g_rec_mutex_unlock(notation_mutex);
1331 }
1332
1333 /**
1334 * ags_notation_remove_note_at_position:
1335 * @notation: the #AgsNotation
1336 * @x: offset
1337 * @y: note
1338 *
1339 * Removes one #AgsNote of notation.
1340 *
1341 * Returns: %TRUE if successfully removed note.
1342 *
1343 * Since: 3.0.0
1344 */
1345 gboolean
ags_notation_remove_note_at_position(AgsNotation * notation,guint x,guint y)1346 ags_notation_remove_note_at_position(AgsNotation *notation,
1347 guint x, guint y)
1348 {
1349 AgsNote *note;
1350
1351 GList *start_list, *list;
1352
1353 guint current_x0, current_y;
1354 gboolean retval;
1355
1356 GRecMutex *notation_mutex;
1357
1358 if(!AGS_IS_NOTATION(notation)){
1359 return(FALSE);
1360 }
1361
1362 /* get notation mutex */
1363 notation_mutex = AGS_NOTATION_GET_OBJ_MUTEX(notation);
1364
1365 /* find note */
1366 g_rec_mutex_lock(notation_mutex);
1367
1368 list =
1369 start_list = g_list_copy_deep(notation->note,
1370 (GCopyFunc) g_object_ref,
1371 NULL);
1372
1373 g_rec_mutex_unlock(notation_mutex);
1374
1375 note = NULL;
1376
1377 retval = FALSE;
1378
1379 while(list != NULL){
1380 g_object_get(list->data,
1381 "x0", ¤t_x0,
1382 "y", ¤t_y,
1383 NULL);
1384
1385 if(current_x0 == x &&
1386 current_y == y){
1387 note = list->data;
1388
1389 retval = TRUE;
1390
1391 break;
1392 }
1393
1394 if(current_x0 > x){
1395 break;
1396 }
1397
1398 list = list->next;
1399 }
1400
1401 /* delete link and unref */
1402 if(retval){
1403 g_rec_mutex_lock(notation_mutex);
1404
1405 notation->note = g_list_remove(notation->note,
1406 note);
1407 g_object_unref(note);
1408
1409 g_rec_mutex_unlock(notation_mutex);
1410 }
1411
1412 g_list_free_full(start_list,
1413 g_object_unref);
1414
1415 return(retval);
1416 }
1417
1418 /**
1419 * ags_notation_get_selection:
1420 * @notation: the #AgsNotation
1421 *
1422 * Retrieve selection.
1423 *
1424 * Returns: (element-type AgsAudio.Note) (transfer none): the selection.
1425 *
1426 * Since: 3.0.0
1427 */
1428 GList*
ags_notation_get_selection(AgsNotation * notation)1429 ags_notation_get_selection(AgsNotation *notation)
1430 {
1431 GList *selection;
1432
1433 GRecMutex *notation_mutex;
1434
1435 if(!AGS_IS_NOTATION(notation)){
1436 return(NULL);
1437 }
1438
1439 /* get notation mutex */
1440 notation_mutex = AGS_NOTATION_GET_OBJ_MUTEX(notation);
1441
1442 /* selection */
1443 g_rec_mutex_lock(notation_mutex);
1444
1445 selection = notation->selection;
1446
1447 g_rec_mutex_unlock(notation_mutex);
1448
1449 return(selection);
1450 }
1451
1452 /**
1453 * ags_notation_is_note_selected:
1454 * @notation: the #AgsNotation
1455 * @note: the #AgsNote to check for
1456 *
1457 * Check selection for note.
1458 *
1459 * Returns: %TRUE if selected otherwise %FALSE
1460 *
1461 * Since: 3.0.0
1462 */
1463 gboolean
ags_notation_is_note_selected(AgsNotation * notation,AgsNote * note)1464 ags_notation_is_note_selected(AgsNotation *notation, AgsNote *note)
1465 {
1466 GList *selection;
1467
1468 guint x0;
1469 guint current_x0;
1470 gboolean retval;
1471
1472 GRecMutex *notation_mutex;
1473
1474 if(!AGS_IS_NOTATION(notation) ||
1475 !AGS_IS_NOTE(note)){
1476 return(FALSE);
1477 }
1478
1479 /* get notation mutex */
1480 notation_mutex = AGS_NOTATION_GET_OBJ_MUTEX(notation);
1481
1482 /* get x */
1483 g_object_get(note,
1484 "x0", &x0,
1485 NULL);
1486
1487 /* match note */
1488 g_rec_mutex_lock(notation_mutex);
1489
1490 selection = notation->selection;
1491 retval = FALSE;
1492
1493 while(selection != NULL){
1494 /* get current x */
1495 g_object_get(selection->data,
1496 "x0", ¤t_x0,
1497 NULL);
1498
1499 if(current_x0 > x0){
1500 break;
1501 }
1502
1503 if(selection->data == note){
1504 retval = TRUE;
1505
1506 break;
1507 }
1508
1509 selection = selection->next;
1510 }
1511
1512 g_rec_mutex_unlock(notation_mutex);
1513
1514 return(retval);
1515 }
1516
1517 /**
1518 * ags_notation_find_point:
1519 * @notation: the #AgsNotation
1520 * @x: offset
1521 * @y: note
1522 * @use_selection_list: if %TRUE selection is searched
1523 *
1524 * Find note by offset and tone.
1525 *
1526 * Returns: (transfer none): the matching note.
1527 *
1528 * Since: 3.0.0
1529 */
1530 AgsNote*
ags_notation_find_point(AgsNotation * notation,guint x,guint y,gboolean use_selection_list)1531 ags_notation_find_point(AgsNotation *notation,
1532 guint x, guint y,
1533 gboolean use_selection_list)
1534 {
1535 AgsNote *retval;
1536
1537 GList *note;
1538
1539 guint current_x0, current_x1, current_y;
1540
1541 GRecMutex *notation_mutex;
1542
1543 if(!AGS_IS_NOTATION(notation)){
1544 return(NULL);
1545 }
1546
1547 /* get notation mutex */
1548 notation_mutex = AGS_NOTATION_GET_OBJ_MUTEX(notation);
1549
1550 /* find note */
1551 g_rec_mutex_lock(notation_mutex);
1552
1553 if(use_selection_list){
1554 note = notation->selection;
1555 }else{
1556 note = notation->note;
1557 }
1558
1559 retval = NULL;
1560
1561 while(note != NULL){
1562 g_object_get(note->data,
1563 "x0", ¤t_x0,
1564 "x1", ¤t_x1,
1565 "y", ¤t_y,
1566 NULL);
1567
1568 if(current_x0 > x){
1569 break;
1570 }
1571
1572 if(x >= current_x0 &&
1573 x < current_x1 &&
1574 current_y == y){
1575 retval = note->data;
1576
1577 break;
1578 }
1579
1580 note = note->next;
1581 }
1582
1583 g_rec_mutex_unlock(notation_mutex);
1584
1585 return(retval);
1586 }
1587
1588 /**
1589 * ags_notation_find_region:
1590 * @notation: the #AgsNotation
1591 * @x0: start offset
1592 * @y0: start tone
1593 * @x1: end offset
1594 * @y1: end tone
1595 * @use_selection_list: if %TRUE selection is searched
1596 *
1597 * Find note by offset and tone region.
1598 *
1599 * Returns: (element-type AgsAudio.Note) (transfer container): the matching notes as #GList-struct
1600 *
1601 * Since: 3.0.0
1602 */
1603 GList*
ags_notation_find_region(AgsNotation * notation,guint x0,guint y0,guint x1,guint y1,gboolean use_selection_list)1604 ags_notation_find_region(AgsNotation *notation,
1605 guint x0, guint y0,
1606 guint x1, guint y1,
1607 gboolean use_selection_list)
1608 {
1609 GList *note;
1610 GList *region;
1611
1612 guint current_x0, current_y;
1613
1614 GRecMutex *notation_mutex;
1615
1616 if(!AGS_IS_NOTATION(notation)){
1617 return(NULL);
1618 }
1619
1620 /* get notation mutex */
1621 notation_mutex = AGS_NOTATION_GET_OBJ_MUTEX(notation);
1622
1623 if(x0 > x1){
1624 guint tmp;
1625
1626 tmp = x1;
1627 x1 = x0;
1628 x0 = x1;
1629 }
1630
1631 if(y0 > y1){
1632 gdouble tmp_y;
1633
1634 tmp_y = y0;
1635 y0 = y1;
1636 y1 = tmp_y;
1637 }
1638
1639 /* find note */
1640 g_rec_mutex_lock(notation_mutex);
1641
1642 if(use_selection_list){
1643 note = notation->selection;
1644 }else{
1645 note = notation->note;
1646 }
1647
1648 while(note != NULL){
1649 g_object_get(note->data,
1650 "x0", ¤t_x0,
1651 NULL);
1652
1653 if(current_x0 >= x0){
1654 break;
1655 }
1656
1657 note = note->next;
1658 }
1659
1660 region = NULL;
1661
1662 while(note != NULL){
1663 g_object_get(note->data,
1664 "x0", ¤t_x0,
1665 "y", ¤t_y,
1666 NULL);
1667
1668 if(current_x0 > x1){
1669 break;
1670 }
1671
1672 if(current_y >= y0 && current_y < y1){
1673 region = g_list_prepend(region,
1674 note->data);
1675 }
1676
1677 note = note->next;
1678 }
1679
1680 g_rec_mutex_unlock(notation_mutex);
1681
1682 region = g_list_reverse(region);
1683
1684 return(region);
1685 }
1686
1687 /**
1688 * ags_notation_find_offset:
1689 * @notation: the #AgsNotation
1690 * @x: offset
1691 * @use_selection_list: if %TRUE selection is searched
1692 *
1693 * Find all notes by offset @x.
1694 *
1695 * Returns: (element-type AgsAudio.Note) (transfer full): the #GList-struct containing matching #AgsNote
1696 *
1697 * Since: 3.0.0
1698 */
1699 GList*
ags_notation_find_offset(AgsNotation * notation,guint x,gboolean use_selection_list)1700 ags_notation_find_offset(AgsNotation *notation,
1701 guint x,
1702 gboolean use_selection_list)
1703 {
1704 GList *retval;
1705 GList *note;
1706 GList *next, *prev;
1707 GList *current_start, *current_end, *current;
1708
1709 guint current_start_x, current_end_x, current_x;
1710 guint length, position;
1711 gboolean success;
1712
1713 GRecMutex *notation_mutex;
1714
1715 if(!AGS_IS_NOTATION(notation)){
1716 return(NULL);
1717 }
1718
1719 /* get notation mutex */
1720 notation_mutex = AGS_NOTATION_GET_OBJ_MUTEX(notation);
1721
1722 /* find note */
1723 g_rec_mutex_lock(notation_mutex);
1724
1725 if(use_selection_list){
1726 note = notation->selection;
1727 }else{
1728 note = notation->note;
1729 }
1730
1731 current_start = note;
1732 current_end = g_list_last(note);
1733
1734 length = g_list_length(note);
1735 position = (length - 1) / 2;
1736
1737 current = g_list_nth(current_start,
1738 position);
1739
1740 retval = NULL;
1741 success = FALSE;
1742
1743 while(!success && current != NULL){
1744 g_object_get(current_start->data,
1745 "x0", ¤t_start_x,
1746 NULL);
1747
1748 if(current_start_x > x){
1749 break;
1750 }
1751
1752 if(current_start_x == x){
1753 retval = g_list_prepend(retval,
1754 current_start->data);
1755 g_object_ref(current_start->data);
1756
1757 break;
1758 }
1759
1760 g_object_get(current_end->data,
1761 "x0", ¤t_end_x,
1762 NULL);
1763
1764 if(current_end_x < x){
1765 break;
1766 }
1767
1768 if(current_end_x == x){
1769 retval = g_list_prepend(retval,
1770 current_end->data);
1771 g_object_ref(current_end->data);
1772
1773 break;
1774 }
1775
1776 g_object_get(current->data,
1777 "x0", ¤t_x,
1778 NULL);
1779
1780 if(current_x == x){
1781 retval = g_list_prepend(retval,
1782 current->data);
1783 g_object_ref(current->data);
1784
1785 break;
1786 }
1787
1788 if(length <= 3){
1789 break;
1790 }
1791
1792 if(current_x < x){
1793 current_start = current->next;
1794 current_end = current_end->prev;
1795 }else if(current_x > x){
1796 current_start = current_start->next;
1797 current_end = current->prev;
1798 }else{
1799 current_start = current_start->next;
1800 //NOTE:JK: we want progression
1801 //current_end = current_end->prev;
1802 }
1803
1804 length = g_list_position(current_start,
1805 current_end) + 1;
1806 position = (length - 1) / 2;
1807
1808 current = g_list_nth(current_start,
1809 position);
1810 }
1811
1812 if(retval != NULL){
1813 next = NULL;
1814 prev = NULL;
1815
1816 if(current_start->data == retval->data){
1817 next = current_start->next;
1818 prev = current_start->prev;
1819 }else if(current_end->data == retval->data){
1820 next = current_end->next;
1821 prev = current_end->prev;
1822 }else if(current->data == retval->data){
1823 next = current->next;
1824 prev = current->prev;
1825 }
1826
1827 /* check next */
1828 current = next;
1829
1830 while(current != NULL){
1831 g_object_get(current->data,
1832 "x0", ¤t_x,
1833 NULL);
1834
1835 if(current_x == x){
1836 retval = g_list_prepend(retval,
1837 current->data);
1838 g_object_ref(current->data);
1839 }else{
1840 break;
1841 }
1842
1843 current = current->next;
1844 }
1845
1846 retval = g_list_reverse(retval);
1847
1848 /* check prev */
1849 current = prev;
1850
1851 while(current != NULL){
1852 g_object_get(current->data,
1853 "x0", ¤t_x,
1854 NULL);
1855
1856 if(current_x == x){
1857 retval = g_list_prepend(retval,
1858 current->data);
1859 g_object_ref(current->data);
1860 }else{
1861 break;
1862 }
1863
1864 current = current->prev;
1865 }
1866 }
1867
1868 g_rec_mutex_unlock(notation_mutex);
1869
1870 return(retval);
1871 }
1872
1873 /**
1874 * ags_notation_free_selection:
1875 * @notation: the #AgsNotation
1876 *
1877 * Clear selection.
1878 *
1879 * Since: 3.0.0
1880 */
1881 void
ags_notation_free_selection(AgsNotation * notation)1882 ags_notation_free_selection(AgsNotation *notation)
1883 {
1884 AgsNote *note;
1885
1886 GList *list_start, *list;
1887
1888 GRecMutex *notation_mutex;
1889
1890 if(!AGS_IS_NOTATION(notation)){
1891 return;
1892 }
1893
1894 /* get notation mutex */
1895 notation_mutex = AGS_NOTATION_GET_OBJ_MUTEX(notation);
1896
1897 /* free selection */
1898 g_rec_mutex_lock(notation_mutex);
1899
1900 list =
1901 list_start = notation->selection;
1902
1903 while(list != NULL){
1904 ags_note_unset_flags(list->data,
1905 AGS_NOTE_IS_SELECTED);
1906
1907 list = list->next;
1908 }
1909
1910 notation->selection = NULL;
1911
1912 g_rec_mutex_unlock(notation_mutex);
1913
1914 g_list_free_full(list_start,
1915 g_object_unref);
1916 }
1917
1918 /**
1919 * ags_notation_add_point_to_selection:
1920 * @notation: the #AgsNotation
1921 * @x: x offset
1922 * @y: y note tone
1923 * @replace_current_selection: if %TRUE selection is replaced
1924 *
1925 * Select notes at position.
1926 *
1927 * Since: 3.0.0
1928 */
1929 void
ags_notation_add_point_to_selection(AgsNotation * notation,guint x,guint y,gboolean replace_current_selection)1930 ags_notation_add_point_to_selection(AgsNotation *notation,
1931 guint x, guint y,
1932 gboolean replace_current_selection)
1933 {
1934 AgsNote *note;
1935
1936 GRecMutex *notation_mutex;
1937
1938 if(!AGS_IS_NOTATION(notation)){
1939 return;
1940 }
1941
1942 /* get notation mutex */
1943 notation_mutex = AGS_NOTATION_GET_OBJ_MUTEX(notation);
1944
1945 /* find note */
1946 note = ags_notation_find_point(notation,
1947 x, y,
1948 FALSE);
1949
1950 if(note == NULL){
1951 /* there is nothing to be selected */
1952 if(replace_current_selection){
1953 ags_notation_free_selection(notation);
1954 }
1955 }else{
1956 /* add to or replace selection */
1957 ags_note_set_flags(note, AGS_NOTE_IS_SELECTED);
1958
1959 if(replace_current_selection){
1960 GList *list;
1961
1962 list = g_list_alloc();
1963 list->data = note;
1964 g_object_ref(note);
1965
1966 ags_notation_free_selection(notation);
1967
1968 /* replace */
1969 g_rec_mutex_lock(notation_mutex);
1970
1971 notation->selection = list;
1972
1973 g_rec_mutex_unlock(notation_mutex);
1974 }else{
1975 if(!ags_notation_is_note_selected(notation,
1976 note)){
1977 /* add */
1978 ags_notation_add_note(notation,
1979 note, TRUE);
1980 }
1981 }
1982 }
1983 }
1984
1985 /**
1986 * ags_notation_remove_point_from_selection:
1987 * @notation: the #AgsNotation
1988 * @x: x offset
1989 * @y: y note tone
1990 *
1991 * Remove notes at position of selection.
1992 *
1993 * Since: 3.0.0
1994 */
1995 void
ags_notation_remove_point_from_selection(AgsNotation * notation,guint x,guint y)1996 ags_notation_remove_point_from_selection(AgsNotation *notation,
1997 guint x, guint y)
1998 {
1999 AgsNote *note;
2000
2001 GRecMutex *notation_mutex;
2002
2003 if(!AGS_IS_NOTATION(notation)){
2004 return;
2005 }
2006
2007 /* get notation mutex */
2008 notation_mutex = AGS_NOTATION_GET_OBJ_MUTEX(notation);
2009
2010 /* find point */
2011 note = ags_notation_find_point(notation,
2012 x, y,
2013 TRUE);
2014
2015 if(note != NULL){
2016 ags_note_unset_flags(note,
2017 AGS_NOTE_IS_SELECTED);
2018
2019 /* remove note from selection */
2020 g_rec_mutex_lock(notation_mutex);
2021
2022 notation->selection = g_list_remove(notation->selection,
2023 note);
2024 g_object_unref(note);
2025
2026 g_rec_mutex_unlock(notation_mutex);
2027 }
2028 }
2029
2030 /**
2031 * ags_notation_add_region_to_selection:
2032 * @notation: the #AgsNotation
2033 * @x0: x start offset
2034 * @y0: y start tone
2035 * @x1: x end offset
2036 * @y1: y end tone
2037 * @replace_current_selection: if %TRUE selection is replaced
2038 *
2039 * Add note within region to selection.
2040 *
2041 * Since: 3.0.0
2042 */
2043 void
ags_notation_add_region_to_selection(AgsNotation * notation,guint x0,guint y0,guint x1,guint y1,gboolean replace_current_selection)2044 ags_notation_add_region_to_selection(AgsNotation *notation,
2045 guint x0, guint y0,
2046 guint x1, guint y1,
2047 gboolean replace_current_selection)
2048 {
2049 AgsNote *note;
2050
2051 GList *region, *list;
2052
2053 GRecMutex *notation_mutex;
2054
2055 if(!AGS_IS_NOTATION(notation)){
2056 return;
2057 }
2058
2059 /* get notation mutex */
2060 notation_mutex = AGS_NOTATION_GET_OBJ_MUTEX(notation);
2061
2062 /* find region */
2063 region = ags_notation_find_region(notation,
2064 x0, y0,
2065 x1, y1,
2066 FALSE);
2067
2068 if(replace_current_selection){
2069 ags_notation_free_selection(notation);
2070
2071 list = region;
2072
2073 while(list != NULL){
2074 ags_note_set_flags(list->data,
2075 AGS_NOTE_IS_SELECTED);
2076 g_object_ref(list->data);
2077
2078 list = list->next;
2079 }
2080
2081 /* replace */
2082 g_rec_mutex_lock(notation_mutex);
2083
2084 notation->selection = region;
2085
2086 g_rec_mutex_unlock(notation_mutex);
2087 }else{
2088 list = region;
2089
2090 while(list != NULL){
2091 if(!ags_notation_is_note_selected(notation, list->data)){
2092 /* add */
2093 ags_notation_add_note(notation,
2094 list->data,
2095 TRUE);
2096 }
2097
2098 list = list->next;
2099 }
2100
2101 g_list_free(region);
2102 }
2103 }
2104
2105 /**
2106 * ags_notation_remove_region_from_selection:
2107 * @notation: the #AgsNotation
2108 * @x0: x start offset
2109 * @y0: y start tone
2110 * @x1: x end offset
2111 * @y1: y end tone
2112 *
2113 * Remove note within region of selection.
2114 *
2115 * Since: 3.0.0
2116 */
2117 void
ags_notation_remove_region_from_selection(AgsNotation * notation,guint x0,guint y0,guint x1,guint y1)2118 ags_notation_remove_region_from_selection(AgsNotation *notation,
2119 guint x0, guint y0,
2120 guint x1, guint y1)
2121 {
2122 AgsNote *note;
2123
2124 GList *region;
2125 GList *list;
2126
2127 GRecMutex *notation_mutex;
2128
2129 if(!AGS_IS_NOTATION(notation)){
2130 return;
2131 }
2132
2133 /* get notation mutex */
2134 notation_mutex = AGS_NOTATION_GET_OBJ_MUTEX(notation);
2135
2136 /* find region */
2137 region = ags_notation_find_region(notation,
2138 x0, y0,
2139 x1, y1,
2140 TRUE);
2141
2142 list = region;
2143
2144 while(list != NULL){
2145 ags_note_unset_flags(list->data,
2146 AGS_NOTE_IS_SELECTED);
2147
2148 /* remove */
2149 g_rec_mutex_lock(notation_mutex);
2150
2151 notation->selection = g_list_remove(notation->selection,
2152 list->data);
2153
2154 g_rec_mutex_unlock(notation_mutex);
2155
2156 g_object_unref(list->data);
2157
2158 /* iterate */
2159 list = list->next;
2160 }
2161
2162 g_list_free(region);
2163 }
2164
2165 /**
2166 * ags_notation_add_all_to_selection:
2167 * @notation: the #AgsNotation
2168 *
2169 * Add all note to selection.
2170 *
2171 * Since: 3.0.0
2172 */
2173 void
ags_notation_add_all_to_selection(AgsNotation * notation)2174 ags_notation_add_all_to_selection(AgsNotation *notation)
2175 {
2176 GList *list;
2177
2178 GRecMutex *notation_mutex;
2179
2180 if(!AGS_IS_NOTATION(notation)){
2181 return;
2182 }
2183
2184 /* get notation mutex */
2185 notation_mutex = AGS_NOTATION_GET_OBJ_MUTEX(notation);
2186
2187 /* select all */
2188 g_rec_mutex_lock(notation_mutex);
2189
2190 list = notation->note;
2191
2192 while(list != NULL){
2193 ags_notation_add_note(notation,
2194 list->data, TRUE);
2195
2196 list = list->next;
2197 }
2198
2199 g_rec_mutex_unlock(notation_mutex);
2200 }
2201
2202 /**
2203 * ags_notation_copy_selection:
2204 * @notation: the #AgsNotation
2205 *
2206 * Copy selection to clipboard.
2207 *
2208 * Returns: (transfer none): the selection as XML.
2209 *
2210 * Since: 3.0.0
2211 */
2212 xmlNode*
ags_notation_copy_selection(AgsNotation * notation)2213 ags_notation_copy_selection(AgsNotation *notation)
2214 {
2215 AgsTimestamp *timestamp;
2216
2217 xmlNode *notation_node, *current_note;
2218 xmlNode *timestamp_node;
2219
2220 GList *selection;
2221
2222 guint current_x0, current_x1, current_y;
2223 guint x_boundary, y_boundary;
2224
2225 GRecMutex *notation_mutex;
2226
2227 if(!AGS_IS_NOTATION(notation)){
2228 return(NULL);
2229 }
2230
2231 /* get notation mutex */
2232 notation_mutex = AGS_NOTATION_GET_OBJ_MUTEX(notation);
2233
2234 /* create root node */
2235 g_rec_mutex_lock(notation_mutex);
2236
2237 notation_node = xmlNewNode(NULL,
2238 BAD_CAST "notation");
2239
2240 xmlNewProp(notation_node,
2241 BAD_CAST "program",
2242 BAD_CAST "ags");
2243 xmlNewProp(notation_node,
2244 BAD_CAST "type",
2245 BAD_CAST (AGS_NOTATION_CLIPBOARD_TYPE));
2246 xmlNewProp(notation_node,
2247 BAD_CAST "version",
2248 BAD_CAST (AGS_NOTATION_CLIPBOARD_VERSION));
2249 xmlNewProp(notation_node,
2250 BAD_CAST "format",
2251 BAD_CAST (AGS_NOTATION_CLIPBOARD_FORMAT));
2252 xmlNewProp(notation_node,
2253 BAD_CAST "audio-channel",
2254 BAD_CAST (g_strdup_printf("%u", notation->audio_channel)));
2255
2256 /* timestamp */
2257 timestamp = notation->timestamp;
2258
2259 if(timestamp != NULL){
2260 timestamp_node = xmlNewNode(NULL,
2261 BAD_CAST "timestamp");
2262 xmlAddChild(notation_node,
2263 timestamp_node);
2264
2265 xmlNewProp(timestamp_node,
2266 BAD_CAST "offset",
2267 BAD_CAST (g_strdup_printf("%lu", ags_timestamp_get_ags_offset(timestamp))));
2268 }
2269
2270 /* selection */
2271 selection = notation->selection;
2272
2273 if(selection != NULL){
2274 g_object_get(selection->data,
2275 "x0", ¤t_x0,
2276 NULL);
2277
2278 x_boundary = current_x0;
2279 y_boundary = G_MAXUINT;
2280 }else{
2281 x_boundary = 0;
2282 y_boundary = 0;
2283 }
2284
2285 while(selection != NULL){
2286 g_object_get(selection->data,
2287 "x0", ¤t_x0,
2288 "x1", ¤t_x1,
2289 "y", ¤t_y,
2290 NULL);
2291
2292 current_note = xmlNewChild(notation_node,
2293 NULL,
2294 BAD_CAST "note",
2295 NULL);
2296
2297 xmlNewProp(current_note,
2298 BAD_CAST "x",
2299 BAD_CAST (g_strdup_printf("%u", current_x0)));
2300 xmlNewProp(current_note,
2301 BAD_CAST "x1",
2302 BAD_CAST (g_strdup_printf("%u", current_x1)));
2303 xmlNewProp(current_note,
2304 BAD_CAST "y",
2305 BAD_CAST (g_strdup_printf("%u", current_y)));
2306
2307 if(y_boundary > current_y){
2308 y_boundary = current_y;
2309 }
2310
2311 selection = selection->next;
2312 }
2313
2314 g_rec_mutex_unlock(notation_mutex);
2315
2316 xmlNewProp(notation_node,
2317 BAD_CAST "x_boundary",
2318 BAD_CAST (g_strdup_printf("%u", x_boundary)));
2319 xmlNewProp(notation_node,
2320 BAD_CAST "y_boundary",
2321 BAD_CAST (g_strdup_printf("%u", y_boundary)));
2322
2323 return(notation_node);
2324 }
2325
2326 /**
2327 * ags_notation_cut_selection:
2328 * @notation: the #AgsNotation
2329 *
2330 * Cut selection to clipboard.
2331 *
2332 * Returns: (transfer none): the selection as xmlNode
2333 *
2334 * Since: 3.0.0
2335 */
2336 xmlNode*
ags_notation_cut_selection(AgsNotation * notation)2337 ags_notation_cut_selection(AgsNotation *notation)
2338 {
2339 xmlNode *notation_node;
2340
2341 GList *selection, *note;
2342
2343 GRecMutex *notation_mutex;
2344
2345 if(!AGS_IS_NOTATION(notation)){
2346 return(NULL);
2347 }
2348
2349 /* get notation mutex */
2350 notation_mutex = AGS_NOTATION_GET_OBJ_MUTEX(notation);
2351
2352 /* copy selection */
2353 notation_node = ags_notation_copy_selection(notation);
2354
2355 /* cut */
2356 g_rec_mutex_lock(notation_mutex);
2357
2358 selection = notation->selection;
2359
2360 while(selection != NULL){
2361 notation->note = g_list_remove(notation->note,
2362 selection->data);
2363 g_object_unref(selection->data);
2364
2365 selection = selection->next;
2366 }
2367
2368 g_rec_mutex_unlock(notation_mutex);
2369
2370 /* free selection */
2371 ags_notation_free_selection(notation);
2372
2373 return(notation_node);
2374 }
2375
2376 void
ags_notation_insert_native_piano_from_clipboard_version_0_3_12(AgsNotation * notation,xmlNode * root_node,char * version,char * base_frequency,char * x_boundary,char * y_boundary,gboolean reset_x_offset,guint x_offset,gboolean reset_y_offset,guint y_offset,gboolean match_channel,gboolean no_duplicates,guint current_audio_channel,gboolean match_timestamp)2377 ags_notation_insert_native_piano_from_clipboard_version_0_3_12(AgsNotation *notation,
2378 xmlNode *root_node, char *version,
2379 char *base_frequency,
2380 char *x_boundary, char *y_boundary,
2381 gboolean reset_x_offset, guint x_offset,
2382 gboolean reset_y_offset, guint y_offset,
2383 gboolean match_channel, gboolean no_duplicates,
2384 guint current_audio_channel,
2385 gboolean match_timestamp)
2386 {
2387 AgsNote *note;
2388
2389 AgsTimestamp *timestamp;
2390
2391 xmlNode *node;
2392
2393 char *x0, *x1, *y;
2394 gchar *offset;
2395 char *endptr;
2396
2397 guint64 timestamp_offset;
2398 guint x_boundary_val, y_boundary_val;
2399 guint x0_val, x1_val, y_val;
2400 guint base_x_difference, base_y_difference;
2401 gboolean subtract_x, subtract_y;
2402
2403 node = root_node->children;
2404
2405 /* retrieve x values for resetting */
2406 base_x_difference = 0;
2407 subtract_x = FALSE;
2408
2409 if(reset_x_offset){
2410 if(x_boundary != NULL){
2411 errno = 0;
2412 x_boundary_val = strtoul(x_boundary,
2413 &endptr,
2414 10);
2415
2416 if(errno == ERANGE){
2417 goto dont_reset_x_offset;
2418 }
2419
2420 if(x_boundary == endptr){
2421 goto dont_reset_x_offset;
2422 }
2423
2424 if(x_boundary_val < x_offset){
2425 base_x_difference = x_offset - x_boundary_val;
2426 subtract_x = FALSE;
2427 }else{
2428 base_x_difference = x_boundary_val - x_offset;
2429 subtract_x = TRUE;
2430 }
2431 }else{
2432 dont_reset_x_offset:
2433 reset_x_offset = FALSE;
2434 }
2435 }
2436
2437 /* retrieve y values for resetting */
2438 base_y_difference = 0;
2439 subtract_y = FALSE;
2440
2441 if(reset_y_offset){
2442 if(y_boundary != NULL){
2443 errno = 0;
2444 y_boundary_val = strtoul(y_boundary,
2445 &endptr,
2446 10);
2447
2448 if(errno == ERANGE){
2449 goto dont_reset_y_offset;
2450 }
2451
2452 if(y_boundary == endptr){
2453 goto dont_reset_y_offset;
2454 }
2455
2456 if(y_boundary_val < y_offset){
2457 base_y_difference = y_offset - y_boundary_val;
2458 subtract_y = FALSE;
2459 }else{
2460 base_y_difference = y_boundary_val - y_offset;
2461 subtract_y = TRUE;
2462 }
2463 }else{
2464 dont_reset_y_offset:
2465 reset_y_offset = FALSE;
2466 }
2467 }
2468
2469 /* parse */
2470 for(; node != NULL; ){
2471 if(node->type == XML_ELEMENT_NODE){
2472 if(!xmlStrncmp("note",
2473 node->name,
2474 5)){
2475 /* retrieve x0 offset */
2476 x0 = xmlGetProp(node, "x");
2477
2478 if(x0 == NULL){
2479 node = node->next;
2480
2481 continue;
2482 }
2483
2484 errno = 0;
2485 x0_val = strtoul(x0, &endptr, 10);
2486
2487 if(errno == ERANGE){
2488 node = node->next;
2489
2490 continue;
2491 }
2492
2493 if(x0 == endptr){
2494 node = node->next;
2495
2496 continue;
2497 }
2498
2499 /* retrieve x1 offset */
2500 x1 = xmlGetProp(node, "x1");
2501
2502 if(x1 == NULL){
2503 node = node->next;
2504
2505 continue;
2506 }
2507
2508 errno = 0;
2509 x1_val = strtoul(x1, &endptr, 10);
2510
2511 if(errno == ERANGE){
2512 node = node->next;
2513
2514 continue;
2515 }
2516
2517 if(x1 == endptr){
2518 node = node->next;
2519
2520 continue;
2521 }
2522
2523 /* retrieve y offset */
2524 y = xmlGetProp(node, "y");
2525
2526 if(y == NULL){
2527 node = node->next;
2528
2529 continue;
2530 }
2531
2532 errno = 0;
2533 y_val = strtoul(y, &endptr, 10);
2534
2535 if(errno == ERANGE){
2536 node = node->next;
2537
2538 continue;
2539 }
2540
2541 if(y == endptr){
2542 node = node->next;
2543
2544 continue;
2545 }
2546
2547 /* switch x values if necessary */
2548 if(x0_val > x1_val){
2549 guint tmp;
2550
2551 tmp = x0_val;
2552 x0_val = x1_val;
2553 x1_val = tmp;
2554 }
2555
2556 /* calculate new offset */
2557 if(reset_x_offset){
2558 errno = 0;
2559
2560 if(subtract_x){
2561 x0_val -= base_x_difference;
2562
2563 if(errno != 0){
2564 node = node->next;
2565
2566 continue;
2567 }
2568
2569 x1_val -= base_x_difference;
2570 }else{
2571 x0_val += base_x_difference;
2572 x1_val += base_x_difference;
2573
2574 if(errno != 0){
2575 node = node->next;
2576
2577 continue;
2578 }
2579 }
2580 }
2581
2582 if(reset_y_offset){
2583 errno = 0;
2584
2585 if(subtract_y){
2586 y_val -= base_y_difference;
2587 }else{
2588 y_val += base_y_difference;
2589 }
2590
2591 if(errno != 0){
2592 node = node->next;
2593
2594 continue;
2595 }
2596 }
2597
2598 /* check if max length wasn't exceeded */
2599 if(x1_val - x0_val > notation->maximum_note_length){
2600 node = node->next;
2601
2602 continue;
2603 }
2604
2605 /* check duplicate */
2606 if(no_duplicates &&
2607 ags_notation_find_point(notation,
2608 x0_val, y_val,
2609 FALSE) != NULL){
2610 node = node->next;
2611
2612 continue;
2613 }
2614
2615 /* add note */
2616 g_object_get(notation,
2617 "timestamp", ×tamp,
2618 NULL);
2619
2620 timestamp_offset = ags_timestamp_get_ags_offset(timestamp);
2621 g_object_unref(timestamp);
2622
2623 if(!match_timestamp ||
2624 (x0_val >= timestamp_offset &&
2625 x0_val < timestamp_offset + AGS_NOTATION_DEFAULT_OFFSET)){
2626 note = ags_note_new();
2627
2628 note->x[0] = x0_val;
2629 note->x[1] = x1_val;
2630
2631 note->y = y_val;
2632
2633 #ifdef AGS_DEBUG
2634 g_message("adding note at: [%u,%u|%u]\n", x0_val, x1_val, y_val);
2635 #endif
2636
2637 ags_notation_add_note(notation,
2638 note,
2639 FALSE);
2640 }
2641 }
2642 }
2643
2644 node = node->next;
2645 }
2646 }
2647
2648 /**
2649 * ags_notation_insert_native_piano_from_clipboard:
2650 * @notation: the #AgsNotation
2651 * @notation_node: the clipboard XML data
2652 * @version: clipboard version
2653 * @base_frequency: lowest frequency of notation
2654 * @x_boundary: region start offset
2655 * @y_boundary: region start tone
2656 * @reset_x_offset: if %TRUE @x_offset used as cursor
2657 * @x_offset: region start cursor offset
2658 * @reset_y_offset: if %TRUE @y_offset used as cursor
2659 * @y_offset: region start cursor tone
2660 * @match_channel: only paste if channel matches
2661 * @no_duplicates: only paste if current note doesn't exist
2662 *
2663 * Paste previously copied notes.
2664 *
2665 * Since: 3.0.0
2666 */
2667 void
ags_notation_insert_native_piano_from_clipboard(AgsNotation * notation,xmlNode * root_node,char * version,char * base_frequency,char * x_boundary,char * y_boundary,gboolean reset_x_offset,guint x_offset,gboolean reset_y_offset,guint y_offset,gboolean match_channel,gboolean no_duplicates)2668 ags_notation_insert_native_piano_from_clipboard(AgsNotation *notation,
2669 xmlNode *root_node, char *version,
2670 char *base_frequency,
2671 char *x_boundary, char *y_boundary,
2672 gboolean reset_x_offset, guint x_offset,
2673 gboolean reset_y_offset, guint y_offset,
2674 gboolean match_channel, gboolean no_duplicates)
2675 {
2676 guint current_audio_channel;
2677
2678 gboolean match_timestamp;
2679
2680 if(!AGS_IS_NOTATION(notation)){
2681 return;
2682 }
2683
2684 g_object_get(notation,
2685 "audio-channel", ¤t_audio_channel,
2686 NULL);
2687
2688 match_timestamp = TRUE;
2689
2690 if(!xmlStrncmp("0.3.12", version, 7)){
2691 ags_notation_insert_native_piano_from_clipboard_version_0_3_12(notation,
2692 root_node, version,
2693 base_frequency,
2694 x_boundary, y_boundary,
2695 reset_x_offset, x_offset,
2696 reset_y_offset, y_offset,
2697 match_channel, no_duplicates,
2698 current_audio_channel,
2699 match_timestamp);
2700 }else if(!xmlStrncmp("0.4.2", version, 6)){
2701 /* changes contain only for UI relevant new informations */
2702 ags_notation_insert_native_piano_from_clipboard_version_0_3_12(notation,
2703 root_node, version,
2704 base_frequency,
2705 x_boundary, y_boundary,
2706 reset_x_offset, x_offset,
2707 reset_y_offset, y_offset,
2708 match_channel, no_duplicates,
2709 current_audio_channel,
2710 match_timestamp);
2711 }else if(!xmlStrncmp("1.2.0", version, 6)){
2712 /* changes contain only optional informations */
2713 match_timestamp = TRUE;
2714
2715 if(match_channel &&
2716 current_audio_channel != g_ascii_strtoull(xmlGetProp(root_node,
2717 "audio-channel"),
2718 NULL,
2719 10)){
2720 return;
2721 }
2722
2723 ags_notation_insert_native_piano_from_clipboard_version_0_3_12(notation,
2724 root_node, version,
2725 base_frequency,
2726 x_boundary, y_boundary,
2727 reset_x_offset, x_offset,
2728 reset_y_offset, y_offset,
2729 match_channel, no_duplicates,
2730 current_audio_channel,
2731 match_timestamp);
2732 }
2733 }
2734
2735 /**
2736 * ags_notation_insert_from_clipboard:
2737 * @notation: the #AgsNotation
2738 * @notation_node: the clipboard XML data
2739 * @reset_x_offset: if %TRUE @x_offset used as cursor
2740 * @x_offset: region start cursor offset
2741 * @reset_y_offset: if %TRUE @y_offset used as cursor
2742 * @y_offset: region start cursor tone
2743 *
2744 * Paste previously copied notes.
2745 *
2746 * Since: 3.0.0
2747 */
2748 void
ags_notation_insert_from_clipboard(AgsNotation * notation,xmlNode * notation_node,gboolean reset_x_offset,guint x_offset,gboolean reset_y_offset,guint y_offset)2749 ags_notation_insert_from_clipboard(AgsNotation *notation,
2750 xmlNode *notation_node,
2751 gboolean reset_x_offset, guint x_offset,
2752 gboolean reset_y_offset, guint y_offset)
2753 {
2754 ags_notation_insert_from_clipboard_extended(notation,
2755 notation_node,
2756 reset_x_offset, x_offset,
2757 reset_y_offset, y_offset,
2758 FALSE, FALSE);
2759 }
2760
2761 /**
2762 * ags_notation_insert_from_clipboard_extended:
2763 * @notation: the #AgsNotation
2764 * @notation_node: the clipboard XML data
2765 * @reset_x_offset: if %TRUE @x_offset used as cursor
2766 * @x_offset: region start cursor offset
2767 * @reset_y_offset: if %TRUE @y_offset used as cursor
2768 * @y_offset: region start cursor tone
2769 * @match_channel: only paste if channel matches
2770 * @no_duplicates: only paste if current note doesn't exist
2771 *
2772 * Paste previously copied notes.
2773 *
2774 * Since: 3.0.0
2775 */
2776 void
ags_notation_insert_from_clipboard_extended(AgsNotation * notation,xmlNode * notation_node,gboolean reset_x_offset,guint x_offset,gboolean reset_y_offset,guint y_offset,gboolean match_channel,gboolean no_duplicates)2777 ags_notation_insert_from_clipboard_extended(AgsNotation *notation,
2778 xmlNode *notation_node,
2779 gboolean reset_x_offset, guint x_offset,
2780 gboolean reset_y_offset, guint y_offset,
2781 gboolean match_channel, gboolean no_duplicates)
2782 {
2783 char *program, *version, *type, *format;
2784 char *base_frequency;
2785 char *x_boundary, *y_boundary;
2786
2787 if(!AGS_IS_NOTATION(notation)){
2788 return;
2789 }
2790
2791 while(notation_node != NULL){
2792 if(notation_node->type == XML_ELEMENT_NODE && !xmlStrncmp("notation", notation_node->name, 9)){
2793 break;
2794 }
2795
2796 notation_node = notation_node->next;
2797 }
2798
2799 if(notation_node != NULL){
2800 program = xmlGetProp(notation_node, "program");
2801
2802 if(!xmlStrncmp("ags", program, 4)){
2803 version = xmlGetProp(notation_node, "version");
2804 type = xmlGetProp(notation_node, "type");
2805 format = xmlGetProp(notation_node, "format");
2806
2807 if(!xmlStrcmp(AGS_NOTATION_CLIPBOARD_FORMAT,
2808 format)){
2809 base_frequency = xmlGetProp(notation_node, "base_frequency");
2810
2811 x_boundary = xmlGetProp(notation_node, "x_boundary");
2812 y_boundary = xmlGetProp(notation_node, "y_boundary");
2813
2814 ags_notation_insert_native_piano_from_clipboard(notation,
2815 notation_node, version,
2816 base_frequency,
2817 x_boundary, y_boundary,
2818 reset_x_offset, x_offset,
2819 reset_y_offset, y_offset,
2820 match_channel, no_duplicates);
2821 }
2822 }
2823 }
2824 }
2825
2826 /**
2827 * ags_notation_to_raw_midi:
2828 * @notation: the #AgsNotation
2829 * @bpm: the source bpm
2830 * @delay_factor: the source delay factor
2831 * @nn: numerator
2832 * @dd: denominator
2833 * @cc: clocks
2834 * @bb: beats
2835 * @tempo: tempo
2836 * @buffer_length: the return location of buffer length
2837 *
2838 * Convert @notation to raw-midi.
2839 *
2840 * Returns: the raw-midi buffer
2841 *
2842 * Since: 3.0.0
2843 */
2844 guchar*
ags_notation_to_raw_midi(AgsNotation * notation,gdouble bpm,gdouble delay_factor,glong nn,glong dd,glong cc,glong bb,glong tempo,guint * buffer_length)2845 ags_notation_to_raw_midi(AgsNotation *notation,
2846 gdouble bpm, gdouble delay_factor,
2847 glong nn, glong dd, glong cc, glong bb,
2848 glong tempo,
2849 guint *buffer_length)
2850 {
2851 AgsNote* midi_note[128];
2852 AgsMidiBuilder *midi_builder;
2853
2854 AgsTimestamp *timestamp;
2855
2856 xmlDoc *midi_doc;
2857 xmlNode *root_node;
2858 xmlNode *midi_header_node;
2859 xmlNode *midi_tracks_node;
2860 xmlNode *midi_track_node;
2861 xmlNode *midi_time_signature_node;
2862 xmlNode *midi_end_of_track_node;
2863
2864 GList *start_note, *note;
2865
2866 guchar *buffer;
2867
2868 gchar *str;
2869
2870 guint64 ags_offset;
2871 guint first_x0;
2872 glong delta_time;
2873 guint division;
2874 guint beat, clicks;
2875 guint length;
2876 int denom;
2877 gboolean pattern_node;
2878 gboolean success;
2879 guint i;
2880
2881 if(!AGS_IS_NOTATION(notation)){
2882 return(NULL);
2883 }
2884
2885 timestamp = ags_notation_get_timestamp(notation);
2886
2887 ags_offset = ags_timestamp_get_ags_offset(timestamp);
2888
2889 first_x0 = 0;
2890
2891 note =
2892 start_note = ags_notation_get_note(notation);
2893
2894 midi_doc = xmlNewDoc("1.0");
2895
2896 root_node = xmlNewNode(NULL, "midi");
2897 xmlDocSetRootElement(midi_doc,
2898 root_node);
2899
2900 midi_header_node = xmlNewNode(NULL,
2901 "midi-header");
2902
2903 xmlAddChild(root_node,
2904 midi_header_node);
2905
2906 division = AGS_NOTATION_DEFAULT_DIVISION;
2907
2908 delta_time = ags_midi_util_offset_to_delta_time(delay_factor,
2909 division,
2910 tempo,
2911 bpm,
2912 first_x0);
2913
2914 str = g_strdup_printf("%d", (gint) delta_time);
2915
2916 xmlNewProp(midi_header_node,
2917 "offset",
2918 str);
2919
2920 g_free(str);
2921
2922 xmlNewProp(midi_header_node,
2923 "format",
2924 "1");
2925
2926 str = g_strdup_printf("%d", division);
2927
2928 xmlNewProp(midi_header_node,
2929 "division",
2930 str);
2931
2932 g_free(str);
2933
2934 beat =
2935 clicks = division;
2936
2937 denom = 1;
2938
2939 while(dd > 0){
2940 denom *= 2;
2941 dd--;
2942 }
2943
2944 str = g_strdup_printf("%d", beat);
2945
2946 xmlNewProp(midi_header_node,
2947 "beat",
2948 str);
2949
2950 g_free(str);
2951
2952 xmlNewProp(midi_header_node,
2953 "track-count",
2954 "1");
2955
2956 /* create tracks node */
2957 midi_tracks_node = xmlNewNode(NULL, "midi-tracks");
2958
2959 xmlAddChild(root_node,
2960 midi_tracks_node);
2961
2962 midi_track_node = xmlNewNode(NULL, "midi-track");
2963
2964 delta_time = ags_midi_util_offset_to_delta_time(delay_factor,
2965 division,
2966 tempo,
2967 bpm,
2968 first_x0);
2969
2970 str = g_strdup_printf("%d", (gint) delta_time);
2971
2972 xmlNewProp(midi_track_node,
2973 "offset",
2974 str);
2975
2976 g_free(str);
2977
2978 xmlAddChild(midi_tracks_node,
2979 midi_track_node);
2980
2981 midi_time_signature_node = xmlNewNode(NULL,
2982 "midi-message");
2983
2984 xmlNewProp(midi_time_signature_node,
2985 "event",
2986 "time-signature");
2987
2988 xmlNewProp(midi_time_signature_node,
2989 "timesig",
2990 g_strdup_printf("%d/%d %d %d", nn, denom, cc, bb));
2991
2992 for(i = 0; i < 128; i++){
2993 midi_note[i] = NULL;
2994 }
2995
2996 note = start_note;
2997
2998 pattern_node = ags_notation_test_flags(notation,
2999 AGS_NOTATION_PATTERN_MODE);
3000
3001 if(start_note != NULL){
3002 first_x0 = ags_note_get_x0(start_note->data);
3003 }
3004
3005 while(note != NULL){
3006 xmlNode *midi_message_node;
3007
3008 guint note_x0;
3009 guint note_y;
3010 glong delta_time;
3011
3012 note_x0 = ags_note_get_x0(note->data);
3013
3014 note_y = ags_note_get_y(note->data);
3015
3016 if(pattern_node){
3017 midi_message_node = xmlNewNode(NULL,
3018 "midi-message");
3019
3020 xmlAddChild(midi_track_node,
3021 midi_message_node);
3022
3023 xmlNewProp(midi_message_node,
3024 "event",
3025 "note-on");
3026
3027 xmlNewProp(midi_message_node,
3028 "key",
3029 "0");
3030
3031 str = g_strdup_printf("%d",
3032 note_y);
3033
3034 xmlNewProp(midi_message_node,
3035 "note",
3036 str);
3037
3038 g_free(str);
3039
3040 xmlNewProp(midi_message_node,
3041 "velocity",
3042 "127");
3043
3044 delta_time = ags_midi_util_offset_to_delta_time(delay_factor,
3045 division,
3046 tempo,
3047 bpm,
3048 note_x0 - first_x0);
3049
3050 str = g_strdup_printf("%d", delta_time);
3051
3052 xmlNewProp(midi_message_node,
3053 "delta-time",
3054 str);
3055
3056 g_free(str);
3057 }else{
3058 /* check key off */
3059 for(i = 0; i < 128; i++){
3060 guint current_x1;
3061 guint current_y;
3062
3063 if(midi_note[i] != NULL &&
3064 (current_x1 = ags_note_get_x1(midi_note[i])) <= note_x0){
3065 current_y = ags_note_get_y(midi_note[i]);
3066
3067 midi_message_node = xmlNewNode(NULL,
3068 "midi-message");
3069
3070 xmlAddChild(midi_track_node,
3071 midi_message_node);
3072
3073 xmlNewProp(midi_message_node,
3074 "event",
3075 "note-off");
3076
3077 xmlNewProp(midi_message_node,
3078 "key",
3079 "0");
3080
3081 str = g_strdup_printf("%d",
3082 note_y);
3083
3084 xmlNewProp(midi_message_node,
3085 "note",
3086 str);
3087
3088 g_free(str);
3089
3090 xmlNewProp(midi_message_node,
3091 "velocity",
3092 "127");
3093
3094 delta_time = ags_midi_util_offset_to_delta_time(delay_factor,
3095 division,
3096 tempo,
3097 bpm,
3098 current_x1 - first_x0);
3099
3100 str = g_strdup_printf("%d", delta_time);
3101
3102 xmlNewProp(midi_message_node,
3103 "delta-time",
3104 str);
3105
3106 g_free(str);
3107
3108 midi_note[i] = NULL;
3109 }
3110 }
3111
3112 /* key on */
3113 midi_note[i] = note->data;
3114
3115 midi_message_node = xmlNewNode(NULL,
3116 "midi-message");
3117
3118 xmlAddChild(midi_track_node,
3119 midi_message_node);
3120
3121 xmlNewProp(midi_message_node,
3122 "event",
3123 "note-on");
3124
3125 xmlNewProp(midi_message_node,
3126 "key",
3127 "0");
3128
3129 str = g_strdup_printf("%d",
3130 note_y);
3131
3132 xmlNewProp(midi_message_node,
3133 "note",
3134 str);
3135
3136 g_free(str);
3137
3138 xmlNewProp(midi_message_node,
3139 "velocity",
3140 "127");
3141
3142 delta_time = ags_midi_util_offset_to_delta_time(delay_factor,
3143 division,
3144 tempo,
3145 bpm,
3146 note_x0 - first_x0);
3147
3148 str = g_strdup_printf("%d", delta_time);
3149
3150 xmlNewProp(midi_message_node,
3151 "delta-time",
3152 str);
3153
3154 g_free(str);
3155 }
3156
3157 /* iterate */
3158 note = note->next;
3159 }
3160
3161 success = FALSE;
3162
3163 while(!success){
3164 xmlNode *midi_message_node;
3165
3166 gint current_index;
3167 guint current_x1;
3168 guint current_y;
3169
3170 current_index = -1;
3171
3172 for(i = 0; i < 128; i++){
3173 if(midi_note[i] != NULL){
3174 if(current_index == -1){
3175 current_index = i;
3176 }else{
3177 guint current_x1, x1;
3178
3179 x1 = ags_note_get_x1(midi_note[i]);
3180 current_x1 = ags_note_get_x1(midi_note[current_index]);
3181
3182 if(x1 < current_x1){
3183 current_index = i;
3184 }
3185 }
3186 }
3187 }
3188
3189 if(current_index == -1){
3190 success = TRUE;
3191
3192 break;
3193 }
3194
3195 current_x1 = ags_note_get_x1(midi_note[current_index]);
3196 current_y = ags_note_get_y(midi_note[current_index]);
3197
3198 midi_message_node = xmlNewNode(NULL,
3199 "midi-message");
3200
3201 xmlAddChild(midi_track_node,
3202 midi_message_node);
3203
3204 xmlNewProp(midi_message_node,
3205 "event",
3206 "note-off");
3207
3208 xmlNewProp(midi_message_node,
3209 "key",
3210 "0");
3211
3212 str = g_strdup_printf("%d",
3213 current_y);
3214
3215 xmlNewProp(midi_message_node,
3216 "note",
3217 str);
3218
3219 g_free(str);
3220
3221 xmlNewProp(midi_message_node,
3222 "velocity",
3223 "127");
3224
3225 delta_time = ags_midi_util_offset_to_delta_time(delay_factor,
3226 division,
3227 tempo,
3228 bpm,
3229 current_x1 - first_x0);
3230
3231 str = g_strdup_printf("%d", delta_time);
3232
3233 xmlNewProp(midi_message_node,
3234 "delta-time",
3235 str);
3236
3237 g_free(str);
3238
3239 midi_note[i] = NULL;
3240 }
3241
3242 midi_end_of_track_node = xmlNewNode(NULL,
3243 "midi-message");
3244
3245 //NOTE:JK: take care of delta time
3246 str = g_strdup_printf("%d", delta_time);
3247
3248 xmlNewProp(midi_end_of_track_node,
3249 "delta-time",
3250 str);
3251
3252 g_free(str);
3253
3254 xmlNewProp(midi_end_of_track_node,
3255 "event",
3256 "end-of-track");
3257
3258 xmlAddChild(midi_track_node,
3259 midi_end_of_track_node);
3260
3261 midi_builder = ags_midi_builder_new(NULL);
3262
3263 ags_midi_builder_from_xml_doc(midi_builder,
3264 midi_doc);
3265
3266 ags_midi_builder_build(midi_builder);
3267
3268 length = 0;
3269
3270 buffer = ags_midi_builder_get_data_with_length(midi_builder,
3271 &length);
3272
3273 if(buffer_length != NULL){
3274 buffer_length[0] = length;
3275 }
3276
3277 g_list_free_full(start_note,
3278 (GDestroyNotify) g_object_unref);
3279
3280 g_object_run_dispose(midi_builder);
3281 g_object_unref(midi_builder);
3282
3283 return(buffer);
3284 }
3285
3286 /**
3287 * ags_notation_from_raw_midi:
3288 * @raw_midi: the data array
3289 * @nn: numerator
3290 * @dd: denominator
3291 * @cc: clocks
3292 * @bb: beats
3293 * @tempo: tempo
3294 * @bpm: the bpm to use
3295 * @delay_factor: the segmentation delay factor
3296 * @buffer_length: the buffer length
3297 *
3298 * Parse @raw_midi data and convert to #AgsNotation.
3299 *
3300 * Returns: (transfer full): the #AgsNotation
3301 *
3302 * Since: 3.0.0
3303 */
3304 AgsNotation*
ags_notation_from_raw_midi(guchar * raw_midi,glong nn,glong dd,glong cc,glong bb,glong tempo,gdouble bpm,gdouble delay_factor,guint buffer_length)3305 ags_notation_from_raw_midi(guchar *raw_midi,
3306 glong nn, glong dd, glong cc, glong bb,
3307 glong tempo,
3308 gdouble bpm, gdouble delay_factor,
3309 guint buffer_length)
3310 {
3311 AgsNotation *notation;
3312 AgsNote* midi_note[128];
3313 AgsMidiParser *midi_parser;
3314
3315 xmlDoc *midi_doc;
3316 xmlNode *root_node;
3317 xmlNode *midi_header_node;
3318 xmlNode *midi_tracks_node;
3319 xmlNode *midi_track_node;
3320 xmlNode *midi_end_of_track_node;
3321 xmlNode *child;
3322
3323 guint division;
3324 guint i;
3325
3326 if(raw_midi == NULL){
3327 return(NULL);
3328 }
3329
3330 notation = ags_notation_new(NULL,
3331 0);
3332
3333 for(i = 0; i < 128; i++){
3334 midi_note[i] = NULL;
3335 }
3336
3337 division = AGS_NOTATION_DEFAULT_DIVISION;
3338
3339 midi_parser = ags_midi_parser_new(NULL);
3340
3341 ags_midi_parser_set_buffer(midi_parser,
3342 raw_midi);
3343 midi_parser->file_length = buffer_length;
3344
3345 midi_doc = ags_midi_parser_parse_full(midi_parser);
3346
3347 root_node = xmlDocGetRootElement(midi_doc);
3348
3349 midi_header_node = NULL;
3350 midi_tracks_node = NULL;
3351 midi_track_node = NULL;
3352 midi_end_of_track_node = NULL;
3353
3354 child = root_node->children;
3355
3356 while(child != NULL){
3357 if(child->type == XML_ELEMENT_NODE){
3358 if(!xmlStrncmp(child->name,
3359 (xmlChar *) "midi-header",
3360 12)){
3361 midi_header_node = child;
3362 }else if(!xmlStrncmp(child->name,
3363 (xmlChar *) "midi-tracks",
3364 12)){
3365 midi_tracks_node = child;
3366 }
3367 }
3368
3369 child = child->next;
3370 }
3371
3372 if(midi_tracks_node != NULL){
3373 child = midi_tracks_node->children;
3374
3375 while(child != NULL){
3376 if(child->type == XML_ELEMENT_NODE){
3377 if(!xmlStrncmp(child->name,
3378 (xmlChar *) "midi-track",
3379 11)){
3380 midi_track_node = child;
3381 }
3382 }
3383
3384 child = child->next;
3385 }
3386 }
3387
3388 /* child nodes */
3389 if(midi_track_node != NULL){
3390 child = midi_track_node->children;
3391
3392 while(child != NULL){
3393 if(child->type == XML_ELEMENT_NODE){
3394 if(!xmlStrncmp(child->name,
3395 (xmlChar *) "midi-message",
3396 13)){
3397 xmlChar *event;
3398 xmlChar *str;
3399
3400 guint delta_time;
3401
3402 /* get event */
3403 delta_time = 0;
3404 str = xmlGetProp(child,
3405 "delta-time");
3406
3407 if(str != NULL){
3408 delta_time = g_ascii_strtoull(str,
3409 NULL,
3410 10);
3411 }
3412
3413 /* get event */
3414 event = xmlGetProp(child,
3415 "event");
3416
3417 /* compute event */
3418 if(!xmlStrncmp(event,
3419 "note-on",
3420 8)){
3421 guint note_x0, note_x1;
3422 guint note_y;
3423
3424 /* note */
3425 note_y = 0;
3426 str = xmlGetProp(child,
3427 "note");
3428
3429 if(str != NULL){
3430 AgsNote *note;
3431
3432 note = ags_note_new();
3433
3434 note_x0 = ags_midi_util_delta_time_to_offset(delay_factor,
3435 division,
3436 tempo,
3437 bpm,
3438 delta_time);
3439 note_x1 = note_x0 + 1;
3440
3441 note_y = g_ascii_strtoull(str,
3442 NULL,
3443 10);
3444
3445 g_object_set(note,
3446 "x0", note_x0,
3447 "x1", note_x1,
3448 "y", note_y,
3449 NULL);
3450
3451 midi_note[note_y] = note;
3452
3453 ags_notation_add_note(notation,
3454 note,
3455 FALSE);
3456 }
3457 }else if(!xmlStrncmp(event,
3458 "note-off",
3459 9)){
3460 guint note_x1;
3461 guint note_y;
3462
3463 /* note */
3464 note_y = 0;
3465 str = xmlGetProp(child,
3466 "note");
3467
3468 if(str != NULL){
3469 note_x1 = ags_midi_util_delta_time_to_offset(delay_factor,
3470 division,
3471 tempo,
3472 bpm,
3473 delta_time);
3474
3475 note_y = g_ascii_strtoull(str,
3476 NULL,
3477 10);
3478
3479 if(midi_note[note_y] != NULL){
3480 g_object_set(midi_note[note_y],
3481 "x1", note_x1,
3482 NULL);
3483
3484 midi_note[note_y] = NULL;
3485 }
3486 }
3487 }
3488 }else if(!xmlStrncmp(child->name,
3489 (xmlChar *) "midi-system-common",
3490 19)){
3491 //empty
3492 }else if(!xmlStrncmp(child->name,
3493 (xmlChar *) "meta-event",
3494 11)){
3495 //empty
3496 }
3497 }
3498
3499 child = child->next;
3500 }
3501 }
3502
3503 return(notation);
3504 }
3505
3506 /**
3507 * ags_notation_new:
3508 * @audio: the assigned #AgsAudio
3509 * @audio_channel: the audio channel to apply
3510 *
3511 * Creates a new instance of #AgsNotation.
3512 *
3513 * Returns: the new #AgsNotation
3514 *
3515 * Since: 3.0.0
3516 */
3517 AgsNotation*
ags_notation_new(GObject * audio,guint audio_channel)3518 ags_notation_new(GObject *audio,
3519 guint audio_channel)
3520 {
3521 AgsNotation *notation;
3522
3523 notation = (AgsNotation *) g_object_new(AGS_TYPE_NOTATION,
3524 "audio", audio,
3525 "audio-channel", audio_channel,
3526 NULL);
3527
3528 return(notation);
3529 }
3530