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