3 * Copyright (C) 1997-2019 Adam Williams <broadcast at earthling dot net>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 #include "bcdisplayinfo.h"
29 #include "spectrogram.h"
31 #include "transportque.inc"
40 REGISTER_PLUGIN(Spectrogram)
43 #define HALF_WINDOW (config.window_size / 2)
45 SpectrogramConfig::SpectrogramConfig()
48 window_size = MAX_WINDOW;
56 int SpectrogramConfig::equivalent(SpectrogramConfig &that)
58 return EQUIV(level, that.level) &&
59 xzoom == that.xzoom &&
60 frequency == that.frequency &&
61 window_size == that.window_size &&
62 normalize == that.normalize &&
64 history_size == that.history_size;
67 void SpectrogramConfig::copy_from(SpectrogramConfig &that)
71 frequency = that.frequency;
72 window_size = that.window_size;
73 normalize = that.normalize;
75 history_size = that.history_size;
77 CLAMP(history_size, MIN_HISTORY, MAX_HISTORY);
78 CLAMP(frequency, MIN_FREQ, MAX_FREQ);
79 CLAMP(xzoom, MIN_XZOOM, MAX_XZOOM);
82 void SpectrogramConfig::interpolate(SpectrogramConfig &prev,
83 SpectrogramConfig &next,
86 int64_t current_frame)
93 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()) ) {
143 SpectrogramWindow *window = (SpectrogramWindow*)plugin->thread->window;
144 window->probe_x = -1;
145 window->probe_y = -1;
146 plugin->config.mode = text_to_mode(get_text());
147 window->canvas->clear_box(0, 0, window->canvas->get_w(), window->canvas->get_h());
148 plugin->send_configure_change();
153 const char* SpectrogramMode::mode_to_text(int mode)
158 return _("Vertical");
161 return _("Horizontal");
165 int SpectrogramMode::text_to_mode(const char *text)
167 if( !strcmp(mode_to_text(VERTICAL), text) ) return VERTICAL;
174 SpectrogramHistory::SpectrogramHistory(Spectrogram *plugin,
177 : BC_IPot(x, y, plugin->config.history_size, MIN_HISTORY, MAX_HISTORY)
179 this->plugin = plugin;
182 int SpectrogramHistory::handle_event()
184 plugin->config.history_size = get_value();
185 plugin->send_configure_change();
194 SpectrogramWindowSize::SpectrogramWindowSize(Spectrogram *plugin,
195 int x, int y, char *text)
196 : BC_PopupMenu(x, y, xS(120), text)
198 this->plugin = plugin;
201 int SpectrogramWindowSize::handle_event()
203 plugin->config.window_size = atoi(get_text());
204 plugin->send_configure_change();
209 SpectrogramWindowSizeTumbler::SpectrogramWindowSizeTumbler(Spectrogram *plugin, int x, int y)
213 this->plugin = plugin;
216 int SpectrogramWindowSizeTumbler::handle_up_event()
218 plugin->config.window_size *= 2;
219 if( plugin->config.window_size > MAX_WINDOW )
220 plugin->config.window_size = MAX_WINDOW;
221 char string[BCTEXTLEN];
222 sprintf(string, "%d", plugin->config.window_size);
223 ((SpectrogramWindow*)plugin->get_thread()->get_window())->window_size->set_text(string);
224 plugin->send_configure_change();
228 int SpectrogramWindowSizeTumbler::handle_down_event()
230 plugin->config.window_size /= 2;
231 if( plugin->config.window_size < MIN_WINDOW )
232 plugin->config.window_size = MIN_WINDOW;
233 char string[BCTEXTLEN];
234 sprintf(string, "%d", plugin->config.window_size);
235 ((SpectrogramWindow*)plugin->get_thread()->get_window())->window_size->set_text(string);
236 plugin->send_configure_change();
244 SpectrogramNormalize::SpectrogramNormalize(Spectrogram *plugin, int x, int y)
245 : BC_CheckBox(x, y, plugin->config.normalize, _("Normalize"))
247 this->plugin = plugin;
250 int SpectrogramNormalize::handle_event()
252 plugin->config.normalize = get_value();
253 plugin->send_configure_change();
260 SpectrogramFreq::SpectrogramFreq(Spectrogram *plugin, int x, int y)
261 : BC_TextBox(x, y, xS(100), 1,
262 (int)plugin->config.frequency)
264 this->plugin = plugin;
267 int SpectrogramFreq::handle_event()
269 plugin->config.frequency = atoi(get_text());
270 CLAMP(plugin->config.frequency, MIN_FREQ, MAX_FREQ);
271 plugin->send_configure_change();
279 SpectrogramXZoom::SpectrogramXZoom(Spectrogram *plugin, int x, int y)
280 : BC_IPot(x, y, plugin->config.xzoom, MIN_XZOOM, MAX_XZOOM)
282 this->plugin = plugin;
285 int SpectrogramXZoom::handle_event()
287 plugin->config.xzoom = get_value();
288 plugin->send_configure_change();
294 SpectrogramCanvas::SpectrogramCanvas(Spectrogram *plugin, int x, int y, int w, int h)
295 : BC_SubWindow(x, y, w, h, BLACK)
297 this->plugin = plugin;
298 current_operation = NONE;
301 int SpectrogramCanvas::button_press_event()
303 if( is_event_win() && cursor_inside() ) {
305 current_operation = DRAG;
306 plugin->send_configure_change();
312 int SpectrogramCanvas::button_release_event()
314 if( current_operation == DRAG ) {
315 current_operation = NONE;
321 int SpectrogramCanvas::cursor_motion_event()
323 if( current_operation == DRAG ) {
330 void SpectrogramCanvas::calculate_point()
332 int x = get_cursor_x();
333 int y = get_cursor_y();
334 CLAMP(x, 0, get_w()-1);
335 CLAMP(y, 0, get_h()-1);
337 ((SpectrogramWindow*)plugin->thread->window)->calculate_frequency(
342 //printf("SpectrogramCanvas::calculate_point %d %d\n", __LINE__, Freq::tofreq(freq_index));
345 void SpectrogramCanvas::draw_overlay()
347 SpectrogramWindow *window = (SpectrogramWindow*)plugin->thread->window;
348 if( window->probe_x >= 0 || window->probe_y >= 0 ) {
351 if( plugin->config.mode == HORIZONTAL ) draw_line(0, window->probe_y, get_w(), window->probe_y);
352 draw_line(window->probe_x, 0, window->probe_x, get_h());
364 SpectrogramWindow::SpectrogramWindow(Spectrogram *plugin)
365 : PluginClientWindow(plugin, plugin->w, plugin->h,
368 this->plugin = plugin;
369 probe_x = probe_y = -1;
372 SpectrogramWindow::~SpectrogramWindow()
376 void SpectrogramWindow::create_objects()
378 int x = plugin->get_theme()->widget_border;
382 char string[BCTEXTLEN];
386 add_subwindow(canvas = new SpectrogramCanvas(plugin,
387 0, 0, get_w(), get_h() - BC_Pot::calculate_h() * 2 -
388 plugin->get_theme()->widget_border * 3));
389 canvas->set_cursor(CROSS_CURSOR, 0, 0);
391 x = plugin->get_theme()->widget_border;
392 y = canvas->get_y() + canvas->get_h() + plugin->get_theme()->widget_border;
396 add_subwindow(level_title = new BC_Title(x, y, _("Level:")));
397 x += level_title->get_w() + plugin->get_theme()->widget_border;
398 add_subwindow(level = new SpectrogramLevel(plugin, x, y));
399 x += level->get_w() + plugin->get_theme()->widget_border;
400 y += level->get_h() + plugin->get_theme()->widget_border;
402 add_subwindow(normalize = new SpectrogramNormalize(plugin, x1, y));
404 x = x1 + level_title->get_w() + level->get_w() + plugin->get_theme()->widget_border * 2;
408 sprintf(string, "%d", plugin->config.window_size);
409 add_subwindow(window_size_title = new BC_Title(x, y, _("Window size:")));
411 x += window_size_title->get_w() + plugin->get_theme()->widget_border;
412 add_subwindow(window_size = new SpectrogramWindowSize(plugin, x, y, string));
413 x += window_size->get_w();
414 add_subwindow(window_size_tumbler = new SpectrogramWindowSizeTumbler(plugin, x, y));
416 for( int i = MIN_WINDOW; i <= MAX_WINDOW; i *= 2 ) {
417 sprintf(string, "%d", i);
418 window_size->add_item(new BC_MenuItem(string));
421 // x += window_size_tumbler->get_w() + plugin->get_theme()->widget_border;
423 y += window_size->get_h() + plugin->get_theme()->widget_border;
427 add_subwindow(mode_title = new BC_Title(x, y, _("Mode:")));
428 x += mode_title->get_w() + plugin->get_theme()->widget_border;
429 add_subwindow(mode = new SpectrogramMode(plugin,
432 mode->create_objects();
433 x += mode->get_w() + plugin->get_theme()->widget_border;
435 x = x1 = window_size_tumbler->get_x() +
436 window_size_tumbler->get_w() +
437 plugin->get_theme()->widget_border;
439 add_subwindow(history_title = new BC_Title(x, y, _("History:")));
440 x += history_title->get_w() + plugin->get_theme()->widget_border;
441 add_subwindow(history = new SpectrogramHistory(plugin,
446 y += history->get_h() + plugin->get_theme()->widget_border;
447 add_subwindow(xzoom_title = new BC_Title(x, y, _("X Zoom:")));
448 x += xzoom_title->get_w() + plugin->get_theme()->widget_border;
449 add_subwindow(xzoom = new SpectrogramXZoom(plugin, x, y));
450 x += xzoom->get_w() + plugin->get_theme()->widget_border;
454 add_subwindow(freq_title = new BC_Title(x1, y, _("Freq: 0 Hz")));
455 // x += freq_title->get_w() + plugin->get_theme()->widget_border;
456 y += freq_title->get_h() + plugin->get_theme()->widget_border;
457 // add_subwindow(freq = new SpectrogramFreq(plugin, x, y));
458 // y += freq->get_h() + plugin->get_theme()->widget_border;
460 add_subwindow(amplitude_title = new BC_Title(x, y, _("Amplitude: 0 dB")));
467 int SpectrogramWindow::resize_event(int w, int h)
469 int canvas_h = canvas->get_h();
470 int canvas_difference = get_h() - canvas_h;
472 canvas->reposition_window(0,
475 h - canvas_difference);
476 canvas->clear_box(0, 0, canvas->get_w(), canvas->get_h());
480 int y_diff = -canvas_h + canvas->get_h();
482 // Remove all columns which may be a different size.
483 // plugin->frame_buffer.remove_all_objects();
484 plugin->frame_history.remove_all_objects();
486 level_title->reposition_window(
487 level_title->get_x(),
488 level_title->get_y() + y_diff);
489 level->reposition_window(level->get_x(),
490 level->get_y() + y_diff);
492 window_size_title->reposition_window(
493 window_size_title->get_x(),
494 window_size_title->get_y() + y_diff);
496 normalize->reposition_window(normalize->get_x(),
497 normalize->get_y() + y_diff);
498 window_size->reposition_window(window_size->get_x(),
499 window_size->get_y() + y_diff);
500 window_size_tumbler->reposition_window(window_size_tumbler->get_x(),
501 window_size_tumbler->get_y() + y_diff);
505 mode_title->reposition_window(mode_title->get_x(),
506 mode_title->get_y() + y_diff);
507 mode->reposition_window(mode->get_x(),
508 mode->get_y() + y_diff);
511 history_title->reposition_window(history_title->get_x(),
512 history_title->get_y() + y_diff);
513 history->reposition_window(history->get_x(),
514 history->get_y() + y_diff);
516 xzoom_title->reposition_window(xzoom_title->get_x(),
517 xzoom_title->get_y() + y_diff);
518 xzoom->reposition_window(xzoom->get_x(),
519 xzoom->get_y() + y_diff);
520 freq_title->reposition_window(freq_title->get_x(),
521 freq_title->get_y() + y_diff);
522 // freq->reposition_window(freq->get_x(),
523 // freq->get_y() + y_diff);
524 amplitude_title->reposition_window(amplitude_title->get_x(),
525 amplitude_title->get_y() + y_diff);
534 void SpectrogramWindow::calculate_frequency(int x, int y, int do_overlay)
536 if( x < 0 && y < 0 ) return;
538 // Clear previous overlay
539 if( do_overlay ) canvas->draw_overlay();
541 // New probe position
545 // Convert to coordinates in frame history
546 int freq_pixel, time_pixel;
547 if( plugin->config.mode == VERTICAL ) {
548 freq_pixel = get_w() - x;
553 time_pixel = get_w() - x;
556 CLAMP(time_pixel, 0, plugin->frame_history.size() - 1);
557 if( plugin->frame_history.size() ) {
558 SpectrogramFrame *ptr = plugin->frame_history.get(
559 plugin->frame_history.size() - time_pixel - 1);
563 if( plugin->config.mode == VERTICAL ) {
564 pixels = canvas->get_w();
565 freq_index = (pixels - freq_pixel) * TOTALFREQS / pixels;
568 pixels = canvas->get_h();
569 freq_index = (pixels - freq_pixel) * TOTALFREQS / pixels;
572 int freq = Freq::tofreq(freq_index);
575 CLAMP(freq_pixel, 0, ptr->data_size - 1);
576 double level = ptr->data[freq_pixel];
578 char string[BCTEXTLEN];
579 sprintf(string, _("Freq: %d Hz"), freq);
580 freq_title->update(string);
582 sprintf(string, _("Amplitude: %.2f dB"), level);
583 amplitude_title->update(string);
587 canvas->draw_overlay();
594 void SpectrogramWindow::update_gui()
596 char string[BCTEXTLEN];
597 level->update(plugin->config.level);
598 sprintf(string, "%d", plugin->config.window_size);
599 window_size->set_text(string);
601 mode->set_text(mode->mode_to_text(plugin->config.mode));
602 history->update(plugin->config.history_size);
604 // sprintf(string, "%d", plugin->config.window_fragment);
605 // window_fragment->set_text(string);
607 normalize->set_value(plugin->config.normalize);
634 Spectrogram::Spectrogram(PluginServer *server)
635 : PluginAClient(server)
642 Spectrogram::~Spectrogram()
648 frame_history.remove_all_objects();
652 void Spectrogram::reset()
658 audio_buffer_start = -MAX_WINDOW * 2;
665 const char* Spectrogram::plugin_title() { return N_("Spectrogram"); }
666 int Spectrogram::is_realtime() { return 1; }
668 int Spectrogram::process_buffer(int64_t size,
670 int64_t start_position,
673 int dir = get_direction() == PLAY_REVERSE ? -1 : 1;
674 double start_pos = (double)start_position / sample_rate;
677 read_samples(buffer, 0, sample_rate, start_position, size);
679 load_configuration();
680 // Reset audio buffer
681 if( window_size != config.window_size ) {
683 window_size = config.window_size;
690 freq_real = new double[MAX_WINDOW];
691 freq_imag = new double[MAX_WINDOW];
695 audio_buffer = new Samples(MAX_WINDOW);
698 // Allocate more audio buffer
699 int needed = buffer_size + size;
700 if( audio_buffer->get_allocated() < needed ) {
701 Samples *new_samples = new Samples(needed);
702 memcpy(new_samples->get_data(),
703 audio_buffer->get_data(),
704 sizeof(double) * buffer_size);
706 audio_buffer = new_samples;
709 double *audio_samples = audio_buffer->get_data();
710 memcpy(audio_samples + buffer_size,
711 buffer->get_data(), sizeof(double) * size);
714 // Append a windows to end of GUI buffer
715 while(buffer_size >= window_size) {
717 fft->do_fft(window_size, // must be a power of 2
718 0, // 0 = forward FFT, 1 = inverse
719 audio_samples, // array of input's real samples
720 0, // array of input's imag samples
721 freq_real, // array of output's reals
724 // Get peak in waveform
726 for( int i = 0; i < window_size; i++ ) {
727 double sample = fabs(audio_samples[i]);
728 if( sample > max ) max = sample;
733 SpectrogramFrame *frame = new SpectrogramFrame(HALF_WINDOW + 1);
734 double widx = get_gui_frames();
735 frame->position = start_pos + dir * window_size * widx / get_samplerate();
736 frame->data[0] = max;
737 for( int i = 0; i < HALF_WINDOW; i++ ) {
738 frame->data[i + 1] = hypot(freq_real[i], freq_imag[i]);
740 frame->window_size = window_size;
741 frame->sample_rate = sample_rate;
742 frame->level = DB::fromdb(config.level);
743 add_gui_frame(frame);
745 // Shift audio buffer out
746 memcpy(audio_samples,
747 audio_samples + window_size,
748 (buffer_size - window_size) * sizeof(double));
750 buffer_size -= window_size;
753 last_position = start_position + dir * size;
757 void Spectrogram::render_stop()
760 audio_buffer_start = -MAX_WINDOW * 2;
761 // frame_buffer.remove_all_objects();
762 frame_history.remove_all_objects();
765 NEW_WINDOW_MACRO(Spectrogram, SpectrogramWindow)
767 void Spectrogram::update_gui()
769 if( !thread ) return;
770 SpectrogramWindow *window = (SpectrogramWindow*)thread->get_window();
771 if( !window ) return;
772 int result = load_configuration();
773 int total_frames = pending_gui_frames();
774 if( !result && !total_frames ) return;
776 window->lock_window("Spectrogram::update_gui");
777 if( result ) // widgets
778 window->update_gui();
779 if( total_frames ) { // spectrogram
780 SpectrogramCanvas *canvas = (SpectrogramCanvas*)window->canvas;
781 canvas->draw_overlay();
783 if( config.mode == HORIZONTAL ) {
785 int pixels = canvas->get_h();
786 canvas->copy_area(total_frames * config.xzoom,
788 canvas->get_w() - total_frames * config.xzoom,
793 for( int frame = 0; frame < total_frames; frame++ ) {
794 int x = canvas->get_w() - (total_frames - frame) * config.xzoom;
795 SpectrogramFrame *ptr = (SpectrogramFrame*)get_gui_frame(-1, 0);
798 for( int i = 0; i < pixels; i++ ) {
799 float db = ptr->data[MIN(i, ptr->data_size - 1)];
801 float r_out, g_out, b_out;
803 #define DIVISION1 0.0
804 #define DIVISION2 -20.0
805 #define DIVISION3 INFINITYGAIN
806 if( db > DIVISION2 ) {
807 h = 240 - (float)(db - DIVISION2) / (DIVISION1 - DIVISION2) *
812 HSV::hsv_to_rgb(r_out, g_out, b_out, h, s, v);
813 r = (int)(r_out * 0xff);
814 g = (int)(g_out * 0xff);
815 b = (int)(b_out * 0xff);
817 else if( db > DIVISION3 ) {
820 v = (float)(db - DIVISION3) / (DIVISION2 - DIVISION3);
821 HSV::hsv_to_rgb(r_out, g_out, b_out, h, s, v);
822 r = (int)(r_out * 0xff);
823 g = (int)(g_out * 0xff);
824 b = (int)(b_out * 0xff);
830 canvas->set_color((r << 16) | (g << 8) | (b));
831 if( config.xzoom == 1 )
832 canvas->draw_pixel(x, i);
834 canvas->draw_line(x, i, x + config.xzoom, i);
837 // Copy a frame into history for each pixel
838 for( int i = 0; i < config.xzoom; i++ ) {
839 SpectrogramFrame *new_frame = new SpectrogramFrame(
841 frame_history.append(new_frame);
842 memcpy(new_frame->data, ptr->data,
843 sizeof(float) * ptr->data_size);
845 // Clip history to canvas size
846 while(frame_history.size() > canvas->get_w())
847 frame_history.remove_object_number(0);
854 // Shift frames into history buffer
855 for( int frame = 0; frame < total_frames; frame++ ) {
856 SpectrogramFrame *ptr = (SpectrogramFrame*)get_gui_frame(-1, 0);
858 frame_history.append(ptr);
860 // Reduce history size
861 while(frame_history.size() > config.history_size)
862 frame_history.remove_object_number(0);
864 // Draw frames from history
865 canvas->clear_box(0, 0, canvas->get_w(), canvas->get_h());
866 for( int frame = 0; frame < frame_history.size(); frame++ ) {
867 SpectrogramFrame *ptr = frame_history.get(frame);
868 //printf("%d %d\n", canvas->get_w(), ptr->data_size);
870 int luma = (frame + 1) * 0x80 / frame_history.size();
871 if( frame == frame_history.size() - 1 ) {
872 canvas->set_color(WHITE);
873 canvas->set_line_width(2);
876 canvas->set_color((luma << 16) | (luma << 8) | luma);
878 int w = canvas->get_w();
879 int h = canvas->get_h();
882 //printf("Spectrogram::update_gui %d ", __LINE__);
884 for( int x2 = 0; x2 < w; x2++ ) {
885 float db = ptr->data[
886 MIN((w - x2), ptr->data_size - 1)];
887 //if( x2 > w - 10 ) printf("%.02f ", ptr->data[x2]);
888 int y2 = h - 1 - (int)((db - INFINITYGAIN) /
894 canvas->draw_line(x1, y1, x2, y2);
903 canvas->set_line_width(1);
908 // Recompute probe level
909 window->calculate_frequency(window->probe_x, window->probe_y, 0);
911 canvas->draw_overlay();
915 window->unlock_window();
919 // convert GUI frame to canvas dimensions & normalized DB
920 void Spectrogram::fix_gui_frame(SpectrogramFrame *frame)
922 int niquist = get_project_samplerate() / 2;
923 int total_slots = frame->window_size / 2;
924 int max_slot = total_slots - 1;
925 BC_SubWindow *canvas = ((SpectrogramWindow*)thread->get_window())->canvas;
926 int pixels = canvas->get_w();
927 if( config.mode == HORIZONTAL ) pixels = canvas->get_h();
929 // allocate new frame
930 float *out_data = new float[pixels];
931 float *in_data = frame->data;
933 // Scale slots to pixels
934 for( int i = 0; i < pixels; i++ ) {
935 // Low frequency of row
936 int freq_index1 = (int)((pixels - i) * TOTALFREQS / pixels);
937 // High frequency of row
938 int freq_index2 = (int)((pixels - i + 1) * TOTALFREQS / pixels);
939 int freq1 = Freq::tofreq(freq_index1);
940 int freq2 = Freq::tofreq(freq_index2);
941 float slot1_f = (float)freq1 * max_slot / niquist;
942 float slot2_f = (float)freq2 * max_slot / niquist;
943 int slot1 = (int)(slot1_f);
944 int slot2 = (int)(slot2_f);
945 slot1 = MIN(slot1, max_slot);
946 slot2 = MIN(slot2, max_slot);
949 // Accumulate multiple slots in the same pixel
950 if( slot2 > slot1 + 1 ) {
951 for( int j = slot1; j <= slot2; j++ )
954 sum /= slot2 - slot1 + 1;
957 // Blend 2 slots to create pixel
958 float weight = slot1_f - floor(slot1_f);
959 int slot3 = MIN(slot1 + 1, max_slot);
960 sum = in_data[slot1] * (1.0 - weight) +
961 in_data[slot3] * weight;
969 if( config.normalize ) {
970 // Get the maximum level in the spectrogram
972 for( int i = 0; i < pixels; i++ ) {
973 if( out_data[i] > max ) max = out_data[i];
977 for( int i = 0; i < pixels; i++ ) {
978 out_data[i] = frame->level *
984 // Get the maximum level in the spectrogram
986 for( int i = 0; i < pixels; i++ ) {
987 if( out_data[i] > max ) max = out_data[i];
990 // Maximum level in spectrogram is the maximum waveform level
991 float frame_max = in_data[0];
992 for( int i = 0; i < pixels; i++ ) {
993 out_data[i] = frame->level *
994 frame_max * out_data[i] / max;
999 //printf("Spectrogram::render_gui %d ", __LINE__);
1000 for( int i = 0; i < pixels; i++ ) {
1001 out_data[i] = DB::todb(out_data[i]);
1002 //if( i > pixels - 10 ) printf("%.02f ", ptr->data[i]);
1007 frame->data = out_data;
1008 frame->data_size = pixels;
1012 LOAD_CONFIGURATION_MACRO(Spectrogram, SpectrogramConfig)
1014 void Spectrogram::read_data(KeyFrame *keyframe)
1016 //printf("Spectrogram::read_data %d this=%p\n", __LINE__, this);
1018 input.set_shared_input(keyframe->xbuf);
1021 while( !(result = input.read_tag()) ) {
1022 if( input.tag.title_is("SPECTROGRAM") ) {
1023 config.level = input.tag.get_property("LEVEL", config.level);
1024 config.normalize = input.tag.get_property("NORMALIZE", config.normalize);
1025 config.window_size = input.tag.get_property("WINDOW_SIZE", config.window_size);
1026 config.xzoom = input.tag.get_property("XZOOM", config.xzoom);
1027 config.mode = input.tag.get_property("MODE", config.mode);
1028 config.history_size = input.tag.get_property("HISTORY_SIZE", config.history_size);
1029 if( is_defaults() ) {
1030 w = input.tag.get_property("W", w);
1031 h = input.tag.get_property("H", h);
1037 void Spectrogram::save_data(KeyFrame *keyframe)
1040 output.set_shared_output(keyframe->xbuf);
1042 output.tag.set_title("SPECTROGRAM");
1043 output.tag.set_property("LEVEL", (double)config.level);
1044 output.tag.set_property("NORMALIZE", (double)config.normalize);
1045 output.tag.set_property("WINDOW_SIZE", (int)config.window_size);
1046 output.tag.set_property("XZOOM", (int)config.xzoom);
1047 output.tag.set_property("MODE", (int)config.mode);
1048 output.tag.set_property("HISTORY_SIZE", (int)config.history_size);
1049 output.tag.set_property("W", (int)w);
1050 output.tag.set_property("H", (int)h);
1051 output.append_tag();
1052 output.tag.set_title("/SPECTROGRAM");
1053 output.append_tag();
1054 output.append_newline();
1055 output.terminate_string();