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/X/ags_notation_editor.h>
21 #include <ags/X/ags_notation_editor_callbacks.h>
22 
23 #include <ags/X/ags_ui_provider.h>
24 #include <ags/X/ags_window.h>
25 
26 #include <ags/X/machine/ags_drum.h>
27 #include <ags/X/machine/ags_matrix.h>
28 
29 #include <libxml/tree.h>
30 #include <libxml/xpath.h>
31 
32 #include <math.h>
33 
34 #include <ags/config.h>
35 #include <ags/i18n.h>
36 
37 void ags_notation_editor_class_init(AgsNotationEditorClass *notation_editor);
38 void ags_notation_editor_connectable_interface_init(AgsConnectableInterface *connectable);
39 void ags_notation_editor_init(AgsNotationEditor *notation_editor);
40 void ags_notation_editor_set_property(GObject *gobject,
41 				      guint prop_id,
42 				      const GValue *value,
43 				      GParamSpec *param_spec);
44 void ags_notation_editor_get_property(GObject *gobject,
45 				      guint prop_id,
46 				      GValue *value,
47 				      GParamSpec *param_spec);
48 void ags_notation_editor_finalize(GObject *gobject);
49 
50 void ags_notation_editor_connect(AgsConnectable *connectable);
51 void ags_notation_editor_disconnect(AgsConnectable *connectable);
52 
53 void ags_notation_editor_show(GtkWidget *widget);
54 void ags_notation_editor_show_all(GtkWidget *widget);
55 
56 void ags_notation_editor_real_machine_changed(AgsNotationEditor *notation_editor,
57 					      AgsMachine *machine);
58 
59 gint ags_notation_editor_paste_notation_all(AgsNotationEditor *notation_editor,
60 					    AgsMachine *machine,
61 					    xmlNode *notation_node,
62 					    AgsTimestamp *timestamp,
63 					    gboolean match_channel, gboolean no_duplicates,
64 					    guint position_x, guint position_y,
65 					    gboolean paste_from_position,
66 					    gint *last_x);
67 gint ags_notation_editor_paste_notation(AgsNotationEditor *notation_editor,
68 					AgsMachine *machine,
69 					xmlNode *audio_node,
70 					guint position_x, guint position_y,
71 					gboolean paste_from_position,
72 					gint *last_x);
73 
74 void ags_notation_editor_get_boundary(AgsNotationEditor *notation_editor,
75 				      AgsMachine *machine,
76 				      AgsNotation *notation,
77 				      guint *lower, guint *upper);
78 void ags_notation_editor_invert_notation(AgsNotationEditor *notation_editor,
79 					 AgsMachine *machine,
80 					 AgsNotation *notation,
81 					 guint lower, guint upper);
82 
83 enum{
84   MACHINE_CHANGED,
85   LAST_SIGNAL,
86 };
87 
88 enum{
89   PROP_0,
90   PROP_SOUNDCARD,
91 };
92 
93 static gpointer ags_notation_editor_parent_class = NULL;
94 static guint notation_editor_signals[LAST_SIGNAL];
95 
96 /**
97  * SECTION:ags_notation_editor
98  * @short_description: A composite widget to edit notation
99  * @title: AgsNotationEditor
100  * @section_id:
101  * @include: ags/X/ags_notation_editor.h
102  *
103  * #AgsNotationEditor is a composite widget to edit notation. You may select machines
104  * or change editor tool to do notation.
105  */
106 
107 GType
ags_notation_editor_get_type(void)108 ags_notation_editor_get_type(void)
109 {
110   static volatile gsize g_define_type_id__volatile = 0;
111 
112   if(g_once_init_enter (&g_define_type_id__volatile)){
113     GType ags_type_notation_editor = 0;
114 
115     static const GTypeInfo ags_notation_editor_info = {
116       sizeof (AgsNotationEditorClass),
117       NULL, /* base_init */
118       NULL, /* base_finalize */
119       (GClassInitFunc) ags_notation_editor_class_init,
120       NULL, /* class_finalize */
121       NULL, /* class_data */
122       sizeof (AgsNotationEditor),
123       0,    /* n_preallocs */
124       (GInstanceInitFunc) ags_notation_editor_init,
125     };
126 
127     static const GInterfaceInfo ags_connectable_interface_info = {
128       (GInterfaceInitFunc) ags_notation_editor_connectable_interface_init,
129       NULL, /* interface_finalize */
130       NULL, /* interface_data */
131     };
132 
133     ags_type_notation_editor = g_type_register_static(GTK_TYPE_BOX,
134 						      "AgsNotationEditor", &ags_notation_editor_info,
135 						      0);
136 
137     g_type_add_interface_static(ags_type_notation_editor,
138 				AGS_TYPE_CONNECTABLE,
139 				&ags_connectable_interface_info);
140 
141     g_once_init_leave(&g_define_type_id__volatile, ags_type_notation_editor);
142   }
143 
144   return g_define_type_id__volatile;
145 }
146 
147 void
ags_notation_editor_connectable_interface_init(AgsConnectableInterface * connectable)148 ags_notation_editor_connectable_interface_init(AgsConnectableInterface *connectable)
149 {
150   connectable->is_ready = NULL;
151   connectable->is_connected = NULL;
152   connectable->connect = ags_notation_editor_connect;
153   connectable->disconnect = ags_notation_editor_disconnect;
154 }
155 
156 void
ags_notation_editor_class_init(AgsNotationEditorClass * notation_editor)157 ags_notation_editor_class_init(AgsNotationEditorClass *notation_editor)
158 {
159   GObjectClass *gobject;
160   GtkWidgetClass *widget;
161 
162   GParamSpec *param_spec;
163 
164   ags_notation_editor_parent_class = g_type_class_peek_parent(notation_editor);
165 
166   /* GObjectClass */
167   gobject = (GObjectClass *) notation_editor;
168 
169   gobject->set_property = ags_notation_editor_set_property;
170   gobject->get_property = ags_notation_editor_get_property;
171 
172   gobject->finalize = ags_notation_editor_finalize;
173 
174   /* properties */
175   /**
176    * AgsNotationEditor:soundcard:
177    *
178    * The assigned #AgsSoundcard acting as default sink.
179    *
180    * Since: 3.0.0
181    */
182   param_spec = g_param_spec_object("soundcard",
183 				   i18n_pspec("assigned soundcard"),
184 				   i18n_pspec("The soundcard it is assigned with"),
185 				   G_TYPE_OBJECT,
186 				   G_PARAM_READABLE | G_PARAM_WRITABLE);
187   g_object_class_install_property(gobject,
188 				  PROP_SOUNDCARD,
189 				  param_spec);
190 
191   /* GtkWidgetClass */
192   widget = (GtkWidgetClass *) notation_editor;
193 
194   widget->show = ags_notation_editor_show;
195   widget->show_all = ags_notation_editor_show_all;
196 
197   /* AgsNotationEditorClass */
198   notation_editor->machine_changed = ags_notation_editor_real_machine_changed;
199 
200   /* signals */
201   /**
202    * AgsNotationEditor::machine-changed:
203    * @editor: the object to change machine.
204    * @machine: the #AgsMachine to set
205    *
206    * The ::machine-changed signal notifies about changed machine.
207    *
208    * Since: 3.0.0
209    */
210   notation_editor_signals[MACHINE_CHANGED] =
211     g_signal_new("machine-changed",
212                  G_TYPE_FROM_CLASS(notation_editor),
213                  G_SIGNAL_RUN_LAST,
214 		 G_STRUCT_OFFSET(AgsNotationEditorClass, machine_changed),
215                  NULL, NULL,
216                  g_cclosure_marshal_VOID__OBJECT,
217                  G_TYPE_NONE, 1,
218 		 G_TYPE_OBJECT);
219 }
220 
221 void
ags_notation_editor_init(AgsNotationEditor * notation_editor)222 ags_notation_editor_init(AgsNotationEditor *notation_editor)
223 {
224   GtkViewport *viewport;
225   GtkScrolledWindow *scrolled_window;
226   GtkGrid *grid;
227 
228   AgsApplicationContext *application_context;
229 
230   gdouble gui_scale_factor;
231 
232   gtk_orientable_set_orientation(GTK_ORIENTABLE(notation_editor),
233 				 GTK_ORIENTATION_VERTICAL);
234 
235   gtk_box_set_homogeneous((GtkBox *) notation_editor,
236 			  FALSE);
237 
238   notation_editor->flags = (AGS_NOTATION_EDITOR_PASTE_MATCH_AUDIO_CHANNEL |
239 			    AGS_NOTATION_EDITOR_PASTE_NO_DUPLICATES);
240 
241   notation_editor->version = AGS_NOTATION_EDITOR_DEFAULT_VERSION;
242   notation_editor->build_id = AGS_NOTATION_EDITOR_DEFAULT_BUILD_ID;
243 
244   application_context = ags_application_context_get_instance();
245 
246   /* scale factor */
247   gui_scale_factor = ags_ui_provider_get_gui_scale_factor(AGS_UI_PROVIDER(application_context));
248 
249   /* offset */
250   notation_editor->tact_counter = 0;
251   notation_editor->current_tact = 0.0;
252 
253   /* active keys */
254   notation_editor->active_key = NULL;
255   notation_editor->active_key_count = 0;
256 
257   /* soundcard */
258   notation_editor->soundcard = NULL;
259 
260   /* notation toolbar */
261   notation_editor->notation_toolbar = ags_notation_toolbar_new();
262   gtk_box_pack_start((GtkBox *) notation_editor,
263 		     (GtkWidget *) notation_editor->notation_toolbar,
264 		     FALSE, FALSE,
265 		     0);
266 
267   /* paned */
268   notation_editor->paned = (GtkPaned *) gtk_paned_new(GTK_ORIENTATION_HORIZONTAL);
269   gtk_box_pack_start((GtkBox *) notation_editor,
270 		     (GtkWidget *) notation_editor->paned,
271 		     TRUE, TRUE, 0);
272 
273   /* machine selector */
274   viewport = (GtkViewport *) gtk_viewport_new(NULL,
275 					      NULL);
276   g_object_set(viewport,
277 	       "shadow-type", GTK_SHADOW_NONE,
278 	       NULL);
279   gtk_paned_pack1((GtkPaned *) notation_editor->paned,
280 		  (GtkWidget *) viewport,
281 		  FALSE, TRUE);
282 
283   scrolled_window = (GtkScrolledWindow *) gtk_scrolled_window_new(NULL, NULL);
284   gtk_container_add(GTK_CONTAINER(viewport),
285 		    GTK_WIDGET(scrolled_window));
286 
287   notation_editor->machine_selector = g_object_new(AGS_TYPE_MACHINE_SELECTOR,
288 						   "homogeneous", FALSE,
289 						   "spacing", 0,
290 						   NULL);
291   notation_editor->machine_selector->flags |= (AGS_MACHINE_SELECTOR_SHOW_REVERSE_MAPPING |
292 					       AGS_MACHINE_SELECTOR_SHOW_SHIFT_PIANO |
293 					       AGS_MACHINE_SELECTOR_NOTATION);
294   gtk_label_set_label(notation_editor->machine_selector->label,
295 		      i18n("notation"));
296 
297   notation_editor->machine_selector->popup = ags_machine_selector_popup_new(notation_editor->machine_selector);
298   g_object_set(notation_editor->machine_selector->menu_button,
299 	       "menu", notation_editor->machine_selector->popup,
300 	       NULL);
301 
302   gtk_container_add((GtkContainer *) scrolled_window,
303 		    (GtkWidget *) notation_editor->machine_selector);
304 
305   /* selected machine */
306   notation_editor->selected_machine = NULL;
307 
308   /* grid */
309   viewport = (GtkViewport *) gtk_viewport_new(NULL,
310 					      NULL);
311   g_object_set(viewport,
312 	       "shadow-type", GTK_SHADOW_NONE,
313 	       NULL);
314   gtk_paned_pack2((GtkPaned *) notation_editor->paned,
315 		  (GtkWidget *) viewport,
316 		  TRUE, TRUE);
317 
318   grid = (GtkGrid *) gtk_grid_new();
319   gtk_container_add(GTK_CONTAINER(viewport),
320 		    GTK_WIDGET(grid));
321 
322   /* notebook */
323   notation_editor->notebook = g_object_new(AGS_TYPE_NOTEBOOK,
324 					   "homogeneous", FALSE,
325 					   "spacing", 0,
326 					   "prefix", i18n("channel"),
327 					   NULL);
328 
329   gtk_widget_set_valign((GtkWidget *) notation_editor->notebook,
330 			GTK_ALIGN_FILL);
331   gtk_widget_set_halign((GtkWidget *) notation_editor->notebook,
332 			GTK_ALIGN_FILL);
333 
334   gtk_widget_set_vexpand((GtkWidget *) notation_editor->notebook,
335 			 FALSE);
336 
337   gtk_grid_attach(grid,
338 		  (GtkWidget *) notation_editor->notebook,
339 		  0, 0,
340 		  3, 1);
341 
342   /* scrolled piano */
343   notation_editor->scrolled_piano = ags_scrolled_piano_new();
344   g_object_set(notation_editor->scrolled_piano,
345 	       "margin-top", (gint) (gui_scale_factor * AGS_RULER_DEFAULT_HEIGHT),
346 	       NULL);
347   g_object_set(notation_editor->scrolled_piano->piano,
348 	       "key-width", (guint) (gui_scale_factor * AGS_PIANO_DEFAULT_KEY_WIDTH),
349 	       "key-height", (guint) (gui_scale_factor * AGS_PIANO_DEFAULT_KEY_HEIGHT),
350 	       NULL);
351 
352   gtk_widget_set_valign((GtkWidget *) notation_editor->scrolled_piano,
353 			GTK_ALIGN_FILL);
354   gtk_widget_set_halign((GtkWidget *) notation_editor->scrolled_piano,
355 			GTK_ALIGN_FILL);
356 
357   gtk_grid_attach(grid,
358 		  (GtkWidget *) notation_editor->scrolled_piano,
359 		  0, 1,
360 		  1, 1);
361 
362   /* notation edit */
363   notation_editor->notation_edit = ags_notation_edit_new();
364 
365   gtk_widget_set_valign((GtkWidget *) notation_editor->notation_edit,
366 			GTK_ALIGN_FILL);
367   gtk_widget_set_halign((GtkWidget *) notation_editor->notation_edit,
368 			GTK_ALIGN_FILL);
369 
370   gtk_widget_set_vexpand((GtkWidget *) notation_editor->notation_edit,
371 			 TRUE);
372   gtk_widget_set_hexpand((GtkWidget *) notation_editor->notation_edit,
373 			 TRUE);
374 
375   gtk_grid_attach(grid,
376 		  (GtkWidget *) notation_editor->notation_edit,
377 		  1, 1,
378 		  1, 1);
379 
380   /* notation meta */
381   notation_editor->notation_meta = ags_notation_meta_new();
382   g_object_set(notation_editor->notation_meta,
383 	       "valign", GTK_ALIGN_START,
384 	       NULL);
385 
386   gtk_widget_set_valign((GtkWidget *) notation_editor->notation_meta,
387 			GTK_ALIGN_FILL);
388   gtk_widget_set_halign((GtkWidget *) notation_editor->notation_meta,
389 			GTK_ALIGN_FILL);
390 
391   gtk_grid_attach(grid,
392 		  (GtkWidget *) notation_editor->notation_meta,
393 		  2, 1,
394 		  1, 1);
395 }
396 
397 void
ags_notation_editor_set_property(GObject * gobject,guint prop_id,const GValue * value,GParamSpec * param_spec)398 ags_notation_editor_set_property(GObject *gobject,
399 				 guint prop_id,
400 				 const GValue *value,
401 				 GParamSpec *param_spec)
402 {
403   AgsNotationEditor *notation_editor;
404 
405   notation_editor = AGS_NOTATION_EDITOR(gobject);
406 
407   switch(prop_id){
408   case PROP_SOUNDCARD:
409     {
410       GObject *soundcard;
411 
412       soundcard = g_value_get_object(value);
413 
414       if(notation_editor->soundcard == soundcard){
415 	return;
416       }
417 
418       if(notation_editor->soundcard != NULL){
419 	g_object_unref(notation_editor->soundcard);
420       }
421 
422       if(soundcard != NULL){
423 	g_object_ref(soundcard);
424       }
425 
426       notation_editor->soundcard = soundcard;
427     }
428     break;
429   default:
430     G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, param_spec);
431     break;
432   }
433 }
434 
435 void
ags_notation_editor_get_property(GObject * gobject,guint prop_id,GValue * value,GParamSpec * param_spec)436 ags_notation_editor_get_property(GObject *gobject,
437 				 guint prop_id,
438 				 GValue *value,
439 				 GParamSpec *param_spec)
440 {
441   AgsNotationEditor *notation_editor;
442 
443   notation_editor = AGS_NOTATION_EDITOR(gobject);
444 
445   switch(prop_id){
446   case PROP_SOUNDCARD:
447     {
448       g_value_set_object(value, notation_editor->soundcard);
449     }
450     break;
451   default:
452     G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, param_spec);
453     break;
454   }
455 }
456 
457 void
ags_notation_editor_finalize(GObject * gobject)458 ags_notation_editor_finalize(GObject *gobject)
459 {
460   AgsNotationEditor *notation_editor;
461 
462   notation_editor = AGS_NOTATION_EDITOR(gobject);
463 
464   if(notation_editor->soundcard != NULL){
465     g_object_unref(notation_editor->soundcard);
466   }
467 
468   /* call parent */
469   G_OBJECT_CLASS(ags_notation_editor_parent_class)->finalize(gobject);
470 }
471 
472 void
ags_notation_editor_connect(AgsConnectable * connectable)473 ags_notation_editor_connect(AgsConnectable *connectable)
474 {
475   AgsNotationEditor *notation_editor;
476 
477   notation_editor = AGS_NOTATION_EDITOR(connectable);
478 
479   if((AGS_NOTATION_EDITOR_CONNECTED & (notation_editor->flags)) != 0){
480     return;
481   }
482 
483   notation_editor->flags |= AGS_NOTATION_EDITOR_CONNECTED;
484 
485   g_signal_connect((GObject *) notation_editor->machine_selector, "changed",
486 		   G_CALLBACK(ags_notation_editor_machine_changed_callback), (gpointer) notation_editor);
487 
488   g_signal_connect((GObject *) notation_editor->scrolled_piano->piano, "key-pressed",
489 		   G_CALLBACK(ags_notation_editor_piano_key_pressed_callback), (gpointer) notation_editor);
490 
491   g_signal_connect((GObject *) notation_editor->scrolled_piano->piano, "key-released",
492 		   G_CALLBACK(ags_notation_editor_piano_key_released_callback), (gpointer) notation_editor);
493 
494 
495   /* toolbar */
496   ags_connectable_connect(AGS_CONNECTABLE(notation_editor->notation_toolbar));
497 
498   /* machine selector */
499   ags_connectable_connect(AGS_CONNECTABLE(notation_editor->machine_selector));
500 
501   /* notation edit */
502   ags_connectable_connect(AGS_CONNECTABLE(notation_editor->notation_edit));
503 
504   /* notation meta */
505   ags_connectable_connect(AGS_CONNECTABLE(notation_editor->notation_meta));
506 }
507 
508 void
ags_notation_editor_disconnect(AgsConnectable * connectable)509 ags_notation_editor_disconnect(AgsConnectable *connectable)
510 {
511   AgsNotationEditor *notation_editor;
512 
513   notation_editor = AGS_NOTATION_EDITOR(connectable);
514 
515   if((AGS_NOTATION_EDITOR_CONNECTED & (notation_editor->flags)) == 0){
516     return;
517   }
518 
519   notation_editor->flags &= (~AGS_NOTATION_EDITOR_CONNECTED);
520 
521   g_object_disconnect((GObject *) notation_editor->machine_selector,
522 		      "any_signal::changed",
523 		      G_CALLBACK(ags_notation_editor_machine_changed_callback),
524 		      (gpointer) notation_editor,
525 		      NULL);
526 
527   g_object_disconnect((GObject *) notation_editor->scrolled_piano->piano,
528 		      "any_signal::key-pressed",
529 		      G_CALLBACK(ags_notation_editor_piano_key_pressed_callback),
530 		      (gpointer) notation_editor,
531 		      "any_signal::key-released",
532 		      G_CALLBACK(ags_notation_editor_piano_key_released_callback),
533 		      (gpointer) notation_editor,
534 		      NULL);
535 
536   /* notation toolbar */
537   ags_connectable_disconnect(AGS_CONNECTABLE(notation_editor->notation_toolbar));
538 
539   /* machine selector */
540   ags_connectable_disconnect(AGS_CONNECTABLE(notation_editor->machine_selector));
541 
542   /* notation edit */
543   ags_connectable_disconnect(AGS_CONNECTABLE(notation_editor->notation_edit));
544 
545   /* notation meta */
546   ags_connectable_disconnect(AGS_CONNECTABLE(notation_editor->notation_meta));
547 }
548 
549 void
ags_notation_editor_show(GtkWidget * widget)550 ags_notation_editor_show(GtkWidget *widget)
551 {
552   GTK_WIDGET_CLASS(ags_notation_editor_parent_class)->show(widget);
553 
554   gtk_widget_hide((GtkWidget *) AGS_NOTATION_EDITOR(widget)->notation_meta);
555 }
556 
557 void
ags_notation_editor_show_all(GtkWidget * widget)558 ags_notation_editor_show_all(GtkWidget *widget)
559 {
560   GTK_WIDGET_CLASS(ags_notation_editor_parent_class)->show_all(widget);
561 
562   gtk_widget_hide((GtkWidget *) AGS_NOTATION_EDITOR(widget)->notation_meta);
563 }
564 
565 void
ags_notation_editor_real_machine_changed(AgsNotationEditor * notation_editor,AgsMachine * machine)566 ags_notation_editor_real_machine_changed(AgsNotationEditor *notation_editor,
567 					 AgsMachine *machine)
568 {
569   AgsMachine *old_machine;
570 
571   GList *tab;
572 
573   guint length;
574   guint audio_channels;
575   guint i;
576 
577   /* disconnect set pads - old */
578   old_machine = notation_editor->selected_machine;
579 
580   if(old_machine != NULL){
581     g_object_disconnect(old_machine,
582 			"any_signal::resize-audio-channels",
583 			G_CALLBACK(ags_notation_editor_resize_audio_channels_callback),
584 			(gpointer) notation_editor,
585 			"any_signal::resize-pads",
586 			G_CALLBACK(ags_notation_editor_resize_pads_callback),
587 			(gpointer) notation_editor,
588 			NULL);
589   }
590 
591   /* notebook - remove tabs */
592   length = g_list_length(notation_editor->notebook->tab);
593 
594   for(i = 0; i < length; i++){
595     ags_notebook_remove_tab(notation_editor->notebook,
596 			    0);
597   }
598 
599   /* check pattern mode */
600   if(AGS_IS_DRUM(machine) ||
601      AGS_IS_MATRIX(machine)){
602     notation_editor->flags |= AGS_NOTATION_EDITOR_PATTERN_MODE;
603   }else{
604     notation_editor->flags &= (~AGS_NOTATION_EDITOR_PATTERN_MODE);
605   }
606 
607   /* notebook - add tabs */
608   if(machine != NULL){
609     g_object_get(machine->audio,
610 		 "audio-channels", &audio_channels,
611 		 NULL);
612 
613     for(i = 0; i < audio_channels; i++){
614       ags_notebook_insert_tab(notation_editor->notebook,
615 			      i);
616 
617       tab = notation_editor->notebook->tab;
618       gtk_toggle_button_set_active(AGS_NOTEBOOK_TAB(tab->data)->toggle,
619 				   TRUE);
620     }
621   }
622 
623   /* piano */
624   if(machine != NULL){
625     guint channel_count;
626 
627     /* get channel count */
628     if(ags_audio_test_behaviour_flags(machine->audio, AGS_SOUND_BEHAVIOUR_DEFAULTS_TO_INPUT)){
629       g_object_get(machine->audio,
630 		   "input-pads", &channel_count,
631 		   NULL);
632     }else{
633       g_object_get(machine->audio,
634 		   "output-pads", &channel_count,
635 		   NULL);
636     }
637 
638     /* apply channel count */
639     g_object_set(notation_editor->scrolled_piano->piano,
640 		 "key-count", channel_count,
641 		 NULL);
642   }else{
643     /* apply default */
644     g_object_set(notation_editor->scrolled_piano->piano,
645 		 "key-count", AGS_PIANO_DEFAULT_KEY_COUNT,
646 		 NULL);
647   }
648 
649   gtk_widget_queue_resize((GtkWidget *) notation_editor->scrolled_piano->piano);
650   gtk_widget_queue_resize((GtkWidget *) notation_editor->scrolled_piano);
651 
652   gtk_widget_queue_draw((GtkWidget *) notation_editor->scrolled_piano->piano);
653   gtk_widget_queue_draw((GtkWidget *) notation_editor->scrolled_piano);
654 
655   /* selected machine */
656   notation_editor->selected_machine = machine;
657 
658   /* reset scrollbars */
659   ags_notation_edit_reset_vscrollbar(notation_editor->notation_edit);
660   ags_notation_edit_reset_hscrollbar(notation_editor->notation_edit);
661 
662   /* redraw */
663   gtk_widget_queue_draw((GtkWidget *) notation_editor->notation_edit);
664 
665   /* connect set-pads - new */
666   if(machine != NULL){
667     g_signal_connect_after(machine, "resize-audio-channels",
668 			   G_CALLBACK(ags_notation_editor_resize_audio_channels_callback), notation_editor);
669 
670     g_signal_connect_after(machine, "resize-pads",
671 			   G_CALLBACK(ags_notation_editor_resize_pads_callback), notation_editor);
672   }
673 }
674 
675 /**
676  * ags_notation_editor_machine_changed:
677  * @notation_editor: the #AgsNotationEditor
678  * @machine: the new #AgsMachine
679  *
680  * Is emitted as machine changed of notation_editor.
681  *
682  * Since: 3.0.0
683  */
684 void
ags_notation_editor_machine_changed(AgsNotationEditor * notation_editor,AgsMachine * machine)685 ags_notation_editor_machine_changed(AgsNotationEditor *notation_editor,
686 				    AgsMachine *machine)
687 {
688   g_return_if_fail(AGS_IS_NOTATION_EDITOR(notation_editor));
689 
690   g_object_ref((GObject *) notation_editor);
691   g_signal_emit((GObject *) notation_editor,
692 		notation_editor_signals[MACHINE_CHANGED], 0,
693 		machine);
694   g_object_unref((GObject *) notation_editor);
695 }
696 
697 /**
698  * ags_notation_editor_add_note:
699  * @notation_editor: the #AgsNotationEditor
700  * @note: the #AgsNote to add
701  *
702  * Add note.
703  *
704  * Since: 3.0.0
705  */
706 void
ags_notation_editor_add_note(AgsNotationEditor * notation_editor,AgsNote * note)707 ags_notation_editor_add_note(AgsNotationEditor *notation_editor,
708 			     AgsNote *note)
709 {
710   AgsMachine *machine;
711 
712   AgsNotation *notation;
713 
714   AgsTimestamp *timestamp;
715 
716   GList *start_list_notation, *list_notation;
717 
718   guint x0;
719   gint i;
720 
721   if(!AGS_IS_NOTATION_EDITOR(notation_editor) ||
722      !AGS_IS_NOTE(note)){
723     return;
724   }
725 
726   if(notation_editor->selected_machine != NULL){
727     machine = notation_editor->selected_machine;
728 
729     /* check all active tabs */
730     timestamp = ags_timestamp_new();
731 
732     timestamp->flags &= (~AGS_TIMESTAMP_UNIX);
733     timestamp->flags |= AGS_TIMESTAMP_OFFSET;
734 
735     g_object_get(note,
736 		 "x0", &x0,
737 		 NULL);
738 
739     timestamp->timer.ags_offset.offset = (guint64) AGS_NOTATION_DEFAULT_OFFSET * floor((double) x0 / (double) AGS_NOTATION_DEFAULT_OFFSET);
740 
741     i = 0;
742 
743     while((i = ags_notebook_next_active_tab(notation_editor->notebook,
744 					    i)) != -1){
745       AgsNote *new_note;
746 
747       g_object_get(machine->audio,
748 		   "notation", &start_list_notation,
749 		   NULL);
750 
751       list_notation = ags_notation_find_near_timestamp(start_list_notation, i,
752 						       timestamp);
753 
754       if(list_notation != NULL){
755 	notation = list_notation->data;
756       }else{
757 	notation = ags_notation_new((GObject *) machine->audio,
758 				    i);
759 	AGS_TIMESTAMP(notation->timestamp)->timer.ags_offset.offset = timestamp->timer.ags_offset.offset;
760 
761 	ags_audio_add_notation(machine->audio,
762 			       (GObject *) notation);
763       }
764 
765       new_note = ags_note_duplicate(note);
766       ags_notation_add_note(notation,
767 			    new_note,
768 			    FALSE);
769 
770       g_list_free_full(start_list_notation,
771 		       g_object_unref);
772 
773       /* iterate */
774       i++;
775     }
776 
777     g_object_unref(timestamp);
778 
779     gtk_widget_queue_draw((GtkWidget *) notation_editor->notation_edit);
780   }
781 }
782 
783 /**
784  * ags_notation_editor_delete_note:
785  * @notation_editor: the #AgsNotationEditor
786  * @x: point x
787  * @y: point y
788  *
789  * Delete note.
790  *
791  * Since: 3.0.0
792  */
793 void
ags_notation_editor_delete_note(AgsNotationEditor * notation_editor,guint x,guint y)794 ags_notation_editor_delete_note(AgsNotationEditor *notation_editor,
795 				guint x, guint y)
796 {
797   AgsMachine *machine;
798 
799   AgsNotation *notation;
800 
801   AgsTimestamp *timestamp;
802 
803   GList *start_list_notation, *list_notation;
804 
805   gint i;
806 
807   if(!AGS_IS_NOTATION_EDITOR(notation_editor)){
808     return;
809   }
810 
811   if(notation_editor->selected_machine != NULL){
812     machine = notation_editor->selected_machine;
813 
814     /* check all active tabs */
815     timestamp = ags_timestamp_new();
816 
817     timestamp->flags &= (~AGS_TIMESTAMP_UNIX);
818     timestamp->flags |= AGS_TIMESTAMP_OFFSET;
819 
820     timestamp->timer.ags_offset.offset = (guint64) AGS_NOTATION_DEFAULT_OFFSET * floor((double) x / (double) AGS_NOTATION_DEFAULT_OFFSET);
821 
822     i = 0;
823 
824     while((i = ags_notebook_next_active_tab(notation_editor->notebook,
825 					    i)) != -1){
826       g_object_get(machine->audio,
827 		   "notation", &start_list_notation,
828 		   NULL);
829 
830       list_notation = ags_notation_find_near_timestamp(start_list_notation, i,
831 						       timestamp);
832 
833       if(list_notation != NULL){
834 	notation = list_notation->data;
835 	ags_notation_remove_note_at_position(notation,
836 					     x, y);
837       }
838 
839       g_list_free_full(start_list_notation,
840 		       g_object_unref);
841 
842       /* iterate */
843       i++;
844     }
845 
846     g_object_unref(timestamp);
847 
848     gtk_widget_queue_draw((GtkWidget *) notation_editor->notation_edit);
849   }
850 }
851 
852 /**
853  * ags_notation_editor_select_region:
854  * @notation_editor: the #AgsNotationEditor
855  * @x0: point x0
856  * @y0: point y0
857  * @x1: point x1
858  * @y1: point y1
859  *
860  * Select region.
861  *
862  * Since: 3.0.0
863  */
864 void
ags_notation_editor_select_region(AgsNotationEditor * notation_editor,guint x0,guint y0,guint x1,guint y1)865 ags_notation_editor_select_region(AgsNotationEditor *notation_editor,
866 				  guint x0, guint y0,
867 				  guint x1, guint y1)
868 {
869   AgsMachine *machine;
870 
871   AgsTimestamp *timestamp;
872 
873   GList *start_list_notation, *list_notation;
874 
875   gint i;
876 
877   if(!AGS_IS_NOTATION_EDITOR(notation_editor)){
878     return;
879   }
880 
881   if(notation_editor->selected_machine != NULL){
882     machine = notation_editor->selected_machine;
883 
884     /* swap values if needed */
885     if(x0 > x1){
886       guint tmp;
887 
888       tmp = x0;
889 
890       x0 = x1;
891       x1 = tmp;
892     }
893 
894     if(y0 > y1){
895       guint tmp;
896 
897       tmp = y0;
898 
899       y0 = y1;
900       y1 = tmp;
901     }
902 
903     /* check all active tabs */
904     timestamp = ags_timestamp_new();
905 
906     timestamp->flags &= (~AGS_TIMESTAMP_UNIX);
907     timestamp->flags |= AGS_TIMESTAMP_OFFSET;
908 
909     i = 0;
910 
911     g_object_get(machine->audio,
912 		 "notation", &start_list_notation,
913 		 NULL);
914 
915     while((i = ags_notebook_next_active_tab(notation_editor->notebook,
916 					    i)) != -1){
917       list_notation = start_list_notation;
918 
919       timestamp->timer.ags_offset.offset = AGS_NOTATION_DEFAULT_OFFSET * floor(x0 / AGS_NOTATION_DEFAULT_OFFSET);
920 
921       while((list_notation = ags_notation_find_near_timestamp(list_notation, i,
922 							      timestamp)) != NULL &&
923 	    timestamp->timer.ags_offset.offset < (AGS_NOTATION_DEFAULT_OFFSET * floor(x1 / AGS_NOTATION_DEFAULT_OFFSET)) + AGS_NOTATION_DEFAULT_OFFSET){
924 	ags_notation_add_region_to_selection(list_notation->data,
925 					     x0, y0,
926 					     x1, y1,
927 					     TRUE);
928 
929 	/* iterate */
930 	timestamp->timer.ags_offset.offset += AGS_NOTATION_DEFAULT_OFFSET;
931 
932 	list_notation = list_notation->next;
933       }
934 
935       /* iterate */
936       i++;
937     }
938 
939     g_list_free_full(start_list_notation,
940 		     g_object_unref);
941 
942     gtk_widget_queue_draw((GtkWidget *) notation_editor->notation_edit);
943   }
944 }
945 
946 /**
947  * ags_notation_editor_do_feedback:
948  * @notation_editor: the #AgsNotationEditor
949  *
950  * Do playback feedback.
951  *
952  * Since: 3.0.0
953  */
954 void
ags_notation_editor_do_feedback(AgsNotationEditor * notation_editor)955 ags_notation_editor_do_feedback(AgsNotationEditor *notation_editor)
956 {
957   if(!AGS_IS_NOTATION_EDITOR(notation_editor)){
958     return;
959   }
960 
961   if(notation_editor->selected_machine != NULL){
962     AgsNotationEdit *notation_edit;
963     AgsMachine *machine;
964 
965     AgsChannel *start_output, *start_input;
966     AgsChannel *nth_channel, *nth_pad;
967     AgsPlayback *playback;
968 
969     AgsTimestamp *timestamp;
970 
971     GList *start_list_notation, *list_notation;
972 
973     guint output_pads, input_pads;
974     gint i;
975 
976     notation_edit = notation_editor->notation_edit;
977     machine = notation_editor->selected_machine;
978 
979     /* check all active tabs */
980     timestamp = ags_timestamp_new();
981 
982     timestamp->flags &= (~AGS_TIMESTAMP_UNIX);
983     timestamp->flags |= AGS_TIMESTAMP_OFFSET;
984 
985     timestamp->timer.ags_offset.offset = AGS_NOTATION_DEFAULT_OFFSET * floor(notation_edit->cursor_position_x / AGS_NOTATION_DEFAULT_OFFSET);
986 
987     i = 0;
988 
989     g_object_get(machine->audio,
990 		 "output", &start_output,
991 		 "output-pads", &output_pads,
992 		 "input", &start_input,
993 		 "input-pads", &input_pads,
994 		 "notation", &start_list_notation,
995 		 NULL);
996 
997     while((i = ags_notebook_next_active_tab(notation_editor->notebook,
998 					    i)) != -1){
999       AgsNote *current_note;
1000       AgsNote *play_note;
1001 
1002       list_notation = start_list_notation;
1003       list_notation = ags_notation_find_near_timestamp(list_notation, i,
1004 						       timestamp);
1005 
1006       if(list_notation == NULL){
1007 	i++;
1008 
1009 	continue;
1010       }
1011 
1012       current_note = ags_notation_find_point(list_notation->data,
1013 					     notation_edit->cursor_position_x, notation_edit->cursor_position_y,
1014 					     FALSE);
1015 
1016       if(current_note != NULL){
1017 	if(ags_audio_test_behaviour_flags(machine->audio, AGS_SOUND_BEHAVIOUR_DEFAULTS_TO_OUTPUT)){
1018 	  nth_channel = ags_channel_nth(start_output,
1019 					i);
1020 	}else{
1021 	  nth_channel = ags_channel_nth(start_input,
1022 					i);
1023 	}
1024 
1025 	if(ags_audio_test_behaviour_flags(machine->audio, AGS_SOUND_BEHAVIOUR_REVERSE_MAPPING)){
1026 	  nth_pad = ags_channel_pad_nth(nth_channel,
1027 					(ags_audio_test_behaviour_flags(machine->audio, AGS_SOUND_BEHAVIOUR_DEFAULTS_TO_OUTPUT) ? output_pads: input_pads) - notation_edit->cursor_position_y - 1);
1028 	}else{
1029 	  nth_pad = ags_channel_pad_nth(nth_channel,
1030 					notation_edit->cursor_position_y);
1031 	}
1032 
1033 	if(nth_pad != NULL){
1034 	  guint x0, x1;
1035 
1036 	  g_object_get(nth_pad,
1037 		       "playback", &playback,
1038 		       NULL);
1039 
1040 	  g_object_get(playback,
1041 		       "play-note", &play_note,
1042 		       NULL);
1043 
1044 	  g_object_get(current_note,
1045 		       "x0", &x0,
1046 		       "x1", &x1,
1047 		       NULL);
1048 
1049 	  g_object_set(play_note,
1050 		       "x0", 0,
1051 		       "x1", x1 - x0,
1052 		       NULL);
1053 
1054 	  ags_machine_playback_set_active(machine,
1055 					  playback,
1056 					  TRUE);
1057 
1058 	  g_object_unref(playback);
1059 
1060 	  g_object_unref(play_note);
1061 	}
1062 
1063 	/* unref */
1064 	if(nth_channel != NULL){
1065 	  g_object_unref(nth_channel);
1066 	}
1067 
1068 	if(nth_pad != NULL){
1069 	  g_object_unref(nth_pad);
1070 	}
1071       }
1072 
1073       /* iterate */
1074       i++;
1075     }
1076 
1077     g_list_free_full(start_list_notation,
1078 		     g_object_unref);
1079 
1080     /* unref */
1081     if(start_output != NULL){
1082       g_object_unref(start_output);
1083     }
1084 
1085     if(start_input != NULL){
1086       g_object_unref(start_input);
1087     }
1088   }
1089 }
1090 
1091 /**
1092  * ags_notation_editor_start_play_key:
1093  * @notation_editor: the #AgsNotationEditor
1094  * @key_code: the key to play
1095  *
1096  * Start play @key_code.
1097  *
1098  * Since: 3.0.0
1099  */
1100 void
ags_notation_editor_start_play_key(AgsNotationEditor * notation_editor,gint key_code)1101 ags_notation_editor_start_play_key(AgsNotationEditor *notation_editor,
1102 				   gint key_code)
1103 {
1104   if(!AGS_IS_NOTATION_EDITOR(notation_editor)){
1105     return;
1106   }
1107 
1108   if(notation_editor->selected_machine != NULL){
1109     AgsMachine *machine;
1110 
1111     AgsChannel *start_output, *start_input;
1112     AgsChannel *nth_channel, *nth_pad;
1113     AgsPlayback *playback;
1114 
1115     GObject *output_soundcard;
1116 
1117     guint output_pads, input_pads;
1118     guint audio_channels;
1119     guint note_offset;
1120     guint y;
1121     gint i;
1122 
1123     machine = notation_editor->selected_machine;
1124 
1125     i = 0;
1126 
1127     g_object_get(machine->audio,
1128 		 "output-soundcard", &output_soundcard,
1129 		 "output", &start_output,
1130 		 "output-pads", &output_pads,
1131 		 "input", &start_input,
1132 		 "input-pads", &input_pads,
1133 		 "audio-channels", &audio_channels,
1134 		 NULL);
1135 
1136     for(i = 0; i < audio_channels; i++){
1137       AgsNote *play_note;
1138 
1139       //TODO:JK: improve me
1140       y = key_code;
1141 
1142 #if 0
1143       if(ags_audio_test_behaviour_flags(machine->audio, AGS_SOUND_BEHAVIOUR_DEFAULTS_TO_OUTPUT)){
1144 	nth_channel = ags_channel_nth(start_output,
1145 				      i);
1146       }else{
1147 	nth_channel = ags_channel_nth(start_input,
1148 				      i);
1149       }
1150 
1151       if(ags_audio_test_behaviour_flags(machine->audio, AGS_SOUND_BEHAVIOUR_REVERSE_MAPPING)){
1152 	nth_pad = ags_channel_pad_nth(nth_channel,
1153 				      (ags_audio_test_behaviour_flags(machine->audio, AGS_SOUND_BEHAVIOUR_DEFAULTS_TO_OUTPUT) ? output_pads: input_pads) - y - 1);
1154       }else{
1155 	nth_pad = ags_channel_pad_nth(nth_channel,
1156 				      y);
1157       }
1158 #else
1159       nth_channel = ags_channel_nth(start_input,
1160 				    i);
1161 
1162       if(ags_audio_test_behaviour_flags(machine->audio, AGS_SOUND_BEHAVIOUR_REVERSE_MAPPING)){
1163 	nth_pad = ags_channel_pad_nth(nth_channel,
1164 				      (input_pads) - y - 1);
1165       }else{
1166 	nth_pad = ags_channel_pad_nth(nth_channel,
1167 				      y);
1168       }
1169 #endif
1170 
1171       if(nth_pad != NULL){
1172 	g_object_get(nth_pad,
1173 		     "playback", &playback,
1174 		     NULL);
1175 
1176 	g_object_get(playback,
1177 		     "play-note", &play_note,
1178 		     NULL);
1179 
1180 	note_offset = ags_soundcard_get_note_offset(AGS_SOUNDCARD(output_soundcard));
1181 
1182 	ags_note_set_flags(play_note, AGS_NOTE_FEED);
1183 	g_object_set(play_note,
1184 		     "x0", note_offset,
1185 		     "x1", note_offset + 1,
1186 		     NULL);
1187 
1188 	ags_machine_playback_set_active(machine,
1189 					playback,
1190 					TRUE);
1191 
1192 	g_object_unref(playback);
1193 
1194 	g_object_unref(play_note);
1195       }
1196 
1197       /* unref */
1198       if(nth_channel != NULL){
1199 	g_object_unref(nth_channel);
1200       }
1201 
1202       if(nth_pad != NULL){
1203 	g_object_unref(nth_pad);
1204       }
1205     }
1206 
1207     /* unref */
1208     if(start_output != NULL){
1209       g_object_unref(start_output);
1210     }
1211 
1212     if(start_input != NULL){
1213       g_object_unref(start_input);
1214     }
1215   }
1216 }
1217 
1218 /**
1219  * ags_notation_editor_stop_play_key:
1220  * @notation_editor: the #AgsNotationEditor
1221  * @key_code: the key to stop
1222  *
1223  * Stop play @key_code.
1224  *
1225  * Since: 3.0.0
1226  */
1227 void
ags_notation_editor_stop_play_key(AgsNotationEditor * notation_editor,gint key_code)1228 ags_notation_editor_stop_play_key(AgsNotationEditor *notation_editor,
1229 				  gint key_code)
1230 {
1231   if(!AGS_IS_NOTATION_EDITOR(notation_editor)){
1232     return;
1233   }
1234 
1235   if(notation_editor->selected_machine != NULL){
1236     AgsMachine *machine;
1237 
1238     AgsChannel *start_output, *start_input;
1239     AgsChannel *nth_channel, *nth_pad;
1240     AgsPlayback *playback;
1241 
1242     guint output_pads, input_pads;
1243     guint audio_channels;
1244     guint y;
1245     gint i;
1246 
1247     machine = notation_editor->selected_machine;
1248 
1249     i = 0;
1250 
1251     g_object_get(machine->audio,
1252 		 "output", &start_output,
1253 		 "output-pads", &output_pads,
1254 		 "input", &start_input,
1255 		 "input-pads", &input_pads,
1256 		 "audio-channels", &audio_channels,
1257 		 NULL);
1258 
1259     for(i = 0; i < audio_channels; i++){
1260       AgsNote *play_note;
1261 
1262       //TODO:JK: improve me
1263       y = key_code;
1264 
1265 #if 0
1266       if(ags_audio_test_behaviour_flags(machine->audio, AGS_SOUND_BEHAVIOUR_DEFAULTS_TO_OUTPUT)){
1267 	nth_channel = ags_channel_nth(start_output,
1268 				      i);
1269       }else{
1270 	nth_channel = ags_channel_nth(start_input,
1271 				      i);
1272       }
1273 
1274       if(ags_audio_test_behaviour_flags(machine->audio, AGS_SOUND_BEHAVIOUR_REVERSE_MAPPING)){
1275 	nth_pad = ags_channel_pad_nth(nth_channel,
1276 				      (ags_audio_test_behaviour_flags(machine->audio, AGS_SOUND_BEHAVIOUR_DEFAULTS_TO_OUTPUT) ? output_pads: input_pads) - y - 1);
1277       }else{
1278 	nth_pad = ags_channel_pad_nth(nth_channel,
1279 				      y);
1280       }
1281 #else
1282       nth_channel = ags_channel_nth(start_input,
1283 				    i);
1284 
1285       if(ags_audio_test_behaviour_flags(machine->audio, AGS_SOUND_BEHAVIOUR_REVERSE_MAPPING)){
1286 	nth_pad = ags_channel_pad_nth(nth_channel,
1287 				      (input_pads) - y - 1);
1288       }else{
1289 	nth_pad = ags_channel_pad_nth(nth_channel,
1290 				      y);
1291       }
1292 #endif
1293 
1294       if(nth_pad != NULL){
1295 	g_object_get(nth_pad,
1296 		     "playback", &playback,
1297 		     NULL);
1298 
1299 	g_object_get(playback,
1300 		     "play-note", &play_note,
1301 		     NULL);
1302 
1303 	ags_note_unset_flags(play_note, AGS_NOTE_FEED);
1304 
1305 	ags_machine_playback_set_active(machine,
1306 					playback,
1307 					FALSE);
1308 
1309 	g_object_unref(playback);
1310 
1311 	g_object_unref(play_note);
1312       }
1313 
1314       /* unref */
1315       if(nth_channel != NULL){
1316 	g_object_unref(nth_channel);
1317       }
1318 
1319       if(nth_pad != NULL){
1320 	g_object_unref(nth_pad);
1321       }
1322     }
1323 
1324     /* unref */
1325     if(start_output != NULL){
1326       g_object_unref(start_output);
1327     }
1328 
1329     if(start_input != NULL){
1330       g_object_unref(start_input);
1331     }
1332   }
1333 }
1334 
1335 /**
1336  * ags_notation_editor_select_all:
1337  * @notation_editor: the #AgsNotationEditor
1338  *
1339  * Is emitted as machine changed of notation_editor.
1340  *
1341  * Since: 3.0.0
1342  */
1343 void
ags_notation_editor_select_all(AgsNotationEditor * notation_editor)1344 ags_notation_editor_select_all(AgsNotationEditor *notation_editor)
1345 {
1346   AgsMachine *machine;
1347 
1348   GList *start_list_notation, *list_notation;
1349 
1350   guint audio_channel;
1351   gint i;
1352 
1353   if(!AGS_IS_NOTATION_EDITOR(notation_editor)){
1354     return;
1355   }
1356 
1357   if(notation_editor->selected_machine != NULL){
1358     machine = notation_editor->selected_machine;
1359 
1360     /* check all active tabs */
1361     g_object_get(machine->audio,
1362 		 "notation", &start_list_notation,
1363 		 NULL);
1364 
1365     i = 0;
1366 
1367     while((i = ags_notebook_next_active_tab(notation_editor->notebook,
1368 					    i)) != -1){
1369       list_notation = start_list_notation;
1370 
1371       while(list_notation != NULL){
1372 	g_object_get(list_notation->data,
1373 		     "audio-channel", &audio_channel,
1374 		     NULL);
1375 
1376 	if(i != audio_channel){
1377 	  list_notation = list_notation->next;
1378 
1379 	  continue;
1380 	}
1381 
1382 	ags_notation_add_all_to_selection(AGS_NOTATION(list_notation->data));
1383 
1384 	list_notation = list_notation->next;
1385       }
1386 
1387       /* iterate */
1388       i++;
1389     }
1390 
1391     g_list_free_full(start_list_notation,
1392 		     g_object_unref);
1393 
1394     gtk_widget_queue_draw((GtkWidget *) notation_editor->notation_edit);
1395   }
1396 }
1397 
1398 gint
ags_notation_editor_paste_notation_all(AgsNotationEditor * notation_editor,AgsMachine * machine,xmlNode * notation_node,AgsTimestamp * timestamp,gboolean match_channel,gboolean no_duplicates,guint position_x,guint position_y,gboolean paste_from_position,gint * last_x)1399 ags_notation_editor_paste_notation_all(AgsNotationEditor *notation_editor,
1400 				       AgsMachine *machine,
1401 				       xmlNode *notation_node,
1402 				       AgsTimestamp *timestamp,
1403 				       gboolean match_channel, gboolean no_duplicates,
1404 				       guint position_x, guint position_y,
1405 				       gboolean paste_from_position,
1406 				       gint *last_x)
1407 {
1408   AgsNotation *notation;
1409 
1410   GList *start_list_notation, *list_notation;
1411 
1412   gint first_x;
1413   guint current_x;
1414   gint i;
1415 
1416   first_x = -1;
1417 
1418   /*  */
1419   i = 0;
1420 
1421   while((i = ags_notebook_next_active_tab(notation_editor->notebook,
1422 					  i)) != -1){
1423     g_object_get(machine->audio,
1424 		 "notation", &start_list_notation,
1425 		 NULL);
1426 
1427     list_notation = ags_notation_find_near_timestamp(start_list_notation, i,
1428 						     timestamp);
1429 
1430     if(list_notation == NULL){
1431       notation = ags_notation_new((GObject *) machine->audio,
1432 				  i);
1433       notation->timestamp->timer.ags_offset.offset = timestamp->timer.ags_offset.offset;
1434 
1435       ags_audio_add_notation(machine->audio,
1436 			     (GObject *) notation);
1437     }else{
1438       notation = AGS_NOTATION(list_notation->data);
1439     }
1440 
1441     g_list_free_full(start_list_notation,
1442 		     g_object_unref);
1443 
1444     if(paste_from_position){
1445       xmlNode *child;
1446 
1447       guint x_boundary;
1448 
1449       ags_notation_insert_from_clipboard_extended(notation,
1450 						  notation_node,
1451 						  TRUE, position_x,
1452 						  TRUE, position_y,
1453 						  match_channel, no_duplicates);
1454 
1455       /* get boundaries */
1456       child = notation_node->children;
1457       current_x = 0;
1458 
1459       while(child != NULL){
1460 	if(child->type == XML_ELEMENT_NODE){
1461 	  if(!xmlStrncmp(child->name,
1462 			 BAD_CAST "note",
1463 			 5)){
1464 	    guint tmp;
1465 
1466 	    tmp = g_ascii_strtoull(xmlGetProp(child,
1467 					      BAD_CAST "x1"),
1468 				   NULL,
1469 				   10);
1470 
1471 	    if(tmp > current_x){
1472 	      current_x = tmp;
1473 	    }
1474 	  }
1475 	}
1476 
1477 	child = child->next;
1478       }
1479 
1480       x_boundary = g_ascii_strtoull(xmlGetProp(notation_node,
1481 					       BAD_CAST "x_boundary"),
1482 				    NULL,
1483 				    10);
1484 
1485 
1486       if(first_x == -1 || x_boundary < first_x){
1487 	first_x = x_boundary;
1488       }
1489 
1490       if(position_x > x_boundary){
1491 	current_x += (position_x - x_boundary);
1492       }else{
1493 	current_x -= (x_boundary - position_x);
1494       }
1495 
1496       if(current_x > last_x[0]){
1497 	last_x[0] = current_x;
1498       }
1499     }else{
1500       xmlNode *child;
1501 
1502       ags_notation_insert_from_clipboard(notation,
1503 					 notation_node,
1504 					 FALSE, 0,
1505 					 FALSE, 0);
1506 
1507       /* get boundaries */
1508       child = notation_node->children;
1509       current_x = 0;
1510 
1511       while(child != NULL){
1512 	if(child->type == XML_ELEMENT_NODE){
1513 	  if(!xmlStrncmp(child->name,
1514 			 BAD_CAST "note",
1515 			 5)){
1516 	    guint tmp;
1517 
1518 	    tmp = g_ascii_strtoull(xmlGetProp(child,
1519 					      BAD_CAST "x1"),
1520 				   NULL,
1521 				   10);
1522 
1523 	    if(tmp > current_x){
1524 	      current_x = tmp;
1525 	    }
1526 	  }
1527 	}
1528 
1529 	child = child->next;
1530       }
1531 
1532       if(current_x > last_x[0]){
1533 	last_x[0] = current_x;
1534       }
1535     }
1536 
1537     /* iterate */
1538     i++;
1539   }
1540 
1541   return(first_x);
1542 }
1543 
1544 gint
ags_notation_editor_paste_notation(AgsNotationEditor * notation_editor,AgsMachine * machine,xmlNode * audio_node,guint position_x,guint position_y,gboolean paste_from_position,gint * last_x)1545 ags_notation_editor_paste_notation(AgsNotationEditor *notation_editor,
1546 				   AgsMachine *machine,
1547 				   xmlNode *audio_node,
1548 				   guint position_x, guint position_y,
1549 				   gboolean paste_from_position,
1550 				   gint *last_x)
1551 {
1552   AgsTimestamp *timestamp;
1553 
1554   xmlNode *notation_list_node, *notation_node;
1555   xmlNode *timestamp_node;
1556 
1557   gint first_x;
1558   gboolean match_channel, no_duplicates;
1559 
1560   first_x = -1;
1561 
1562   match_channel = ((AGS_NOTATION_EDITOR_PASTE_MATCH_AUDIO_CHANNEL & (notation_editor->flags)) != 0) ? TRUE: FALSE;
1563   no_duplicates = ((AGS_NOTATION_EDITOR_PASTE_NO_DUPLICATES & (notation_editor->flags)) != 0) ? TRUE: FALSE;
1564 
1565   /* timestamp */
1566   timestamp = ags_timestamp_new();
1567 
1568   timestamp->flags &= (~AGS_TIMESTAMP_UNIX);
1569   timestamp->flags |= AGS_TIMESTAMP_OFFSET;
1570 
1571   timestamp->timer.ags_offset.offset = 0;
1572 
1573   /* paste notation */
1574   notation_list_node = audio_node->children;
1575 
1576   while(notation_list_node != NULL){
1577     if(notation_list_node->type == XML_ELEMENT_NODE){
1578       if(!xmlStrncmp(notation_list_node->name,
1579 		     BAD_CAST "notation-list",
1580 		     14)){
1581 	notation_node = notation_list_node->children;
1582 
1583 	while(notation_node != NULL){
1584 	  if(notation_node->type == XML_ELEMENT_NODE){
1585 	    if(!xmlStrncmp(notation_node->name,
1586 			   BAD_CAST "notation",
1587 			   9)){
1588 	      guint64 offset;
1589 
1590 	      timestamp_node = notation_node->children;
1591 	      offset = 0;
1592 
1593 	      while(timestamp_node != NULL){
1594 		if(timestamp_node->type == XML_ELEMENT_NODE){
1595 		  if(!xmlStrncmp(timestamp_node->name,
1596 				 BAD_CAST "timestamp",
1597 				 10)){
1598 		    offset = g_ascii_strtoull((gchar *) xmlGetProp(timestamp_node,
1599 								   BAD_CAST "offset"),
1600 					      NULL,
1601 					      10);
1602 
1603 		    break;
1604 		  }
1605 		}
1606 
1607 		timestamp_node = timestamp_node->next;
1608 	      }
1609 
1610 	      /* 1st attempt */
1611 	      timestamp->timer.ags_offset.offset = (guint64) AGS_NOTATION_DEFAULT_OFFSET * floor((double) position_x / (double) AGS_NOTATION_DEFAULT_OFFSET);
1612 
1613 	      first_x = ags_notation_editor_paste_notation_all(notation_editor,
1614 							       machine,
1615 							       notation_node,
1616 							       timestamp,
1617 							       match_channel, no_duplicates,
1618 							       position_x, position_y,
1619 							       paste_from_position,
1620 							       last_x);
1621 
1622 	      /* 2nd attempt */
1623 	      timestamp->timer.ags_offset.offset += AGS_NOTATION_DEFAULT_OFFSET;
1624 
1625 	      ags_notation_editor_paste_notation_all(notation_editor,
1626 						     machine,
1627 						     notation_node,
1628 						     timestamp,
1629 						     match_channel, no_duplicates,
1630 						     position_x, position_y,
1631 						     paste_from_position,
1632 						     last_x);
1633 	    }
1634 	  }
1635 
1636 	  notation_node = notation_node->next;
1637 	}
1638       }
1639     }
1640 
1641     notation_list_node = notation_list_node->next;
1642   }
1643 
1644   g_object_unref(timestamp);
1645 
1646   return(first_x);
1647 }
1648 
1649 /**
1650  * ags_notation_editor_paste:
1651  * @notation_editor: the #AgsNotationEditor
1652  *
1653  * Is emitted as machine changed of notation_editor.
1654  *
1655  * Since: 3.0.0
1656  */
1657 void
ags_notation_editor_paste(AgsNotationEditor * notation_editor)1658 ags_notation_editor_paste(AgsNotationEditor *notation_editor)
1659 {
1660   AgsMachine *machine;
1661   AgsNotationEdit *notation_edit;
1662 
1663   xmlDoc *clipboard;
1664   xmlNode *audio_node;
1665 
1666   gchar *buffer;
1667 
1668   guint position_x, position_y;
1669   gint first_x, last_x;
1670   gboolean paste_from_position;
1671 
1672   if(!AGS_IS_NOTATION_EDITOR(notation_editor)){
1673     return;
1674   }
1675 
1676   if((machine = notation_editor->selected_machine) != NULL){
1677     notation_edit = notation_editor->notation_edit;
1678 
1679     /* get clipboard */
1680     buffer = gtk_clipboard_wait_for_text(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
1681 
1682     if(buffer == NULL){
1683       return;
1684     }
1685 
1686     position_x = 0;
1687     position_y = 0;
1688 
1689     /* get position */
1690     if(notation_editor->notation_toolbar->selected_edit_mode == notation_editor->notation_toolbar->position){
1691       last_x = 0;
1692       paste_from_position = TRUE;
1693 
1694       position_x = notation_editor->notation_edit->cursor_position_x;
1695       position_y = notation_editor->notation_edit->cursor_position_y;
1696 
1697 #ifdef DEBUG
1698       printf("pasting at position: [%u,%u]\n", position_x, position_y);
1699 #endif
1700     }else{
1701       paste_from_position = FALSE;
1702     }
1703 
1704     /* get xml tree */
1705     clipboard = xmlReadMemory(buffer, strlen(buffer),
1706 			      NULL, "UTF-8",
1707 			      0);
1708     audio_node = xmlDocGetRootElement(clipboard);
1709 
1710     first_x = -1;
1711 
1712     /* iterate xml tree */
1713     while(audio_node != NULL){
1714       if(audio_node->type == XML_ELEMENT_NODE){
1715 	if(!xmlStrncmp(audio_node->name,
1716 		       BAD_CAST "audio",
1717 		       6)){
1718 	  first_x = ags_notation_editor_paste_notation(notation_editor,
1719 						       machine,
1720 						       audio_node,
1721 						       position_x, position_y,
1722 						       paste_from_position,
1723 						       &last_x);
1724 
1725 	  break;
1726 	}
1727       }
1728 
1729       audio_node = audio_node->next;
1730     }
1731 
1732     if(first_x == -1){
1733       first_x = 0;
1734     }
1735 
1736     xmlFreeDoc(clipboard);
1737 
1738     if(paste_from_position){
1739       gint big_step, small_step;
1740 
1741       //TODO:JK: implement me
1742       big_step = (guint) ceil((double) last_x / 16.0) * 16.0 + (notation_edit->cursor_position_x % (guint) 16);
1743       small_step = (guint) big_step - 16;
1744 
1745       if(small_step < last_x){
1746 	notation_editor->notation_edit->cursor_position_x = big_step;
1747       }else{
1748 	notation_editor->notation_edit->cursor_position_x = small_step;
1749       }
1750     }
1751 
1752     gtk_widget_queue_draw((GtkWidget *) notation_editor->notation_edit);
1753   }
1754 }
1755 
1756 /**
1757  * ags_notation_editor_copy:
1758  * @notation_editor: the #AgsNotationEditor
1759  *
1760  * Is emitted as machine changed of notation_editor.
1761  *
1762  * Since: 3.0.0
1763  */
1764 void
ags_notation_editor_copy(AgsNotationEditor * notation_editor)1765 ags_notation_editor_copy(AgsNotationEditor *notation_editor)
1766 {
1767   AgsMachine *machine;
1768 
1769   xmlDoc *clipboard;
1770   xmlNode *audio_node, *notation_list_node, *notation_node;
1771 
1772   GList *start_list_notation, *list_notation;
1773 
1774   xmlChar *buffer;
1775 
1776   int buffer_size;
1777   guint audio_channel;
1778   gint i;
1779 
1780   if(!AGS_IS_NOTATION_EDITOR(notation_editor)){
1781     return;
1782   }
1783 
1784   if(notation_editor->selected_machine != NULL){
1785     machine = notation_editor->selected_machine;
1786 
1787     /* create document */
1788     clipboard = xmlNewDoc(BAD_CAST XML_DEFAULT_VERSION);
1789 
1790     /* create root node */
1791     audio_node = xmlNewNode(NULL,
1792 			    BAD_CAST "audio");
1793     xmlDocSetRootElement(clipboard, audio_node);
1794 
1795     notation_list_node = xmlNewNode(NULL,
1796 				    BAD_CAST "notation-list");
1797     xmlAddChild(audio_node,
1798 		notation_list_node);
1799 
1800     /* create notation nodes */
1801     g_object_get(machine->audio,
1802 		 "notation", &start_list_notation,
1803 		 NULL);
1804 
1805     i = 0;
1806 
1807     while((i = ags_notebook_next_active_tab(notation_editor->notebook,
1808 					    i)) != -1){
1809       list_notation = start_list_notation;
1810 
1811       /* copy */
1812       while(list_notation != NULL){
1813 	g_object_get(list_notation->data,
1814 		     "audio-channel", &audio_channel,
1815 		     NULL);
1816 
1817 	if(i != audio_channel){
1818 	  list_notation = list_notation->next;
1819 
1820 	  continue;
1821 	}
1822 
1823 	notation_node = ags_notation_copy_selection(AGS_NOTATION(list_notation->data));
1824 	xmlAddChild(notation_list_node,
1825 		    notation_node);
1826 
1827 	list_notation = list_notation->next;
1828       }
1829 
1830       /* iterate */
1831       i++;
1832     }
1833 
1834     g_list_free_full(start_list_notation,
1835 		     g_object_unref);
1836 
1837     /* write to clipboard */
1838     xmlDocDumpFormatMemoryEnc(clipboard, &buffer, &buffer_size, "UTF-8", TRUE);
1839     gtk_clipboard_set_text(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
1840 			   (gchar *) buffer, buffer_size);
1841     gtk_clipboard_store(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
1842 
1843     xmlFreeDoc(clipboard);
1844   }
1845 }
1846 
1847 /**
1848  * ags_notation_editor_cut:
1849  * @notation_editor: the #AgsNotationEditor
1850  *
1851  * Is emitted as machine changed of notation_editor.
1852  *
1853  * Since: 3.0.0
1854  */
1855 void
ags_notation_editor_cut(AgsNotationEditor * notation_editor)1856 ags_notation_editor_cut(AgsNotationEditor *notation_editor)
1857 {
1858   AgsMachine *machine;
1859 
1860   xmlDoc *clipboard;
1861   xmlNode *audio_node;
1862   xmlNode *notation_list_node, *notation_node;
1863 
1864   GList *start_list_notation, *list_notation;
1865 
1866   xmlChar *buffer;
1867 
1868   int buffer_size;
1869   guint audio_channel;
1870   gint i;
1871 
1872   if(!AGS_IS_NOTATION_EDITOR(notation_editor)){
1873     return;
1874   }
1875 
1876   if(notation_editor->selected_machine != NULL){
1877     machine = notation_editor->selected_machine;
1878 
1879     /* create document */
1880     clipboard = xmlNewDoc(BAD_CAST XML_DEFAULT_VERSION);
1881 
1882     /* create root node */
1883     audio_node = xmlNewNode(NULL,
1884 			    BAD_CAST "audio");
1885     xmlDocSetRootElement(clipboard, audio_node);
1886 
1887     notation_list_node = xmlNewNode(NULL,
1888 				    BAD_CAST "notation-list");
1889     xmlAddChild(audio_node,
1890 		notation_list_node);
1891 
1892     /* create notation nodes */
1893     g_object_get(machine->audio,
1894 		 "notation", &start_list_notation,
1895 		 NULL);
1896 
1897     i = 0;
1898 
1899     while((i = ags_notebook_next_active_tab(notation_editor->notebook,
1900 					    i)) != -1){
1901       list_notation = start_list_notation;
1902 
1903       /* cut */
1904       while(list_notation != NULL){
1905 	g_object_get(list_notation->data,
1906 		     "audio-channel", &audio_channel,
1907 		     NULL);
1908 
1909 	if(i != audio_channel){
1910 	  list_notation = list_notation->next;
1911 
1912 	  continue;
1913 	}
1914 
1915 	notation_node = ags_notation_cut_selection(AGS_NOTATION(list_notation->data));
1916 	xmlAddChild(notation_list_node,
1917 		    notation_node);
1918 
1919 	list_notation = list_notation->next;
1920       }
1921 
1922       /* iterate */
1923       i++;
1924     }
1925 
1926     g_list_free_full(start_list_notation,
1927 		     g_object_unref);
1928 
1929     gtk_widget_queue_draw((GtkWidget *) notation_editor->notation_edit);
1930 
1931     /* write to clipboard */
1932     xmlDocDumpFormatMemoryEnc(clipboard, &buffer, &buffer_size, "UTF-8", TRUE);
1933     gtk_clipboard_set_text(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
1934 			   (gchar *) buffer, buffer_size);
1935     gtk_clipboard_store(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
1936 
1937     xmlFreeDoc(clipboard);
1938   }
1939 }
1940 
1941 
1942 void
ags_notation_editor_get_boundary(AgsNotationEditor * notation_editor,AgsMachine * machine,AgsNotation * notation,guint * lower,guint * upper)1943 ags_notation_editor_get_boundary(AgsNotationEditor *notation_editor,
1944 				 AgsMachine *machine,
1945 				 AgsNotation *notation,
1946 				 guint *lower, guint *upper)
1947 {
1948   GList *start_note, *note;
1949 
1950   guint note_y;
1951 
1952   g_object_get(notation,
1953 	       "note", &start_note,
1954 	       NULL);
1955 
1956   /* retrieve upper and lower */
1957   note = start_note;
1958 
1959   while(note != NULL){
1960     g_object_get(note->data,
1961 		 "y", &note_y,
1962 		 NULL);
1963 
1964     if(note_y < lower[0]){
1965       lower[0] = note_y;
1966     }
1967 
1968     if(note_y > upper[0]){
1969       upper[0] = note_y;
1970     }
1971 
1972     note = note->next;
1973   }
1974 
1975   g_list_free_full(start_note,
1976 		   g_object_unref);
1977 }
1978 
1979 void
ags_notation_editor_invert_notation(AgsNotationEditor * notation_editor,AgsMachine * machine,AgsNotation * notation,guint lower,guint upper)1980 ags_notation_editor_invert_notation(AgsNotationEditor *notation_editor,
1981 				    AgsMachine *machine,
1982 				    AgsNotation *notation,
1983 				    guint lower, guint upper)
1984 {
1985   GList *start_note, *note;
1986 
1987   guint note_y;
1988 
1989   g_object_get(notation,
1990 	       "note", &start_note,
1991 	       NULL);
1992 
1993   /* invert */
1994   note = start_note;
1995 
1996   while(note != NULL){
1997     g_object_get(note->data,
1998 		 "y", &note_y,
1999 		 NULL);
2000 
2001     if((gdouble) note_y < (gdouble) (upper - lower) / 2.0){
2002       g_object_set(note->data,
2003 		   "y", (upper - (note_y - lower)),
2004 		   NULL);
2005     }else if((gdouble) note_y > (gdouble) (upper - lower) / 2.0){
2006       g_object_set(note->data,
2007 		   "y", (lower + (upper - AGS_NOTE(note->data)->y)),
2008 		   NULL);
2009     }
2010 
2011     note = note->next;
2012   }
2013 
2014   g_list_free_full(start_note,
2015 		   g_object_unref);
2016 }
2017 
2018 /**
2019  * ags_notation_editor_invert:
2020  * @notation_editor: the #AgsNotationEditor
2021  *
2022  * Invert all notation of @notation_editor's selected machine.
2023  *
2024  * Since: 3.0.0
2025  */
2026 void
ags_notation_editor_invert(AgsNotationEditor * notation_editor)2027 ags_notation_editor_invert(AgsNotationEditor *notation_editor)
2028 {
2029   AgsMachine *machine;
2030 
2031   GList *start_list_notation, *list_notation;
2032 
2033   guint audio_channel;
2034   gint i;
2035 
2036   if(!AGS_IS_NOTATION_EDITOR(notation_editor)){
2037     return;
2038   }
2039 
2040   if(notation_editor->selected_machine != NULL){
2041     guint lower, upper;
2042 
2043     /* create notation nodes */
2044     machine = notation_editor->selected_machine;
2045 
2046     g_object_get(machine->audio,
2047 		 "notation", &start_list_notation,
2048 		 NULL);
2049 
2050     i = 0;
2051 
2052     while((i = ags_notebook_next_active_tab(notation_editor->notebook,
2053 					    i)) != -1){
2054       /* get boundary */
2055       list_notation = start_list_notation;
2056 
2057       lower = G_MAXUINT;
2058       upper = 0;
2059 
2060       while(list_notation != NULL){
2061 	g_object_get(list_notation->data,
2062 		     "audio-channel", &audio_channel,
2063 		     NULL);
2064 
2065 	if(i != audio_channel){
2066 	  list_notation = list_notation->next;
2067 
2068 	  continue;
2069 	}
2070 
2071 	ags_notation_editor_get_boundary(notation_editor,
2072 					 machine,
2073 					 AGS_NOTATION(list_notation->data),
2074 					 &lower, &upper);
2075 
2076 	list_notation = list_notation->next;
2077       }
2078 
2079       /* invert */
2080       list_notation = start_list_notation;
2081 
2082       while(list_notation != NULL){
2083 	g_object_get(list_notation->data,
2084 		     "audio-channel", &audio_channel,
2085 		     NULL);
2086 
2087 	if(i != audio_channel){
2088 	  list_notation = list_notation->next;
2089 
2090 	  continue;
2091 	}
2092 
2093 	ags_notation_editor_invert_notation(notation_editor,
2094 					    machine,
2095 					    AGS_NOTATION(list_notation->data),
2096 					    lower, upper);
2097 
2098 	list_notation = list_notation->next;
2099       }
2100 
2101       i++;
2102     }
2103 
2104     g_list_free_full(start_list_notation,
2105 		     g_object_unref);
2106 
2107     gtk_widget_queue_draw((GtkWidget *) notation_editor->notation_edit);
2108   }
2109 }
2110 
2111 /**
2112  * ags_notation_editor_new:
2113  *
2114  * Creates the #AgsNotationEditor
2115  *
2116  * Returns: a new #AgsNotationEditor
2117  *
2118  * Since: 3.0.0
2119  */
2120 AgsNotationEditor*
ags_notation_editor_new()2121 ags_notation_editor_new()
2122 {
2123   AgsNotationEditor *notation_editor;
2124 
2125   notation_editor = (AgsNotationEditor *) g_object_new(AGS_TYPE_NOTATION_EDITOR,
2126 						       NULL);
2127 
2128   return(notation_editor);
2129 }
2130