X-Git-Url: http://git.cinelerra-gg.org/git/?a=blobdiff_plain;f=cinelerra-5.1%2Fplugins%2Fspectrogram%2Fspectrogram.C;fp=cinelerra-5.1%2Fplugins%2Fspectrogram%2Fspectrogram.C;h=ff971a7d2b576153c9c9890202e11a610851fb85;hb=30bdb85eb33a8ee7ba675038a86c6be59c43d7bd;hp=0000000000000000000000000000000000000000;hpb=52fcc46226f9df46f9ce9d0566dc568455a7db0b;p=goodguy%2Fhistory.git diff --git a/cinelerra-5.1/plugins/spectrogram/spectrogram.C b/cinelerra-5.1/plugins/spectrogram/spectrogram.C new file mode 100644 index 00000000..ff971a7d --- /dev/null +++ b/cinelerra-5.1/plugins/spectrogram/spectrogram.C @@ -0,0 +1,1261 @@ + +/* + * CINELERRA + * Copyright (C) 1997-2011 Adam Williams + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "bcdisplayinfo.h" +#include "clip.h" +#include "cursors.h" +#include "bchash.h" +#include "filexml.h" +#include "language.h" +#include "cicolors.h" +#include "samples.h" +#include "spectrogram.h" +#include "theme.h" +#include "transportque.inc" +#include "units.h" +#include "vframe.h" + + +#include + + + +REGISTER_PLUGIN(Spectrogram) + + +#define HALF_WINDOW (config.window_size / 2) + +SpectrogramConfig::SpectrogramConfig() +{ + level = 0.0; + window_size = MAX_WINDOW; + xzoom = 1; + frequency = 440; + normalize = 0; + mode = VERTICAL; + history_size = 4; +} + +int SpectrogramConfig::equivalent(SpectrogramConfig &that) +{ + return EQUIV(level, that.level) && + xzoom == that.xzoom && + frequency == that.frequency && + window_size == that.window_size && + normalize == that.normalize && + mode == that.mode && + history_size == that.history_size; +} + +void SpectrogramConfig::copy_from(SpectrogramConfig &that) +{ + level = that.level; + xzoom = that.xzoom; + frequency = that.frequency; + window_size = that.window_size; + normalize = that.normalize; + mode = that.mode; + history_size = that.history_size; + + CLAMP(history_size, MIN_HISTORY, MAX_HISTORY); + CLAMP(frequency, MIN_FREQ, MAX_FREQ); + CLAMP(xzoom, MIN_XZOOM, MAX_XZOOM); +} + +void SpectrogramConfig::interpolate(SpectrogramConfig &prev, + SpectrogramConfig &next, + int64_t prev_frame, + int64_t next_frame, + int64_t current_frame) +{ + copy_from(prev); +} + + + +SpectrogramFrame::SpectrogramFrame(int data_size) +{ + this->data_size = data_size; + data = new float[data_size]; + force = 0; +} + +SpectrogramFrame::~SpectrogramFrame() +{ + delete [] data; +} + + + +SpectrogramLevel::SpectrogramLevel(Spectrogram *plugin, int x, int y) + : BC_FPot(x, y, plugin->config.level, INFINITYGAIN, 40) +{ + this->plugin = plugin; +} + +int SpectrogramLevel::handle_event() +{ + plugin->config.level = get_value(); + plugin->send_configure_change(); + return 1; +} + + + + + +SpectrogramMode::SpectrogramMode(Spectrogram *plugin, + int x, + int y) + : BC_PopupMenu(x, + y, + 120, + mode_to_text(plugin->config.mode)) +{ + this->plugin = plugin; +} + +void SpectrogramMode::create_objects() +{ + add_item(new BC_MenuItem(mode_to_text(VERTICAL))); + add_item(new BC_MenuItem(mode_to_text(HORIZONTAL))); +} + +int SpectrogramMode::handle_event() +{ + if(plugin->config.mode != text_to_mode(get_text())) + { + SpectrogramWindow *window = (SpectrogramWindow*)plugin->thread->window; + window->probe_x = -1; + window->probe_y = -1; + plugin->config.mode = text_to_mode(get_text()); + window->canvas->clear_box(0, 0, window->canvas->get_w(), window->canvas->get_h()); + plugin->send_configure_change(); + } + return 1; +} + +const char* SpectrogramMode::mode_to_text(int mode) +{ + switch(mode) + { + case VERTICAL: + return _("Vertical"); + case HORIZONTAL: + default: + return _("Horizontal"); + } +} + +int SpectrogramMode::text_to_mode(const char *text) +{ + if(!strcmp(mode_to_text(VERTICAL), text)) return VERTICAL; + return HORIZONTAL; +} + + + + +SpectrogramHistory::SpectrogramHistory(Spectrogram *plugin, + int x, + int y) + : BC_IPot(x, y, plugin->config.history_size, MIN_HISTORY, MAX_HISTORY) +{ + this->plugin = plugin; +} + +int SpectrogramHistory::handle_event() +{ + plugin->config.history_size = get_value(); + plugin->send_configure_change(); + return 1; +} + + + + + + +SpectrogramWindowSize::SpectrogramWindowSize(Spectrogram *plugin, + int x, + int y, + char *text) + : BC_PopupMenu(x, + y, + 80, + text) +{ + this->plugin = plugin; +} + +int SpectrogramWindowSize::handle_event() +{ + plugin->config.window_size = atoi(get_text()); + plugin->send_configure_change(); + return 1; +} + + +SpectrogramWindowSizeTumbler::SpectrogramWindowSizeTumbler(Spectrogram *plugin, int x, int y) + : BC_Tumbler(x, + y) +{ + this->plugin = plugin; +} + +int SpectrogramWindowSizeTumbler::handle_up_event() +{ + plugin->config.window_size *= 2; + if(plugin->config.window_size > MAX_WINDOW) + plugin->config.window_size = MAX_WINDOW; + char string[BCTEXTLEN]; + sprintf(string, "%d", plugin->config.window_size); + ((SpectrogramWindow*)plugin->get_thread()->get_window())->window_size->set_text(string); + plugin->send_configure_change(); + return 0; +} + +int SpectrogramWindowSizeTumbler::handle_down_event() +{ + plugin->config.window_size /= 2; + if(plugin->config.window_size < MIN_WINDOW) + plugin->config.window_size = MIN_WINDOW; + char string[BCTEXTLEN]; + sprintf(string, "%d", plugin->config.window_size); + ((SpectrogramWindow*)plugin->get_thread()->get_window())->window_size->set_text(string); + plugin->send_configure_change(); + return 1; +} + + + + + +SpectrogramNormalize::SpectrogramNormalize(Spectrogram *plugin, int x, int y) + : BC_CheckBox(x, y, plugin->config.normalize, _("Normalize")) +{ + this->plugin = plugin; +} + +int SpectrogramNormalize::handle_event() +{ + plugin->config.normalize = get_value(); + plugin->send_configure_change(); + return 1; +} + + + + +SpectrogramFreq::SpectrogramFreq(Spectrogram *plugin, int x, int y) + : BC_TextBox(x, + y, + 100, + 1, + (int)plugin->config.frequency) +{ + this->plugin = plugin; +} + +int SpectrogramFreq::handle_event() +{ + plugin->config.frequency = atoi(get_text()); + CLAMP(plugin->config.frequency, MIN_FREQ, MAX_FREQ); + plugin->send_configure_change(); + return 1; +} + + + + + +SpectrogramXZoom::SpectrogramXZoom(Spectrogram *plugin, int x, int y) + : BC_IPot(x, y, plugin->config.xzoom, MIN_XZOOM, MAX_XZOOM) +{ + this->plugin = plugin; +} + +int SpectrogramXZoom::handle_event() +{ + plugin->config.xzoom = get_value(); + plugin->send_configure_change(); + return 1; +} + + + +SpectrogramCanvas::SpectrogramCanvas(Spectrogram *plugin, int x, int y, int w, int h) + : BC_SubWindow(x, y, w, h, BLACK) +{ + this->plugin = plugin; + current_operation = NONE; +} + +int SpectrogramCanvas::button_press_event() +{ + if(is_event_win() && cursor_inside()) + { + calculate_point(); + current_operation = DRAG; + plugin->send_configure_change(); + return 1; + } + return 0; +} + +int SpectrogramCanvas::button_release_event() +{ + if(current_operation == DRAG) + { + current_operation = NONE; + return 1; + } + return 0; +} + +int SpectrogramCanvas::cursor_motion_event() +{ + if(current_operation == DRAG) + { + calculate_point(); + } + return 0; +} + + +void SpectrogramCanvas::calculate_point() +{ + int x = get_cursor_x(); + int y = get_cursor_y(); + CLAMP(x, 0, get_w() - 1); + CLAMP(y, 0, get_h() - 1); + + ((SpectrogramWindow*)plugin->thread->window)->calculate_frequency( + x, + y, + 1); + +//printf("SpectrogramCanvas::calculate_point %d %d\n", __LINE__, Freq::tofreq(freq_index)); +} + +void SpectrogramCanvas::draw_overlay() +{ + SpectrogramWindow *window = (SpectrogramWindow*)plugin->thread->window; + if(window->probe_x >= 0 || window->probe_y >= 0) + { + set_color(GREEN); + set_inverse(); + if(plugin->config.mode == HORIZONTAL) draw_line(0, window->probe_y, get_w(), window->probe_y); + draw_line(window->probe_x, 0, window->probe_x, get_h()); + set_opaque(); + } +} + + + + + + + + +SpectrogramWindow::SpectrogramWindow(Spectrogram *plugin) + : PluginClientWindow(plugin, + plugin->w, + plugin->h, + 320, + 320, + 1) +{ + this->plugin = plugin; + probe_x = probe_y = -1; +} + +SpectrogramWindow::~SpectrogramWindow() +{ +} + +void SpectrogramWindow::create_objects() +{ + int x = plugin->get_theme()->widget_border; + int y = x; + int x1 = x; + int y1 = y; + char string[BCTEXTLEN]; + + + + add_subwindow(canvas = new SpectrogramCanvas(plugin, + 0, + 0, + get_w(), + get_h() - + BC_Pot::calculate_h() * 2 - + plugin->get_theme()->widget_border * 3)); + canvas->set_cursor(CROSS_CURSOR, 0, 0); + + x = plugin->get_theme()->widget_border; + y = canvas->get_y() + canvas->get_h() + plugin->get_theme()->widget_border; + + x1 = x; + y1 = y; + add_subwindow(level_title = new BC_Title(x, y, _("Level:"))); + x += level_title->get_w() + plugin->get_theme()->widget_border; + add_subwindow(level = new SpectrogramLevel(plugin, x, y)); + x += level->get_w() + plugin->get_theme()->widget_border; + y += level->get_h() + plugin->get_theme()->widget_border; + + add_subwindow(normalize = new SpectrogramNormalize(plugin, x1, y)); + + x = x1 + level_title->get_w() + level->get_w() + plugin->get_theme()->widget_border * 2; + x1 = x; + y = y1; + + sprintf(string, "%d", plugin->config.window_size); + add_subwindow(window_size_title = new BC_Title(x, y, _("Window size:"))); + + x += window_size_title->get_w() + plugin->get_theme()->widget_border; + add_subwindow(window_size = new SpectrogramWindowSize(plugin, x, y, string)); + x += window_size->get_w(); + add_subwindow(window_size_tumbler = new SpectrogramWindowSizeTumbler(plugin, x, y)); + + for(int i = MIN_WINDOW; i <= MAX_WINDOW; i *= 2) + { + sprintf(string, "%d", i); + window_size->add_item(new BC_MenuItem(string)); + } + +// x += window_size_tumbler->get_w() + plugin->get_theme()->widget_border; + x = x1; + y += window_size->get_h() + plugin->get_theme()->widget_border; + + + + add_subwindow(mode_title = new BC_Title(x, y, _("Mode:"))); + x += mode_title->get_w() + plugin->get_theme()->widget_border; + add_subwindow(mode = new SpectrogramMode(plugin, + x, + y)); + mode->create_objects(); + x += mode->get_w() + plugin->get_theme()->widget_border; + + x = x1 = window_size_tumbler->get_x() + + window_size_tumbler->get_w() + + plugin->get_theme()->widget_border; + y = y1; + add_subwindow(history_title = new BC_Title(x, y, _("History:"))); + x += history_title->get_w() + plugin->get_theme()->widget_border; + add_subwindow(history = new SpectrogramHistory(plugin, + x, + y)); + + x = x1; + y += history->get_h() + plugin->get_theme()->widget_border; + add_subwindow(xzoom_title = new BC_Title(x, y, _("X Zoom:"))); + x += xzoom_title->get_w() + plugin->get_theme()->widget_border; + add_subwindow(xzoom = new SpectrogramXZoom(plugin, x, y)); + x += xzoom->get_w() + plugin->get_theme()->widget_border; + + y = y1; + x1 = x; + add_subwindow(freq_title = new BC_Title(x1, y, _("Freq: 0 Hz"))); +// x += freq_title->get_w() + plugin->get_theme()->widget_border; + y += freq_title->get_h() + plugin->get_theme()->widget_border; +// add_subwindow(freq = new SpectrogramFreq(plugin, x, y)); +// y += freq->get_h() + plugin->get_theme()->widget_border; + x = x1; + add_subwindow(amplitude_title = new BC_Title(x, y, _("Amplitude: 0 dB"))); + + + + show_window(); +} + +int SpectrogramWindow::resize_event(int w, int h) +{ + int canvas_h = canvas->get_h(); + int canvas_difference = get_h() - canvas_h; + + canvas->reposition_window(0, + 0, + w, + h - canvas_difference); + canvas->clear_box(0, 0, canvas->get_w(), canvas->get_h()); + probe_x = -1; + probe_y = -1; + + int y_diff = -canvas_h + canvas->get_h(); + +// Remove all columns which may be a different size. + plugin->frame_buffer.remove_all_objects(); + plugin->frame_history.remove_all_objects(); + + level_title->reposition_window( + level_title->get_x(), + level_title->get_y() + y_diff); + level->reposition_window(level->get_x(), + level->get_y() + y_diff); + + window_size_title->reposition_window( + window_size_title->get_x(), + window_size_title->get_y() + y_diff); + + normalize->reposition_window(normalize->get_x(), + normalize->get_y() + y_diff); + window_size->reposition_window(window_size->get_x(), + window_size->get_y() + y_diff); + window_size_tumbler->reposition_window(window_size_tumbler->get_x(), + window_size_tumbler->get_y() + y_diff); + + + + mode_title->reposition_window(mode_title->get_x(), + mode_title->get_y() + y_diff); + mode->reposition_window(mode->get_x(), + mode->get_y() + y_diff); + + + history_title->reposition_window(history_title->get_x(), + history_title->get_y() + y_diff); + history->reposition_window(history->get_x(), + history->get_y() + y_diff); + + xzoom_title->reposition_window(xzoom_title->get_x(), + xzoom_title->get_y() + y_diff); + xzoom->reposition_window(xzoom->get_x(), + xzoom->get_y() + y_diff); + freq_title->reposition_window(freq_title->get_x(), + freq_title->get_y() + y_diff); +// freq->reposition_window(freq->get_x(), +// freq->get_y() + y_diff); + amplitude_title->reposition_window(amplitude_title->get_x(), + amplitude_title->get_y() + y_diff); + + flush(); + plugin->w = w; + plugin->h = h; + return 0; +} + + +void SpectrogramWindow::calculate_frequency(int x, int y, int do_overlay) +{ + if(x < 0 && y < 0) return; + +// Clear previous overlay + if(do_overlay) canvas->draw_overlay(); + +// New probe position + probe_x = x; + probe_y = y; + +// Convert to coordinates in frame history + int freq_pixel, time_pixel; + if(plugin->config.mode == VERTICAL) + { + freq_pixel = get_w() - x; + time_pixel = 0; + } + else + { + freq_pixel = y; + time_pixel = get_w() - x; + } + + CLAMP(time_pixel, 0, plugin->frame_history.size() - 1); + if(plugin->frame_history.size()) + { + SpectrogramFrame *ptr = plugin->frame_history.get( + plugin->frame_history.size() - time_pixel - 1); + + int pixels; + int freq_index; + if(plugin->config.mode == VERTICAL) + { + pixels = canvas->get_w(); + freq_index = (pixels - freq_pixel) * TOTALFREQS / pixels; + } + else + { + pixels = canvas->get_h(); + freq_index = (pixels - freq_pixel) * TOTALFREQS / pixels; + } + + int freq = Freq::tofreq(freq_index); + + + CLAMP(freq_pixel, 0, ptr->data_size - 1); + double level = ptr->data[freq_pixel]; + + char string[BCTEXTLEN]; + sprintf(string, _("Freq: %d Hz"), freq); + freq_title->update(string); + + sprintf(string, _("Amplitude: %.2f dB"), level); + amplitude_title->update(string); + } + + if(do_overlay) + { + canvas->draw_overlay(); + canvas->flash(); + } +} + + + +void SpectrogramWindow::update_gui() +{ + char string[BCTEXTLEN]; + level->update(plugin->config.level); + sprintf(string, "%d", plugin->config.window_size); + window_size->set_text(string); + + mode->set_text(mode->mode_to_text(plugin->config.mode)); + history->update(plugin->config.history_size); + +// sprintf(string, "%d", plugin->config.window_fragment); +// window_fragment->set_text(string); + + normalize->set_value(plugin->config.normalize); +} + + + + + + + + + + + + + + + + + + + + + + + + + +Spectrogram::Spectrogram(PluginServer *server) + : PluginAClient(server) +{ + reset(); + timer = new Timer; + w = 640; + h = 480; +} + +Spectrogram::~Spectrogram() +{ + delete fft; + delete [] data; + delete audio_buffer; + delete [] freq_real; + delete [] freq_imag; + delete timer; + frame_buffer.remove_all_objects(); + frame_history.remove_all_objects(); +} + + +void Spectrogram::reset() +{ + thread = 0; + fft = 0; + done = 0; + data = 0; + audio_buffer = 0; + audio_buffer_start = -MAX_WINDOW * 2; + freq_real = 0; + freq_imag = 0; + allocated_data = 0; + total_windows = 0; + bzero(&header, sizeof(data_header_t)); +} + + +const char* Spectrogram::plugin_title() { return _("Spectrogram"); } +int Spectrogram::is_realtime() { return 1; } + +int Spectrogram::process_buffer(int64_t size, + Samples *buffer, + int64_t start_position, + int sample_rate) +{ +// Pass through + read_samples(buffer, + 0, + sample_rate, + start_position, + size); + + + load_configuration(); + +// Reset audio buffer + if(window_size != config.window_size) + { + render_stop(); + window_size = config.window_size; + } + + + + + + if(!fft) + { + fft = new FFT; + } + + if(!data) + { + data = new unsigned char[sizeof(data_header_t)]; + allocated_data = 0; + } + + if(!freq_real) + { + freq_real = new double[MAX_WINDOW]; + freq_imag = new double[MAX_WINDOW]; + } + + if(!audio_buffer) + { + audio_buffer = new Samples(MAX_WINDOW); + } + + +// Accumulate audio + int needed = buffer_size + size; + if(audio_buffer->get_allocated() < needed) + { + Samples *new_samples = new Samples(needed); + memcpy(new_samples->get_data(), + audio_buffer->get_data(), + sizeof(double) * buffer_size); + delete audio_buffer; + audio_buffer = new_samples; + } + + double *audio_samples = audio_buffer->get_data(); + memcpy(audio_samples + buffer_size, + buffer->get_data(), + sizeof(double) * size); + buffer_size += size; + + +//printf("Spectrogram::process_buffer %d %d\n", __LINE__, buffer_size); + +// Append a windows to end of GUI buffer + total_windows = 0; + while(buffer_size >= window_size) + { +// Process FFT + fft->do_fft(window_size, // must be a power of 2 + 0, // 0 = forward FFT, 1 = inverse + audio_samples, // array of input's real samples + 0, // array of input's imag samples + freq_real, // array of output's reals + freq_imag); + +// Get peak in waveform + double max = 0; + for(int i = 0; i < window_size; i++) + { + double sample = fabs(audio_samples[i]); + if(sample > max) max = sample; + } + +// Append to end of data buffer + if(allocated_data < (total_windows + 1) * (HALF_WINDOW + 1)) + { + int new_allocation = (total_windows + 1) * (HALF_WINDOW + 1); + unsigned char *new_data = new unsigned char[sizeof(data_header_t) + + sizeof(float) * new_allocation]; + data_header_t *new_header = (data_header_t*)new_data; + data_header_t *old_header = (data_header_t*)data; + memcpy(new_header->samples, + old_header->samples, + sizeof(float) * allocated_data); + delete [] data; + data = new_data; + allocated_data = new_allocation; + } + + data_header_t *header = (data_header_t*)data; + float *sample_output = header->samples + total_windows * (HALF_WINDOW + 1); +// 1st sample is maximum + sample_output[0] = max; + for(int i = 0; i < HALF_WINDOW; i++) + { + sample_output[i + 1] = sqrt(freq_real[i] * freq_real[i] + + freq_imag[i] * freq_imag[i]); +// sample_output[i + 1] = freq_real[i]; + } + +// Shift audio buffer out + memcpy(audio_samples, + audio_samples + window_size, + (buffer_size - window_size) * sizeof(double)); + + total_windows++; + buffer_size -= window_size; + } + + data_header_t *header = (data_header_t*)data; + header->window_size = window_size; + header->sample_rate = sample_rate; + header->total_windows = total_windows; +// Linear output level + header->level = DB::fromdb(config.level); + + send_render_gui(data, + sizeof(data_header_t) + + sizeof(float) * total_windows * (HALF_WINDOW + 1)); + + return 0; +} + +void Spectrogram::render_stop() +{ + buffer_size = 0; + audio_buffer_start = -MAX_WINDOW * 2; + frame_buffer.remove_all_objects(); + frame_history.remove_all_objects(); +} + + + + +NEW_WINDOW_MACRO(Spectrogram, SpectrogramWindow) + +void Spectrogram::update_gui() +{ + if(thread) + { + int result = load_configuration(); + SpectrogramWindow *window = (SpectrogramWindow*)thread->get_window(); + window->lock_window("Spectrogram::update_gui"); + if(result) window->update_gui(); + + +//printf("Spectrogram::update_gui %d\n", __LINE__); +// Shift in accumulated canvas columns + if(frame_buffer.size()) + { + SpectrogramCanvas *canvas = (SpectrogramCanvas*)window->canvas; + canvas->draw_overlay(); +// Z to draw in this iteration + int total_frames = timer->get_difference() * + header.sample_rate / + header.window_size / + 1000; + +//printf("Spectrogram::update_gui %d %d %ld\n", __LINE__, frame_buffer.size(), timer->get_difference()); + if(total_frames) timer->subtract(total_frames * + header.window_size * + 1000 / + header.sample_rate); + +// Add forced column drawing + for(int i = 0; i < frame_buffer.size(); i++) + if(frame_buffer.get(i)->force) total_frames++; + total_frames = MIN(frame_buffer.size(), total_frames); + +// Limit to canvas width + if(config.mode == HORIZONTAL) + total_frames = MIN(canvas->get_w(), total_frames); + + + + if(config.mode == HORIZONTAL) + { +// Shift left + int pixels = canvas->get_h(); + canvas->copy_area(total_frames * config.xzoom, + 0, + 0, + 0, + canvas->get_w() - total_frames * config.xzoom, + canvas->get_h()); + + +// Draw new columns + for(int frame = 0; + frame < total_frames; + frame++) + { + int x = canvas->get_w() - (total_frames - frame) * config.xzoom; + SpectrogramFrame *ptr = frame_buffer.get(0); + + for(int i = 0; i < pixels; i++) + { + float db = ptr->data[ + MIN(i, ptr->data_size - 1)]; + float h, s, v; + float r_out, g_out, b_out; + int r, g, b; + +#define DIVISION1 0.0 +#define DIVISION2 -20.0 +#define DIVISION3 INFINITYGAIN + if(db > DIVISION2) + { + h = 240 - (float)(db - DIVISION2) / (DIVISION1 - DIVISION2) * + 240; + CLAMP(h, 0, 240); + s = 1.0; + v = 1.0; + HSV::hsv_to_rgb(r_out, g_out, b_out, h, s, v); + r = (int)(r_out * 0xff); + g = (int)(g_out * 0xff); + b = (int)(b_out * 0xff); + } + else + if(db > DIVISION3) + { + h = 0.0; + s = 0.0; + v = (float)(db - DIVISION3) / (DIVISION2 - DIVISION3); + HSV::hsv_to_rgb(r_out, g_out, b_out, h, s, v); + r = (int)(r_out * 0xff); + g = (int)(g_out * 0xff); + b = (int)(b_out * 0xff); + } + else + { + r = g = b = 0; + } + + canvas->set_color((r << 16) | + (g << 8) | + (b)); + if(config.xzoom == 1) + canvas->draw_pixel(x, i); + else + canvas->draw_line(x, + i, + x + config.xzoom, + i); + } + +// Push frames onto history + for(int i = 0; i < config.xzoom; i++) + { + SpectrogramFrame *new_frame = new SpectrogramFrame( + ptr->data_size); + frame_history.append(new_frame); + memcpy(new_frame->data, ptr->data, sizeof(float) * ptr->data_size); + } + +// Clip history to canvas size + while(frame_history.size() > canvas->get_w()) + frame_history.remove_object_number(0); + + frame_buffer.remove_object_number(0); + } + } + else +// mode == VERTICAL + { +// Shift frames into history + for(int frame = 0; frame < total_frames; frame++) + { + if(frame_history.size() >= config.history_size) + frame_history.remove_object_number(0); + + frame_history.append(frame_buffer.get(0)); + frame_buffer.remove_number(0); + } + +// Reduce history size + while(frame_history.size() > config.history_size) + frame_history.remove_object_number(0); + +// Draw frames from history + canvas->clear_box(0, 0, canvas->get_w(), canvas->get_h()); + for(int frame = 0; frame < frame_history.size(); frame++) + { + SpectrogramFrame *ptr = frame_history.get(frame); +//printf("%d %d\n", canvas->get_w(), ptr->data_size); + + int luma = (frame + 1) * 0x80 / frame_history.size(); + if(frame == frame_history.size() - 1) + { + canvas->set_color(WHITE); + canvas->set_line_width(2); + } + else + canvas->set_color((luma << 16) | + (luma << 8) | + luma); + + + int x1 = 0; + int y1 = 0; + int w = canvas->get_w(); + int h = canvas->get_h(); + int number = 0; + +//printf("Spectrogram::update_gui %d ", __LINE__); + + for(int x2 = 0; x2 < w; x2++) + { + float db = ptr->data[ + MIN((w - x2), ptr->data_size - 1)]; +//if(x2 > w - 10) printf("%.02f ", ptr->data[x2]); + int y2 = h - 1 - (int)((db - INFINITYGAIN) / + (0 - INFINITYGAIN) * + h); + CLAMP(y2, 0, h - 1); + + if(number) + { + canvas->draw_line(x1, y1, x2, y2); + } + else + { + number++; + } + + x1 = x2; + y1 = y2; + } + + canvas->set_line_width(1); +//printf("\n"); + } + + } + + +// Recompute probe level + window->calculate_frequency(window->probe_x, window->probe_y, 0); + + canvas->draw_overlay(); + canvas->flash(); + } + + while(frame_buffer.size() > MAX_COLUMNS) + frame_buffer.remove_object_number(0); + + window->unlock_window(); + } +} + +void Spectrogram::render_gui(void *data, int size) +{ + if(thread) + { + thread->get_window()->lock_window("Spectrogram::render_gui"); + data_header_t *header = (data_header_t*)data; + memcpy(&this->header, header, sizeof(data_header_t)); + BC_SubWindow *canvas = ((SpectrogramWindow*)thread->get_window())->canvas; + int pixels = canvas->get_w(); + if(config.mode == HORIZONTAL) pixels = canvas->get_h(); + +// Set all previous columns to draw immediately + for(int i = 0; i < frame_buffer.size(); i++) + frame_buffer.get(i)->force = 1; + + + for(int current_window = 0; + current_window < header->total_windows; + current_window++) + { + float *frame = header->samples + + current_window * (header->window_size / 2 + 1); + float frame_max = *frame; + frame++; + int niquist = get_project_samplerate() / 2; + int total_slots = header->window_size / 2; + int max_slot = total_slots - 1; +// int slot1 = total_slots - 1; + SpectrogramFrame *ptr = + new SpectrogramFrame( + pixels); + +// Scale slots to pixels + for(int i = 0; i < pixels; i++) + { +// Low frequency of row + int freq_index1 = (int)((pixels - i) * TOTALFREQS / pixels); +// High frequency of row + int freq_index2 = (int)((pixels - i + 1) * TOTALFREQS / pixels); + int freq1 = Freq::tofreq(freq_index1); + int freq2 = Freq::tofreq(freq_index2); + float slot1_f = (float)freq1 * max_slot / niquist; + float slot2_f = (float)freq2 * max_slot / niquist; + int slot1 = (int)(slot1_f); + int slot2 = (int)(slot2_f); + slot1 = MIN(slot1, max_slot); + slot2 = MIN(slot2, max_slot); + double sum = 0; + +// Accumulate multiple slots in the same pixel + if(slot2 > slot1 + 1) + { + for(int j = slot1; j <= slot2; j++) + sum += frame[j]; + + sum /= slot2 - slot1 + 1; + } + else +// Blend 2 slots to create pixel + { + float weight = slot1_f - floor(slot1_f); + int slot3 = MIN(slot1 + 1, max_slot); + sum = frame[slot1] * (1.0 - weight) + + frame[slot3] * weight; + } + + ptr->data[i] = sum; +// slot1 = slot2; + } + +// Normalize + if(config.normalize) + { +// Get the maximum level in the spectrogram + float max = 0; + for(int i = 0; i < pixels; i++) + { + if(ptr->data[i] > max) max = ptr->data[i]; + } + +// Scale all levels + for(int i = 0; i < pixels; i++) + { + ptr->data[i] = header->level * + ptr->data[i] / + max; + } + } + else + { +// Get the maximum level in the spectrogram + float max = 0; + for(int i = 0; i < pixels; i++) + { + if(ptr->data[i] > max) max = ptr->data[i]; + } + +// Maximum level in spectrogram is the maximum waveform level + for(int i = 0; i < pixels; i++) + { + ptr->data[i] = header->level * + frame_max * + ptr->data[i] / + max; + } + + +// for(int i = 0; i < pixels; i++) +// { +// ptr->data[i] = header->level * ptr->data[i]; +// } + } + +// DB conversion +//printf("Spectrogram::render_gui %d ", __LINE__); + for(int i = 0; i < pixels; i++) + { + ptr->data[i] = DB::todb(ptr->data[i]); +//if(i > pixels - 10) printf("%.02f ", ptr->data[i]); + + } +//printf("\n"); + + + frame_buffer.append(ptr); + total_windows++; + } + + timer->update(); + thread->get_window()->unlock_window(); + } +} + +LOAD_CONFIGURATION_MACRO(Spectrogram, SpectrogramConfig) + +void Spectrogram::read_data(KeyFrame *keyframe) +{ + FileXML input; + input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data())); + + int result = 0; + while(!result) + { + result = input.read_tag(); + + if(!result) + { + if(input.tag.title_is("SPECTROGRAM")) + { + config.level = input.tag.get_property("LEVEL", config.level); + config.normalize = input.tag.get_property("NORMALIZE", config.normalize); + config.window_size = input.tag.get_property("WINDOW_SIZE", config.window_size); + config.xzoom = input.tag.get_property("XZOOM", config.xzoom); + config.mode = input.tag.get_property("MODE", config.mode); + config.history_size = input.tag.get_property("HISTORY_SIZE", config.history_size); + if(is_defaults()) + { + w = input.tag.get_property("W", w); + h = input.tag.get_property("H", h); + } + } + } + } +} + +void Spectrogram::save_data(KeyFrame *keyframe) +{ + FileXML output; + output.set_shared_output(keyframe->get_data(), MESSAGESIZE); + + output.tag.set_title("SPECTROGRAM"); + output.tag.set_property("LEVEL", (double)config.level); + output.tag.set_property("NORMALIZE", (double)config.normalize); + output.tag.set_property("WINDOW_SIZE", (int)config.window_size); + output.tag.set_property("XZOOM", (int)config.xzoom); + output.tag.set_property("MODE", (int)config.mode); + output.tag.set_property("HISTORY_SIZE", (int)config.history_size); + output.tag.set_property("W", (int)w); + output.tag.set_property("H", (int)h); + output.append_tag(); + output.tag.set_title("/SPECTROGRAM"); + output.append_tag(); + output.append_newline(); + output.terminate_string(); +} + + + + +