first pass (incomplete) at canvas meters (in push2 context)

This commit is contained in:
Paul Davis
2016-09-23 18:18:27 -05:00
parent 879b09d920
commit 7a0e2ceec0
7 changed files with 1050 additions and 0 deletions

View File

@@ -68,6 +68,8 @@ class Push2Layout : public sigc::trackable, public ArdourCanvas::Container
virtual void strip_vpot (int, int) = 0;
virtual void strip_vpot_touch (int, bool) = 0;
virtual void update_meters () {}
protected:
Push2& p2;
ARDOUR::Session& session;

View File

@@ -0,0 +1,844 @@
/*
Copyright (C) 2003-2016 Paul Davis
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., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id$
*/
#include <iostream>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <stdlib.h>
#include <glibmm.h>
#include <gtkmm2ext/utils.h>
#include "canvas/canvas.h"
#include "meter.h"
#define UINT_TO_RGB(u,r,g,b) { (*(r)) = ((u)>>16)&0xff; (*(g)) = ((u)>>8)&0xff; (*(b)) = (u)&0xff; }
#define UINT_TO_RGBA(u,r,g,b,a) { UINT_TO_RGB(((u)>>8),r,g,b); (*(a)) = (u)&0xff; }
using namespace Glib;
using namespace Gtkmm2ext;
using namespace std;
using namespace ArdourCanvas;
using namespace ArdourSurface;
int Meter::min_pattern_metric_size = 16;
int Meter::max_pattern_metric_size = 1024;
bool Meter::no_rgba_overlay = false;
Meter::Pattern10Map Meter::vm_pattern_cache;
Meter::PatternBgMap Meter::vb_pattern_cache;
Meter::Pattern10Map Meter::hm_pattern_cache;
Meter::PatternBgMap Meter::hb_pattern_cache;
Meter::Meter (Item* parent, long hold, unsigned long dimen, Orientation o, int len,
int clr0, int clr1, int clr2, int clr3,
int clr4, int clr5, int clr6, int clr7,
int clr8, int clr9,
int bgc0, int bgc1,
int bgh0, int bgh1,
float stp0, float stp1,
float stp2, float stp3,
int styleflags
)
: Item (parent)
, pixheight(0)
, pixwidth(0)
, _styleflags(styleflags)
, orientation(o)
, hold_cnt(hold)
, hold_state(0)
, bright_hold(false)
, current_level(0)
, current_peak(0)
, highlight(false)
{
last_peak_rect.width = 0;
last_peak_rect.height = 0;
last_peak_rect.x = 0;
last_peak_rect.y = 0;
no_rgba_overlay = ! Glib::getenv("NO_METER_SHADE").empty();
_clr[0] = clr0;
_clr[1] = clr1;
_clr[2] = clr2;
_clr[3] = clr3;
_clr[4] = clr4;
_clr[5] = clr5;
_clr[6] = clr6;
_clr[7] = clr7;
_clr[8] = clr8;
_clr[9] = clr9;
_bgc[0] = bgc0;
_bgc[1] = bgc1;
_bgh[0] = bgh0;
_bgh[1] = bgh1;
_stp[0] = stp0;
_stp[1] = stp1;
_stp[2] = stp2;
_stp[3] = stp3;
pixrect.x = 1;
pixrect.y = 1;
if (!len) {
len = 250;
}
if (orientation == Vertical) {
pixheight = len;
pixwidth = dimen;
fgpattern = request_vertical_meter(pixwidth + 2, pixheight + 2, _clr, _stp, _styleflags);
bgpattern = request_vertical_background (pixwidth + 2, pixheight + 2, _bgc, false);
} else {
pixheight = dimen;
pixwidth = len;
fgpattern = request_horizontal_meter(pixwidth + 2, pixheight + 2, _clr, _stp, _styleflags);
bgpattern = request_horizontal_background (pixwidth + 2, pixheight + 2, _bgc, false);
}
pixrect.width = pixwidth;
pixrect.height = pixheight;
}
void
Meter::compute_bounding_box () const
{
if (!_canvas) {
_bounding_box = boost::optional<Rect> ();
_bounding_box_dirty = false;
return;
}
Rect r (0, 0, pixwidth + 2, pixheight + 2);
_bounding_box = r;
_bounding_box_dirty = false;
}
Meter::~Meter ()
{
}
void
Meter::flush_pattern_cache () {
hb_pattern_cache.clear();
hm_pattern_cache.clear();
vb_pattern_cache.clear();
vm_pattern_cache.clear();
}
Cairo::RefPtr<Cairo::Pattern>
Meter::generate_meter_pattern (int width, int height, int *clr, float *stp, int styleflags, bool horiz)
{
guint8 r,g,b,a;
double knee;
const double soft = 3.0 / (double) height;
const double offs = -1.0 / (double) height;
cairo_pattern_t* pat = cairo_pattern_create_linear (0.0, 0.0, 0.0, height);
/*
Cairo coordinate space goes downwards as y value goes up, so invert
knee-based positions by using (1.0 - y)
*/
UINT_TO_RGBA (clr[9], &r, &g, &b, &a); // top/clip
cairo_pattern_add_color_stop_rgb (pat, 0.0,
r/255.0, g/255.0, b/255.0);
knee = offs + stp[3] / 115.0f; // -0dB
UINT_TO_RGBA (clr[8], &r, &g, &b, &a);
cairo_pattern_add_color_stop_rgb (pat, 1.0 - knee,
r/255.0, g/255.0, b/255.0);
UINT_TO_RGBA (clr[7], &r, &g, &b, &a);
cairo_pattern_add_color_stop_rgb (pat, 1.0 - knee + soft,
r/255.0, g/255.0, b/255.0);
knee = offs + stp[2]/ 115.0f; // -3dB || -2dB
UINT_TO_RGBA (clr[6], &r, &g, &b, &a);
cairo_pattern_add_color_stop_rgb (pat, 1.0 - knee,
r/255.0, g/255.0, b/255.0);
UINT_TO_RGBA (clr[5], &r, &g, &b, &a);
cairo_pattern_add_color_stop_rgb (pat, 1.0 - knee + soft,
r/255.0, g/255.0, b/255.0);
knee = offs + stp[1] / 115.0f; // -9dB
UINT_TO_RGBA (clr[4], &r, &g, &b, &a);
cairo_pattern_add_color_stop_rgb (pat, 1.0 - knee,
r/255.0, g/255.0, b/255.0);
UINT_TO_RGBA (clr[3], &r, &g, &b, &a);
cairo_pattern_add_color_stop_rgb (pat, 1.0 - knee + soft,
r/255.0, g/255.0, b/255.0);
knee = offs + stp[0] / 115.0f; // -18dB
UINT_TO_RGBA (clr[2], &r, &g, &b, &a);
cairo_pattern_add_color_stop_rgb (pat, 1.0 - knee,
r/255.0, g/255.0, b/255.0);
UINT_TO_RGBA (clr[1], &r, &g, &b, &a);
cairo_pattern_add_color_stop_rgb (pat, 1.0 - knee + soft,
r/255.0, g/255.0, b/255.0);
UINT_TO_RGBA (clr[0], &r, &g, &b, &a); // bottom
cairo_pattern_add_color_stop_rgb (pat, 1.0,
r/255.0, g/255.0, b/255.0);
if ((styleflags & 1) && !no_rgba_overlay) {
cairo_pattern_t* shade_pattern = cairo_pattern_create_linear (0.0, 0.0, width, 0.0);
cairo_pattern_add_color_stop_rgba (shade_pattern, 0, 0.0, 0.0, 0.0, 0.15);
cairo_pattern_add_color_stop_rgba (shade_pattern, 0.4, 1.0, 1.0, 1.0, 0.05);
cairo_pattern_add_color_stop_rgba (shade_pattern, 1, 0.0, 0.0, 0.0, 0.25);
cairo_surface_t* surface;
cairo_t* tc = 0;
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
tc = cairo_create (surface);
cairo_set_source (tc, pat);
cairo_rectangle (tc, 0, 0, width, height);
cairo_fill (tc);
cairo_pattern_destroy (pat);
cairo_set_source (tc, shade_pattern);
cairo_rectangle (tc, 0, 0, width, height);
cairo_fill (tc);
cairo_pattern_destroy (shade_pattern);
if (styleflags & 2) { // LED stripes
cairo_save (tc);
cairo_set_line_width(tc, 1.0);
cairo_set_source_rgba(tc, .0, .0, .0, 0.4);
//cairo_set_operator (tc, CAIRO_OPERATOR_SOURCE);
for (int i = 0; float y = 0.5 + i * 2.0; ++i) {
if (y >= height) {
break;
}
cairo_move_to(tc, 0, y);
cairo_line_to(tc, width, y);
cairo_stroke (tc);
}
cairo_restore (tc);
}
pat = cairo_pattern_create_for_surface (surface);
cairo_destroy (tc);
cairo_surface_destroy (surface);
}
if (horiz) {
cairo_surface_t* surface;
cairo_t* tc = 0;
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, height, width);
tc = cairo_create (surface);
cairo_matrix_t m;
cairo_matrix_init_rotate (&m, -M_PI/2.0);
cairo_matrix_translate (&m, -height, 0);
cairo_pattern_set_matrix (pat, &m);
cairo_set_source (tc, pat);
cairo_rectangle (tc, 0, 0, height, width);
cairo_fill (tc);
cairo_pattern_destroy (pat);
pat = cairo_pattern_create_for_surface (surface);
cairo_destroy (tc);
cairo_surface_destroy (surface);
}
Cairo::RefPtr<Cairo::Pattern> p (new Cairo::Pattern (pat, false));
return p;
}
Cairo::RefPtr<Cairo::Pattern>
Meter::generate_meter_background (int width, int height, int *clr, bool shade, bool horiz)
{
guint8 r0,g0,b0,r1,g1,b1,a;
cairo_pattern_t* pat = cairo_pattern_create_linear (0.0, 0.0, 0.0, height);
UINT_TO_RGBA (clr[0], &r0, &g0, &b0, &a);
UINT_TO_RGBA (clr[1], &r1, &g1, &b1, &a);
cairo_pattern_add_color_stop_rgb (pat, 0.0,
r1/255.0, g1/255.0, b1/255.0);
cairo_pattern_add_color_stop_rgb (pat, 1.0,
r0/255.0, g0/255.0, b0/255.0);
if (shade && !no_rgba_overlay) {
cairo_pattern_t* shade_pattern = cairo_pattern_create_linear (0.0, 0.0, width, 0.0);
cairo_pattern_add_color_stop_rgba (shade_pattern, 0.0, 1.0, 1.0, 1.0, 0.15);
cairo_pattern_add_color_stop_rgba (shade_pattern, 0.6, 0.0, 0.0, 0.0, 0.10);
cairo_pattern_add_color_stop_rgba (shade_pattern, 1.0, 1.0, 1.0, 1.0, 0.20);
cairo_surface_t* surface;
cairo_t* tc = 0;
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
tc = cairo_create (surface);
cairo_set_source (tc, pat);
cairo_rectangle (tc, 0, 0, width, height);
cairo_fill (tc);
cairo_set_source (tc, shade_pattern);
cairo_rectangle (tc, 0, 0, width, height);
cairo_fill (tc);
cairo_pattern_destroy (pat);
cairo_pattern_destroy (shade_pattern);
pat = cairo_pattern_create_for_surface (surface);
cairo_destroy (tc);
cairo_surface_destroy (surface);
}
if (horiz) {
cairo_surface_t* surface;
cairo_t* tc = 0;
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, height, width);
tc = cairo_create (surface);
cairo_matrix_t m;
cairo_matrix_init_rotate (&m, -M_PI/2.0);
cairo_matrix_translate (&m, -height, 0);
cairo_pattern_set_matrix (pat, &m);
cairo_set_source (tc, pat);
cairo_rectangle (tc, 0, 0, height, width);
cairo_fill (tc);
cairo_pattern_destroy (pat);
pat = cairo_pattern_create_for_surface (surface);
cairo_destroy (tc);
cairo_surface_destroy (surface);
}
Cairo::RefPtr<Cairo::Pattern> p (new Cairo::Pattern (pat, false));
return p;
}
Cairo::RefPtr<Cairo::Pattern>
Meter::request_vertical_meter (int width, int height, int *clr, float *stp, int styleflags)
{
height = max(height, min_pattern_metric_size);
height = min(height, max_pattern_metric_size);
const Pattern10MapKey key (width, height,
stp[0], stp[1], stp[2], stp[3],
clr[0], clr[1], clr[2], clr[3],
clr[4], clr[5], clr[6], clr[7],
clr[8], clr[9], styleflags);
Pattern10Map::iterator i;
if ((i = vm_pattern_cache.find (key)) != vm_pattern_cache.end()) {
return i->second;
}
// TODO flush pattern cache if it gets too large
Cairo::RefPtr<Cairo::Pattern> p = generate_meter_pattern (
width, height, clr, stp, styleflags, false);
vm_pattern_cache[key] = p;
return p;
}
Cairo::RefPtr<Cairo::Pattern>
Meter::request_vertical_background (int width, int height, int *bgc, bool shade)
{
height = max(height, min_pattern_metric_size);
height = min(height, max_pattern_metric_size);
height += 2;
const PatternBgMapKey key (width, height, bgc[0], bgc[1], shade);
PatternBgMap::iterator i;
if ((i = vb_pattern_cache.find (key)) != vb_pattern_cache.end()) {
return i->second;
}
// TODO flush pattern cache if it gets too large
Cairo::RefPtr<Cairo::Pattern> p = generate_meter_background (
width, height, bgc, shade, false);
vb_pattern_cache[key] = p;
return p;
}
Cairo::RefPtr<Cairo::Pattern>
Meter::request_horizontal_meter (int width, int height, int *clr, float *stp, int styleflags)
{
width = max(width, min_pattern_metric_size);
width = min(width, max_pattern_metric_size);
const Pattern10MapKey key (width, height,
stp[0], stp[1], stp[2], stp[3],
clr[0], clr[1], clr[2], clr[3],
clr[4], clr[5], clr[6], clr[7],
clr[8], clr[9], styleflags);
Pattern10Map::iterator i;
if ((i = hm_pattern_cache.find (key)) != hm_pattern_cache.end()) {
return i->second;
}
// TODO flush pattern cache if it gets too large
Cairo::RefPtr<Cairo::Pattern> p = generate_meter_pattern (
height, width, clr, stp, styleflags, true);
hm_pattern_cache[key] = p;
return p;
}
Cairo::RefPtr<Cairo::Pattern>
Meter::request_horizontal_background(
int width, int height, int *bgc, bool shade)
{
width = max(width, min_pattern_metric_size);
width = min(width, max_pattern_metric_size);
width += 2;
const PatternBgMapKey key (width, height, bgc[0], bgc[1], shade);
PatternBgMap::iterator i;
if ((i = hb_pattern_cache.find (key)) != hb_pattern_cache.end()) {
return i->second;
}
// TODO flush pattern cache if it gets too large
Cairo::RefPtr<Cairo::Pattern> p = generate_meter_background (
height, width, bgc, shade, true);
hb_pattern_cache[key] = p;
return p;
}
void
Meter::set_hold_count (long val)
{
if (val < 1) {
val = 1;
}
hold_cnt = val;
hold_state = 0;
current_peak = 0;
redraw ();
}
void
Meter::render (ArdourCanvas::Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
{
if (orientation == Vertical) {
return vertical_expose (area, context);
} else {
return horizontal_expose (area, context);
}
}
void
Meter::vertical_expose (ArdourCanvas::Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
{
gint top_of_meter;
Cairo::RectangleInt background;
Cairo::RectangleInt area_r;
area_r.x = area.x0;
area_r.y = area.y0;
area_r.width = area.width();
area_r.height = area.height();
context->set_source_rgb (0, 0, 0); // black
rounded_rectangle (context, 0, 0, pixwidth + 2, pixheight + 2, 2);
context->stroke ();
top_of_meter = (gint) floor (pixheight * current_level);
/* reset the height & origin of the rect that needs to show the pixbuf
*/
pixrect.height = top_of_meter;
pixrect.y = 1 + pixheight - top_of_meter;
background.x = 1;
background.y = 1;
background.width = pixrect.width;
background.height = pixheight - top_of_meter;
Cairo::RefPtr<Cairo::Region> r1 = Cairo::Region::create (area_r);
r1->intersect (background);
if (!r1->empty()) {
Cairo::RectangleInt i (r1->get_extents ());
context->rectangle (i.x, i.y, i.width, i.height);
context->set_source (bgpattern);
context->fill ();
}
Cairo::RefPtr<Cairo::Region> r2 = Cairo::Region::create (area_r);
r2->intersect (pixrect);
if (!r2->empty()) {
// draw the part of the meter image that we need. the area we draw is bounded "in reverse" (top->bottom)
Cairo::RectangleInt i (r2->get_extents ());
context->rectangle (i.x, i.y, i.width, i.height);
context->set_source (fgpattern);
context->fill ();
}
// draw peak bar
if (hold_state) {
last_peak_rect.x = 1;
last_peak_rect.width = pixwidth;
last_peak_rect.y = max(1, 1 + pixheight - (int) floor (pixheight * current_peak));
if (_styleflags & 2) { // LED stripes
last_peak_rect.y = max(0, (last_peak_rect.y & (~1)));
}
if (bright_hold || (_styleflags & 2)) {
last_peak_rect.height = max(0, min(3, pixheight - last_peak_rect.y - 1 ));
} else {
last_peak_rect.height = max(0, min(2, pixheight - last_peak_rect.y - 1 ));
}
context->set_source (fgpattern);
context->rectangle (last_peak_rect.x, last_peak_rect.y, last_peak_rect.width, last_peak_rect.height);
if (bright_hold && !no_rgba_overlay) {
context->fill_preserve ();
context->set_source_rgba (1.0, 1.0, 1.0, 0.3);
}
context->fill ();
} else {
last_peak_rect.width = 0;
last_peak_rect.height = 0;
}
}
void
Meter::horizontal_expose (ArdourCanvas::Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
{
gint right_of_meter;
Cairo::RectangleInt background;
Cairo::RectangleInt area_r;
Rect area2 = window_to_item (area);
area_r.x = area2.x0;
area_r.y = area2.y0;
area_r.width = area2.width();
area_r.height = area2.height();
context->set_source_rgb (0, 0, 0); // black
rounded_rectangle (context, 0, 0, pixwidth + 2, pixheight + 2, 2);
context->stroke ();
right_of_meter = (gint) floor (pixwidth * current_level);
/* reset the height & origin of the rect that needs to show the pixbuf
*/
pixrect.width = right_of_meter;
background.x = 1 + right_of_meter;
background.y = 1;
background.width = pixwidth - right_of_meter;
background.height = pixheight;
Cairo::RefPtr<Cairo::Region> r1 = Cairo::Region::create (area_r);
r1->intersect (background);
if (!r1->empty()) {
Cairo::RectangleInt i (r1->get_extents ());
context->rectangle (i.x, i.y, i.width, i.height);
context->set_source (bgpattern);
context->fill ();
}
Cairo::RefPtr<Cairo::Region> r2 = Cairo::Region::create (area_r);
r2->intersect (pixrect);
if (!r2->empty()) {
// draw the part of the meter image that we need. the area we draw is bounded "in reverse" (top->bottom)
Cairo::RectangleInt i (r2->get_extents ());
cerr << "h-render-fg: " << i.x << ", " << i.y << ' ' << i.width << " + " << i.height << endl;
context->rectangle (i.x, i.y, i.width, i.height);
context->set_source (fgpattern);
context->fill ();
}
// draw peak bar
if (hold_state) {
last_peak_rect.y = 1;
last_peak_rect.height = pixheight;
const int xpos = floor (pixwidth * current_peak);
if (bright_hold || (_styleflags & 2)) {
last_peak_rect.width = min(3, xpos );
} else {
last_peak_rect.width = min(2, xpos );
}
last_peak_rect.x = 1 + max(0, xpos - last_peak_rect.width);
context->set_source (fgpattern);
context->rectangle (last_peak_rect.x, last_peak_rect.y, last_peak_rect.width, last_peak_rect.height);
if (bright_hold && !no_rgba_overlay) {
context->fill_preserve ();
context->set_source_rgba (1.0, 1.0, 1.0, 0.3);
}
context->fill ();
} else {
last_peak_rect.width = 0;
last_peak_rect.height = 0;
}
}
void
Meter::set (float lvl, float peak)
{
float old_level = current_level;
float old_peak = current_peak;
if (pixwidth <= 0 || pixheight <=0) return;
if (peak == -1) {
if (lvl >= current_peak) {
current_peak = lvl;
hold_state = hold_cnt;
}
if (hold_state > 0) {
if (--hold_state == 0) {
current_peak = lvl;
}
}
bright_hold = false;
} else {
current_peak = peak;
hold_state = 1;
bright_hold = true;
}
current_level = lvl;
const float pixscale = (orientation == Vertical) ? pixheight : pixwidth;
#define PIX(X) floor(pixscale * (X))
if (PIX(current_level) == PIX(old_level) && PIX(current_peak) == PIX(old_peak) && (hold_state == 0 || peak != -1)) {
return;
}
if (orientation == Vertical) {
//queue_vertical_redraw (old_level);
} else {
//queue_horizontal_redraw (old_level);
}
redraw ();
}
void
Meter::queue_vertical_redraw (float old_level)
{
Cairo::RectangleInt rect;
gint new_top = (gint) floor (pixheight * current_level);
rect.x = 1;
rect.width = pixwidth;
rect.height = new_top;
rect.y = 1 + pixheight - new_top;
if (current_level > old_level) {
/* colored/pixbuf got larger, just draw the new section */
/* rect.y stays where it is because of X coordinates */
/* height of invalidated area is between new.y (smaller) and old.y
(larger).
X coordinates just make my brain hurt.
*/
rect.height = pixrect.y - rect.y;
} else {
/* it got smaller, compute the difference */
/* rect.y becomes old.y (the smaller value) */
rect.y = pixrect.y;
/* rect.height is the old.y (smaller) minus the new.y (larger)
*/
rect.height = pixrect.height - rect.height;
}
Cairo::RefPtr<Cairo::Region> region;
bool queue = false;
if (rect.height != 0) {
/* ok, first region to draw ... */
region = Cairo::Region::create (rect);
queue = true;
}
/* redraw the last place where the last peak hold bar was;
the next expose will draw the new one whether its part of
expose region or not.
*/
if (last_peak_rect.width * last_peak_rect.height != 0) {
if (!queue) {
region = Cairo::Region::create ();
queue = true;
}
region->do_union (last_peak_rect);
}
if (hold_state && current_peak > 0) {
if (!queue) {
region = Cairo::Region::create ();
queue = true;
}
rect.x = 1;
rect.y = max(1, 1 + pixheight - (int) floor (pixheight * current_peak));
if (_styleflags & 2) { // LED stripes
rect.y = max(0, (rect.y & (~1)));
}
if (bright_hold || (_styleflags & 2)) {
rect.height = max(0, min(3, pixheight - last_peak_rect.y -1 ));
} else {
rect.height = max(0, min(2, pixheight - last_peak_rect.y -1 ));
}
rect.width = pixwidth;
region->do_union (rect);
}
if (queue) {
if (visible() && _bounding_box && _canvas) {
Cairo::RectangleInt iri = region->get_extents();
Rect ir (iri.x, iri.y, iri.x + iri.width, iri.y + iri.height);
_canvas->request_redraw (item_to_window (ir));
}
}
}
void
Meter::queue_horizontal_redraw (float old_level)
{
Cairo::RectangleInt rect;
gint new_right = (gint) floor (pixwidth * current_level);
rect.height = pixheight;
rect.y = 1;
if (current_level > old_level) {
rect.x = 1 + pixrect.width;
/* colored/pixbuf got larger, just draw the new section */
rect.width = new_right - pixrect.width;
} else {
/* it got smaller, compute the difference */
rect.x = 1 + new_right;
/* rect.height is the old.x (smaller) minus the new.x (larger) */
rect.width = pixrect.width - new_right;
}
Cairo::RefPtr<Cairo::Region> region;
bool queue = false;
if (rect.height != 0) {
/* ok, first region to draw ... */
region = Cairo::Region::create (rect);
queue = true;
}
/* redraw the last place where the last peak hold bar was;
the next expose will draw the new one whether its part of
expose region or not.
*/
if (last_peak_rect.width * last_peak_rect.height != 0) {
if (!queue) {
region = Cairo::Region::create ();
queue = true;
}
region->do_union (last_peak_rect);
}
if (hold_state && current_peak > 0) {
if (!queue) {
region = Cairo::Region::create ();
queue = true;
}
rect.y = 1;
rect.height = pixheight;
const int xpos = floor (pixwidth * current_peak);
if (bright_hold || (_styleflags & 2)) {
rect.width = min(3, xpos);
} else {
rect.width = min(2, xpos);
}
rect.x = 1 + max(0, xpos - rect.width);
region->do_union (rect);
}
if (queue) {
if (visible() && _bounding_box && _canvas) {
Cairo::RectangleInt iri = region->get_extents();
Rect ir (iri.x, iri.y, iri.x + iri.width, iri.y + iri.height);
_canvas->request_redraw (item_to_window (ir));
}
}
}
void
Meter::set_highlight (bool onoff)
{
if (highlight == onoff) {
return;
}
highlight = onoff;
if (orientation == Vertical) {
bgpattern = request_vertical_background (pixwidth + 2, pixheight + 2, highlight ? _bgh : _bgc, highlight);
} else {
bgpattern = request_horizontal_background (pixwidth + 2, pixheight + 2, highlight ? _bgh : _bgc, highlight);
}
redraw ();
}
void
Meter::clear ()
{
current_level = 0;
current_peak = 0;
hold_state = 0;
redraw ();
}

173
libs/surfaces/push2/meter.h Normal file
View File

@@ -0,0 +1,173 @@
/*
Copyright (C) 2003 Paul Davis
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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __push2_meter_h__
#define __push2_meter_h__
#include <map>
#include <boost/tuple/tuple.hpp>
#include <boost/tuple/tuple_comparison.hpp>
#include <cairomm/pattern.h>
#include <cairomm/region.h>
#include "canvas/item.h"
namespace ArdourSurface {
class Meter : public ArdourCanvas::Item {
public:
enum Orientation {
Horizontal,
Vertical
};
Meter (Item* parent,
long hold_cnt, unsigned long width, Orientation, int len=0,
int clr0=0x008800ff, int clr1=0x008800ff,
int clr2=0x00ff00ff, int clr3=0x00ff00ff,
int clr4=0xffaa00ff, int clr5=0xffaa00ff,
int clr6=0xffff00ff, int clr7=0xffff00ff,
int clr8=0xff0000ff, int clr9=0xff0000ff,
int bgc0=0x333333ff, int bgc1=0x444444ff,
int bgh0=0x991122ff, int bgh1=0x551111ff,
float stp0 = 55.0, // log_meter(-18);
float stp1 = 77.5, // log_meter(-9);
float stp2 = 92.5, // log_meter(-3); // 95.0, // log_meter(-2);
float stp3 = 100.0,
int styleflags = 3
);
virtual ~Meter ();
static void flush_pattern_cache();
void set (float level, float peak = -1);
void clear ();
float get_level() { return current_level; }
float get_user_level() { return current_user_level; }
float get_peak() { return current_peak; }
long hold_count() { return hold_cnt; }
void set_hold_count (long);
void set_highlight (bool);
bool get_highlight () { return highlight; }
void render (ArdourCanvas::Rect const &, Cairo::RefPtr<Cairo::Context>) const;
void compute_bounding_box() const;
private:
Cairo::RefPtr<Cairo::Pattern> fgpattern;
Cairo::RefPtr<Cairo::Pattern> bgpattern;
gint pixheight;
gint pixwidth;
float _stp[4];
int _clr[10];
int _bgc[2];
int _bgh[2];
int _styleflags;
Orientation orientation;
mutable Cairo::RectangleInt pixrect;
mutable Cairo::RectangleInt last_peak_rect;
gint request_width;
gint request_height;
unsigned long hold_cnt;
unsigned long hold_state;
bool bright_hold;
float current_level;
float current_peak;
float current_user_level;
bool highlight;
void vertical_expose (ArdourCanvas::Rect const & area, Cairo::RefPtr<Cairo::Context> context) const;
void queue_vertical_redraw (float old_level);
void horizontal_expose (ArdourCanvas::Rect const & area, Cairo::RefPtr<Cairo::Context> context) const;
void queue_horizontal_redraw (float old_level);
static bool no_rgba_overlay;
static Cairo::RefPtr<Cairo::Pattern> generate_meter_pattern (
int, int, int *, float *, int, bool);
static Cairo::RefPtr<Cairo::Pattern> request_vertical_meter (
int, int, int *, float *, int);
static Cairo::RefPtr<Cairo::Pattern> request_horizontal_meter (
int, int, int *, float *, int);
static Cairo::RefPtr<Cairo::Pattern> generate_meter_background (
int, int, int *, bool, bool);
static Cairo::RefPtr<Cairo::Pattern> request_vertical_background (
int, int, int *, bool);
static Cairo::RefPtr<Cairo::Pattern> request_horizontal_background (
int, int, int *, bool);
struct Pattern10MapKey {
Pattern10MapKey (
int w, int h,
float stp0, float stp1, float stp2, float stp3,
int c0, int c1, int c2, int c3,
int c4, int c5, int c6, int c7,
int c8, int c9, int st
)
: dim(w, h)
, stp(stp0, stp1, stp2, stp3)
, cols(c0, c1, c2, c3, c4, c5, c6, c7, c8, c9)
, style(st)
{}
inline bool operator<(const Pattern10MapKey& rhs) const {
return (dim < rhs.dim)
|| (dim == rhs.dim && stp < rhs.stp)
|| (dim == rhs.dim && stp == rhs.stp && cols < rhs.cols)
|| (dim == rhs.dim && stp == rhs.stp && cols == rhs.cols && style < rhs.style);
}
boost::tuple<int, int> dim;
boost::tuple<float, float, float, float> stp;
boost::tuple<int, int, int, int, int, int, int, int, int, int> cols;
int style;
};
typedef std::map<Pattern10MapKey, Cairo::RefPtr<Cairo::Pattern> > Pattern10Map;
struct PatternBgMapKey {
PatternBgMapKey (int w, int h, int c0, int c1, bool shade)
: dim(w, h)
, cols(c0, c1)
, sh(shade)
{}
inline bool operator<(const PatternBgMapKey& rhs) const {
return (dim < rhs.dim) || (dim == rhs.dim && cols < rhs.cols) || (dim == rhs.dim && cols == rhs.cols && (sh && !rhs.sh));
}
boost::tuple<int, int> dim;
boost::tuple<int, int> cols;
bool sh;
};
typedef std::map<PatternBgMapKey, Cairo::RefPtr<Cairo::Pattern> > PatternBgMap;
static Pattern10Map vm_pattern_cache;
static PatternBgMap vb_pattern_cache;
static Pattern10Map hm_pattern_cache;
static PatternBgMap hb_pattern_cache;
static int min_pattern_metric_size; // min dimension for axis that displays the meter level
static int max_pattern_metric_size; // max dimension for axis that displays the meter level
};
} /* namespace */
#endif /* __push2_meter_h__ */

View File

@@ -480,6 +480,8 @@ Push2::vblank ()
}
}
track_mix_layout->update_meters ();
_canvas->vblank();
return true;

View File

@@ -35,11 +35,13 @@
#include "ardour/async_midi_port.h"
#include "ardour/audioengine.h"
#include "ardour/debug.h"
#include "ardour/dsp_filter.h"
#include "ardour/filesystem_paths.h"
#include "ardour/midiport_manager.h"
#include "ardour/midi_track.h"
#include "ardour/midi_port.h"
#include "ardour/monitor_control.h"
#include "ardour/meter.h"
#include "ardour/session.h"
#include "ardour/solo_isolate_control.h"
#include "ardour/solo_safe_control.h"
@@ -55,6 +57,7 @@
#include "canvas.h"
#include "knob.h"
#include "menu.h"
#include "meter.h"
#include "push2.h"
#include "track_mix.h"
#include "utils.h"
@@ -137,6 +140,9 @@ TrackMixLayout::TrackMixLayout (Push2& p, Session& s)
name_text->set_font_description (fd);
name_text->set_position (Duple (10 + (4*Push2Canvas::inter_button_spacing()), 2));
meter = new Meter (this, 24, 32, Meter::Horizontal, 200);
meter->set_position (Duple (10 + (4 * Push2Canvas::inter_button_spacing()), 50));
ControlProtocol::StripableSelectionChanged.connect (selection_connection, invalidator (*this), boost::bind (&TrackMixLayout::selection_changed, this), &p2);
}
@@ -155,6 +161,7 @@ TrackMixLayout::selection_changed ()
set_stripable (s);
}
}
void
TrackMixLayout::show ()
{
@@ -492,3 +499,20 @@ TrackMixLayout::strip_vpot_touch (int n, bool touching)
}
}
}
void
TrackMixLayout::update_meters ()
{
if (!stripable) {
return;
}
boost::shared_ptr<PeakMeter> peak_meter = stripable->peak_meter ();
if (!peak_meter) {
return;
}
const float mpeak = peak_meter->meter_level (0, MeterPeak);
meter->set (DSP::log_meter (mpeak));
}

View File

@@ -37,6 +37,7 @@ namespace ArdourCanvas {
namespace ArdourSurface {
class Push2Knob;
class Meter;
class TrackMixLayout : public Push2Layout
{
@@ -58,6 +59,8 @@ class TrackMixLayout : public Push2Layout
void strip_vpot (int, int);
void strip_vpot_touch (int, bool);
void update_meters ();
private:
boost::shared_ptr<ARDOUR::Stripable> stripable;
PBD::ScopedConnectionList stripable_connections;
@@ -70,6 +73,7 @@ class TrackMixLayout : public Push2Layout
uint8_t selection_color;
Push2Knob* knobs[8];
Meter* meter;
void stripable_property_change (PBD::PropertyChange const& what_changed);
void simple_control_change (boost::shared_ptr<ARDOUR::AutomationControl> ac, Push2::ButtonID bid);

View File

@@ -31,6 +31,7 @@ def build(bld):
layout.cc
mode.cc
menu.cc
meter.cc
mix.cc
scale.cc
splash.cc