/* * CINELERRA * Copyright (C) 2008 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 "bcbutton.h" #include "bcmeter.h" #include "bcpixmap.h" #include "bcresources.h" #include "bcwindow.h" #include "colors.h" #include "fonts.h" #include "vframe.h" #include // Which source image to replicate #define METER_NORMAL 0 #define METER_GREEN 1 #define METER_RED 2 #define METER_YELLOW 3 #define METER_WHITE 4 #define METER_OVER 5 #define METER_DOWNMIX 6 // Region of source image to replicate #define METER_LEFT 0 #define METER_MID 1 #define METER_RIGHT 3 BC_Meter::BC_Meter(int x, int y, int orientation, int pixels, int min, int max, int mode, int use_titles, int span, int is_downmix, int is_gain_change) : BC_SubWindow(x, y, -1, -1) { this->over_delay = 150; this->peak_delay = 15; this->use_titles = use_titles; this->min = min; this->max = max; this->mode = mode; this->orientation = orientation; this->pixels = pixels; this->span = span; this->is_downmix = is_downmix; this->is_gain_change = is_gain_change; //printf("BC_Meter::draw_face %d w=%d pixels=%d\n", __LINE__, w, pixels); for( int i = 0; i < TOTAL_METER_IMAGES; i++ ) images[i] = 0; db_titles.set_array_delete(); } BC_Meter::~BC_Meter() { db_titles.remove_all_objects(); title_pixels.remove_all(); tick_w.remove_all(); tick_pixels.remove_all(); for( int i = 0; i < TOTAL_METER_IMAGES; i++ ) delete images[i]; } int BC_Meter::get_title_w() { return get_resources()->meter_title_w; } int BC_Meter::get_meter_w() { return get_resources()->ymeter_images[0]->get_w(); } int BC_Meter::set_delays(int over_delay, int peak_delay) { this->over_delay = over_delay; this->peak_delay = peak_delay; return 0; } int BC_Meter::initialize() { peak_timer = 0; level_pixel = peak_pixel = 0; over_timer = 0; over_count = 0; if( is_gain_change ) { peak = level = 0; } else { peak = level = -100; } if( orientation == METER_VERT ) { set_images(get_resources()->ymeter_images); h = pixels; if( span < 0 ) { w = images[0]->get_w(); if( use_titles ) w += get_title_w(); } else w = span; } else { set_images(get_resources()->xmeter_images); h = images[0]->get_h(); w = pixels; if( use_titles ) h += get_title_w(); } // calibrate the db titles get_divisions(); BC_SubWindow::initialize(); draw_titles(0); draw_face(0); show_window(0); return 0; } void BC_Meter::set_images(VFrame **data) { for( int i = 0; i < TOTAL_METER_IMAGES; i++ ) delete images[i]; for( int i = 0; i < TOTAL_METER_IMAGES; i++ ) images[i] = new BC_Pixmap(parent_window, data[i], PIXMAP_ALPHA); } int BC_Meter::reposition_window(int x, int y, int span, int pixels) { if( pixels < 0 ) pixels = this->pixels; this->span = span; this->pixels = pixels; if( orientation == METER_VERT ) BC_SubWindow::reposition_window(x, y, this->span < 0 ? w : span, pixels); else BC_SubWindow::reposition_window(x, y, pixels, get_h()); //printf("BC_Meter::reposition_window 1 %d %d %d %d\n", x, y, w, h); get_divisions(); //set_color(WHITE); //draw_box(0, 0, w, h); //flash(); //return 0; draw_titles(0); draw_face(0); return 0; } int BC_Meter::reset(int downmix) { level = min; peak = min; level_pixel = peak_pixel = 0; peak_timer = 0; over_timer = 0; over_count = 0; if( downmix >= 0 ) is_downmix = downmix; draw_face(1); return 0; } int BC_Meter::button_press_event() { if( cursor_inside() && is_event_win() ) { reset_over(); return 1; } return 0; } int BC_Meter::reset_over() { over_timer = 0; return 0; } int BC_Meter::change_format(int mode, int min, int max) { this->mode = mode; this->min = min; this->max = max; reposition_window(get_x(), get_y(), w, pixels); return 0; } int BC_Meter::level_to_pixel(float level) { int result; if( mode == METER_DB ) { result = (int)(pixels * (level - min) / (max - min)); if( level <= min ) result = 0; } else { // Not implemented anymore result = 0; } return result; } void BC_Meter::get_divisions() { char string[BCTEXTLEN]; char *new_string; db_titles.remove_all_objects(); title_pixels.remove_all(); tick_pixels.remove_all(); tick_w.remove_all(); low_division = 0; medium_division = 0; high_division = pixels; int current_pixel; // Create tick marks and titles in one pass for( int current = min; current <= max; current++ ) { if( orientation == METER_VERT ) { // Create tick mark current_pixel = (pixels - METER_MARGIN * 2 - 2) * (current - min) / (max - min) + 2; tick_pixels.append(current_pixel); // Create titles in selected positions if( current == min || current == max || current == 0 || (current - min > 4 && max - current > 4 && !(current % 5)) ) { int title_pixel = (pixels - METER_MARGIN * 2) * (current - min) / (max - min); sprintf(string, "%d", (int)labs(current)); new_string = new char[strlen(string) + 1]; strcpy(new_string, string); db_titles.append(new_string); title_pixels.append(title_pixel); tick_w.append(TICK_W1); } else { tick_w.append(TICK_W2); } } else { current_pixel = (pixels - METER_MARGIN * 2) * (current - min) / (max - min); tick_pixels.append(current_pixel); tick_w.append(TICK_W1); // Titles not supported for horizontal } // Create color divisions if( current == -20 ) { low_division = current_pixel; } else if( current == -5 ) { medium_division = current_pixel; } else if( current == 0 ) { high_division = current_pixel; } } // if( orientation == METER_VERT ) // printf("BC_Meter::get_divisions %d %d %d %d\n", // low_division, medium_division, high_division, pixels); } void BC_Meter::draw_titles(int flush) { if( !use_titles ) return; int tick_xfudge = xS(1); set_font(get_resources()->meter_font); if( orientation == METER_HORIZ ) { draw_top_background(parent_window, 0, 0, get_w(), get_title_w()); for( int i = 0; i < db_titles.total; i++ ) { draw_text(0, title_pixels.values[i], db_titles.values[i]); } flash(0, 0, get_w(), get_title_w(), flush); } else if( orientation == METER_VERT ) { draw_top_background(parent_window, 0, 0, get_title_w(), get_h()); // Titles for( int i = 0; i < db_titles.total; i++ ) { int title_y = pixels - title_pixels.values[i]; if( i == 0 ) title_y -= get_text_descent(get_resources()->meter_font); else if( i == db_titles.total - 1 ) title_y += get_text_ascent(get_resources()->meter_font); else title_y += get_text_ascent(get_resources()->meter_font) / 2; int title_x = get_title_w() - TICK_W1 - tick_xfudge - get_text_width(get_resources()->meter_font, db_titles.values[i]); set_color(get_resources()->meter_font_color); draw_text(title_x, title_y, db_titles.values[i]); } for( int i = 0; i < tick_pixels.total; i++ ) { // Tick marks int tick_y = pixels - tick_pixels.values[i] - METER_MARGIN; set_color(get_resources()->meter_font_color); draw_line(get_title_w() - tick_w.get(i) - tick_xfudge, tick_y, get_title_w() - tick_xfudge, tick_y); if( get_resources()->meter_3d ) { set_color(BLACK); draw_line(get_title_w() - tick_w.get(i), tick_y + 1, get_title_w(), tick_y + 1); } } flash(0, 0, get_title_w(), get_h(), flush); } } int BC_Meter::region_pixel(int region) { VFrame **reference_images = get_resources()->xmeter_images; int result = 0; if( region == METER_RIGHT ) result = region * reference_images[0]->get_w() / 4; else result = region * reference_images[0]->get_w() / 4; return result; } int BC_Meter::region_pixels(int region) { int x1; int x2; int result; VFrame **reference_images = get_resources()->xmeter_images; x1 = region * reference_images[0]->get_w() / 4; x2 = (region + 1) * reference_images[0]->get_w() / 4; if( region == METER_MID ) result = (x2 - x1) * 2; else result = x2 - x1; return result; } void BC_Meter::draw_face(int flush) { int level_pixel = level_to_pixel(level); int peak_pixel2 = level_to_pixel(peak); int peak_pixel1 = peak_pixel2 - 2; int left_pixel = region_pixel(METER_MID); int right_pixel = pixels - region_pixels(METER_RIGHT); int pixel = 0; int image_number = 0, region = 0; int in_span, in_start; int x = use_titles ? get_title_w() : 0; int w = use_titles ? (this->w - get_title_w()) : this->w; draw_top_background(parent_window, x, 0, w, h); // printf("BC_Meter::draw_face %d span=%d this->w=%d get_title_w()=%d %d %d\n", // __LINE__, span, this->w, get_title_w(), w, h); if( is_gain_change ) { int in_h = images[0]->get_h(); int in_third = in_h / 3; int in_third3 = in_h - in_third * 2; // printf("BC_Meter::draw_face %d level=%f level_pixel=%d high_division=%d\n", // __LINE__, level, level_pixel, high_division); // fudge a line when no gain change if( level_pixel == high_division ) { level_pixel += 1; } while( pixel < pixels ) { // Select image to draw & extents if( level_pixel < high_division ) { // always vertical if( pixel < level_pixel ) { image_number = METER_NORMAL; in_span = level_pixel - pixel; } else if( pixel < high_division ) { image_number = METER_RED; in_span = high_division - pixel; } else { image_number = METER_NORMAL; in_span = pixels - pixel; } } else { // determine pixel range & image to draw if( pixel < high_division ) { image_number = METER_NORMAL; in_span = high_division - pixel; } else if( pixel < level_pixel ) { image_number = METER_GREEN; in_span = level_pixel - pixel; } else { image_number = METER_NORMAL; in_span = pixels - pixel; } } // determine starting point in source to draw // draw starting section if( pixel == 0 ) { in_start = 0; } else // draw middle section if( pixels - pixel > in_third3 ) { in_start = in_third; } else // draw last section { in_start = in_third * 2; } // clamp the region to the source dimensions if( in_start < in_third * 2 ) { if( in_span > in_third ) { in_span = in_third; } } else // last segment if( pixels - pixel < in_third3 ) { in_span = pixels - pixel; in_start = in_h - in_span; } // printf("BC_Meter::draw_face %d dst_y=%d src_y=%d" // " pixels=%d pixel=%d in_h=%d in_start=%d in_span=%d in_third=%d in_third3=%d\n", // __LINE__, get_h() - pixel - in_span, in_h - in_start - in_span, // pixels, pixel, in_h, in_start, in_span, in_third, in_third3); draw_pixmap(images[image_number], x, get_h() - pixel - in_span, get_w(), in_span + 1, 0, in_h - in_start - in_span); pixel += in_span; } } else { while( pixel < pixels ) { // Select image to draw if( pixel < level_pixel || (pixel >= peak_pixel1 && pixel < peak_pixel2) ) { if( pixel < low_division ) image_number = METER_GREEN; else if( pixel < medium_division ) image_number = METER_YELLOW; else if( pixel < high_division ) image_number = METER_RED; else image_number = METER_WHITE; } else { image_number = METER_NORMAL; } // Select region of image to duplicate if( pixel < left_pixel ) { region = METER_LEFT; in_start = pixel + region_pixel(region); in_span = region_pixels(region) - (in_start - region_pixel(region)); } else if( pixel < right_pixel ) { region = METER_MID; in_start = region_pixel(region); in_span = region_pixels(region); } else { region = METER_RIGHT; in_start = (pixel - right_pixel) + region_pixel(region); in_span = region_pixels(region) - (in_start - region_pixel(region));; } //printf("BC_Meter::draw_face region %d pixel %d pixels %d in_start %d in_span %d\n", region, pixel, pixels, in_start, in_span); if( in_span > 0 ) { // Clip length to peaks if( pixel < level_pixel && pixel + in_span > level_pixel ) in_span = level_pixel - pixel; else if( pixel < peak_pixel1 && pixel + in_span > peak_pixel1 ) in_span = peak_pixel1 - pixel; else if( pixel < peak_pixel2 && pixel + in_span > peak_pixel2 ) in_span = peak_pixel2 - pixel; // Clip length to color changes if( image_number == METER_GREEN && pixel + in_span > low_division ) in_span = low_division - pixel; else if( image_number == METER_YELLOW && pixel + in_span > medium_division ) in_span = medium_division - pixel; else if( image_number == METER_RED && pixel + in_span > high_division ) in_span = high_division - pixel; // Clip length to regions if( pixel < left_pixel && pixel + in_span > left_pixel ) in_span = left_pixel - pixel; else if( pixel < right_pixel && pixel + in_span > right_pixel ) in_span = right_pixel - pixel; //printf("BC_Meter::draw_face image_number %d pixel %d pixels %d in_start %d in_span %d\n", image_number, pixel, pixels, in_start, in_span); //printf("BC_Meter::draw_face %d %d %d %d\n", orientation, region, images[image_number]->get_h() - in_start - in_span); if( orientation == METER_HORIZ ) { draw_pixmap(images[image_number], pixel, x, in_span + 1, get_h(), in_start, 0); } else { //printf("BC_Meter::draw_face %d %d\n", __LINE__, span); if( span < 0 ) { draw_pixmap(images[image_number], x, get_h() - pixel - in_span, get_w(), in_span + 1, 0, images[image_number]->get_h() - in_start - in_span); } else { int total_w = get_w() - x; int third = images[image_number]->get_w() / 3 + 1; for( int x1 = 0; x1 < total_w; x1 += third ) { int in_x = 0; int in_w = third; if( x1 >= third ) in_x = third; if( x1 >= total_w - third ) { in_x = images[image_number]->get_w() - (total_w - x1); in_w = total_w - x1; } int in_y = images[image_number]->get_h() - in_start - in_span; //printf("BC_Meter::draw_face %d %d %d\n", __LINE__, get_w(), x + x1 + in_w, in_x, in_y, in_w, span); draw_pixmap(images[image_number], x + x1, get_h() - pixel - in_span, in_w, in_span + 1, in_x, in_y); } } } pixel += in_span; } else { // Sanity check break; } } } if( is_downmix ) { if( orientation == METER_HORIZ ) draw_pixmap(images[METER_DOWNMIX], 0, 0); else draw_pixmap(images[METER_DOWNMIX], x, get_h() - images[METER_DOWNMIX]->get_h() - 1); } if( over_timer ) { if( orientation == METER_HORIZ ) draw_pixmap(images[METER_OVER], xS(20), yS(2)); else draw_pixmap(images[METER_OVER], x + xS(2), get_h() - yS(100)); over_timer--; } if( orientation == METER_HORIZ ) flash(0, 0, pixels, get_h(), flush); else flash(x, 0, w, pixels, flush); } int BC_Meter::update(float new_value, int over, int downmix) { peak_timer++; if( mode == METER_DB ) { if( new_value == 0 ) level = min; else level = DB::todb(new_value); // db value } if( is_gain_change && fabs(level) > fabs(peak) || !is_gain_change && level > peak || peak_timer > peak_delay ) { peak = level; peak_timer = 0; } // if( orientation == METER_HORIZ ) // printf("BC_Meter::update %f\n", level); if( over ) over_timer = over_delay; // only draw if window is visible if( downmix >= 0 ) is_downmix = downmix; draw_face(1); return 0; }