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_navigation.h>
21 #include <ags/X/ags_navigation_callbacks.h>
22 
23 #include <ags/X/ags_ui_provider.h>
24 #include <ags/X/ags_window.h>
25 #include <ags/X/ags_notation_editor.h>
26 
27 #include <ags/i18n.h>
28 
29 void ags_navigation_class_init(AgsNavigationClass *navigation);
30 void ags_navigation_connectable_interface_init(AgsConnectableInterface *connectable);
31 void ags_navigation_init(AgsNavigation *navigation);
32 void ags_navigation_set_property(GObject *gobject,
33 				 guint prop_id,
34 				 const GValue *value,
35 				 GParamSpec *param_spec);
36 void ags_navigation_get_property(GObject *gobject,
37 				 guint prop_id,
38 				 GValue *value,
39 				 GParamSpec *param_spec);
40 void ags_navigation_finalize(GObject *gobject);
41 
42 void ags_navigation_connect(AgsConnectable *connectable);
43 void ags_navigation_disconnect(AgsConnectable *connectable);
44 
45 void ags_navigation_real_change_position(AgsNavigation *navigation,
46 					 gdouble tact);
47 
48 /**
49  * SECTION:ags_navigation
50  * @short_description: control audio object's playback.
51  * @title: AgsNavigation
52  * @section_id:
53  * @include: ags/X/ags_navigation.h
54  *
55  * #AgsNavigation is a composite widget to control playback of #AgsAudio objects.
56  * It can start #AgsMachine in bulk mode or position the stream.
57  */
58 
59 enum{
60   PROP_0,
61   PROP_SOUNDCARD,
62 };
63 
64 enum{
65   CHANGE_POSITION,
66   LAST_SIGNAL,
67 };
68 
69 static gpointer ags_navigation_parent_class = NULL;
70 static guint navigation_signals[LAST_SIGNAL];
71 
72 GType
ags_navigation_get_type(void)73 ags_navigation_get_type(void)
74 {
75   static volatile gsize g_define_type_id__volatile = 0;
76 
77   if(g_once_init_enter (&g_define_type_id__volatile)){
78     GType ags_type_navigation = 0;
79 
80     static const GTypeInfo ags_navigation_info = {
81       sizeof (AgsNavigationClass),
82       NULL, /* base_init */
83       NULL, /* base_finalize */
84       (GClassInitFunc) ags_navigation_class_init,
85       NULL, /* class_finalize */
86       NULL, /* class_data */
87       sizeof (AgsNavigation),
88       0,    /* n_preallocs */
89       (GInstanceInitFunc) ags_navigation_init,
90     };
91 
92     static const GInterfaceInfo ags_connectable_interface_info = {
93       (GInterfaceInitFunc) ags_navigation_connectable_interface_init,
94       NULL, /* interface_finalize */
95       NULL, /* interface_data */
96     };
97 
98     ags_type_navigation = g_type_register_static(GTK_TYPE_BOX,
99 						 "AgsNavigation", &ags_navigation_info,
100 						 0);
101 
102     g_type_add_interface_static(ags_type_navigation,
103 				AGS_TYPE_CONNECTABLE,
104 				&ags_connectable_interface_info);
105 
106     g_once_init_leave(&g_define_type_id__volatile, ags_type_navigation);
107   }
108 
109   return g_define_type_id__volatile;
110 }
111 
112 void
ags_navigation_class_init(AgsNavigationClass * navigation)113 ags_navigation_class_init(AgsNavigationClass *navigation)
114 {
115   GObjectClass *gobject;
116   GParamSpec *param_spec;
117 
118   ags_navigation_parent_class = g_type_class_peek_parent(navigation);
119 
120   /* GObjectClass */
121   gobject = (GObjectClass *) navigation;
122 
123   gobject->set_property = ags_navigation_set_property;
124   gobject->get_property = ags_navigation_get_property;
125 
126   gobject->finalize = ags_navigation_finalize;
127 
128   /* properties */
129   /**
130    * AgsNavigation:soundcard:
131    *
132    * The assigned #AgsSoundcard to use as default sink.
133    *
134    * Since: 3.0.0
135    */
136   param_spec = g_param_spec_object("soundcard",
137 				   i18n_pspec("assigned soundcard"),
138 				   i18n_pspec("The soundcard it is assigned with"),
139 				   G_TYPE_OBJECT,
140 				   G_PARAM_READABLE | G_PARAM_WRITABLE);
141   g_object_class_install_property(gobject,
142 				  PROP_SOUNDCARD,
143 				  param_spec);
144 
145   /* AgsNavigationClass */
146   navigation->change_position = ags_navigation_real_change_position;
147 
148   /* signals */
149   /**
150    * AgsNavigation::change-position:
151    * @navigation: the #AgsNavigation
152    * @tact: the new position
153    *
154    * The ::change-position seeks the stream.
155    *
156    * Since: 3.0.0
157    */
158   navigation_signals[CHANGE_POSITION] =
159     g_signal_new("change-position",
160 		 G_TYPE_FROM_CLASS (navigation),
161 		 G_SIGNAL_RUN_LAST,
162 		 G_STRUCT_OFFSET (AgsNavigationClass, change_position),
163 		 NULL, NULL,
164 		 g_cclosure_marshal_VOID__DOUBLE,
165 		 G_TYPE_NONE, 1,
166 		 G_TYPE_DOUBLE);
167 }
168 
169 void
ags_navigation_connectable_interface_init(AgsConnectableInterface * connectable)170 ags_navigation_connectable_interface_init(AgsConnectableInterface *connectable)
171 {
172   connectable->is_ready = NULL;
173   connectable->is_connected = NULL;
174   connectable->connect = ags_navigation_connect;
175   connectable->disconnect = ags_navigation_disconnect;
176 }
177 
178 void
ags_navigation_init(AgsNavigation * navigation)179 ags_navigation_init(AgsNavigation *navigation)
180 {
181   GtkBox *hbox;
182   GtkLabel *label;
183 
184   gtk_orientable_set_orientation(GTK_ORIENTABLE(navigation),
185 				 GTK_ORIENTATION_VERTICAL);
186 
187   navigation->flags = AGS_NAVIGATION_BLOCK_TIC;
188 
189   navigation->soundcard = NULL;
190 
191   navigation->start_tact = 0.0;
192   navigation->note_offset = 0.0;
193 
194   /* GtkWidget */
195   hbox = (GtkBox *) gtk_box_new(GTK_ORIENTATION_HORIZONTAL,
196 				0);
197   gtk_box_pack_start((GtkBox *) navigation,
198 		     (GtkWidget *) hbox,
199 		     FALSE, FALSE,
200 		     0);
201 
202   navigation->expander_image = gtk_image_new_from_icon_name("down",
203 							    GTK_ICON_SIZE_LARGE_TOOLBAR);
204 
205   navigation->expander = (GtkToggleButton *) gtk_toggle_button_new();
206   gtk_widget_set_name((GtkWidget *) navigation->expander,
207 		      "ags-navigation-expander");
208   gtk_box_pack_start((GtkBox*) hbox, (GtkWidget *) navigation->expander,
209 		     FALSE, FALSE,
210 		     0);
211   gtk_container_add((GtkContainer *) navigation->expander,
212 		    (GtkWidget *) navigation->expander_image);
213 
214   label = (GtkLabel *) gtk_label_new(i18n("bpm"));
215   gtk_box_pack_start((GtkBox*) hbox,
216 		     (GtkWidget *) label,
217 		     FALSE, FALSE,
218 		     0);
219 
220   navigation->bpm = (GtkSpinButton *) gtk_spin_button_new_with_range(1.0, 1000.0, 1.0);
221   gtk_spin_button_set_value(navigation->bpm, 120.0);
222   gtk_box_pack_start((GtkBox*) hbox,
223 		     (GtkWidget *) navigation->bpm,
224 		     FALSE, FALSE,
225 		     0);
226 
227   navigation->current_bpm = 120.0;
228 
229   navigation->rewind = (GtkToggleButton *) g_object_new(GTK_TYPE_BUTTON,
230 							"image", (GtkWidget *) gtk_image_new_from_icon_name("media-skip-backward",
231 													    GTK_ICON_SIZE_LARGE_TOOLBAR),
232 							NULL);
233   gtk_box_pack_start((GtkBox *) hbox,
234 		     (GtkWidget *) navigation->rewind,
235 		     FALSE, FALSE,
236 		     0);
237 
238   navigation->previous = (GtkButton *) g_object_new(GTK_TYPE_BUTTON,
239 						    "image", (GtkWidget *) gtk_image_new_from_icon_name("media-seek-backward",
240 													GTK_ICON_SIZE_LARGE_TOOLBAR),
241 						    NULL);
242   gtk_box_pack_start((GtkBox *) hbox,
243 		     (GtkWidget *) navigation->previous,
244 		     FALSE, FALSE,
245 		     0);
246 
247   navigation->play = (GtkToggleButton *) g_object_new(GTK_TYPE_TOGGLE_BUTTON,
248 						      "image", (GtkWidget *) gtk_image_new_from_icon_name("media-playback-start",
249 													  GTK_ICON_SIZE_LARGE_TOOLBAR),
250 						      NULL);
251   gtk_box_pack_start((GtkBox *) hbox,
252 		     (GtkWidget *) navigation->play,
253 		     FALSE, FALSE,
254 		     0);
255 
256   navigation->stop = (GtkButton *) g_object_new(GTK_TYPE_BUTTON,
257 						"image", (GtkWidget *) gtk_image_new_from_icon_name("media-playback-stop",
258 												    GTK_ICON_SIZE_LARGE_TOOLBAR),
259 						NULL);
260   gtk_box_pack_start((GtkBox *) hbox,
261 		     (GtkWidget *) navigation->stop,
262 		     FALSE, FALSE,
263 		     0);
264 
265   navigation->next = (GtkButton *) g_object_new(GTK_TYPE_BUTTON,
266 						"image", (GtkWidget *) gtk_image_new_from_icon_name("media-seek-forward",
267 												    GTK_ICON_SIZE_LARGE_TOOLBAR),
268 						NULL);
269   gtk_box_pack_start((GtkBox *) hbox,
270 		     (GtkWidget *) navigation->next,
271 		     FALSE, FALSE,
272 		     0);
273 
274   navigation->forward = (GtkToggleButton *) g_object_new(GTK_TYPE_BUTTON,
275 							 "image", (GtkWidget *) gtk_image_new_from_icon_name("media-skip-forward",
276 													     GTK_ICON_SIZE_LARGE_TOOLBAR),
277 							 NULL);
278   gtk_box_pack_start((GtkBox *) hbox,
279 		     (GtkWidget *) navigation->forward,
280 		     FALSE, FALSE,
281 		     0);
282 
283   navigation->loop = (GtkCheckButton *) gtk_check_button_new_with_label(i18n("loop"));
284   gtk_box_pack_start((GtkBox *) hbox,
285 		     (GtkWidget *) navigation->loop,
286 		     FALSE, FALSE,
287 		     0);
288 
289   label = (GtkLabel *) gtk_label_new("position");
290   gtk_box_pack_start((GtkBox *) hbox,
291 		     (GtkWidget *) label,
292 		     FALSE, FALSE,
293 		     0);
294 
295   navigation->position_time = (GtkLabel *) gtk_label_new("00:00.000");
296   gtk_box_pack_start((GtkBox *) hbox,
297 		     (GtkWidget *) navigation->position_time,
298 		     FALSE, FALSE,
299 		     0);
300 
301   navigation->position_tact = (GtkSpinButton *) gtk_spin_button_new_with_range(0.0, AGS_NOTATION_EDITOR_MAX_CONTROLS, 1.0);
302   gtk_box_pack_start((GtkBox *) hbox,
303 		     (GtkWidget *) navigation->position_tact,
304 		     FALSE, FALSE,
305 		     2);
306 
307 
308   label = (GtkLabel *) gtk_label_new("duration");
309   gtk_box_pack_start((GtkBox *) hbox,
310 		     (GtkWidget *) label,
311 		     FALSE, FALSE,
312 		     0);
313 
314   navigation->duration_time = (GtkLabel *) gtk_label_new(NULL);
315   g_object_set(navigation->duration_time,
316 	       "label", "0000:00.000",
317 	       NULL);
318   gtk_box_pack_start((GtkBox *) hbox, (GtkWidget *) navigation->duration_time, FALSE, FALSE, 2);
319   g_timeout_add((guint) floor(AGS_UI_PROVIDER_DEFAULT_TIMEOUT * 1000.0), (GSourceFunc) ags_navigation_duration_time_queue_draw, (gpointer) navigation);
320 
321   navigation->duration_tact = NULL;
322   //  navigation->duration_tact = (GtkSpinButton *) gtk_spin_button_new_with_range(0.0, AGS_NOTATION_EDITOR_MAX_CONTROLS, 1.0);
323   //  gtk_box_pack_start((GtkBox *) hbox, (GtkWidget *) navigation->duration_tact, FALSE, FALSE, 2);
324 
325 
326   /* expansion */
327   hbox = (GtkBox *) gtk_box_new(GTK_ORIENTATION_HORIZONTAL,
328 				0);
329   //  GTK_WIDGET_SET_FLAGS((GtkWidget *) hbox, GTK_NO_SHOW_ALL);
330   gtk_box_pack_start((GtkBox *) navigation,
331 		     (GtkWidget *) hbox,
332 		     FALSE, FALSE,
333 		     0);
334 
335   label = (GtkLabel *) gtk_label_new(i18n("loop L"));
336   gtk_box_pack_start((GtkBox *) hbox,
337 		     (GtkWidget *) label,
338 		     FALSE, FALSE,
339 		     0);
340 
341   navigation->loop_left_tact = (GtkSpinButton *) gtk_spin_button_new_with_range(0.0, 65000.0, 1.0);
342   gtk_box_pack_start((GtkBox *) hbox,
343 		     (GtkWidget *) navigation->loop_left_tact,
344 		     FALSE, FALSE,
345 		     0);
346 
347   label = (GtkLabel *) gtk_label_new(i18n("loop R"));
348   gtk_box_pack_start((GtkBox *) hbox,
349 		     (GtkWidget *) label,
350 		     FALSE, FALSE,
351 		     0);
352 
353   navigation->loop_right_tact = (GtkSpinButton *) gtk_spin_button_new_with_range(0.0, 65000.0, 1.0);
354   gtk_spin_button_set_value(navigation->loop_right_tact,
355 			    4.0);
356   gtk_box_pack_start((GtkBox *) hbox,
357 		     (GtkWidget *) navigation->loop_right_tact,
358 		     FALSE, FALSE,
359 		     0);
360 
361   navigation->scroll = NULL;
362   /*
363   navigation->scroll = (GtkCheckButton *) gtk_check_button_new_with_label(i18n("auto-scroll"));
364   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(navigation->scroll),
365 			       FALSE);
366   gtk_box_pack_start((GtkBox *) hbox, (GtkWidget *) navigation->scroll, FALSE, FALSE, 2);
367   */
368 
369   navigation->exclude_sequencer = (GtkCheckButton *) gtk_check_button_new_with_label(i18n("exclude sequencers"));
370   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(navigation->exclude_sequencer),
371 			       TRUE);
372   gtk_box_pack_start((GtkBox *) hbox,
373 		     (GtkWidget *) navigation->exclude_sequencer,
374 		     FALSE, FALSE,
375 		     0);
376 
377 }
378 
379 void
ags_navigation_set_property(GObject * gobject,guint prop_id,const GValue * value,GParamSpec * param_spec)380 ags_navigation_set_property(GObject *gobject,
381 			    guint prop_id,
382 			    const GValue *value,
383 			    GParamSpec *param_spec)
384 {
385   AgsNavigation *navigation;
386 
387   navigation = AGS_NAVIGATION(gobject);
388 
389   switch(prop_id){
390   case PROP_SOUNDCARD:
391     {
392       GObject *soundcard;
393 
394       soundcard = g_value_get_object(value);
395 
396       if(navigation->soundcard == soundcard){
397 	return;
398       }
399 
400       if(navigation->soundcard != NULL){
401 	g_object_unref(navigation->soundcard);
402       }
403 
404       if(soundcard != NULL){
405 	//FIXME:JK: no direct callback
406 	g_signal_connect_after(soundcard, "stop",
407 			       G_CALLBACK(ags_navigation_soundcard_stop_callback), (gpointer) navigation);
408 
409 	g_object_ref(soundcard);
410       }
411 
412       navigation->soundcard = soundcard;
413     }
414     break;
415   default:
416     G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, param_spec);
417     break;
418   }
419 }
420 
421 void
ags_navigation_get_property(GObject * gobject,guint prop_id,GValue * value,GParamSpec * param_spec)422 ags_navigation_get_property(GObject *gobject,
423 			    guint prop_id,
424 			    GValue *value,
425 			    GParamSpec *param_spec)
426 {
427   AgsNavigation *navigation;
428 
429   navigation = AGS_NAVIGATION(gobject);
430 
431   switch(prop_id){
432   case PROP_SOUNDCARD:
433     g_value_set_object(value, navigation->soundcard);
434     break;
435   default:
436     G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, param_spec);
437     break;
438   }
439 }
440 
441 void
ags_navigation_finalize(GObject * gobject)442 ags_navigation_finalize(GObject *gobject)
443 {
444   /* call parent */
445   G_OBJECT_CLASS(ags_navigation_parent_class)->finalize(gobject);
446 }
447 
448 void
ags_navigation_connect(AgsConnectable * connectable)449 ags_navigation_connect(AgsConnectable *connectable)
450 {
451   AgsNavigation *navigation;
452 
453   navigation = AGS_NAVIGATION(connectable);
454 
455   if((AGS_NAVIGATION_CONNECTED & (navigation->flags)) != 0){
456     return;
457   }
458 
459   navigation->flags |= AGS_NAVIGATION_CONNECTED;
460 
461   g_signal_connect((GObject *) navigation->expander, "clicked",
462 		   G_CALLBACK(ags_navigation_expander_callback), (gpointer) navigation);
463 
464   g_signal_connect_after((GObject *) navigation->bpm, "value-changed",
465 			 G_CALLBACK(ags_navigation_bpm_callback), (gpointer) navigation);
466 
467   g_signal_connect((GObject *) navigation->rewind, "clicked",
468 		   G_CALLBACK(ags_navigation_rewind_callback), (gpointer) navigation);
469 
470   g_signal_connect((GObject *) navigation->previous, "clicked",
471 		   G_CALLBACK(ags_navigation_prev_callback), (gpointer) navigation);
472 
473   g_signal_connect((GObject *) navigation->play, "clicked",
474 		   G_CALLBACK(ags_navigation_play_callback), (gpointer) navigation);
475 
476   g_signal_connect((GObject *) navigation->stop, "clicked",
477 		   G_CALLBACK(ags_navigation_stop_callback), (gpointer) navigation);
478 
479   g_signal_connect((GObject *) navigation->next, "clicked",
480 		   G_CALLBACK(ags_navigation_next_callback), (gpointer) navigation);
481 
482   g_signal_connect((GObject *) navigation->forward, "clicked",
483 		   G_CALLBACK(ags_navigation_forward_callback), (gpointer) navigation);
484 
485   g_signal_connect((GObject *) navigation->loop, "clicked",
486 		   G_CALLBACK(ags_navigation_loop_callback), (gpointer) navigation);
487 
488   g_signal_connect_after((GObject *) navigation->position_tact, "value-changed",
489 			 G_CALLBACK(ags_navigation_position_tact_callback), (gpointer) navigation);
490 
491   //  g_signal_connect((GObject *) navigation->duration_tact, "value-changed",
492   //		   G_CALLBACK(ags_navigation_duration_tact_callback), (gpointer) navigation);
493 
494   /* soundcard */
495   //  g_signal_connect_after((GObject *) navigation->soundcard, "tic",
496   //			 G_CALLBACK(ags_navigation_tic_callback), (gpointer) navigation);
497 
498   if(navigation->soundcard != NULL){
499     g_signal_connect_after(navigation->soundcard, "stop",
500 			   G_CALLBACK(ags_navigation_soundcard_stop_callback), (gpointer) navigation);
501   }
502 
503   /* expansion */
504   g_signal_connect((GObject *) navigation->loop_left_tact, "value-changed",
505 		   G_CALLBACK(ags_navigation_loop_left_tact_callback), (gpointer) navigation);
506 
507   g_signal_connect((GObject *) navigation->loop_right_tact, "value-changed",
508 		   G_CALLBACK(ags_navigation_loop_right_tact_callback), (gpointer) navigation);
509 }
510 
511 void
ags_navigation_disconnect(AgsConnectable * connectable)512 ags_navigation_disconnect(AgsConnectable *connectable)
513 {
514   AgsNavigation *navigation;
515 
516   navigation = AGS_NAVIGATION(connectable);
517 
518   if((AGS_NAVIGATION_CONNECTED & (navigation->flags)) == 0){
519     return;
520   }
521 
522   navigation->flags &= (~AGS_NAVIGATION_CONNECTED);
523 
524   g_object_disconnect((GObject *) navigation->expander,
525 		      "clicked",
526 		      G_CALLBACK(ags_navigation_expander_callback),
527 		      (gpointer) navigation,
528 		      NULL);
529 
530   g_object_disconnect((GObject *) navigation->bpm,
531 		      "value-changed",
532 		      G_CALLBACK(ags_navigation_bpm_callback),
533 		      (gpointer) navigation,
534 		      NULL);
535 
536   g_object_disconnect((GObject *) navigation->rewind,
537 		      "clicked",
538 		      G_CALLBACK(ags_navigation_rewind_callback),
539 		      (gpointer) navigation,
540 		      NULL);
541 
542   g_object_disconnect((GObject *) navigation->previous,
543 		      "clicked",
544 		      G_CALLBACK(ags_navigation_prev_callback),
545 		      (gpointer) navigation,
546 		      NULL);
547 
548   g_object_disconnect((GObject *) navigation->play,
549 		      "clicked",
550 		      G_CALLBACK(ags_navigation_play_callback),
551 		      (gpointer) navigation,
552 		      NULL);
553 
554   g_object_disconnect((GObject *) navigation->stop,
555 		      "clicked",
556 		      G_CALLBACK(ags_navigation_stop_callback),
557 		      (gpointer) navigation,
558 		      NULL);
559 
560   g_object_disconnect((GObject *) navigation->next,
561 		      "clicked",
562 		      G_CALLBACK(ags_navigation_next_callback),
563 		      (gpointer) navigation,
564 		      NULL);
565 
566   g_object_disconnect((GObject *) navigation->forward,
567 		      "clicked",
568 		      G_CALLBACK(ags_navigation_forward_callback),
569 		      (gpointer) navigation,
570 		      NULL);
571 
572   g_object_disconnect((GObject *) navigation->loop,
573 		      "clicked",
574 		      G_CALLBACK(ags_navigation_loop_callback),
575 		      (gpointer) navigation,
576 		      NULL);
577 
578   g_object_disconnect((GObject *) navigation->position_tact,
579 		      "value-changed",
580 		      G_CALLBACK(ags_navigation_position_tact_callback),
581 		      (gpointer) navigation,
582 		      NULL);
583 
584   if(navigation->soundcard != NULL){
585     g_object_disconnect(navigation->soundcard,
586 			"stop",
587 			G_CALLBACK(ags_navigation_soundcard_stop_callback),
588 			(gpointer) navigation,
589 			NULL);
590   }
591 
592   /* expansion */
593   g_object_disconnect((GObject *) navigation->loop_left_tact,
594 		      "value-changed",
595 		      G_CALLBACK(ags_navigation_loop_left_tact_callback),
596 		      (gpointer) navigation,
597 		      NULL);
598 
599   g_object_disconnect((GObject *) navigation->loop_right_tact,
600 		      "value-changed",
601 		      G_CALLBACK(ags_navigation_loop_right_tact_callback),
602 		      (gpointer) navigation,
603 		      NULL);
604 }
605 
606 void
ags_navigation_real_change_position(AgsNavigation * navigation,gdouble tact_counter)607 ags_navigation_real_change_position(AgsNavigation *navigation,
608 				    gdouble tact_counter)
609 {
610   AgsSeekSoundcard *seek_soundcard;
611 
612   AgsApplicationContext *application_context;
613 
614   GObject *default_soundcard;
615 
616   GList *start_list, *list;
617 
618   gchar *timestr;
619   gdouble delay;
620   gdouble delay_factor;
621   gint64 new_offset;
622 
623   application_context = ags_application_context_get_instance();
624 
625   default_soundcard = ags_sound_provider_get_default_soundcard(AGS_SOUND_PROVIDER(application_context));
626 
627   /* seek soundcard */
628   delay = ags_soundcard_get_delay(AGS_SOUNDCARD(default_soundcard));
629   delay_factor = ags_soundcard_get_delay_factor(AGS_SOUNDCARD(default_soundcard));
630 
631   new_offset = (16 * tact_counter);
632 
633   seek_soundcard = ags_seek_soundcard_new(default_soundcard,
634 					  new_offset,
635 					  AGS_SEEK_SET);
636 
637   ags_ui_provider_schedule_task(AGS_UI_PROVIDER(application_context),
638 				(AgsTask *) seek_soundcard);
639 
640   /* soundcard - start offset */
641   list =
642     start_list = ags_sound_provider_get_soundcard(AGS_SOUND_PROVIDER(application_context));
643 
644   while(list != NULL){
645     ags_soundcard_set_start_note_offset(AGS_SOUNDCARD(list->data),
646 					new_offset);
647 
648     list = list->next;
649   }
650 
651   g_list_free_full(start_list,
652 		   g_object_unref);
653 
654   /* sequencer - start offset */
655   list =
656     start_list = ags_sound_provider_get_sequencer(AGS_SOUND_PROVIDER(application_context));
657 
658   while(list != NULL){
659     ags_sequencer_set_start_note_offset(AGS_SEQUENCER(list->data),
660 					new_offset);
661 
662     list = list->next;
663   }
664 
665   g_list_free_full(start_list,
666 		   g_object_unref);
667 
668   //TODO:JK: implement me
669 
670   /* update */
671   timestr = ags_time_get_uptime_from_offset(16.0 * tact_counter,
672 					    gtk_spin_button_get_value(navigation->bpm),
673 					    delay,
674 					    delay_factor);
675   gtk_label_set_text(navigation->position_time, timestr);
676 
677   g_free(timestr);
678 }
679 
680 /**
681  * ags_navigation_change_position:
682  * @navigation: the #AgsNavigation
683  * @tact: the new position
684  *
685  * Change tact position of editor. The scrollbar is adjustet
686  * and its playback position seeked.
687  *
688  * Since: 3.0.0
689  */
690 void
ags_navigation_change_position(AgsNavigation * navigation,gdouble tact)691 ags_navigation_change_position(AgsNavigation *navigation,
692 			       gdouble tact)
693 {
694   g_return_if_fail(AGS_IS_NAVIGATION(navigation));
695 
696   g_object_ref(G_OBJECT(navigation));
697   g_signal_emit(G_OBJECT(navigation),
698 		navigation_signals[CHANGE_POSITION], 0,
699 		tact);
700   g_object_unref(G_OBJECT(navigation));
701 }
702 
703 /**
704  * ags_navigation_tact_to_time_string:
705  * @tact: the new position
706  * @bpm: the BPM
707  * @delay_factor: the delay factor
708  *
709  * Convert tact unit to time.
710  *
711  * Returns: tact as time string
712  *
713  * Since: 3.0.0
714  */
715 gchar*
ags_navigation_tact_to_time_string(gdouble tact,gdouble bpm,gdouble delay_factor)716 ags_navigation_tact_to_time_string(gdouble tact,
717 				   gdouble bpm,
718 				   gdouble delay_factor)
719 {
720   gdouble delay_min, delay_sec, delay_msec;
721   gchar *timestr;
722   gdouble tact_redux;
723   guint min, sec, msec;
724 
725   delay_min = bpm / delay_factor;
726   delay_sec = delay_min / 60.0;
727   delay_msec = delay_sec / 1000.0;
728 
729   tact_redux = (tact + (tact / 16.0)) * 16.0;
730 
731   min = (guint) floor(tact_redux / delay_min);
732 
733   if(min > 0){
734     tact_redux = tact_redux - (min * delay_min);
735   }
736 
737   sec = (guint) floor(tact_redux / delay_sec);
738 
739   if(sec > 0){
740     tact_redux = tact_redux - (sec * delay_sec);
741   }
742 
743   msec = (guint) floor(tact_redux / delay_msec);
744 
745   timestr = g_strdup_printf("%.4d:%.2d.%.3d", min, sec, msec);
746 
747   return(timestr);
748 }
749 
750 /**
751  * ags_navigation_update_time_string:
752  * @tact: the new position
753  * @bpm: the BPM
754  * @delay_factor: the delay factor
755  * @time_string: the pointer location to set
756  *
757  * Updates time as string.
758  *
759  * Since: 3.0.0
760  */
761 void
ags_navigation_update_time_string(double tact,gdouble bpm,gdouble delay_factor,gchar * time_string)762 ags_navigation_update_time_string(double tact,
763 				  gdouble bpm,
764 				  gdouble delay_factor,
765 				  gchar *time_string)
766 {
767   gdouble delay_min, delay_sec, delay_msec;
768   gdouble tact_redux;
769   guint min, sec, msec;
770 
771   delay_min = bpm * (60.0 / bpm) * (60.0 / bpm) * delay_factor;
772   delay_sec = delay_min / 60.0;
773   delay_msec = delay_sec / 1000.0;
774 
775   tact_redux = 1.0 / 16.0;
776 
777   min = (guint) floor(tact_redux / delay_min);
778 
779   if(min > 0){
780     tact_redux = tact_redux - (min * delay_min);
781   }
782 
783   sec = (guint) floor(tact_redux / delay_sec);
784 
785   if(sec > 0){
786     tact_redux = tact_redux - (sec * delay_sec);
787   }
788 
789   msec = (guint) floor(tact_redux / delay_msec);
790 
791   sprintf(time_string, "%.4d:%.2d.%.3d", min, sec, msec);
792 }
793 
794 gchar*
ags_navigation_relative_tact_to_time_string(gchar * timestr,gdouble delay,gdouble bpm,gdouble delay_factor)795 ags_navigation_relative_tact_to_time_string(gchar *timestr,
796 					    gdouble delay,
797 					    gdouble bpm,
798 					    gdouble delay_factor)
799 {
800   guint min, sec, msec;
801   guint prev_min, prev_sec, prev_msec;
802 
803   gdouble sec_value;
804 
805   sscanf(timestr, "%d:%d.%d", &prev_min, &prev_sec, &prev_msec);
806   sec_value = prev_min * 60.0;
807   sec_value += prev_sec;
808   sec_value += (1.0 / (16.0 * delay_factor) * (60.0 / bpm) + (1.0 / delay)) / 2.0;
809 
810   if(prev_msec != 0){
811     sec_value += (prev_msec / 1000.0);
812   }
813 
814   //  sec_value += (1.0 / delay);
815 
816   min = (guint) floor(sec_value / 60.0);
817 
818   sec = sec_value - 60 * min;
819 
820   msec = (sec_value - sec - min * 60) * 1000;
821 
822   timestr = g_strdup_printf("%.4d:%.2d.%.3d", min, sec, msec);
823 
824   return(timestr);
825 }
826 
827 gchar*
ags_navigation_absolute_tact_to_time_string(gdouble tact,gdouble bpm,gdouble delay_factor)828 ags_navigation_absolute_tact_to_time_string(gdouble tact,
829 					    gdouble bpm,
830 					    gdouble delay_factor)
831 {
832   gchar *timestr;
833 
834   gdouble delay_min, delay_sec, delay_msec;
835   gdouble tact_redux;
836   guint min, sec, msec;
837 
838   /* calculate delays */
839   delay_sec = ((bpm / delay_factor) / 60.0);
840   delay_min = delay_sec * 60.0;
841   delay_msec = delay_sec / 1000.0;
842 
843   /* translate to time string */
844   tact_redux = tact;
845 
846   min = (guint) floor(tact_redux / delay_min);
847 
848   if(min > 0){
849     tact_redux = tact_redux - (min * delay_min);
850   }
851 
852   sec = (guint) floor(tact_redux / delay_sec);
853 
854   if(sec > 0){
855     tact_redux = tact_redux - (sec * delay_sec);
856   }
857 
858   msec = (guint) floor(tact_redux / delay_msec);
859 
860   timestr = g_strdup_printf("%.4d:%.2d.%.3d", min, sec, msec);
861 
862   return(timestr);
863 }
864 
865 /**
866  * ags_navigation_set_seeking_sensitive_new:
867  * @navigation: the #AgsNavigation
868  * @enabled: if %TRUE then sensitive, otherwise insensitive.
869  *
870  * Enables/Disables the #AgsNavigation to control the tree.
871  *
872  * Since: 3.0.0
873  */
874 void
ags_navigation_set_seeking_sensitive(AgsNavigation * navigation,gboolean enabled)875 ags_navigation_set_seeking_sensitive(AgsNavigation *navigation,
876 				     gboolean enabled)
877 {
878   gtk_widget_set_sensitive((GtkWidget *) navigation->rewind,
879 			   enabled);
880   gtk_widget_set_sensitive((GtkWidget *) navigation->previous,
881 			   enabled);
882   gtk_widget_set_sensitive((GtkWidget *) navigation->play,
883 			   enabled);
884   gtk_widget_set_sensitive((GtkWidget *) navigation->stop,
885 			   enabled);
886   gtk_widget_set_sensitive((GtkWidget *) navigation->next,
887 			   enabled);
888   gtk_widget_set_sensitive((GtkWidget *) navigation->forward,
889 			   enabled);
890 }
891 
892 gboolean
ags_navigation_duration_time_queue_draw(GtkWidget * widget)893 ags_navigation_duration_time_queue_draw(GtkWidget *widget)
894 {
895   AgsNavigation *navigation;
896 
897   AgsApplicationContext *application_context;
898 
899   GObject *default_soundcard;
900 
901   gchar *str;
902 
903   navigation = AGS_NAVIGATION(widget);
904 
905   application_context = ags_application_context_get_instance();
906 
907   default_soundcard = ags_sound_provider_get_default_soundcard(AGS_SOUND_PROVIDER(application_context));
908 
909   if(default_soundcard != NULL){
910     str = ags_soundcard_get_uptime(AGS_SOUNDCARD(default_soundcard));
911 
912     g_object_set(navigation->duration_time,
913 		 "label", str,
914 		 NULL);
915     g_free(str);
916 
917     gtk_widget_queue_draw((GtkWidget *) navigation->duration_time);
918   }
919 
920   return(TRUE);
921 }
922 
923 /**
924  * ags_navigation_new:
925  *
926  * Create a new instance of #AgsNavigation to control the tree.
927  *
928  * Returns: the new #AgsNavigation
929  *
930  * Since: 3.0.0
931  */
932 AgsNavigation*
ags_navigation_new()933 ags_navigation_new()
934 {
935   AgsNavigation *navigation;
936 
937   navigation = (AgsNavigation *) g_object_new(AGS_TYPE_NAVIGATION, NULL);
938 
939   return(navigation);
940 }
941