4 * Copyright (C) 1997-2011 Adam Williams <broadcast at earthling dot net>
6 * This program 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 2 of the License, or
9 * (at your option) any later version.
11 * This program 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.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include "bcdisplayinfo.h"
30 #include "spectrogram.h"
32 #include "transportque.inc"
41 REGISTER_PLUGIN(Spectrogram)
44 #define HALF_WINDOW (config.window_size / 2)
46 SpectrogramConfig::SpectrogramConfig()
49 window_size = MAX_WINDOW;
57 int SpectrogramConfig::equivalent(SpectrogramConfig &that)
59 return EQUIV(level, that.level) &&
60 xzoom == that.xzoom &&
61 frequency == that.frequency &&
62 window_size == that.window_size &&
63 normalize == that.normalize &&
65 history_size == that.history_size;
68 void SpectrogramConfig::copy_from(SpectrogramConfig &that)
72 frequency = that.frequency;
73 window_size = that.window_size;
74 normalize = that.normalize;
76 history_size = that.history_size;
78 CLAMP(history_size, MIN_HISTORY, MAX_HISTORY);
79 CLAMP(frequency, MIN_FREQ, MAX_FREQ);
80 CLAMP(xzoom, MIN_XZOOM, MAX_XZOOM);
83 void SpectrogramConfig::interpolate(SpectrogramConfig &prev,
84 SpectrogramConfig &next,
87 int64_t current_frame)
94 SpectrogramFrame::SpectrogramFrame(int data_size)
96 this->data_size = data_size;
97 data = new float[data_size];
101 SpectrogramFrame::~SpectrogramFrame()
108 SpectrogramLevel::SpectrogramLevel(Spectrogram *plugin, int x, int y)
109 : BC_FPot(x, y, plugin->config.level, INFINITYGAIN, 40)
111 this->plugin = plugin;
114 int SpectrogramLevel::handle_event()
116 plugin->config.level = get_value();
117 plugin->send_configure_change();
125 SpectrogramMode::SpectrogramMode(Spectrogram *plugin,
128 : BC_PopupMenu(x, y, xS(120),
129 mode_to_text(plugin->config.mode))
131 this->plugin = plugin;
134 void SpectrogramMode::create_objects()
136 add_item(new BC_MenuItem(mode_to_text(VERTICAL)));
137 add_item(new BC_MenuItem(mode_to_text(HORIZONTAL)));
140 int SpectrogramMode::handle_event()
142 if(plugin->config.mode != text_to_mode(get_text()))
144 SpectrogramWindow *window = (SpectrogramWindow*)plugin->thread->window;
145 window->probe_x = -1;
146 window->probe_y = -1;
147 plugin->config.mode = text_to_mode(get_text());
148 window->canvas->clear_box(0, 0, window->canvas->get_w(), window->canvas->get_h());
149 plugin->send_configure_change();
154 const char* SpectrogramMode::mode_to_text(int mode)
159 return _("Vertical");
162 return _("Horizontal");
166 int SpectrogramMode::text_to_mode(const char *text)
168 if(!strcmp(mode_to_text(VERTICAL), text)) return VERTICAL;
175 SpectrogramHistory::SpectrogramHistory(Spectrogram *plugin,
178 : BC_IPot(x, y, plugin->config.history_size, MIN_HISTORY, MAX_HISTORY)
180 this->plugin = plugin;
183 int SpectrogramHistory::handle_event()
185 plugin->config.history_size = get_value();
186 plugin->send_configure_change();
195 SpectrogramWindowSize::SpectrogramWindowSize(Spectrogram *plugin,
196 int x, int y, char *text)
197 : BC_PopupMenu(x, y, xS(120), text)
199 this->plugin = plugin;
202 int SpectrogramWindowSize::handle_event()
204 plugin->config.window_size = atoi(get_text());
205 plugin->send_configure_change();
210 SpectrogramWindowSizeTumbler::SpectrogramWindowSizeTumbler(Spectrogram *plugin, int x, int y)
214 this->plugin = plugin;
217 int SpectrogramWindowSizeTumbler::handle_up_event()
219 plugin->config.window_size *= 2;
220 if(plugin->config.window_size > MAX_WINDOW)
221 plugin->config.window_size = MAX_WINDOW;
222 char string[BCTEXTLEN];
223 sprintf(string, "%d", plugin->config.window_size);
224 ((SpectrogramWindow*)plugin->get_thread()->get_window())->window_size->set_text(string);
225 plugin->send_configure_change();
229 int SpectrogramWindowSizeTumbler::handle_down_event()
231 plugin->config.window_size /= 2;
232 if(plugin->config.window_size < MIN_WINDOW)
233 plugin->config.window_size = MIN_WINDOW;
234 char string[BCTEXTLEN];
235 sprintf(string, "%d", plugin->config.window_size);
236 ((SpectrogramWindow*)plugin->get_thread()->get_window())->window_size->set_text(string);
237 plugin->send_configure_change();
245 SpectrogramNormalize::SpectrogramNormalize(Spectrogram *plugin, int x, int y)
246 : BC_CheckBox(x, y, plugin->config.normalize, _("Normalize"))
248 this->plugin = plugin;
251 int SpectrogramNormalize::handle_event()
253 plugin->config.normalize = get_value();
254 plugin->send_configure_change();
261 SpectrogramFreq::SpectrogramFreq(Spectrogram *plugin, int x, int y)
262 : BC_TextBox(x, y, xS(100), 1,
263 (int)plugin->config.frequency)
265 this->plugin = plugin;
268 int SpectrogramFreq::handle_event()
270 plugin->config.frequency = atoi(get_text());
271 CLAMP(plugin->config.frequency, MIN_FREQ, MAX_FREQ);
272 plugin->send_configure_change();
280 SpectrogramXZoom::SpectrogramXZoom(Spectrogram *plugin, int x, int y)
281 : BC_IPot(x, y, plugin->config.xzoom, MIN_XZOOM, MAX_XZOOM)
283 this->plugin = plugin;
286 int SpectrogramXZoom::handle_event()
288 plugin->config.xzoom = get_value();
289 plugin->send_configure_change();
295 SpectrogramCanvas::SpectrogramCanvas(Spectrogram *plugin, int x, int y, int w, int h)
296 : BC_SubWindow(x, y, w, h, BLACK)
298 this->plugin = plugin;
299 current_operation = NONE;
302 int SpectrogramCanvas::button_press_event()
304 if(is_event_win() && cursor_inside())
307 current_operation = DRAG;
308 plugin->send_configure_change();
314 int SpectrogramCanvas::button_release_event()
316 if(current_operation == DRAG)
318 current_operation = NONE;
324 int SpectrogramCanvas::cursor_motion_event()
326 if(current_operation == DRAG)
334 void SpectrogramCanvas::calculate_point()
336 int x = get_cursor_x();
337 int y = get_cursor_y();
338 CLAMP(x, 0, get_w()-1);
339 CLAMP(y, 0, get_h()-1);
341 ((SpectrogramWindow*)plugin->thread->window)->calculate_frequency(
346 //printf("SpectrogramCanvas::calculate_point %d %d\n", __LINE__, Freq::tofreq(freq_index));
349 void SpectrogramCanvas::draw_overlay()
351 SpectrogramWindow *window = (SpectrogramWindow*)plugin->thread->window;
352 if(window->probe_x >= 0 || window->probe_y >= 0)
356 if(plugin->config.mode == HORIZONTAL) draw_line(0, window->probe_y, get_w(), window->probe_y);
357 draw_line(window->probe_x, 0, window->probe_x, get_h());
369 SpectrogramWindow::SpectrogramWindow(Spectrogram *plugin)
370 : PluginClientWindow(plugin, plugin->w, plugin->h,
373 this->plugin = plugin;
374 probe_x = probe_y = -1;
377 SpectrogramWindow::~SpectrogramWindow()
381 void SpectrogramWindow::create_objects()
383 int x = plugin->get_theme()->widget_border;
387 char string[BCTEXTLEN];
391 add_subwindow(canvas = new SpectrogramCanvas(plugin,
392 0, 0, get_w(), get_h() - BC_Pot::calculate_h() * 2 -
393 plugin->get_theme()->widget_border * 3));
394 canvas->set_cursor(CROSS_CURSOR, 0, 0);
396 x = plugin->get_theme()->widget_border;
397 y = canvas->get_y() + canvas->get_h() + plugin->get_theme()->widget_border;
401 add_subwindow(level_title = new BC_Title(x, y, _("Level:")));
402 x += level_title->get_w() + plugin->get_theme()->widget_border;
403 add_subwindow(level = new SpectrogramLevel(plugin, x, y));
404 x += level->get_w() + plugin->get_theme()->widget_border;
405 y += level->get_h() + plugin->get_theme()->widget_border;
407 add_subwindow(normalize = new SpectrogramNormalize(plugin, x1, y));
409 x = x1 + level_title->get_w() + level->get_w() + plugin->get_theme()->widget_border * 2;
413 sprintf(string, "%d", plugin->config.window_size);
414 add_subwindow(window_size_title = new BC_Title(x, y, _("Window size:")));
416 x += window_size_title->get_w() + plugin->get_theme()->widget_border;
417 add_subwindow(window_size = new SpectrogramWindowSize(plugin, x, y, string));
418 x += window_size->get_w();
419 add_subwindow(window_size_tumbler = new SpectrogramWindowSizeTumbler(plugin, x, y));
421 for(int i = MIN_WINDOW; i <= MAX_WINDOW; i *= 2)
423 sprintf(string, "%d", i);
424 window_size->add_item(new BC_MenuItem(string));
427 // x += window_size_tumbler->get_w() + plugin->get_theme()->widget_border;
429 y += window_size->get_h() + plugin->get_theme()->widget_border;
433 add_subwindow(mode_title = new BC_Title(x, y, _("Mode:")));
434 x += mode_title->get_w() + plugin->get_theme()->widget_border;
435 add_subwindow(mode = new SpectrogramMode(plugin,
438 mode->create_objects();
439 x += mode->get_w() + plugin->get_theme()->widget_border;
441 x = x1 = window_size_tumbler->get_x() +
442 window_size_tumbler->get_w() +
443 plugin->get_theme()->widget_border;
445 add_subwindow(history_title = new BC_Title(x, y, _("History:")));
446 x += history_title->get_w() + plugin->get_theme()->widget_border;
447 add_subwindow(history = new SpectrogramHistory(plugin,
452 y += history->get_h() + plugin->get_theme()->widget_border;
453 add_subwindow(xzoom_title = new BC_Title(x, y, _("X Zoom:")));
454 x += xzoom_title->get_w() + plugin->get_theme()->widget_border;
455 add_subwindow(xzoom = new SpectrogramXZoom(plugin, x, y));
456 x += xzoom->get_w() + plugin->get_theme()->widget_border;
460 add_subwindow(freq_title = new BC_Title(x1, y, _("Freq: 0 Hz")));
461 // x += freq_title->get_w() + plugin->get_theme()->widget_border;
462 y += freq_title->get_h() + plugin->get_theme()->widget_border;
463 // add_subwindow(freq = new SpectrogramFreq(plugin, x, y));
464 // y += freq->get_h() + plugin->get_theme()->widget_border;
466 add_subwindow(amplitude_title = new BC_Title(x, y, _("Amplitude: 0 dB")));
473 int SpectrogramWindow::resize_event(int w, int h)
475 int canvas_h = canvas->get_h();
476 int canvas_difference = get_h() - canvas_h;
478 canvas->reposition_window(0,
481 h - canvas_difference);
482 canvas->clear_box(0, 0, canvas->get_w(), canvas->get_h());
486 int y_diff = -canvas_h + canvas->get_h();
488 // Remove all columns which may be a different size.
489 plugin->frame_buffer.remove_all_objects();
490 plugin->frame_history.remove_all_objects();
492 level_title->reposition_window(
493 level_title->get_x(),
494 level_title->get_y() + y_diff);
495 level->reposition_window(level->get_x(),
496 level->get_y() + y_diff);
498 window_size_title->reposition_window(
499 window_size_title->get_x(),
500 window_size_title->get_y() + y_diff);
502 normalize->reposition_window(normalize->get_x(),
503 normalize->get_y() + y_diff);
504 window_size->reposition_window(window_size->get_x(),
505 window_size->get_y() + y_diff);
506 window_size_tumbler->reposition_window(window_size_tumbler->get_x(),
507 window_size_tumbler->get_y() + y_diff);
511 mode_title->reposition_window(mode_title->get_x(),
512 mode_title->get_y() + y_diff);
513 mode->reposition_window(mode->get_x(),
514 mode->get_y() + y_diff);
517 history_title->reposition_window(history_title->get_x(),
518 history_title->get_y() + y_diff);
519 history->reposition_window(history->get_x(),
520 history->get_y() + y_diff);
522 xzoom_title->reposition_window(xzoom_title->get_x(),
523 xzoom_title->get_y() + y_diff);
524 xzoom->reposition_window(xzoom->get_x(),
525 xzoom->get_y() + y_diff);
526 freq_title->reposition_window(freq_title->get_x(),
527 freq_title->get_y() + y_diff);
528 // freq->reposition_window(freq->get_x(),
529 // freq->get_y() + y_diff);
530 amplitude_title->reposition_window(amplitude_title->get_x(),
531 amplitude_title->get_y() + y_diff);
540 void SpectrogramWindow::calculate_frequency(int x, int y, int do_overlay)
542 if(x < 0 && y < 0) return;
544 // Clear previous overlay
545 if(do_overlay) canvas->draw_overlay();
547 // New probe position
551 // Convert to coordinates in frame history
552 int freq_pixel, time_pixel;
553 if(plugin->config.mode == VERTICAL)
555 freq_pixel = get_w() - x;
561 time_pixel = get_w() - x;
564 CLAMP(time_pixel, 0, plugin->frame_history.size() - 1);
565 if(plugin->frame_history.size())
567 SpectrogramFrame *ptr = plugin->frame_history.get(
568 plugin->frame_history.size() - time_pixel - 1);
572 if(plugin->config.mode == VERTICAL)
574 pixels = canvas->get_w();
575 freq_index = (pixels - freq_pixel) * TOTALFREQS / pixels;
579 pixels = canvas->get_h();
580 freq_index = (pixels - freq_pixel) * TOTALFREQS / pixels;
583 int freq = Freq::tofreq(freq_index);
586 CLAMP(freq_pixel, 0, ptr->data_size - 1);
587 double level = ptr->data[freq_pixel];
589 char string[BCTEXTLEN];
590 sprintf(string, _("Freq: %d Hz"), freq);
591 freq_title->update(string);
593 sprintf(string, _("Amplitude: %.2f dB"), level);
594 amplitude_title->update(string);
599 canvas->draw_overlay();
606 void SpectrogramWindow::update_gui()
608 char string[BCTEXTLEN];
609 level->update(plugin->config.level);
610 sprintf(string, "%d", plugin->config.window_size);
611 window_size->set_text(string);
613 mode->set_text(mode->mode_to_text(plugin->config.mode));
614 history->update(plugin->config.history_size);
616 // sprintf(string, "%d", plugin->config.window_fragment);
617 // window_fragment->set_text(string);
619 normalize->set_value(plugin->config.normalize);
646 Spectrogram::Spectrogram(PluginServer *server)
647 : PluginAClient(server)
655 Spectrogram::~Spectrogram()
663 frame_buffer.remove_all_objects();
664 frame_history.remove_all_objects();
668 void Spectrogram::reset()
675 audio_buffer_start = -MAX_WINDOW * 2;
680 bzero(&header, sizeof(data_header_t));
684 const char* Spectrogram::plugin_title() { return N_("Spectrogram"); }
685 int Spectrogram::is_realtime() { return 1; }
687 int Spectrogram::process_buffer(int64_t size,
689 int64_t start_position,
700 load_configuration();
702 // Reset audio buffer
703 if(window_size != config.window_size)
706 window_size = config.window_size;
720 data = new unsigned char[sizeof(data_header_t)];
726 freq_real = new double[MAX_WINDOW];
727 freq_imag = new double[MAX_WINDOW];
732 audio_buffer = new Samples(MAX_WINDOW);
737 int needed = buffer_size + size;
738 if(audio_buffer->get_allocated() < needed)
740 Samples *new_samples = new Samples(needed);
741 memcpy(new_samples->get_data(),
742 audio_buffer->get_data(),
743 sizeof(double) * buffer_size);
745 audio_buffer = new_samples;
748 double *audio_samples = audio_buffer->get_data();
749 memcpy(audio_samples + buffer_size,
751 sizeof(double) * size);
755 //printf("Spectrogram::process_buffer %d %d\n", __LINE__, buffer_size);
757 // Append a windows to end of GUI buffer
759 while(buffer_size >= window_size)
762 fft->do_fft(window_size, // must be a power of 2
763 0, // 0 = forward FFT, 1 = inverse
764 audio_samples, // array of input's real samples
765 0, // array of input's imag samples
766 freq_real, // array of output's reals
769 // Get peak in waveform
771 for(int i = 0; i < window_size; i++)
773 double sample = fabs(audio_samples[i]);
774 if(sample > max) max = sample;
777 // Append to end of data buffer
778 if(allocated_data < (total_windows + 1) * (HALF_WINDOW + 1))
780 int new_allocation = (total_windows + 1) * (HALF_WINDOW + 1);
781 unsigned char *new_data = new unsigned char[sizeof(data_header_t) +
782 sizeof(float) * new_allocation];
783 data_header_t *new_header = (data_header_t*)new_data;
784 data_header_t *old_header = (data_header_t*)data;
785 memcpy(new_header->samples,
787 sizeof(float) * allocated_data);
790 allocated_data = new_allocation;
793 data_header_t *header = (data_header_t*)data;
794 float *sample_output = header->samples + total_windows * (HALF_WINDOW + 1);
795 // 1st sample is maximum
796 sample_output[0] = max;
797 for(int i = 0; i < HALF_WINDOW; i++)
799 sample_output[i + 1] = sqrt(freq_real[i] * freq_real[i] +
800 freq_imag[i] * freq_imag[i]);
801 // sample_output[i + 1] = freq_real[i];
804 // Shift audio buffer out
805 memcpy(audio_samples,
806 audio_samples + window_size,
807 (buffer_size - window_size) * sizeof(double));
810 buffer_size -= window_size;
813 data_header_t *header = (data_header_t*)data;
814 header->window_size = window_size;
815 header->sample_rate = sample_rate;
816 header->total_windows = total_windows;
817 // Linear output level
818 header->level = DB::fromdb(config.level);
820 send_render_gui(data,
821 sizeof(data_header_t) +
822 sizeof(float) * total_windows * (HALF_WINDOW + 1));
827 void Spectrogram::render_stop()
830 audio_buffer_start = -MAX_WINDOW * 2;
831 frame_buffer.remove_all_objects();
832 frame_history.remove_all_objects();
838 NEW_WINDOW_MACRO(Spectrogram, SpectrogramWindow)
840 void Spectrogram::update_gui()
844 int result = load_configuration();
845 SpectrogramWindow *window = (SpectrogramWindow*)thread->get_window();
846 window->lock_window("Spectrogram::update_gui");
847 if(result) window->update_gui();
850 //printf("Spectrogram::update_gui %d\n", __LINE__);
851 // Shift in accumulated canvas columns
852 if(frame_buffer.size())
854 SpectrogramCanvas *canvas = (SpectrogramCanvas*)window->canvas;
855 canvas->draw_overlay();
856 // Z to draw in this iteration
857 int total_frames = timer->get_difference() *
862 //printf("Spectrogram::update_gui %d %d %ld\n", __LINE__, frame_buffer.size(), timer->get_difference());
863 if(total_frames) timer->subtract(total_frames *
868 // Add forced column drawing
869 for(int i = 0; i < frame_buffer.size(); i++)
870 if(frame_buffer.get(i)->force) total_frames++;
871 total_frames = MIN(frame_buffer.size(), total_frames);
873 // Limit to canvas width
874 if(config.mode == HORIZONTAL)
875 total_frames = MIN(canvas->get_w(), total_frames);
879 if(config.mode == HORIZONTAL)
882 int pixels = canvas->get_h();
883 canvas->copy_area(total_frames * config.xzoom,
887 canvas->get_w() - total_frames * config.xzoom,
893 frame < total_frames;
896 int x = canvas->get_w() - (total_frames - frame) * config.xzoom;
897 SpectrogramFrame *ptr = frame_buffer.get(0);
899 for(int i = 0; i < pixels; i++)
901 float db = ptr->data[
902 MIN(i, ptr->data_size - 1)];
904 float r_out, g_out, b_out;
907 #define DIVISION1 0.0
908 #define DIVISION2 -20.0
909 #define DIVISION3 INFINITYGAIN
912 h = 240 - (float)(db - DIVISION2) / (DIVISION1 - DIVISION2) *
917 HSV::hsv_to_rgb(r_out, g_out, b_out, h, s, v);
918 r = (int)(r_out * 0xff);
919 g = (int)(g_out * 0xff);
920 b = (int)(b_out * 0xff);
927 v = (float)(db - DIVISION3) / (DIVISION2 - DIVISION3);
928 HSV::hsv_to_rgb(r_out, g_out, b_out, h, s, v);
929 r = (int)(r_out * 0xff);
930 g = (int)(g_out * 0xff);
931 b = (int)(b_out * 0xff);
938 canvas->set_color((r << 16) |
941 if(config.xzoom == 1)
942 canvas->draw_pixel(x, i);
950 // Push frames onto history
951 for(int i = 0; i < config.xzoom; i++)
953 SpectrogramFrame *new_frame = new SpectrogramFrame(
955 frame_history.append(new_frame);
956 memcpy(new_frame->data, ptr->data, sizeof(float) * ptr->data_size);
959 // Clip history to canvas size
960 while(frame_history.size() > canvas->get_w())
961 frame_history.remove_object_number(0);
963 frame_buffer.remove_object_number(0);
969 // Shift frames into history
970 for(int frame = 0; frame < total_frames; frame++)
972 if(frame_history.size() >= config.history_size)
973 frame_history.remove_object_number(0);
975 frame_history.append(frame_buffer.get(0));
976 frame_buffer.remove_number(0);
979 // Reduce history size
980 while(frame_history.size() > config.history_size)
981 frame_history.remove_object_number(0);
983 // Draw frames from history
984 canvas->clear_box(0, 0, canvas->get_w(), canvas->get_h());
985 for(int frame = 0; frame < frame_history.size(); frame++)
987 SpectrogramFrame *ptr = frame_history.get(frame);
988 //printf("%d %d\n", canvas->get_w(), ptr->data_size);
990 int luma = (frame + 1) * 0x80 / frame_history.size();
991 if(frame == frame_history.size() - 1)
993 canvas->set_color(WHITE);
994 canvas->set_line_width(2);
997 canvas->set_color((luma << 16) |
1004 int w = canvas->get_w();
1005 int h = canvas->get_h();
1008 //printf("Spectrogram::update_gui %d ", __LINE__);
1010 for(int x2 = 0; x2 < w; x2++)
1012 float db = ptr->data[
1013 MIN((w - x2), ptr->data_size - 1)];
1014 //if(x2 > w - 10) printf("%.02f ", ptr->data[x2]);
1015 int y2 = h-1 - (int)((db - INFINITYGAIN) /
1016 (0 - INFINITYGAIN) *
1022 canvas->draw_line(x1, y1, x2, y2);
1033 canvas->set_line_width(1);
1040 // Recompute probe level
1041 window->calculate_frequency(window->probe_x, window->probe_y, 0);
1043 canvas->draw_overlay();
1047 while(frame_buffer.size() > MAX_COLUMNS)
1048 frame_buffer.remove_object_number(0);
1050 window->unlock_window();
1054 void Spectrogram::render_gui(void *data, int size)
1058 thread->get_window()->lock_window("Spectrogram::render_gui");
1059 data_header_t *header = (data_header_t*)data;
1060 memcpy(&this->header, header, sizeof(data_header_t));
1061 BC_SubWindow *canvas = ((SpectrogramWindow*)thread->get_window())->canvas;
1062 int pixels = canvas->get_w();
1063 if(config.mode == HORIZONTAL) pixels = canvas->get_h();
1065 // Set all previous columns to draw immediately
1066 for(int i = 0; i < frame_buffer.size(); i++)
1067 frame_buffer.get(i)->force = 1;
1070 for(int current_window = 0;
1071 current_window < header->total_windows;
1074 float *frame = header->samples +
1075 current_window * (header->window_size / 2 + 1);
1076 float frame_max = *frame;
1078 int niquist = get_project_samplerate() / 2;
1079 int total_slots = header->window_size / 2;
1080 int max_slot = total_slots - 1;
1081 // int slot1 = total_slots - 1;
1082 SpectrogramFrame *ptr =
1083 new SpectrogramFrame(
1086 // Scale slots to pixels
1087 for(int i = 0; i < pixels; i++)
1089 // Low frequency of row
1090 int freq_index1 = (int)((pixels - i) * TOTALFREQS / pixels);
1091 // High frequency of row
1092 int freq_index2 = (int)((pixels - i + 1) * TOTALFREQS / pixels);
1093 int freq1 = Freq::tofreq(freq_index1);
1094 int freq2 = Freq::tofreq(freq_index2);
1095 float slot1_f = (float)freq1 * max_slot / niquist;
1096 float slot2_f = (float)freq2 * max_slot / niquist;
1097 int slot1 = (int)(slot1_f);
1098 int slot2 = (int)(slot2_f);
1099 slot1 = MIN(slot1, max_slot);
1100 slot2 = MIN(slot2, max_slot);
1103 // Accumulate multiple slots in the same pixel
1104 if(slot2 > slot1 + 1)
1106 for(int j = slot1; j <= slot2; j++)
1109 sum /= slot2 - slot1 + 1;
1112 // Blend 2 slots to create pixel
1114 float weight = slot1_f - floor(slot1_f);
1115 int slot3 = MIN(slot1 + 1, max_slot);
1116 sum = frame[slot1] * (1.0 - weight) +
1117 frame[slot3] * weight;
1125 if(config.normalize)
1127 // Get the maximum level in the spectrogram
1129 for(int i = 0; i < pixels; i++)
1131 if(ptr->data[i] > max) max = ptr->data[i];
1135 for(int i = 0; i < pixels; i++)
1137 ptr->data[i] = header->level *
1144 // Get the maximum level in the spectrogram
1146 for(int i = 0; i < pixels; i++)
1148 if(ptr->data[i] > max) max = ptr->data[i];
1151 // Maximum level in spectrogram is the maximum waveform level
1152 for(int i = 0; i < pixels; i++)
1154 ptr->data[i] = header->level *
1161 // for(int i = 0; i < pixels; i++)
1163 // ptr->data[i] = header->level * ptr->data[i];
1168 //printf("Spectrogram::render_gui %d ", __LINE__);
1169 for(int i = 0; i < pixels; i++)
1171 ptr->data[i] = DB::todb(ptr->data[i]);
1172 //if(i > pixels - 10) printf("%.02f ", ptr->data[i]);
1178 frame_buffer.append(ptr);
1183 thread->get_window()->unlock_window();
1187 LOAD_CONFIGURATION_MACRO(Spectrogram, SpectrogramConfig)
1189 void Spectrogram::read_data(KeyFrame *keyframe)
1192 input.set_shared_input(keyframe->xbuf);
1197 result = input.read_tag();
1201 if(input.tag.title_is("SPECTROGRAM"))
1203 config.level = input.tag.get_property("LEVEL", config.level);
1204 config.normalize = input.tag.get_property("NORMALIZE", config.normalize);
1205 config.window_size = input.tag.get_property("WINDOW_SIZE", config.window_size);
1206 config.xzoom = input.tag.get_property("XZOOM", config.xzoom);
1207 config.mode = input.tag.get_property("MODE", config.mode);
1208 config.history_size = input.tag.get_property("HISTORY_SIZE", config.history_size);
1211 w = input.tag.get_property("W", w);
1212 h = input.tag.get_property("H", h);
1219 void Spectrogram::save_data(KeyFrame *keyframe)
1222 output.set_shared_output(keyframe->xbuf);
1224 output.tag.set_title("SPECTROGRAM");
1225 output.tag.set_property("LEVEL", (double)config.level);
1226 output.tag.set_property("NORMALIZE", (double)config.normalize);
1227 output.tag.set_property("WINDOW_SIZE", (int)config.window_size);
1228 output.tag.set_property("XZOOM", (int)config.xzoom);
1229 output.tag.set_property("MODE", (int)config.mode);
1230 output.tag.set_property("HISTORY_SIZE", (int)config.history_size);
1231 output.tag.set_property("W", (int)w);
1232 output.tag.set_property("H", (int)h);
1233 output.append_tag();
1234 output.tag.set_title("/SPECTROGRAM");
1235 output.append_tag();
1236 output.append_newline();
1237 output.terminate_string();