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", &current_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", &current_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", &current_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", &current_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", &current_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", &current_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", &timestamp_a,
849 	       NULL);
850 
851   g_object_get(b,
852 	       "timestamp", &timestamp_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", &timestamp,
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", &note,
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", &timestamp,
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", &timestamp,
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", &current_x0,
1382 		 "y", &current_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", &current_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", &current_x0,
1564 		 "x1", &current_x1,
1565 		 "y", &current_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", &current_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", &current_x0,
1665 		 "y", &current_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", &current_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", &current_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", &current_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", &current_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", &current_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", &current_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", &current_x0,
2288 		 "x1", &current_x1,
2289 		 "y", &current_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", &timestamp,
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", &current_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