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/task/ags_move_note.h>
21 
22 #include <ags/i18n.h>
23 
24 #include <math.h>
25 
26 void ags_move_note_class_init(AgsMoveNoteClass *move_note);
27 void ags_move_note_init(AgsMoveNote *move_note);
28 void ags_move_note_set_property(GObject *gobject,
29 				guint prop_id,
30 				const GValue *value,
31 				GParamSpec *param_spec);
32 void ags_move_note_get_property(GObject *gobject,
33 				guint prop_id,
34 				GValue *value,
35 				GParamSpec *param_spec);
36 void ags_move_note_dispose(GObject *gobject);
37 void ags_move_note_finalize(GObject *gobject);
38 
39 void ags_move_note_launch(AgsTask *task);
40 
41 /**
42  * SECTION:ags_move_note
43  * @short_description: move notation
44  * @title: AgsMoveNote
45  * @section_id:
46  * @include: ags/audio/task/ags_move_note.h
47  *
48  * The #AgsMoveNote task moves #AgsNotation.
49  */
50 
51 static gpointer ags_move_note_parent_class = NULL;
52 
53 enum{
54   PROP_0,
55   PROP_AUDIO,
56   PROP_NOTATION,
57   PROP_SELECTION,
58   PROP_FIRST_X,
59   PROP_FIRST_Y,
60   PROP_MOVE_X,
61   PROP_MOVE_Y,
62   PROP_RELATIVE,
63   PROP_ABSOLUTE,
64 };
65 
66 GType
ags_move_note_get_type()67 ags_move_note_get_type()
68 {
69   static volatile gsize g_define_type_id__volatile = 0;
70 
71   if(g_once_init_enter (&g_define_type_id__volatile)){
72     GType ags_type_move_note = 0;
73 
74     static const GTypeInfo ags_move_note_info = {
75       sizeof(AgsMoveNoteClass),
76       NULL, /* base_init */
77       NULL, /* base_finalize */
78       (GClassInitFunc) ags_move_note_class_init,
79       NULL, /* class_finalize */
80       NULL, /* class_data */
81       sizeof(AgsMoveNote),
82       0,    /* n_preallocs */
83       (GInstanceInitFunc) ags_move_note_init,
84     };
85 
86     ags_type_move_note = g_type_register_static(AGS_TYPE_TASK,
87 						"AgsMoveNote",
88 						&ags_move_note_info,
89 						0);
90 
91     g_once_init_leave(&g_define_type_id__volatile, ags_type_move_note);
92   }
93 
94   return g_define_type_id__volatile;
95 }
96 
97 void
ags_move_note_class_init(AgsMoveNoteClass * move_note)98 ags_move_note_class_init(AgsMoveNoteClass *move_note)
99 {
100   GObjectClass *gobject;
101   AgsTaskClass *task;
102 
103   GParamSpec *param_spec;
104 
105   ags_move_note_parent_class = g_type_class_peek_parent(move_note);
106 
107   /* gobject */
108   gobject = (GObjectClass *) move_note;
109 
110   gobject->set_property = ags_move_note_set_property;
111   gobject->get_property = ags_move_note_get_property;
112 
113   gobject->dispose = ags_move_note_dispose;
114   gobject->finalize = ags_move_note_finalize;
115 
116   /* properties */
117   /**
118    * AgsMoveNote:audio:
119    *
120    * The assigned #AgsAudio
121    *
122    * Since: 3.0.0
123    */
124   param_spec = g_param_spec_object("audio",
125 				   i18n_pspec("audio of move note"),
126 				   i18n_pspec("The audio of move note task"),
127 				   AGS_TYPE_AUDIO,
128 				   G_PARAM_READABLE | G_PARAM_WRITABLE);
129   g_object_class_install_property(gobject,
130 				  PROP_AUDIO,
131 				  param_spec);
132 
133   /**
134    * AgsMoveNote:notation:
135    *
136    * The assigned #AgsNotation
137    *
138    * Since: 3.0.0
139    */
140   param_spec = g_param_spec_object("notation",
141 				   i18n_pspec("notation of move note"),
142 				   i18n_pspec("The notation of move note task"),
143 				   AGS_TYPE_NOTATION,
144 				   G_PARAM_READABLE | G_PARAM_WRITABLE);
145   g_object_class_install_property(gobject,
146 				  PROP_NOTATION,
147 				  param_spec);
148 
149   /**
150    * AgsMoveNote:selection: (type GList(AgsNote))
151    *
152    * The assigned #AgsNote
153    *
154    * Since: 3.0.0
155    */
156   param_spec = g_param_spec_pointer("selection",
157 				    i18n_pspec("selection to move"),
158 				    i18n_pspec("The selection to move"),
159 				    G_PARAM_READABLE | G_PARAM_WRITABLE);
160   g_object_class_install_property(gobject,
161 				  PROP_SELECTION,
162 				  param_spec);
163 
164   /**
165    * AgsMoveNote:first-x:
166    *
167    * Move notation from x offset.
168    *
169    * Since: 3.0.0
170    */
171   param_spec =  g_param_spec_uint("first-x",
172 				  i18n_pspec("move from x offset"),
173 				  i18n_pspec("Move the notation from x offset"),
174 				  0,
175 				  AGS_MOVE_NOTE_DEFAULT_X_LENGTH,
176 				  0,
177 				  G_PARAM_READABLE | G_PARAM_WRITABLE);
178   g_object_class_install_property(gobject,
179 				  PROP_FIRST_X,
180 				  param_spec);
181 
182   /**
183    * AgsMoveNote:first-y:
184    *
185    * Move notation with x padding.
186    *
187    * Since: 3.0.0
188    */
189   param_spec =  g_param_spec_uint("first-y",
190 				  i18n_pspec("move with x padding"),
191 				  i18n_pspec("Move the notation with x padding"),
192 				  0,
193 				  AGS_MOVE_NOTE_DEFAULT_X_LENGTH,
194 				  0,
195 				  G_PARAM_READABLE | G_PARAM_WRITABLE);
196   g_object_class_install_property(gobject,
197 				  PROP_FIRST_Y,
198 				  param_spec);
199 
200   /**
201    * AgsMoveNote:move-x:
202    *
203    * Move notation by move-x amount.
204    *
205    * Since: 3.0.0
206    */
207   param_spec = g_param_spec_int("move-x",
208 				i18n_pspec("move with move-x amount"),
209 				i18n_pspec("Move the notation by move-x amount"),
210 				-1 * AGS_MOVE_NOTE_DEFAULT_X_LENGTH,
211 				AGS_MOVE_NOTE_DEFAULT_X_LENGTH,
212 				0,
213 				G_PARAM_READABLE | G_PARAM_WRITABLE);
214   g_object_class_install_property(gobject,
215 				  PROP_MOVE_X,
216 				  param_spec);
217 
218   /**
219    * AgsMoveNote:move-y:
220    *
221    * Move notation by move-y amount.
222    *
223    * Since: 3.0.0
224    */
225   param_spec =  g_param_spec_int("move-y",
226 				 i18n_pspec("move with move-y amount"),
227 				 i18n_pspec("Move the notation by move-y amount"),
228 				 -1 * AGS_MOVE_NOTE_DEFAULT_Y_LENGTH,
229 				 AGS_MOVE_NOTE_DEFAULT_Y_LENGTH,
230 				 0,
231 				 G_PARAM_READABLE | G_PARAM_WRITABLE);
232   g_object_class_install_property(gobject,
233 				  PROP_MOVE_Y,
234 				  param_spec);
235 
236   /**
237    * AgsMoveNote:relative:
238    *
239    * Move notation by relative position.
240    *
241    * Since: 3.0.0
242    */
243   param_spec =  g_param_spec_boolean("relative",
244 				     i18n_pspec("move relative"),
245 				     i18n_pspec("Move the notation by relative position"),
246 				     FALSE,
247 				     G_PARAM_READABLE | G_PARAM_WRITABLE);
248   g_object_class_install_property(gobject,
249 				  PROP_RELATIVE,
250 				  param_spec);
251 
252   /**
253    * AgsMoveNote:absolute:
254    *
255    * Move notation by absolute position.
256    *
257    * Since: 3.0.0
258    */
259   param_spec =  g_param_spec_boolean("absolute",
260 				     i18n_pspec("move absolute"),
261 				     i18n_pspec("Move the notation by absolute position"),
262 				     FALSE,
263 				     G_PARAM_READABLE | G_PARAM_WRITABLE);
264   g_object_class_install_property(gobject,
265 				  PROP_ABSOLUTE,
266 				  param_spec);
267 
268   /* task */
269   task = (AgsTaskClass *) move_note;
270 
271   task->launch = ags_move_note_launch;
272 }
273 
274 void
ags_move_note_init(AgsMoveNote * move_note)275 ags_move_note_init(AgsMoveNote *move_note)
276 {
277   move_note->audio = NULL;
278   move_note->notation = NULL;
279 
280   move_note->selection = NULL;
281 
282   move_note->first_x = 0;
283   move_note->first_y = 0;
284   move_note->move_x = 0;
285   move_note->move_y = 0;
286 
287   move_note->relative = FALSE;
288   move_note->absolute = FALSE;
289 }
290 
291 void
ags_move_note_set_property(GObject * gobject,guint prop_id,const GValue * value,GParamSpec * param_spec)292 ags_move_note_set_property(GObject *gobject,
293 			   guint prop_id,
294 			   const GValue *value,
295 			   GParamSpec *param_spec)
296 {
297   AgsMoveNote *move_note;
298 
299   move_note = AGS_MOVE_NOTE(gobject);
300 
301   switch(prop_id){
302   case PROP_AUDIO:
303     {
304       AgsAudio *audio;
305 
306       audio = (AgsAudio *) g_value_get_object(value);
307 
308       if(move_note->audio == audio){
309 	return;
310       }
311 
312       if(move_note->audio != NULL){
313 	g_object_unref(move_note->audio);
314       }
315 
316       if(audio != NULL){
317 	g_object_ref(audio);
318       }
319 
320       move_note->audio = audio;
321     }
322     break;
323   case PROP_NOTATION:
324     {
325       AgsNotation *notation;
326 
327       notation = (AgsNotation *) g_value_get_object(value);
328 
329       if(move_note->notation == notation){
330 	return;
331       }
332 
333       if(move_note->notation != NULL){
334 	g_object_unref(move_note->notation);
335       }
336 
337       if(notation != NULL){
338 	g_object_ref(notation);
339       }
340 
341       move_note->notation = notation;
342     }
343     break;
344   case PROP_SELECTION:
345     {
346       AgsNote *note;
347 
348       note = (AgsNote *) g_value_get_pointer(value);
349 
350       if(note == NULL ||
351 	 g_list_find(move_note->selection, note) != NULL){
352 	return;
353       }
354 
355       g_object_ref(note);
356       move_note->selection = g_list_prepend(move_note->selection,
357 					    note);
358     }
359     break;
360   case PROP_FIRST_X:
361     {
362       move_note->first_x = g_value_get_uint(value);
363     }
364     break;
365   case PROP_FIRST_Y:
366     {
367       move_note->first_y = g_value_get_uint(value);
368     }
369     break;
370   case PROP_MOVE_X:
371     {
372       move_note->move_x = g_value_get_int(value);
373     }
374     break;
375   case PROP_MOVE_Y:
376     {
377       move_note->move_y = g_value_get_int(value);
378     }
379     break;
380   case PROP_RELATIVE:
381     {
382       move_note->relative = g_value_get_boolean(value);
383     }
384     break;
385   case PROP_ABSOLUTE:
386     {
387       move_note->absolute = g_value_get_boolean(value);
388     }
389     break;
390   default:
391     G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, param_spec);
392     break;
393   }
394 }
395 
396 void
ags_move_note_get_property(GObject * gobject,guint prop_id,GValue * value,GParamSpec * param_spec)397 ags_move_note_get_property(GObject *gobject,
398 			   guint prop_id,
399 			   GValue *value,
400 			   GParamSpec *param_spec)
401 {
402   AgsMoveNote *move_note;
403 
404   move_note = AGS_MOVE_NOTE(gobject);
405 
406   switch(prop_id){
407   case PROP_AUDIO:
408     {
409       g_value_set_object(value, move_note->audio);
410     }
411     break;
412   case PROP_NOTATION:
413     {
414       g_value_set_object(value, move_note->notation);
415     }
416     break;
417   case PROP_SELECTION:
418     {
419       g_value_set_pointer(value,
420 			  g_list_copy_deep(move_note->selection,
421 					   (GCopyFunc) g_object_ref,
422 					   NULL));
423     }
424     break;
425   case PROP_FIRST_X:
426     {
427       g_value_set_int(value, move_note->first_x);
428     }
429     break;
430   case PROP_FIRST_Y:
431     {
432       g_value_set_uint(value, move_note->first_y);
433     }
434     break;
435   case PROP_MOVE_X:
436     {
437       g_value_set_uint(value, move_note->move_x);
438     }
439     break;
440   case PROP_MOVE_Y:
441     {
442       g_value_set_uint(value, move_note->move_y);
443     }
444     break;
445   case PROP_RELATIVE:
446     {
447       g_value_set_boolean(value, move_note->relative);
448     }
449     break;
450   case PROP_ABSOLUTE:
451     {
452       g_value_set_boolean(value, move_note->absolute);
453     }
454     break;
455   default:
456     G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, param_spec);
457     break;
458   }
459 }
460 
461 void
ags_move_note_dispose(GObject * gobject)462 ags_move_note_dispose(GObject *gobject)
463 {
464   AgsMoveNote *move_note;
465 
466   move_note = AGS_MOVE_NOTE(gobject);
467 
468   if(move_note->audio != NULL){
469     g_object_unref(move_note->audio);
470 
471     move_note->audio = NULL;
472   }
473 
474   if(move_note->notation != NULL){
475     g_object_unref(move_note->notation);
476 
477     move_note->notation = NULL;
478   }
479 
480   if(move_note->selection != NULL){
481     g_list_free(move_note->selection);
482 
483     move_note->selection = NULL;
484   }
485 
486   /* call parent */
487   G_OBJECT_CLASS(ags_move_note_parent_class)->dispose(gobject);
488 }
489 
490 void
ags_move_note_finalize(GObject * gobject)491 ags_move_note_finalize(GObject *gobject)
492 {
493   AgsMoveNote *move_note;
494 
495   move_note = AGS_MOVE_NOTE(gobject);
496 
497   if(move_note->audio != NULL){
498     g_object_unref(move_note->audio);
499   }
500 
501   if(move_note->notation != NULL){
502     g_object_unref(move_note->notation);
503   }
504 
505   if(move_note->selection != NULL){
506     g_list_free(move_note->selection);
507   }
508 
509   /* call parent */
510   G_OBJECT_CLASS(ags_move_note_parent_class)->finalize(gobject);
511 }
512 
513 void
ags_move_note_launch(AgsTask * task)514 ags_move_note_launch(AgsTask *task)
515 {
516   AgsAudio *audio;
517   AgsNotation *notation, *current_notation;
518   AgsNote *note;
519 
520   AgsMoveNote *move_note;
521 
522   GList *selection;
523 
524   guint audio_channel;
525   guint first_x;
526   guint first_y;
527   gint move_x;
528   gint move_y;
529 
530   gboolean relative;
531   gboolean absolute;
532 
533   move_note = AGS_MOVE_NOTE(task);
534 
535   g_return_if_fail(AGS_IS_AUDIO(move_note->audio));
536   g_return_if_fail(AGS_IS_NOTATION(move_note->notation));
537 
538   g_object_get(move_note,
539 	       "audio", &audio,
540 	       NULL);
541 
542   /* get some properties */
543   notation =
544     current_notation = move_note->notation;
545 
546   g_object_get(notation,
547 	       "audio-channel", &audio_channel,
548 	       NULL);
549 
550   selection = move_note->selection;
551 
552   first_x = move_note->first_x;
553   first_y = move_note->first_y;
554 
555   move_x = move_note->move_x;
556   move_y = move_note->move_y;
557 
558   relative = move_note->relative;
559   absolute = move_note->absolute;
560 
561   /* move */
562   while(selection != NULL){
563     note = ags_note_duplicate(AGS_NOTE(selection->data));
564 
565     if(relative){
566       note->x[0] = note->x[0] + move_x;
567       note->x[1] = note->x[1] + move_x;
568 
569       note->y = note->y + move_y;
570     }else if(absolute){
571       note->x[0] = move_x + (note->x[0] - first_x);
572       note->x[1] = move_x + (note->x[1] - first_x);
573 
574       note->y = move_y + (note->y + first_y);
575     }
576 
577     if(note->x[0] >= ags_timestamp_get_ags_offset(current_notation->timestamp) + AGS_NOTATION_DEFAULT_OFFSET){
578       AgsTimestamp *timestamp;
579 
580       GList *list_start, *list;
581 
582       g_object_get(audio,
583 		   "notation", &list_start,
584 		   NULL);
585 
586       timestamp = ags_timestamp_new();
587       timestamp->flags &= (~AGS_TIMESTAMP_UNIX);
588       timestamp->flags |= AGS_TIMESTAMP_OFFSET;
589 
590       timestamp->timer.ags_offset.offset = (guint64) (AGS_NOTATION_DEFAULT_OFFSET * floor(note->x[0] / AGS_NOTATION_DEFAULT_OFFSET));
591 
592       if((list = ags_notation_find_near_timestamp(list_start, audio_channel,
593 						  timestamp)) == NULL){
594 	current_notation = ags_notation_new((GObject *) audio,
595 					    audio_channel);
596 
597 	current_notation->timestamp->timer.ags_offset.offset = ags_timestamp_get_ags_offset(timestamp);
598 	ags_audio_add_notation(audio,
599 			       (GObject *) current_notation);
600       }else{
601 	current_notation = list->data;
602       }
603 
604       g_list_free_full(list_start,
605 		       g_object_unref);
606       g_object_unref(timestamp);
607     }
608 
609     /* remove old note */
610     ags_notation_remove_note(notation,
611 			     selection->data,
612 			     TRUE);
613     ags_notation_remove_note(notation,
614 			     selection->data,
615 			     FALSE);
616 
617     /* add new note */
618     ags_notation_add_note(current_notation,
619 			  note,
620 			  FALSE);
621 
622     selection = selection->next;
623   }
624 
625   g_object_unref(audio);
626 }
627 
628 /**
629  * ags_move_note_set_selection:
630  * @move_note: the #AgsMoveNote
631  * @selection: (element-type AgsAudio.Note) (transfer none): the selection as #GList-struct
632  *
633  * Set @selection of @move_note.
634  *
635  * Since: 3.2.3
636  */
637 void
ags_move_note_set_selection(AgsMoveNote * move_note,GList * selection)638 ags_move_note_set_selection(AgsMoveNote *move_note,
639 			    GList *selection)
640 {
641   if(!AGS_IS_MOVE_NOTE(move_note) ||
642      move_note->selection == selection){
643     return;
644   }
645 
646   if(move_note->selection != NULL){
647     g_list_free_full(move_note->selection,
648 		     (GDestroyNotify) g_object_unref);
649   }
650 
651   move_note->selection = g_list_copy_deep(selection,
652 					  (GCopyFunc) g_object_ref,
653 					  NULL);
654 }
655 
656 /**
657  * ags_move_note_new:
658  * @audio: the #AgsAudio
659  * @notation: the #AgsNotation
660  * @selection: (element-type AgsAudio.Note) (transfer none): the selection as #GList-struct
661  * @first_x: the x offset to move from
662  * @first_y: the x padding to use
663  * @move_x: the amout to move in x direction
664  * @move_y: the amout to move in y direction
665  * @relative: if %TRUE move relative position
666  * @absolute: if %TRUE move absolute position
667  *
668  * Create a new instance of #AgsMoveNote task. Note either @relative or @absolute shall
669  * be %TRUE else it won't have any effect.
670  *
671  * Returns: a new #AgsMoveNote
672  *
673  * Since: 3.0.0
674  */
675 AgsMoveNote*
ags_move_note_new(AgsAudio * audio,AgsNotation * notation,GList * selection,guint first_x,guint first_y,gint move_x,gint move_y,gboolean relative,gboolean absolute)676 ags_move_note_new(AgsAudio *audio,
677 		  AgsNotation *notation,
678 		  GList *selection,
679 		  guint first_x, guint first_y,
680 		  gint move_x, gint move_y,
681 		  gboolean relative, gboolean absolute)
682 {
683   AgsMoveNote *move_note;
684 
685   move_note = (AgsMoveNote *) g_object_new(AGS_TYPE_MOVE_NOTE,
686 					   "audio", audio,
687 					   "notation", notation,
688 					   "first-x", first_x,
689 					   "first-y", first_y,
690 					   "move-x", move_x,
691 					   "move-y", move_y,
692 					   "relative", relative,
693 					   "absolute", absolute,
694 					   NULL);
695 
696   ags_move_note_set_selection(move_note,
697 			      selection);
698 
699   return(move_note);
700 }
701