1 /* GSequencer - Advanced GTK Sequencer
2  * Copyright (C) 2005-2021 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/machine/ags_spectrometer.h>
21 #include <ags/X/machine/ags_spectrometer_callbacks.h>
22 
23 #include <ags/X/ags_ui_provider.h>
24 
25 void ags_spectrometer_class_init(AgsSpectrometerClass *spectrometer);
26 void ags_spectrometer_connectable_interface_init(AgsConnectableInterface *connectable);
27 void ags_spectrometer_init(AgsSpectrometer *spectrometer);
28 void ags_spectrometer_finalize(GObject *gobject);
29 
30 void ags_spectrometer_map_recall(AgsMachine *machine);
31 
32 void ags_spectrometer_connect(AgsConnectable *connectable);
33 void ags_spectrometer_disconnect(AgsConnectable *connectable);
34 
35 void ags_spectrometer_resize_audio_channels_callback(AgsMachine *machine,
36 						     guint audio_channels, guint audio_channels_old,
37 						     gpointer data);
38 void ags_spectrometer_resize_pads_callback(AgsMachine *machine,
39 					   GType channel_type,
40 					   guint pads, guint pads_old,
41 					   gpointer data);
42 
43 void ags_spectrometer_buffer_size_changed_callback(AgsMachine *machine,
44 						   guint buffer_size, guint old_buffer_size,
45 						   gpointer data);
46 
47 gdouble ags_spectrometer_x_small_scale_func(gdouble value,
48 					    gpointer data);
49 gdouble ags_spectrometer_x_big_scale_func(gdouble value,
50 					  gpointer data);
51 
52 gchar* ags_spectrometer_x_label_func(gdouble value,
53 				     gpointer data);
54 gchar* ags_spectrometer_y_label_func(gdouble value,
55 				     gpointer data);
56 
57 /**
58  * SECTION:ags_spectrometer
59  * @short_description: spectrometer sequencer
60  * @title: AgsSpectrometer
61  * @section_id:
62  * @include: ags/X/machine/ags_spectrometer.h
63  *
64  * The #AgsSpectrometer is a composite widget to act as spectrometer sequencer.
65  */
66 
67 static gpointer ags_spectrometer_parent_class = NULL;
68 
69 static AgsConnectableInterface *ags_spectrometer_parent_connectable_interface;
70 
71 GHashTable *ags_spectrometer_cartesian_queue_draw = NULL;
72 
73 GType
ags_spectrometer_get_type(void)74 ags_spectrometer_get_type(void)
75 {
76   static volatile gsize g_define_type_id__volatile = 0;
77 
78   if(g_once_init_enter (&g_define_type_id__volatile)){
79     GType ags_type_spectrometer = 0;
80 
81     static const GTypeInfo ags_spectrometer_info = {
82       sizeof(AgsSpectrometerClass),
83       NULL, /* base_init */
84       NULL, /* base_finalize */
85       (GClassInitFunc) ags_spectrometer_class_init,
86       NULL, /* class_finalize */
87       NULL, /* class_data */
88       sizeof(AgsSpectrometer),
89       0,    /* n_preallocs */
90       (GInstanceInitFunc) ags_spectrometer_init,
91     };
92 
93     static const GInterfaceInfo ags_connectable_interface_info = {
94       (GInterfaceInitFunc) ags_spectrometer_connectable_interface_init,
95       NULL, /* interface_finalize */
96       NULL, /* interface_data */
97     };
98 
99     ags_type_spectrometer = g_type_register_static(AGS_TYPE_MACHINE,
100 						   "AgsSpectrometer", &ags_spectrometer_info,
101 						   0);
102 
103     g_type_add_interface_static(ags_type_spectrometer,
104 				AGS_TYPE_CONNECTABLE,
105 				&ags_connectable_interface_info);
106 
107     g_once_init_leave(&g_define_type_id__volatile, ags_type_spectrometer);
108   }
109 
110   return g_define_type_id__volatile;
111 }
112 
113 void
ags_spectrometer_class_init(AgsSpectrometerClass * spectrometer)114 ags_spectrometer_class_init(AgsSpectrometerClass *spectrometer)
115 {
116   GObjectClass *gobject;
117   AgsMachineClass *machine;
118 
119   ags_spectrometer_parent_class = g_type_class_peek_parent(spectrometer);
120 
121   /* GObjectClass */
122   gobject = (GObjectClass *) spectrometer;
123 
124   gobject->finalize = ags_spectrometer_finalize;
125 
126   /*  */
127   machine = (AgsMachineClass *) spectrometer;
128 
129   machine->map_recall = ags_spectrometer_map_recall;
130 }
131 
132 void
ags_spectrometer_connectable_interface_init(AgsConnectableInterface * connectable)133 ags_spectrometer_connectable_interface_init(AgsConnectableInterface *connectable)
134 {
135   ags_spectrometer_parent_connectable_interface = g_type_interface_peek_parent(connectable);
136 
137   connectable->connect = ags_spectrometer_connect;
138   connectable->disconnect = ags_spectrometer_disconnect;
139 }
140 
141 void
ags_spectrometer_init(AgsSpectrometer * spectrometer)142 ags_spectrometer_init(AgsSpectrometer *spectrometer)
143 {
144   GtkBox *vbox;
145   AgsCartesian *cartesian;
146 
147   AgsPlot *fg_plot;
148 
149   guint buffer_size;
150   gdouble width, height;
151   gdouble default_width, default_height;
152 
153   g_signal_connect_after((GObject *) spectrometer, "parent_set",
154 			 G_CALLBACK(ags_spectrometer_parent_set_callback), (gpointer) spectrometer);
155 
156   ags_audio_set_flags(AGS_MACHINE(spectrometer)->audio, (AGS_AUDIO_SYNC));
157   g_object_set(AGS_MACHINE(spectrometer)->audio,
158 	       "min-audio-channels", 1,
159 	       "min-output-pads", 1,
160 	       "min-input-pads", 1,
161 	       NULL);
162 
163   g_signal_connect_after(spectrometer, "resize-audio-channels",
164 			 G_CALLBACK(ags_spectrometer_resize_audio_channels_callback), NULL);
165 
166   g_signal_connect_after(spectrometer, "resize-pads",
167 			 G_CALLBACK(ags_spectrometer_resize_pads_callback), NULL);
168 
169   g_signal_connect_after(spectrometer, "buffer-size-changed",
170 			 G_CALLBACK(ags_spectrometer_buffer_size_changed_callback), NULL);
171 
172   if(ags_spectrometer_cartesian_queue_draw == NULL){
173     ags_spectrometer_cartesian_queue_draw = g_hash_table_new_full(g_direct_hash, g_direct_equal,
174 								  NULL,
175 								  NULL);
176   }
177 
178   spectrometer->name = NULL;
179   spectrometer->xml_type = "ags-spectrometer";
180 
181   spectrometer->mapped_output_pad = 0;
182   spectrometer->mapped_input_pad = 0;
183 
184   spectrometer->analyse_play_container = ags_recall_container_new();
185   spectrometer->analyse_recall_container = ags_recall_container_new();
186 
187   vbox = (GtkBox *) gtk_box_new(GTK_ORIENTATION_VERTICAL,
188 				0);
189   gtk_container_add((GtkContainer*) gtk_bin_get_child((GtkBin *) spectrometer),
190 		    (GtkWidget *) vbox);
191 
192   /* cartesian */
193   cartesian =
194     spectrometer->cartesian = ags_cartesian_new();
195 
196   cartesian->x_start = AGS_SPECTROMETER_DEFAULT_X_START;
197   cartesian->x_end = AGS_SPECTROMETER_DEFAULT_X_END;
198 
199   cartesian->y_start = AGS_SPECTROMETER_DEFAULT_Y_START;
200   cartesian->y_end = AGS_SPECTROMETER_DEFAULT_Y_END;
201 
202   cartesian->surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24,
203 						  cartesian->x_end - cartesian->x_start, cartesian->y_end - cartesian->y_start);
204 
205   cartesian->x_small_scale_func = ags_spectrometer_x_small_scale_func;
206   cartesian->x_big_scale_func = ags_spectrometer_x_big_scale_func;
207 
208   cartesian->x_label_func = ags_spectrometer_x_label_func;
209   cartesian->y_label_func = ags_spectrometer_y_label_func;
210 
211   /* label */
212   cartesian->x_label_precision = 1.0;
213 
214   ags_cartesian_reallocate_label(cartesian,
215 				 TRUE);
216   ags_cartesian_reallocate_label(cartesian,
217 				 FALSE);
218 
219   ags_cartesian_fill_label(cartesian,
220 			   TRUE);
221   ags_cartesian_fill_label(cartesian,
222 			   FALSE);
223 
224   width = cartesian->x_end - cartesian->x_start;
225   height = cartesian->y_end - cartesian->y_start;
226 
227   spectrometer->fg_plot = NULL;
228 
229   /* alloc fg plot */
230   fg_plot = ags_spectrometer_fg_plot_alloc(spectrometer,
231 					   0.125, 0.5, 1.0);
232   ags_cartesian_add_plot(cartesian,
233 			 fg_plot);
234 
235   spectrometer->fg_plot = g_list_prepend(spectrometer->fg_plot,
236 					 fg_plot);
237 
238   /* cartesian - size, pack and redraw */
239   gtk_widget_set_size_request((GtkWidget *) cartesian,
240 			      (gint) (width + 2.0 * cartesian->x_margin), (gint) (height + 2.0 * cartesian->y_margin));
241   gtk_box_pack_start(vbox,
242 		     (GtkWidget *) cartesian,
243 		     FALSE, FALSE,
244 		     0);
245 
246   gtk_widget_queue_draw((GtkWidget *) cartesian);
247 
248   /* buffer */
249   buffer_size = AGS_MACHINE(spectrometer)->buffer_size;
250 
251   spectrometer->magnitude_cache = (double *) g_malloc(buffer_size * sizeof(double));
252   ags_audio_buffer_util_clear_double(spectrometer->magnitude_cache, 1,
253 				     buffer_size);
254 
255   spectrometer->magnitude = (double *) g_malloc(buffer_size * sizeof(double));
256   ags_audio_buffer_util_clear_double(spectrometer->magnitude, 1,
257 				     buffer_size);
258 
259   /* queue draw */
260   g_hash_table_insert(ags_spectrometer_cartesian_queue_draw,
261 		      cartesian, ags_spectrometer_cartesian_queue_draw_timeout);
262   g_timeout_add(AGS_UI_PROVIDER_DEFAULT_TIMEOUT * 1000.0, (GSourceFunc) ags_spectrometer_cartesian_queue_draw_timeout, (gpointer) cartesian);
263 }
264 
265 void
ags_spectrometer_finalize(GObject * gobject)266 ags_spectrometer_finalize(GObject *gobject)
267 {
268   AgsSpectrometer *spectrometer;
269 
270   spectrometer = (AgsSpectrometer *) gobject;
271 
272   g_hash_table_remove(ags_spectrometer_cartesian_queue_draw,
273 		      spectrometer->cartesian);
274 
275   g_free(spectrometer->magnitude_cache);
276 
277   /* call parent */
278   G_OBJECT_CLASS(ags_spectrometer_parent_class)->finalize(gobject);
279 }
280 
281 void
ags_spectrometer_connect(AgsConnectable * connectable)282 ags_spectrometer_connect(AgsConnectable *connectable)
283 {
284   AgsSpectrometer *spectrometer;
285 
286   if((AGS_MACHINE_CONNECTED & (AGS_MACHINE(connectable)->flags)) != 0){
287     return;
288   }
289 
290   spectrometer = AGS_SPECTROMETER(connectable);
291 
292   /* call parent */
293   ags_spectrometer_parent_connectable_interface->connect(connectable);
294 }
295 
296 void
ags_spectrometer_disconnect(AgsConnectable * connectable)297 ags_spectrometer_disconnect(AgsConnectable *connectable)
298 {
299   AgsSpectrometer *spectrometer;
300 
301   if((AGS_MACHINE_CONNECTED & (AGS_MACHINE(connectable)->flags)) == 0){
302     return;
303   }
304 
305   spectrometer = AGS_SPECTROMETER(connectable);
306 
307   /* call parent */
308   ags_spectrometer_parent_connectable_interface->disconnect(connectable);
309 }
310 
311 void
ags_spectrometer_resize_audio_channels_callback(AgsMachine * machine,guint audio_channels,guint audio_channels_old,gpointer data)312 ags_spectrometer_resize_audio_channels_callback(AgsMachine *machine,
313 						guint audio_channels, guint audio_channels_old,
314 						gpointer data)
315 {
316   AgsSpectrometer *spectrometer;
317 
318   spectrometer = (AgsSpectrometer *) machine;
319 
320   if(audio_channels > audio_channels_old){
321     /* recall */
322     if((AGS_MACHINE_MAPPED_RECALL & (machine->flags)) != 0){
323       ags_spectrometer_input_map_recall(spectrometer,
324 					audio_channels_old,
325 					0);
326 
327       ags_spectrometer_output_map_recall(spectrometer,
328 					 audio_channels_old,
329 					 0);
330     }
331   }
332 }
333 
334 void
ags_spectrometer_resize_pads_callback(AgsMachine * machine,GType channel_type,guint pads,guint pads_old,gpointer data)335 ags_spectrometer_resize_pads_callback(AgsMachine *machine,
336 				      GType channel_type,
337 				      guint pads, guint pads_old,
338 				      gpointer data)
339 {
340   AgsSpectrometer *spectrometer;
341 
342   guint audio_channels;
343   gboolean grow;
344 
345   spectrometer = (AgsSpectrometer *) machine;
346 
347   /* get some fields */
348   audio_channels = machine->audio_channels;
349 
350   /* check available */
351   if(pads == pads_old ||
352      audio_channels == 0){
353     return;
354   }
355 
356   if(pads_old < pads){
357     grow = TRUE;
358   }else{
359     grow = FALSE;
360   }
361 
362   if(g_type_is_a(channel_type, AGS_TYPE_INPUT)){
363     if(grow){
364       /* recall */
365       if((AGS_MACHINE_MAPPED_RECALL & (machine->flags)) != 0){
366 	ags_spectrometer_input_map_recall(spectrometer,
367 					  0,
368 					  pads_old);
369       }
370     }else{
371       spectrometer->mapped_input_pad = pads;
372     }
373   }else{
374     if(grow){
375       /* recall */
376       if((AGS_MACHINE_MAPPED_RECALL & (machine->flags)) != 0){
377 	ags_spectrometer_output_map_recall(spectrometer,
378 					   0,
379 					   pads_old);
380       }
381     }else{
382       spectrometer->mapped_output_pad = pads;
383     }
384   }
385 }
386 
387 void
ags_spectrometer_buffer_size_changed_callback(AgsMachine * machine,guint buffer_size,guint old_buffer_size,gpointer data)388 ags_spectrometer_buffer_size_changed_callback(AgsMachine *machine,
389 					      guint buffer_size, guint old_buffer_size,
390 					      gpointer data)
391 {
392   AgsSpectrometer *spectrometer;
393 
394   if(buffer_size == old_buffer_size){
395     return;
396   }
397 
398   spectrometer = (AgsSpectrometer *) machine;
399 
400   if(buffer_size > 0){
401     if(spectrometer->magnitude_cache == NULL){
402       spectrometer->magnitude_cache = (double *) g_malloc(buffer_size * sizeof(double));
403       ags_audio_buffer_util_clear_double(spectrometer->magnitude_cache, 1,
404 					 buffer_size);
405 
406       spectrometer->magnitude = (double *) g_malloc(buffer_size * sizeof(double));
407       ags_audio_buffer_util_clear_double(spectrometer->magnitude, 1,
408 					 buffer_size);
409     }else{
410       spectrometer->magnitude_cache = (double *) g_realloc(spectrometer->magnitude_cache,
411 							   buffer_size * sizeof(double));
412       ags_audio_buffer_util_clear_double(spectrometer->magnitude_cache, 1,
413 					 buffer_size);
414 
415       spectrometer->magnitude = (double *) g_realloc(spectrometer->magnitude,
416 						     buffer_size * sizeof(double));
417       ags_audio_buffer_util_clear_double(spectrometer->magnitude, 1,
418 					 buffer_size);
419     }
420   }else{
421     g_free(spectrometer->magnitude_cache);
422     g_free(spectrometer->magnitude);
423 
424     spectrometer->magnitude_cache = NULL;
425     spectrometer->magnitude = NULL;
426   }
427 }
428 
429 void
ags_spectrometer_map_recall(AgsMachine * machine)430 ags_spectrometer_map_recall(AgsMachine *machine)
431 {
432   AgsSpectrometer *spectrometer;
433 
434   AgsAudio *audio;
435 
436   GList *start_recall;
437 
438   gint position;
439 
440   if((AGS_MACHINE_MAPPED_RECALL & (machine->flags)) != 0 ||
441      (AGS_MACHINE_PREMAPPED_RECALL & (machine->flags)) != 0){
442     return;
443   }
444 
445   spectrometer = (AgsSpectrometer *) machine;
446 
447   audio = machine->audio;
448 
449   position = 0;
450 
451   /* add new controls */
452   start_recall = ags_fx_factory_create(audio,
453 				       spectrometer->analyse_play_container, spectrometer->analyse_recall_container,
454 				       "ags-fx-analyse",
455 				       NULL,
456 				       NULL,
457 				       0, 0,
458 				       0, 0,
459 				       position,
460 				       (AGS_FX_FACTORY_ADD | AGS_FX_FACTORY_INPUT), 0);
461 
462   g_list_free_full(start_recall,
463 		   (GDestroyNotify) g_object_unref);
464 
465   /* depending on destination */
466   ags_spectrometer_input_map_recall(spectrometer,
467 				    0,
468 				    0);
469 
470   /* depending on destination */
471   ags_spectrometer_output_map_recall(spectrometer,
472 				     0,
473 				     0);
474 
475   /* call parent */
476   AGS_MACHINE_CLASS(ags_spectrometer_parent_class)->map_recall(machine);
477 }
478 
479 void
ags_spectrometer_input_map_recall(AgsSpectrometer * spectrometer,guint audio_channel_start,guint input_pad_start)480 ags_spectrometer_input_map_recall(AgsSpectrometer *spectrometer,
481 				  guint audio_channel_start,
482 				  guint input_pad_start)
483 {
484   AgsAudio *audio;
485 
486   GList *start_recall;
487 
488   gint position;
489   guint input_pads;
490   guint audio_channels;
491 
492   if(spectrometer->mapped_input_pad > input_pad_start){
493     return;
494   }
495 
496   audio = AGS_MACHINE(spectrometer)->audio;
497 
498   position = 0;
499 
500   input_pads = 0;
501   audio_channels = 0;
502 
503   /* get some fields */
504   g_object_get(audio,
505 	       "input-pads", &input_pads,
506 	       "audio-channels", &audio_channels,
507 	       NULL);
508 
509   /* add to machine */
510   ags_fx_factory_create(AGS_MACHINE(spectrometer)->audio,
511 			spectrometer->analyse_play_container, spectrometer->analyse_recall_container,
512 			"ags-fx-analyse",
513 			NULL,
514 			NULL,
515 			audio_channel_start, audio_channels,
516 			input_pad_start, input_pads,
517 			position,
518 			(AGS_FX_FACTORY_REMAP | AGS_FX_FACTORY_INPUT), 0);
519 
520   spectrometer->mapped_input_pad = input_pads;
521 }
522 
523 void
ags_spectrometer_output_map_recall(AgsSpectrometer * spectrometer,guint audio_channel_start,guint output_pad_start)524 ags_spectrometer_output_map_recall(AgsSpectrometer *spectrometer,
525 				   guint audio_channel_start,
526 				   guint output_pad_start)
527 {
528   AgsAudio *audio;
529 
530   guint output_pads;
531 
532   if(spectrometer->mapped_output_pad > output_pad_start){
533     return;
534   }
535 
536   audio = AGS_MACHINE(spectrometer)->audio;
537 
538   /* get some fields */
539   g_object_get(audio,
540 	       "output-pads", &output_pads,
541 	       NULL);
542 
543   spectrometer->mapped_output_pad = output_pads;
544 }
545 
546 gdouble
ags_spectrometer_x_small_scale_func(gdouble value,gpointer data)547 ags_spectrometer_x_small_scale_func(gdouble value, gpointer data)
548 {
549   if(data == NULL ||
550      !AGS_IS_CARTESIAN(data)){
551     return(value);
552   }
553 
554   return(AGS_CARTESIAN(data)->x_small_scale_factor * value);
555 }
556 
557 gdouble
ags_spectrometer_x_big_scale_func(gdouble value,gpointer data)558 ags_spectrometer_x_big_scale_func(gdouble value, gpointer data)
559 {
560   if(data == NULL ||
561      !AGS_IS_CARTESIAN(data)){
562     return(value);
563   }
564 
565   return(AGS_CARTESIAN(data)->x_big_scale_factor * value);
566 }
567 
568 gchar*
ags_spectrometer_x_label_func(gdouble value,gpointer data)569 ags_spectrometer_x_label_func(gdouble value,
570 			      gpointer data)
571 {
572   gchar *format;
573   gchar *str;
574 
575   gdouble correction;
576 
577   correction = (44100.0 - AGS_SOUNDCARD_DEFAULT_BUFFER_SIZE) / (double) AGS_SOUNDCARD_DEFAULT_BUFFER_SIZE;
578 
579   format = g_strdup_printf("%%.%df",
580 			   (guint) ceil(AGS_CARTESIAN(data)->x_label_precision));
581 
582   str = g_strdup_printf(format,
583 			(correction / 2.0) * (exp(value / 12.0) - 1.0));
584   g_free(format);
585 
586   return(str);
587 }
588 
589 gchar*
ags_spectrometer_y_label_func(gdouble value,gpointer data)590 ags_spectrometer_y_label_func(gdouble value,
591 			      gpointer data)
592 {
593   gchar *format;
594   gchar *str;
595 
596   format = g_strdup_printf("%%.%df",
597 			   (guint) ceil(AGS_CARTESIAN(data)->y_label_precision));
598 
599   str = g_strdup_printf(format,
600 			value / 20.0);
601 
602   return(str);
603 }
604 
605 AgsPort*
ags_spectrometer_find_specifier(GList * recall,gchar * specifier)606 ags_spectrometer_find_specifier(GList *recall, gchar *specifier)
607 {
608   GList *port;
609 
610   while(recall != NULL){
611     port = AGS_RECALL(recall->data)->port;
612 
613 #ifdef AGS_DEBUG
614     g_message("search port in %s", G_OBJECT_TYPE_NAME(recall->data));
615 #endif
616 
617     while(port != NULL){
618       if(!g_strcmp0(AGS_PORT(port->data)->specifier,
619 		    specifier)){
620 	return(AGS_PORT(port->data));
621       }
622 
623       port = port->next;
624     }
625 
626     recall = recall->next;
627   }
628 
629   return(NULL);
630 }
631 
632 AgsPlot*
ags_spectrometer_fg_plot_alloc(AgsSpectrometer * spectrometer,gdouble color_r,gdouble color_g,double color_b)633 ags_spectrometer_fg_plot_alloc(AgsSpectrometer *spectrometer,
634 			       gdouble color_r, gdouble color_g, double color_b)
635 {
636   AgsCartesian *cartesian;
637   AgsPlot *plot;
638 
639   guint i, i_stop;
640 
641   cartesian = spectrometer->cartesian;
642 
643   i_stop = AGS_SPECTROMETER_PLOT_DEFAULT_POINT_COUNT + 1;
644 
645   plot = ags_plot_alloc(i_stop, 0, 0);
646   plot->join_points = TRUE;
647 
648   for(i = 0; i < i_stop; i++){
649     plot->point_color[i][0] = color_r;
650     plot->point_color[i][1] = color_g;
651     plot->point_color[i][2] = color_b;
652 
653     plot->point[i][0] = ((gdouble) i / (gdouble) AGS_SPECTROMETER_PLOT_DEFAULT_POINT_COUNT) * ((double) cartesian->x_end);
654     plot->point[i][1] = 0.0;
655   }
656 
657   return(plot);
658 }
659 
660 /**
661  * ags_spectrometer_cartesian_queue_draw_timeout:
662  * @widget: the widget
663  *
664  * Queue draw widget
665  *
666  * Returns: %TRUE if proceed with redraw, otherwise %FALSE
667  *
668  * Since: 3.0.0
669  */
670 gboolean
ags_spectrometer_cartesian_queue_draw_timeout(GtkWidget * widget)671 ags_spectrometer_cartesian_queue_draw_timeout(GtkWidget *widget)
672 {
673   if(g_hash_table_lookup(ags_spectrometer_cartesian_queue_draw,
674 			 widget) != NULL){
675     AgsSpectrometer *spectrometer;
676 
677     AgsPort *port;
678 
679     GList *fg_plot;
680     GList *start_recall, *recall;
681 
682     guint samplerate;
683     guint buffer_size;
684     guint audio_buffer_size;
685     gdouble correction;
686     gdouble frequency;
687     gdouble gfrequency;
688     double magnitude;
689     guint copy_mode;
690     guint i;
691     guint j;
692     guint k;
693 
694     GValue value = {0,};
695 
696     spectrometer = (AgsSpectrometer *) gtk_widget_get_ancestor(widget,
697 							       AGS_TYPE_SPECTROMETER);
698 
699     samplerate = AGS_MACHINE(spectrometer)->samplerate;
700     buffer_size = AGS_MACHINE(spectrometer)->buffer_size;
701 
702     audio_buffer_size = 0;
703 
704     g_object_get(AGS_MACHINE(spectrometer)->audio,
705 		 "buffer-size", &audio_buffer_size,
706 		 NULL);
707 
708     if(buffer_size != audio_buffer_size){
709       return(TRUE);
710     }
711 
712     ags_audio_buffer_util_clear_double(spectrometer->magnitude, 1,
713 				       buffer_size);
714 
715     copy_mode = AGS_AUDIO_BUFFER_UTIL_COPY_DOUBLE_TO_DOUBLE;
716 
717     /* play context */
718     recall = ags_recall_container_get_recall_channel(spectrometer->analyse_play_container);
719 
720     start_recall = g_list_copy(recall);
721 
722     recall = start_recall;
723 
724     while(recall != NULL){
725       /* get magnitude */
726       port = NULL;
727 
728       g_object_get(recall->data,
729 		   "magnitude", &port,
730 		   NULL);
731 
732       if(port != NULL){
733 	g_value_init(&value, G_TYPE_POINTER);
734 
735 	g_value_set_pointer(&value, spectrometer->magnitude_cache);
736 
737 	ags_port_safe_read(port, &value);
738 
739 	g_value_unset(&value);
740 
741 	g_object_unref(port);
742       }
743 
744       /* copy cache */
745       ags_audio_buffer_util_copy_buffer_to_buffer(spectrometer->magnitude, 1, 0,
746 						  spectrometer->magnitude_cache, 1, 0,
747 						  buffer_size, copy_mode);
748 
749       recall = recall->next;
750     }
751 
752     /* recall context */
753     recall = ags_recall_container_get_recall_channel(spectrometer->analyse_recall_container);
754 
755     start_recall = g_list_copy(recall);
756 
757     while(recall != NULL){
758       /* get magnitude */
759       port = NULL;
760 
761       g_object_get(recall->data,
762 		   "magnitude", &port,
763 		   NULL);
764 
765       if(port != NULL){
766 	g_value_init(&value, G_TYPE_POINTER);
767 
768 	g_value_set_pointer(&value, spectrometer->magnitude_cache);
769 
770 	ags_port_safe_read(port, &value);
771 
772 	g_value_unset(&value);
773 
774 	g_object_unref(port);
775       }
776 
777       /* copy cache */
778       ags_audio_buffer_util_copy_buffer_to_buffer(spectrometer->magnitude, 1, 0,
779 						  spectrometer->magnitude_cache, 1, 0,
780 						  buffer_size, copy_mode);
781 
782       recall = recall->next;
783     }
784 
785     /* plot */
786     fg_plot = spectrometer->fg_plot;
787 
788     correction = (44100.0 - AGS_SOUNDCARD_DEFAULT_BUFFER_SIZE) / (double) AGS_SOUNDCARD_DEFAULT_BUFFER_SIZE;
789 
790     while(fg_plot != NULL){
791       magnitude = 0.0;
792 
793       frequency = 0.0;
794       gfrequency = 0.0;
795 
796       for(i = 0, j = 1; i < AGS_SPECTROMETER_PLOT_DEFAULT_POINT_COUNT && j < buffer_size / 2; i++){
797 	frequency = (double) j * correction;
798 
799 	if(AGS_SPECTROMETER_DEFAULT_X_END >= 0.0 &&
800 	   AGS_SPECTROMETER_DEFAULT_X_START < 0.0){
801 	  gfrequency = (correction / 2.0) * (exp((((double) i) / (gdouble) AGS_SPECTROMETER_PLOT_DEFAULT_POINT_COUNT * ((AGS_SPECTROMETER_DEFAULT_X_END + AGS_SPECTROMETER_DEFAULT_X_START) / AGS_CARTESIAN_DEFAULT_X_STEP_WIDTH)) / 12.0) - 1.0);
802 	}else if(AGS_SPECTROMETER_DEFAULT_X_END >= 0.0 &&
803 		 AGS_SPECTROMETER_DEFAULT_X_START >= 0.0){
804 	  gfrequency = (correction / 2.0) * (exp((((double) i) / (gdouble) AGS_SPECTROMETER_PLOT_DEFAULT_POINT_COUNT * ((AGS_SPECTROMETER_DEFAULT_X_END - AGS_SPECTROMETER_DEFAULT_X_START) / AGS_CARTESIAN_DEFAULT_X_STEP_WIDTH)) / 12.0) - 1.0);
805 	}else{
806 	  g_message("only positive frequencies allowed");
807 	}
808 
809 	magnitude = 0.0;
810 
811 	for(k = 0; j < buffer_size / 2 && frequency < gfrequency; j++, k++){
812 	  frequency = (double) j * correction;
813 
814 	  magnitude += spectrometer->magnitude[j];
815 	}
816 
817 	if(magnitude < 0.0){
818 	  magnitude *= -1.0;
819 	}
820 
821 	if(k != 0){
822 	  AGS_PLOT(fg_plot->data)->point[i][1] = 20.0 * log10(((double) magnitude / (double) k) + 1.0) * AGS_SPECTROMETER_EXTRA_SCALE;
823 	}else{
824 	  AGS_PLOT(fg_plot->data)->point[i][1] = 0.0;
825 	}
826 
827 	if(frequency > samplerate ||
828 	   gfrequency > samplerate){
829 	  break;
830 	}
831       }
832 
833       /* iterate */
834       fg_plot = fg_plot->next;
835     }
836 
837     /* queue draw */
838     gtk_widget_queue_draw(widget);
839 
840     return(TRUE);
841   }else{
842     return(FALSE);
843   }
844 }
845 
846 /**
847  * ags_spectrometer_new:
848  * @soundcard: the assigned soundcard.
849  *
850  * Creates an #AgsSpectrometer
851  *
852  * Returns: a new #AgsSpectrometer
853  *
854  * Since: 3.0.0
855  */
856 AgsSpectrometer*
ags_spectrometer_new(GObject * soundcard)857 ags_spectrometer_new(GObject *soundcard)
858 {
859   AgsSpectrometer *spectrometer;
860 
861   spectrometer = (AgsSpectrometer *) g_object_new(AGS_TYPE_SPECTROMETER,
862 						  NULL);
863 
864   g_object_set(G_OBJECT(AGS_MACHINE(spectrometer)->audio),
865 	       "output-soundcard", soundcard,
866 	       NULL);
867 
868   return(spectrometer);
869 }
870