first compiling and theoretically correct version of Push2 canvas display.
Not tested with device at this point
This commit is contained in:
190
libs/surfaces/push2/canvas.cc
Normal file
190
libs/surfaces/push2/canvas.cc
Normal file
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
Copyright (C) 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.
|
||||
|
||||
*/
|
||||
|
||||
#include <cairomm/region.h>
|
||||
#include <cairomm/surface.h>
|
||||
#include <cairomm/context.h>
|
||||
|
||||
#include "canvas.h"
|
||||
#include "layout.h"
|
||||
#include "push2.h"
|
||||
|
||||
using namespace ArdourCanvas;
|
||||
using namespace ArdourSurface;
|
||||
|
||||
const int Push2Canvas::pixels_per_row = 1024;
|
||||
|
||||
Push2Canvas::Push2Canvas (Push2& pr, int c, int r)
|
||||
: p2 (pr)
|
||||
, _cols (c)
|
||||
, _rows (r)
|
||||
, frame_buffer (Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, _cols, _rows))
|
||||
{
|
||||
context = Cairo::Context::create (frame_buffer);
|
||||
expose_region = Cairo::Region::create ();
|
||||
|
||||
device_frame_buffer = new uint16_t[pixel_area()];
|
||||
memset (device_frame_buffer, 0, sizeof (uint16_t) * pixel_area());
|
||||
|
||||
frame_header[0] = 0xef;
|
||||
frame_header[1] = 0xcd;
|
||||
frame_header[2] = 0xab;
|
||||
frame_header[3] = 0x89;
|
||||
|
||||
memset (&frame_header[4], 0, 12);
|
||||
}
|
||||
|
||||
Push2Canvas::~Push2Canvas ()
|
||||
{
|
||||
delete [] device_frame_buffer;
|
||||
device_frame_buffer = 0;
|
||||
}
|
||||
|
||||
bool
|
||||
Push2Canvas::vblank ()
|
||||
{
|
||||
/* re-render dirty areas, if any */
|
||||
|
||||
if (expose ()) {
|
||||
/* something rendered, update device_frame_buffer */
|
||||
blit_to_device_frame_buffer ();
|
||||
}
|
||||
|
||||
int transferred = 0;
|
||||
const int timeout_msecs = 1000;
|
||||
int err;
|
||||
|
||||
/* transfer to device */
|
||||
|
||||
if ((err = libusb_bulk_transfer (p2.usb_handle(), 0x01, frame_header, sizeof (frame_header), &transferred, timeout_msecs))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((err = libusb_bulk_transfer (p2.usb_handle(), 0x01, (uint8_t*) device_frame_buffer, 2 * pixel_area (), &transferred, timeout_msecs))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
Push2Canvas::request_redraw ()
|
||||
{
|
||||
request_redraw (Rect (0, 0, _cols, _rows));
|
||||
}
|
||||
|
||||
void
|
||||
Push2Canvas::request_redraw (Rect const & r)
|
||||
{
|
||||
Cairo::RectangleInt cr;
|
||||
|
||||
cr.x = r.x1;
|
||||
cr.y = r.y1;
|
||||
cr.width = r.width();
|
||||
cr.width = r.height();
|
||||
|
||||
expose_region->do_union (cr);
|
||||
|
||||
/* next vblank will redraw */
|
||||
}
|
||||
|
||||
bool
|
||||
Push2Canvas::expose ()
|
||||
{
|
||||
if (expose_region->empty()) {
|
||||
return false; /* nothing drawn */
|
||||
}
|
||||
|
||||
/* set up clipping */
|
||||
|
||||
const int nrects = expose_region->get_num_rectangles ();
|
||||
|
||||
for (int n = 0; n < nrects; ++n) {
|
||||
Cairo::RectangleInt r = expose_region->get_rectangle (n);
|
||||
context->rectangle (r.x, r.y, r.width, r.height);
|
||||
}
|
||||
|
||||
context->clip ();
|
||||
|
||||
Push2Layout* layout = p2.current_layout();
|
||||
|
||||
if (layout) {
|
||||
Cairo::RectangleInt r = expose_region->get_extents();
|
||||
layout->render (Rect (r.x, r.y, r.x + r.width, r.y + r.height), context);
|
||||
}
|
||||
|
||||
context->reset_clip ();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** render host-side frame buffer (a Cairo ImageSurface) to the current
|
||||
* device-side frame buffer. The device frame buffer will be pushed to the
|
||||
* device on the next call to vblank()
|
||||
*/
|
||||
|
||||
int
|
||||
Push2Canvas::blit_to_device_frame_buffer ()
|
||||
{
|
||||
/* ensure that all drawing has been done before we fetch pixel data */
|
||||
|
||||
frame_buffer->flush ();
|
||||
|
||||
const int stride = 3840; /* bytes per row for Cairo::FORMAT_ARGB32 */
|
||||
const uint8_t* data = frame_buffer->get_data ();
|
||||
|
||||
/* fill frame buffer (320kB) */
|
||||
|
||||
uint16_t* fb = (uint16_t*) device_frame_buffer;
|
||||
|
||||
for (int row = 0; row < _rows; ++row) {
|
||||
|
||||
const uint8_t* dp = data + row * stride;
|
||||
|
||||
for (int col = 0; col < _cols; ++col) {
|
||||
|
||||
/* fetch r, g, b (range 0..255). Ignore alpha */
|
||||
|
||||
const int r = (*((const uint32_t*)dp) >> 16) & 0xff;
|
||||
const int g = (*((const uint32_t*)dp) >> 8) & 0xff;
|
||||
const int b = *((const uint32_t*)dp) & 0xff;
|
||||
|
||||
/* convert to 5 bits, 6 bits, 5 bits, respectively */
|
||||
/* generate 16 bit BGB565 value */
|
||||
|
||||
*fb++ = (r >> 3) | ((g & 0xfc) << 3) | ((b & 0xf8) << 8);
|
||||
|
||||
/* the push2 docs state that we should xor the pixel
|
||||
* data. Doing so doesn't work correctly, and not doing
|
||||
* so seems to work fine (colors roughly match intended
|
||||
* values).
|
||||
*/
|
||||
|
||||
dp += 4;
|
||||
}
|
||||
|
||||
/* skip 128 bytes to next line. This is filler, used to avoid line borders occuring in the middle of 512
|
||||
byte USB buffers
|
||||
*/
|
||||
|
||||
fb += 64; /* 128 bytes = 64 int16_t */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
95
libs/surfaces/push2/canvas.h
Normal file
95
libs/surfaces/push2/canvas.h
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
Copyright (C) 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.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef __ardour_push2_canvas_h__
|
||||
#define __ardour_push2_canvas_h__
|
||||
|
||||
#include <cairomm/refptr.h>
|
||||
#include <glibmm/threads.h>
|
||||
|
||||
#include "canvas/canvas.h"
|
||||
|
||||
namespace Cairo {
|
||||
class ImageSurface;
|
||||
class Context;
|
||||
class Region;
|
||||
}
|
||||
|
||||
namespace ArdourSurface {
|
||||
|
||||
class Push2;
|
||||
|
||||
/* A canvas which renders to the Push2 display */
|
||||
|
||||
class Push2Canvas : public ArdourCanvas::Canvas
|
||||
{
|
||||
public:
|
||||
Push2Canvas (Push2& p2, int cols, int rows);
|
||||
~Push2Canvas();
|
||||
|
||||
void request_redraw ();
|
||||
void request_redraw (ArdourCanvas::Rect const &);
|
||||
bool vblank ();
|
||||
|
||||
void splash ();
|
||||
|
||||
Cairo::RefPtr<Cairo::Context> image_context() { return context; }
|
||||
|
||||
int rows() const { return _rows; }
|
||||
int cols() const { return _cols; }
|
||||
|
||||
static double inter_button_spacing() { return 120.0; }
|
||||
|
||||
ArdourCanvas::Coord width() const { return cols(); }
|
||||
ArdourCanvas::Coord height() const { return rows(); }
|
||||
void request_size (ArdourCanvas::Duple);
|
||||
ArdourCanvas::Rect visible_area () const;
|
||||
|
||||
/* API that does nothing since we have no input events */
|
||||
void ungrab () {}
|
||||
void grab (ArdourCanvas::Item*) {}
|
||||
void focus (ArdourCanvas::Item*) {}
|
||||
void unfocus (ArdourCanvas::Item*) {}
|
||||
void re_enter() {}
|
||||
void pick_current_item (int) {}
|
||||
void pick_current_item (ArdourCanvas::Duple const &, int) {}
|
||||
bool get_mouse_position (ArdourCanvas::Duple&) const { return false; }
|
||||
|
||||
private:
|
||||
Push2& p2;
|
||||
int _cols;
|
||||
int _rows;
|
||||
|
||||
static const int pixels_per_row;
|
||||
int pixel_area () const { return _rows * pixels_per_row; }
|
||||
|
||||
uint8_t frame_header[16];
|
||||
uint16_t* device_frame_buffer;
|
||||
|
||||
Cairo::RefPtr<Cairo::ImageSurface> frame_buffer;
|
||||
Cairo::RefPtr<Cairo::Context> context;
|
||||
Cairo::RefPtr<Cairo::Region> expose_region;
|
||||
|
||||
bool expose ();
|
||||
int blit_to_device_frame_buffer ();
|
||||
};
|
||||
|
||||
} /* namespace ArdourSurface */
|
||||
|
||||
#endif /* __ardour_push2_canvas_h__ */
|
||||
354
libs/surfaces/push2/knob.cc
Normal file
354
libs/surfaces/push2/knob.cc
Normal file
@@ -0,0 +1,354 @@
|
||||
/*
|
||||
Copyright (C) 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.
|
||||
*/
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include <cairomm/context.h>
|
||||
#include <cairomm/pattern.h>
|
||||
|
||||
#include "ardour/automation_control.h"
|
||||
#include "ardour/dB.h"
|
||||
|
||||
#include "gtkmm2ext/gui_thread.h"
|
||||
#include "gtkmm2ext/rgb_macros.h"
|
||||
|
||||
#include "canvas/colors.h"
|
||||
|
||||
#include "knob.h"
|
||||
#include "push2.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include "pbd/i18n.h"
|
||||
|
||||
using namespace PBD;
|
||||
using namespace ARDOUR;
|
||||
using namespace ArdourSurface;
|
||||
using namespace ArdourCanvas;
|
||||
|
||||
Push2Knob::Element Push2Knob::default_elements = Push2Knob::Element (Push2Knob::Arc);
|
||||
|
||||
Push2Knob::Push2Knob (Push2& p, Item* parent, Element e, Flags flags)
|
||||
: Item (parent)
|
||||
, p2 (p)
|
||||
, _elements (e)
|
||||
, _flags (flags)
|
||||
, _r (0)
|
||||
, _val (0)
|
||||
, _normal (0)
|
||||
, text (this)
|
||||
{
|
||||
Pango::FontDescription fd ("Sans 10");
|
||||
text.set_font_description (fd);
|
||||
text.set_position (Duple (0, -20)); /* changed when radius changes */
|
||||
|
||||
/* typically over-ridden */
|
||||
|
||||
text_color = p2.get_color (Push2::ParameterName);
|
||||
arc_start_color = p2.get_color (Push2::KnobArcStart);
|
||||
arc_end_color = p2.get_color (Push2::KnobArcEnd);
|
||||
}
|
||||
|
||||
Push2Knob::~Push2Knob ()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
Push2Knob::set_text_color (Color c)
|
||||
{
|
||||
text.set_color (c);
|
||||
}
|
||||
|
||||
void
|
||||
Push2Knob::set_radius (double r)
|
||||
{
|
||||
_r = r;
|
||||
text.set_position (Duple (-_r, -_r - 20));
|
||||
redraw ();
|
||||
}
|
||||
|
||||
void
|
||||
Push2Knob::compute_bounding_box () const
|
||||
{
|
||||
if (!_canvas || _r == 0) {
|
||||
_bounding_box = boost::optional<Rect> ();
|
||||
_bounding_box_dirty = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_bounding_box_dirty) {
|
||||
Rect r = Rect (0, 0, _r * 2.0, _r * 2.0);
|
||||
_bounding_box = r;
|
||||
_bounding_box_dirty = false;
|
||||
}
|
||||
|
||||
add_child_bounding_boxes ();
|
||||
}
|
||||
|
||||
void
|
||||
Push2Knob::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
|
||||
{
|
||||
if (!_controllable) {
|
||||
/* no controllable, nothing to draw */
|
||||
return;
|
||||
}
|
||||
|
||||
const float scale = 2.0 * _r;
|
||||
const float pointer_thickness = 3.0 * (scale/80); //(if the knob is 80 pixels wide, we want a 3-pix line on it)
|
||||
|
||||
const float start_angle = ((180 - 65) * G_PI) / 180;
|
||||
const float end_angle = ((360 + 65) * G_PI) / 180;
|
||||
|
||||
float zero = 0;
|
||||
|
||||
if (_flags & ArcToZero) {
|
||||
zero = _normal;
|
||||
}
|
||||
|
||||
const float value_angle = start_angle + (_val * (end_angle - start_angle));
|
||||
const float zero_angle = start_angle + (zero * (end_angle - start_angle));
|
||||
|
||||
float value_x = cos (value_angle);
|
||||
float value_y = sin (value_angle);
|
||||
|
||||
context->translate (_position.x, _position.y); //after this, everything is based on the center of the knob
|
||||
context->begin_new_path ();
|
||||
|
||||
float center_radius = 0.48*scale;
|
||||
float border_width = 0.8;
|
||||
|
||||
const bool arc = (_elements & Arc)==Arc;
|
||||
const bool flat = false;
|
||||
|
||||
if (arc) {
|
||||
center_radius = scale*0.33;
|
||||
|
||||
float inner_progress_radius = scale*0.38;
|
||||
float outer_progress_radius = scale*0.48;
|
||||
float progress_width = (outer_progress_radius-inner_progress_radius);
|
||||
float progress_radius = inner_progress_radius + progress_width/2.0;
|
||||
|
||||
//dark arc background
|
||||
set_source_rgb (context, p2.get_color (Push2::KnobArcBackground));
|
||||
context->set_line_width (progress_width);
|
||||
context->arc (0, 0, progress_radius, start_angle, end_angle);
|
||||
context->stroke ();
|
||||
|
||||
|
||||
double red_start, green_start, blue_start, astart;
|
||||
double red_end, green_end, blue_end, aend;
|
||||
|
||||
ArdourCanvas::color_to_rgba (arc_start_color, red_start, green_start, blue_start, astart);
|
||||
ArdourCanvas::color_to_rgba (arc_end_color, red_end, green_end, blue_end, aend);
|
||||
|
||||
//vary the arc color over the travel of the knob
|
||||
float intensity = fabsf (_val - zero) / std::max(zero, (1.f - zero));
|
||||
const float intensity_inv = 1.0 - intensity;
|
||||
float r = intensity_inv * red_end + intensity * red_start;
|
||||
float g = intensity_inv * green_end + intensity * green_start;
|
||||
float b = intensity_inv * blue_end + intensity * blue_start;
|
||||
|
||||
//draw the arc
|
||||
context->set_source_rgb (r,g,b);
|
||||
context->set_line_width (progress_width);
|
||||
if (zero_angle > value_angle) {
|
||||
context->arc (0, 0, progress_radius, value_angle, zero_angle);
|
||||
} else {
|
||||
context->arc (0, 0, progress_radius, zero_angle, value_angle);
|
||||
}
|
||||
context->stroke ();
|
||||
|
||||
//shade the arc
|
||||
if (!flat) {
|
||||
//note we have to offset the pattern from our centerpoint
|
||||
Cairo::RefPtr<Cairo::LinearGradient> pattern = Cairo::LinearGradient::create (0.0, -_position.y, 0.0, _position.y);
|
||||
pattern->add_color_stop_rgba (0.0, 1,1,1, 0.15);
|
||||
pattern->add_color_stop_rgba (0.5, 1,1,1, 0.0);
|
||||
pattern->add_color_stop_rgba (1.0, 1,1,1, 0.0);
|
||||
context->set_source (pattern);
|
||||
context->arc (0, 0, outer_progress_radius-1, 0, 2.0*G_PI);
|
||||
context->fill ();
|
||||
}
|
||||
}
|
||||
|
||||
if (!flat) {
|
||||
//knob shadow
|
||||
context->save();
|
||||
context->translate(pointer_thickness+1, pointer_thickness+1 );
|
||||
set_source_rgba (context, p2.get_color (Push2::KnobShadow));
|
||||
context->arc (0, 0, center_radius-1, 0, 2.0*G_PI);
|
||||
context->fill ();
|
||||
context->restore();
|
||||
|
||||
//inner circle
|
||||
set_source_rgb (context, p2.get_color (Push2::KnobForeground));
|
||||
context->arc (0, 0, center_radius, 0, 2.0*G_PI);
|
||||
context->fill ();
|
||||
|
||||
//radial gradient as a lightness shade
|
||||
Cairo::RefPtr<Cairo::RadialGradient> pattern = Cairo::RadialGradient::create (-center_radius, -center_radius, 1, -center_radius, -center_radius, center_radius*2.5 ); //note we have to offset the gradient from our centerpoint
|
||||
pattern->add_color_stop_rgba (0.0, 0, 0, 0, 0.2);
|
||||
pattern->add_color_stop_rgba (1.0, 1, 1, 1, 0.3);
|
||||
context->set_source (pattern);
|
||||
context->arc (0, 0, center_radius, 0, 2.0*G_PI);
|
||||
context->fill ();
|
||||
|
||||
}
|
||||
|
||||
//black knob border
|
||||
context->set_line_width (border_width);
|
||||
set_source_rgba (context, p2.get_color (Push2::KnobBorder));
|
||||
context->set_source_rgba (0, 0, 0, 1 );
|
||||
context->arc (0, 0, center_radius, 0, 2.0*G_PI);
|
||||
context->stroke ();
|
||||
|
||||
//line shadow
|
||||
if (!flat) {
|
||||
context->save();
|
||||
context->translate(1, 1 );
|
||||
set_source_rgba (context, p2.get_color (Push2::KnobLineShadow));
|
||||
context->set_line_cap (Cairo::LINE_CAP_ROUND);
|
||||
context->set_line_width (pointer_thickness);
|
||||
context->move_to ((center_radius * value_x), (center_radius * value_y));
|
||||
context->line_to (((center_radius*0.4) * value_x), ((center_radius*0.4) * value_y));
|
||||
context->stroke ();
|
||||
context->restore();
|
||||
}
|
||||
|
||||
//line
|
||||
set_source_rgba (context, p2.get_color (Push2::KnobLine));
|
||||
context->set_line_cap (Cairo::LINE_CAP_ROUND);
|
||||
context->set_line_width (pointer_thickness);
|
||||
context->move_to ((center_radius * value_x), (center_radius * value_y));
|
||||
context->line_to (((center_radius*0.4) * value_x), ((center_radius*0.4) * value_y));
|
||||
context->stroke ();
|
||||
|
||||
/* reset all translations, scaling etc. */
|
||||
context->set_identity_matrix();
|
||||
|
||||
render_children (area, context);
|
||||
}
|
||||
|
||||
void
|
||||
Push2Knob::set_controllable (boost::shared_ptr<AutomationControl> c)
|
||||
{
|
||||
watch_connection.disconnect (); //stop watching the old controllable
|
||||
|
||||
if (!c) {
|
||||
_controllable.reset ();
|
||||
return;
|
||||
}
|
||||
|
||||
_controllable = c;
|
||||
_controllable->Changed.connect (watch_connection, invalidator(*this), boost::bind (&Push2Knob::controllable_changed, this), &p2);
|
||||
|
||||
controllable_changed ();
|
||||
}
|
||||
|
||||
void
|
||||
Push2Knob::set_pan_azimuth_text (double pos)
|
||||
{
|
||||
/* We show the position of the center of the image relative to the left & right.
|
||||
This is expressed as a pair of percentage values that ranges from (100,0)
|
||||
(hard left) through (50,50) (hard center) to (0,100) (hard right).
|
||||
|
||||
This is pretty wierd, but its the way audio engineers expect it. Just remember that
|
||||
the center of the USA isn't Kansas, its (50LA, 50NY) and it will all make sense.
|
||||
*/
|
||||
|
||||
char buf[64];
|
||||
snprintf (buf, sizeof (buf), _("L:%3d R:%3d"), (int) rint (100.0 * (1.0 - pos)), (int) rint (100.0 * pos));
|
||||
text.set (buf);
|
||||
}
|
||||
|
||||
void
|
||||
Push2Knob::set_pan_width_text (double val)
|
||||
{
|
||||
char buf[16];
|
||||
snprintf (buf, sizeof (buf), "%d%%", (int) floor (val*100));
|
||||
text.set (buf);
|
||||
}
|
||||
|
||||
void
|
||||
Push2Knob::set_gain_text (double)
|
||||
{
|
||||
char buf[16];
|
||||
|
||||
/* need to ignore argument, because it has already been converted into
|
||||
the "interface" (0..1) range.
|
||||
*/
|
||||
|
||||
snprintf (buf, sizeof (buf), "%.1f dB", accurate_coefficient_to_dB (_controllable->get_value()));
|
||||
text.set (buf);
|
||||
}
|
||||
|
||||
void
|
||||
Push2Knob::controllable_changed ()
|
||||
{
|
||||
if (_controllable) {
|
||||
_normal = _controllable->internal_to_interface (_controllable->normal());
|
||||
_val = _controllable->internal_to_interface (_controllable->get_value());
|
||||
|
||||
switch (_controllable->parameter().type()) {
|
||||
case ARDOUR::PanAzimuthAutomation:
|
||||
set_pan_azimuth_text (_val);
|
||||
break;
|
||||
|
||||
case ARDOUR::PanWidthAutomation:
|
||||
set_pan_width_text (_val);
|
||||
break;
|
||||
|
||||
case ARDOUR::GainAutomation:
|
||||
case ARDOUR::BusSendLevel:
|
||||
set_gain_text (_val);
|
||||
break;
|
||||
|
||||
default:
|
||||
text.set (std::string());
|
||||
}
|
||||
}
|
||||
|
||||
redraw ();
|
||||
}
|
||||
|
||||
void
|
||||
Push2Knob::add_flag (Flags f)
|
||||
{
|
||||
_flags = Flags (_flags | f);
|
||||
redraw ();
|
||||
}
|
||||
|
||||
void
|
||||
Push2Knob::remove_flag (Flags f)
|
||||
{
|
||||
_flags = Flags (_flags & ~f);
|
||||
redraw ();
|
||||
}
|
||||
|
||||
void
|
||||
Push2Knob::set_arc_start_color (uint32_t c)
|
||||
{
|
||||
arc_start_color = c;
|
||||
redraw ();
|
||||
}
|
||||
|
||||
void
|
||||
Push2Knob::set_arc_end_color (uint32_t c)
|
||||
{
|
||||
arc_end_color = c;
|
||||
redraw ();
|
||||
}
|
||||
90
libs/surfaces/push2/knob.h
Normal file
90
libs/surfaces/push2/knob.h
Normal file
@@ -0,0 +1,90 @@
|
||||
#ifndef __ardour_push2_knob_h__
|
||||
#define __ardour_push2_knob_h__
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <sigc++/trackable.h>
|
||||
|
||||
#include <cairomm/refptr.h>
|
||||
|
||||
#include "pbd/signals.h"
|
||||
|
||||
#include "canvas/item.h"
|
||||
#include "canvas/text.h"
|
||||
|
||||
namespace ARDOUR {
|
||||
class AutomationControl;
|
||||
}
|
||||
|
||||
namespace Cairo {
|
||||
class Context;
|
||||
class Region;
|
||||
}
|
||||
|
||||
namespace ArdourSurface {
|
||||
|
||||
class Push2;
|
||||
|
||||
class Push2Knob : public sigc::trackable, public ArdourCanvas::Item
|
||||
{
|
||||
public:
|
||||
enum Element {
|
||||
Arc = 0x1,
|
||||
Bevel = 0x2,
|
||||
unused2 = 0x4,
|
||||
unused3 = 0x8,
|
||||
unused4 = 0x10,
|
||||
unused5 = 0x20,
|
||||
};
|
||||
|
||||
enum Flags {
|
||||
NoFlags = 0,
|
||||
Detent = 0x1,
|
||||
ArcToZero = 0x2,
|
||||
};
|
||||
|
||||
Push2Knob (Push2& p, ArdourCanvas::Item*, Element e = default_elements, Flags flags = NoFlags);
|
||||
virtual ~Push2Knob ();
|
||||
|
||||
static Element default_elements;
|
||||
|
||||
void add_flag (Flags);
|
||||
void remove_flag (Flags);
|
||||
|
||||
void set_controllable (boost::shared_ptr<ARDOUR::AutomationControl> c);
|
||||
boost::shared_ptr<ARDOUR::AutomationControl> controllable() const { return _controllable; }
|
||||
|
||||
void set_text_color (ArdourCanvas::Color);
|
||||
void set_arc_start_color (ArdourCanvas::Color);
|
||||
void set_arc_end_color (ArdourCanvas::Color);
|
||||
void set_position (double x, double y);
|
||||
void set_radius (double r);
|
||||
|
||||
void render (ArdourCanvas::Rect const &, Cairo::RefPtr<Cairo::Context>) const;
|
||||
void compute_bounding_box() const;
|
||||
|
||||
protected:
|
||||
void controllable_changed ();
|
||||
PBD::ScopedConnection watch_connection;
|
||||
boost::shared_ptr<ARDOUR::AutomationControl> _controllable;
|
||||
|
||||
private:
|
||||
Push2& p2;
|
||||
Element _elements;
|
||||
Flags _flags;
|
||||
double _r;
|
||||
float _val; // current value [0..1]
|
||||
float _normal; // default value, arc
|
||||
|
||||
ArdourCanvas::Color text_color;
|
||||
ArdourCanvas::Color arc_start_color;
|
||||
ArdourCanvas::Color arc_end_color;
|
||||
ArdourCanvas::Text text;
|
||||
|
||||
void set_pan_azimuth_text (double);
|
||||
void set_pan_width_text (double);
|
||||
void set_gain_text (double);
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif /* __ardour_push2_knob_h__ */
|
||||
@@ -16,14 +16,17 @@
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "canvas.h"
|
||||
#include "layout.h"
|
||||
#include "push2.h"
|
||||
|
||||
using namespace ARDOUR;
|
||||
using namespace ArdourSurface;
|
||||
using namespace ArdourCanvas;
|
||||
|
||||
Push2Layout::Push2Layout (Push2& p, Session& s)
|
||||
: p2 (p)
|
||||
: Container (p.canvas())
|
||||
, p2 (p)
|
||||
, session (s)
|
||||
{
|
||||
}
|
||||
@@ -32,8 +35,24 @@ Push2Layout::~Push2Layout ()
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
Push2Layout::mapped () const
|
||||
void
|
||||
Push2Layout::compute_bounding_box () const
|
||||
{
|
||||
return p2.current_layout() == this;
|
||||
/* all layouts occupy at least the full screen, even if their combined
|
||||
* child boxes do not.
|
||||
*/
|
||||
_bounding_box = Rect (0, 0, display_width(), display_height());
|
||||
_bounding_box_dirty = false;
|
||||
}
|
||||
|
||||
int
|
||||
Push2Layout::display_height() const
|
||||
{
|
||||
return p2.canvas()->rows();
|
||||
}
|
||||
|
||||
int
|
||||
Push2Layout::display_width() const
|
||||
{
|
||||
return p2.canvas()->cols();
|
||||
}
|
||||
|
||||
@@ -25,6 +25,12 @@
|
||||
|
||||
#include <cairomm/refptr.h>
|
||||
|
||||
#include "canvas/container.h"
|
||||
|
||||
namespace Cairo {
|
||||
class Region;
|
||||
}
|
||||
|
||||
namespace ARDOUR {
|
||||
class Session;
|
||||
}
|
||||
@@ -37,17 +43,16 @@ namespace ArdourSurface {
|
||||
|
||||
class Push2;
|
||||
|
||||
class Push2Layout : public sigc::trackable
|
||||
class Push2Layout : public sigc::trackable, public ArdourCanvas::Container
|
||||
{
|
||||
public:
|
||||
Push2Layout (Push2& p, ARDOUR::Session& s);
|
||||
virtual ~Push2Layout ();
|
||||
|
||||
bool mapped() const;
|
||||
int display_width () const;
|
||||
int display_height () const;
|
||||
|
||||
virtual bool redraw (Cairo::RefPtr<Cairo::Context>, bool force) const = 0;
|
||||
virtual void on_show () {}
|
||||
virtual void on_hide () {}
|
||||
void compute_bounding_box () const;
|
||||
|
||||
virtual void button_upper (uint32_t n) {}
|
||||
virtual void button_lower (uint32_t n) {}
|
||||
|
||||
@@ -18,38 +18,59 @@
|
||||
|
||||
#include <cairomm/context.h>
|
||||
#include <cairomm/surface.h>
|
||||
#include <cairomm/region.h>
|
||||
#include <pangomm/layout.h>
|
||||
|
||||
#include "push2.h"
|
||||
#include "canvas/text.h"
|
||||
#include "canvas/rectangle.h"
|
||||
#include "canvas/colors.h"
|
||||
|
||||
#include "canvas.h"
|
||||
#include "gui.h"
|
||||
#include "push2.h"
|
||||
|
||||
using namespace ARDOUR;
|
||||
using namespace std;
|
||||
using namespace PBD;
|
||||
using namespace Glib;
|
||||
using namespace ArdourSurface;
|
||||
using namespace ArdourCanvas;
|
||||
|
||||
#include "pbd/i18n.h"
|
||||
#include "menu.h"
|
||||
|
||||
Push2Menu::Push2Menu (Cairo::RefPtr<Cairo::Context> context)
|
||||
: _dirty (true)
|
||||
Push2Menu::Push2Menu (Item* parent)
|
||||
: Container (parent)
|
||||
, baseline (-1)
|
||||
{
|
||||
Pango::FontDescription fd2 ("Sans 10");
|
||||
|
||||
{
|
||||
Glib::RefPtr<Pango::Layout> throwaway = Pango::Layout::create (context);
|
||||
if (baseline < 0) {
|
||||
Push2Canvas* p2c = dynamic_cast<Push2Canvas*> (canvas());
|
||||
Glib::RefPtr<Pango::Layout> throwaway = Pango::Layout::create (p2c->image_context());
|
||||
throwaway->set_font_description (fd2);
|
||||
throwaway->set_text (X_("Hg")); /* ascender + descender) */
|
||||
int h, w;
|
||||
throwaway->get_pixel_size (w, h);
|
||||
baseline = h;
|
||||
nrows = Push2::rows / baseline;
|
||||
// nrows = Push2::rows / baseline;
|
||||
}
|
||||
|
||||
|
||||
for (int n = 0; n < 8; ++n) {
|
||||
columns[n].layout = Pango::Layout::create (context);
|
||||
columns[n].layout->set_font_description (fd2);
|
||||
Text* t = new Text (this);
|
||||
t->set_font_description (fd2);
|
||||
t->set_color (rgba_to_color (0.23, 0.0, 0.349, 1.0));
|
||||
|
||||
const double x = 10.0 + (n * Push2Canvas::inter_button_spacing());
|
||||
const double y = 2.0;
|
||||
t->set_position (Duple (x, y));
|
||||
|
||||
Rectangle* r = new Rectangle (this);
|
||||
r->set (Rect (x, y, x + Push2Canvas::inter_button_spacing(), y + baseline));
|
||||
|
||||
columns[n].lines = t;
|
||||
columns[n].active_bg = r;
|
||||
columns[n].top = -1;
|
||||
columns[n].active = -1;
|
||||
}
|
||||
@@ -71,8 +92,6 @@ Push2Menu::fill_column (int col, vector<string> v)
|
||||
}
|
||||
|
||||
set_text (col, 0);
|
||||
|
||||
_dirty = true;
|
||||
}
|
||||
|
||||
void
|
||||
@@ -86,6 +105,7 @@ Push2Menu::set_text (int col, int top_row)
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
vector<string>::iterator s = columns[col].text.begin();
|
||||
s += top_row;
|
||||
|
||||
@@ -101,10 +121,10 @@ Push2Menu::set_text (int col, int top_row)
|
||||
}
|
||||
}
|
||||
|
||||
columns[col].layout->set_text (rows);
|
||||
columns[col].lines->set (rows);
|
||||
columns[col].top = top_row;
|
||||
|
||||
_dirty = true;
|
||||
redraw ();
|
||||
}
|
||||
|
||||
void
|
||||
@@ -115,24 +135,43 @@ Push2Menu::scroll (int col, int dir)
|
||||
} else {
|
||||
set_text (col, columns[col].top - 1);
|
||||
}
|
||||
|
||||
redraw ();
|
||||
}
|
||||
|
||||
void
|
||||
Push2Menu::set_active (int col, int index)
|
||||
{
|
||||
if (col < 0 || col > 7) {
|
||||
columns[col].active_bg->hide ();
|
||||
return;
|
||||
}
|
||||
|
||||
if (index < 0 || index > (int) columns[col].text.size()) {
|
||||
columns[col].active_bg->hide ();
|
||||
return;
|
||||
}
|
||||
|
||||
columns[col].active = index;
|
||||
int effective_row = columns[col].active - columns[col].top;
|
||||
|
||||
/* Move active bg */
|
||||
|
||||
Duple p (columns[col].active_bg->position());
|
||||
|
||||
columns[col].active_bg->set (Rect (p.x, p.y + (effective_row * baseline),
|
||||
p.x + Push2Canvas::inter_button_spacing(), p.y + baseline));
|
||||
columns[col].active_bg->show ();
|
||||
|
||||
if (columns[col].active < nrows/2) {
|
||||
set_text (col, 0);
|
||||
} else {
|
||||
set_text (col, columns[col].active - (nrows/2) + 1);
|
||||
}
|
||||
|
||||
ActiveChanged (); /* emit signal */
|
||||
|
||||
_dirty = true;
|
||||
redraw ();
|
||||
}
|
||||
|
||||
void
|
||||
@@ -149,31 +188,25 @@ Push2Menu::step_active (int col, int dir)
|
||||
|
||||
if (dir < 0) {
|
||||
if (columns[col].active == -1) {
|
||||
columns[col].active = 0;
|
||||
set_active (col, -1);
|
||||
} else {
|
||||
columns[col].active = columns[col].active - 1;
|
||||
if (columns[col].active < 0) {
|
||||
columns[col].active = columns[col].text.size() - 1;
|
||||
set_active (col, columns[col].text.size() - 1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (columns[col].active == -1) {
|
||||
columns[col].active = 0;
|
||||
set_active (col, 0);
|
||||
} else {
|
||||
columns[col].active = columns[col].active + 1;
|
||||
if (columns[col].active >= (int) columns[col].text.size()) {
|
||||
columns[col].active = 0;
|
||||
set_active (col, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (columns[col].active < nrows/2) {
|
||||
set_text (col, 0);
|
||||
} else {
|
||||
set_text (col, columns[col].active - (nrows/2) + 1);
|
||||
}
|
||||
|
||||
_dirty = true;
|
||||
redraw ();
|
||||
}
|
||||
|
||||
int
|
||||
@@ -187,32 +220,7 @@ Push2Menu::get_active (int col)
|
||||
}
|
||||
|
||||
void
|
||||
Push2Menu::redraw (Cairo::RefPtr<Cairo::Context> context, bool force) const
|
||||
Push2Menu::render (Rect const& area, Cairo::RefPtr<Cairo::Context> context) const
|
||||
{
|
||||
for (int n = 0; n < 8; ++n) {
|
||||
|
||||
/* Active: move to column/now, draw background indicator
|
||||
for active row.
|
||||
*/
|
||||
|
||||
const double x = 10.0 + (n * 120.0);
|
||||
const double y = 2.0;
|
||||
|
||||
if (columns[n].active >= 0) {
|
||||
int effective_row = columns[n].active - columns[n].top;
|
||||
context->rectangle (x, y + (effective_row * baseline), 120.0, baseline);
|
||||
context->set_source_rgb (1.0, 1.0, 1.0);
|
||||
context->fill ();
|
||||
}
|
||||
|
||||
/* now draw all the text, in one go */
|
||||
|
||||
context->move_to (x, y);
|
||||
context->set_source_rgb (0.23, 0.0, 0.349);
|
||||
columns[n].layout->update_from_cairo_context (context);
|
||||
columns[n].layout->show_in_cairo_context (context);
|
||||
|
||||
}
|
||||
|
||||
_dirty = false;
|
||||
render_children (area, context);
|
||||
}
|
||||
|
||||
@@ -19,20 +19,30 @@
|
||||
#ifndef __ardour_push2_menu_h__
|
||||
#define __ardour_push2_menu_h__
|
||||
|
||||
#include <cairomm/context.h>
|
||||
#include <cairomm/surface.h>
|
||||
namespace Cairo {
|
||||
class Context;
|
||||
class Region;
|
||||
}
|
||||
|
||||
#include <pangomm/layout.h>
|
||||
|
||||
#include "pbd/signals.h"
|
||||
|
||||
#include "canvas/container.h"
|
||||
|
||||
namespace ArdourCanvas {
|
||||
class Text;
|
||||
class Rectangle;
|
||||
}
|
||||
|
||||
namespace ArdourSurface {
|
||||
|
||||
class Push2Menu {
|
||||
class Push2Menu : public ArdourCanvas::Container
|
||||
{
|
||||
public:
|
||||
Push2Menu (Cairo::RefPtr<Cairo::Context>);
|
||||
Push2Menu (ArdourCanvas::Item* parent);
|
||||
|
||||
void redraw (Cairo::RefPtr<Cairo::Context>, bool force) const;
|
||||
bool dirty () const { return _dirty; }
|
||||
void render (ArdourCanvas::Rect const& area, Cairo::RefPtr<Cairo::Context> context) const;
|
||||
|
||||
void fill_column (int col, std::vector<std::string>);
|
||||
void set_active (int col, int index);
|
||||
@@ -45,7 +55,8 @@ class Push2Menu {
|
||||
private:
|
||||
struct Column {
|
||||
std::vector<std::string> text;
|
||||
Glib::RefPtr<Pango::Layout> layout;
|
||||
ArdourCanvas::Rectangle* active_bg;
|
||||
ArdourCanvas::Text* lines;
|
||||
int top;
|
||||
int active;
|
||||
};
|
||||
@@ -56,9 +67,7 @@ class Push2Menu {
|
||||
void set_text (int col, int top);
|
||||
|
||||
int nrows;
|
||||
double baseline;
|
||||
|
||||
mutable bool _dirty;
|
||||
mutable double baseline;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <cairomm/region.h>
|
||||
#include <pangomm/layout.h>
|
||||
|
||||
#include "pbd/compose.h"
|
||||
@@ -42,9 +43,11 @@
|
||||
#include "ardour/vca_manager.h"
|
||||
|
||||
#include "canvas/colors.h"
|
||||
#include "canvas/rectangle.h"
|
||||
|
||||
#include "gtkmm2ext/gui_thread.h"
|
||||
|
||||
#include "canvas.h"
|
||||
#include "mix.h"
|
||||
#include "knob.h"
|
||||
#include "push2.h"
|
||||
@@ -57,17 +60,22 @@ using namespace std;
|
||||
using namespace PBD;
|
||||
using namespace Glib;
|
||||
using namespace ArdourSurface;
|
||||
using namespace ArdourCanvas;
|
||||
|
||||
MixLayout::MixLayout (Push2& p, Session& s, Cairo::RefPtr<Cairo::Context> context)
|
||||
MixLayout::MixLayout (Push2& p, Session& s)
|
||||
: Push2Layout (p, s)
|
||||
, _dirty (true)
|
||||
, bank_start (0)
|
||||
, vpot_mode (Volume)
|
||||
{
|
||||
selection_bg = new Rectangle (this);
|
||||
selection_bg->hide ();
|
||||
|
||||
Pango::FontDescription fd2 ("Sans 10");
|
||||
for (int n = 0; n < 8; ++n) {
|
||||
upper_layout[n] = Pango::Layout::create (context);
|
||||
upper_layout[n]->set_font_description (fd2);
|
||||
Text* t = new Text (this);
|
||||
upper_text.push_back (t);
|
||||
t->set_font_description (fd2);
|
||||
t->set_color (p2.get_color (Push2::ParameterName));
|
||||
|
||||
string txt;
|
||||
switch (n) {
|
||||
@@ -96,18 +104,19 @@ MixLayout::MixLayout (Push2& p, Session& s, Cairo::RefPtr<Cairo::Context> contex
|
||||
txt = _("E Sends");
|
||||
break;
|
||||
}
|
||||
upper_layout[n]->set_text (txt);
|
||||
}
|
||||
t->set (txt);
|
||||
|
||||
Pango::FontDescription fd3 ("Sans 10");
|
||||
for (int n = 0; n < 8; ++n) {
|
||||
lower_layout[n] = Pango::Layout::create (context);
|
||||
lower_layout[n]->set_font_description (fd3);
|
||||
}
|
||||
t = new Text (this);
|
||||
lower_text.push_back (t);
|
||||
t->set_font_description (fd2);
|
||||
t->set_color (p2.get_color (Push2::ParameterName));
|
||||
|
||||
for (int n = 0; n < 8; ++n) {
|
||||
knobs[n] = new Push2Knob (p2, context);
|
||||
knobs[n]->set_position (60 + (n*120), 95);
|
||||
Rectangle* r = new Rectangle (this);
|
||||
r->set (Rect (10 + (n*Push2Canvas::inter_button_spacing()) - 5, 2, Push2Canvas::inter_button_spacing(), 21));
|
||||
backgrounds.push_back (r);
|
||||
|
||||
knobs[n] = new Push2Knob (p2, this);
|
||||
knobs[n]->set_position (60 + (n*Push2Canvas::inter_button_spacing()), 95);
|
||||
knobs[n]->set_radius (25);
|
||||
}
|
||||
|
||||
@@ -119,14 +128,14 @@ MixLayout::MixLayout (Push2& p, Session& s, Cairo::RefPtr<Cairo::Context> contex
|
||||
|
||||
MixLayout::~MixLayout ()
|
||||
{
|
||||
for (int n = 0; n < 8; ++n) {
|
||||
delete knobs[n];
|
||||
}
|
||||
// Item destructor deletes all children
|
||||
}
|
||||
|
||||
void
|
||||
MixLayout::on_show ()
|
||||
MixLayout::show ()
|
||||
{
|
||||
Item::show ();
|
||||
|
||||
mode_button->set_color (Push2::LED::White);
|
||||
mode_button->set_state (Push2::LED::OneShot24th);
|
||||
p2.write (mode_button->state_msg());
|
||||
@@ -134,116 +143,19 @@ MixLayout::on_show ()
|
||||
switch_bank (bank_start);
|
||||
}
|
||||
|
||||
bool
|
||||
MixLayout::redraw (Cairo::RefPtr<Cairo::Context> context, bool force) const
|
||||
void
|
||||
MixLayout::render (Rect const& area, Cairo::RefPtr<Cairo::Context> context) const
|
||||
{
|
||||
bool children_dirty = false;
|
||||
|
||||
for (int n = 0; n < 8; ++n) {
|
||||
if (knobs[n]->dirty()) {
|
||||
children_dirty = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int n = 0; n < 8; ++n) {
|
||||
if (stripable[n]) {
|
||||
|
||||
string shortname = short_version (stripable[n]->name(), 10);
|
||||
string text;
|
||||
boost::shared_ptr<AutomationControl> ac;
|
||||
ac = stripable[n]->solo_control();
|
||||
if (ac && ac->get_value()) {
|
||||
text += "* ";
|
||||
}
|
||||
boost::shared_ptr<MuteControl> mc;
|
||||
mc = stripable[n]->mute_control ();
|
||||
if (mc) {
|
||||
if (mc->muted_by_self_or_masters()) {
|
||||
text += "! ";
|
||||
} else if (mc->muted_by_others_soloing()) {
|
||||
text += "- "; // it would be nice to use Unicode mute"\uD83D\uDD07 ";
|
||||
}
|
||||
}
|
||||
text += shortname;
|
||||
|
||||
if (text != lower_layout[n]->get_text()) {
|
||||
lower_layout[n]->set_text (text);
|
||||
children_dirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!children_dirty && !_dirty && !force) {
|
||||
return false;
|
||||
}
|
||||
|
||||
set_source_rgb (context, p2.get_color (Push2::DarkBackground));
|
||||
context->rectangle (0, 0, p2.cols, p2.rows);
|
||||
context->rectangle (0, 0, display_width(), display_height());
|
||||
context->fill ();
|
||||
|
||||
set_source_rgb (context, p2.get_color (Push2::ParameterName));
|
||||
|
||||
for (int n = 0; n < 8; ++n) {
|
||||
|
||||
if (upper_layout[n]->get_text().empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Draw highlight box */
|
||||
|
||||
uint32_t color = p2.get_color (Push2::ParameterName);
|
||||
|
||||
if (n == (int) vpot_mode) {
|
||||
set_source_rgb (context, color);
|
||||
context->rectangle (10 + (n*120) - 5, 2, 120, 21);
|
||||
context->fill();
|
||||
set_source_rgb (context, ArdourCanvas::contrasting_text_color (color));
|
||||
} else {
|
||||
set_source_rgb (context, color);
|
||||
}
|
||||
|
||||
context->move_to (10 + (n*120), 2);
|
||||
upper_layout[n]->update_from_cairo_context (context);
|
||||
upper_layout[n]->show_in_cairo_context (context);
|
||||
}
|
||||
|
||||
context->move_to (0, 22.5);
|
||||
context->line_to (p2.cols, 22.5);
|
||||
context->line_to (display_width(), 22.5);
|
||||
context->set_line_width (1.0);
|
||||
context->stroke ();
|
||||
|
||||
for (int n = 0; n < 8; ++n) {
|
||||
knobs[n]->redraw (context, force);
|
||||
}
|
||||
|
||||
for (int n = 0; n < 8; ++n) {
|
||||
|
||||
if (lower_layout[n]->get_text().empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (stripable[n]) {
|
||||
uint32_t color = stripable[n]->presentation_info().color();
|
||||
|
||||
if (stripable[n]->presentation_info().selected()) {
|
||||
set_source_rgb (context, color);
|
||||
context->rectangle (10 + (n*120) - 5, 137, 120, 21);
|
||||
context->fill();
|
||||
set_source_rgb (context, ArdourCanvas::contrasting_text_color (color));
|
||||
} else {
|
||||
set_source_rgb (context, color);
|
||||
}
|
||||
|
||||
context->move_to (10 + (n*120), 140);
|
||||
lower_layout[n]->update_from_cairo_context (context);
|
||||
lower_layout[n]->show_in_cairo_context (context);
|
||||
}
|
||||
}
|
||||
|
||||
_dirty = false;
|
||||
|
||||
return true;
|
||||
render_children (area, context);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -466,14 +378,55 @@ MixLayout::stripable_property_change (PropertyChange const& what_changed, int wh
|
||||
return;
|
||||
}
|
||||
|
||||
/* cancel string, which will cause a redraw on the next update
|
||||
* cycle. The redraw will reflect selected status
|
||||
*/
|
||||
|
||||
lower_layout[which]->set_text (string());
|
||||
if (stripable[which]->presentation_info().selected()) {
|
||||
selection_bg->show ();
|
||||
selection_bg->set_fill_color (stripable[which]->presentation_info().color());
|
||||
selection_bg->set (Rect (10 + (which*Push2Canvas::inter_button_spacing()) - 5, 137,
|
||||
10 + (which*Push2Canvas::inter_button_spacing()) - 5 + Push2Canvas::inter_button_spacing(),
|
||||
137 + 21));
|
||||
lower_text[which]->set_color (ArdourCanvas::contrasting_text_color (selection_bg->fill_color()));
|
||||
} else {
|
||||
selection_bg->hide ();
|
||||
lower_text[which]->set_color (stripable[which]->presentation_info().color());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MixLayout::solo_changed (uint32_t n)
|
||||
{
|
||||
solo_mute_changed (n);
|
||||
}
|
||||
|
||||
void
|
||||
MixLayout::mute_changed (uint32_t n)
|
||||
{
|
||||
solo_mute_changed (n);
|
||||
}
|
||||
|
||||
void
|
||||
MixLayout::solo_mute_changed (uint32_t n)
|
||||
{
|
||||
string shortname = short_version (stripable[n]->name(), 10);
|
||||
string text;
|
||||
boost::shared_ptr<AutomationControl> ac;
|
||||
ac = stripable[n]->solo_control();
|
||||
if (ac && ac->get_value()) {
|
||||
text += "* ";
|
||||
}
|
||||
boost::shared_ptr<MuteControl> mc;
|
||||
mc = stripable[n]->mute_control ();
|
||||
if (mc) {
|
||||
if (mc->muted_by_self_or_masters()) {
|
||||
text += "! ";
|
||||
} else if (mc->muted_by_others_soloing()) {
|
||||
text += "- "; // it would be nice to use Unicode mute"\uD83D\uDD07 ";
|
||||
}
|
||||
}
|
||||
text += shortname;
|
||||
lower_text[n]->set (text);
|
||||
}
|
||||
|
||||
void
|
||||
MixLayout::switch_bank (uint32_t base)
|
||||
{
|
||||
@@ -499,6 +452,11 @@ MixLayout::switch_bank (uint32_t base)
|
||||
/* some missing strips; new bank the same or more empty stripables than the old one, do
|
||||
nothing since we had already reached the end.
|
||||
*/
|
||||
for (int n = 0; n < 8; ++n) {
|
||||
upper_text[n]->hide ();
|
||||
lower_text[n]->hide ();
|
||||
backgrounds[n]->hide ();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -512,13 +470,25 @@ MixLayout::switch_bank (uint32_t base)
|
||||
|
||||
for (int n = 0; n < 8; ++n) {
|
||||
if (!stripable[n]) {
|
||||
upper_text[n]->hide ();
|
||||
lower_text[n]->hide ();
|
||||
backgrounds[n]->hide ();
|
||||
continue;
|
||||
}
|
||||
|
||||
upper_text[n]->show ();
|
||||
lower_text[n]->show ();
|
||||
backgrounds[n]->show ();
|
||||
backgrounds[n]->set_fill_color (stripable[n]->presentation_info().color());
|
||||
|
||||
/* stripable goes away? refill the bank, starting at the same point */
|
||||
|
||||
stripable[n]->DropReferences.connect (stripable_connections, invalidator (*this), boost::bind (&MixLayout::switch_bank, this, bank_start), &p2);
|
||||
stripable[n]->presentation_info().PropertyChanged.connect (stripable_connections, invalidator (*this), boost::bind (&MixLayout::stripable_property_change, this, _1, n), &p2);
|
||||
stripable[n]->solo_control()->Changed.connect (stripable_connections, invalidator (*this), boost::bind (&MixLayout::solo_changed, this, n), &p2);
|
||||
stripable[n]->mute_control()->Changed.connect (stripable_connections, invalidator (*this), boost::bind (&MixLayout::mute_changed, this, n), &p2);
|
||||
|
||||
solo_mute_changed (n);
|
||||
|
||||
Push2::Button* b;
|
||||
|
||||
|
||||
@@ -26,6 +26,11 @@ namespace ARDOUR {
|
||||
class Stripable;
|
||||
}
|
||||
|
||||
namespace ArdourCanvas {
|
||||
class Rectangle;
|
||||
class Text;
|
||||
}
|
||||
|
||||
namespace ArdourSurface {
|
||||
|
||||
class Push2Knob;
|
||||
@@ -33,11 +38,11 @@ class Push2Knob;
|
||||
class MixLayout : public Push2Layout
|
||||
{
|
||||
public:
|
||||
MixLayout (Push2& p, ARDOUR::Session&, Cairo::RefPtr<Cairo::Context>);
|
||||
MixLayout (Push2& p, ARDOUR::Session&);
|
||||
~MixLayout ();
|
||||
|
||||
bool redraw (Cairo::RefPtr<Cairo::Context>, bool force) const;
|
||||
void on_show ();
|
||||
void render (ArdourCanvas::Rect const &, Cairo::RefPtr<Cairo::Context>) const;
|
||||
void show ();
|
||||
|
||||
void button_upper (uint32_t n);
|
||||
void button_lower (uint32_t n);
|
||||
@@ -53,8 +58,10 @@ class MixLayout : public Push2Layout
|
||||
|
||||
private:
|
||||
mutable bool _dirty;
|
||||
Glib::RefPtr<Pango::Layout> upper_layout[8];
|
||||
Glib::RefPtr<Pango::Layout> lower_layout[8];
|
||||
std::vector<ArdourCanvas::Text*> upper_text;
|
||||
std::vector<ArdourCanvas::Text*> lower_text;
|
||||
std::vector<ArdourCanvas::Rectangle*> backgrounds;
|
||||
ArdourCanvas::Rectangle* selection_bg;
|
||||
Push2Knob* knobs[8];
|
||||
|
||||
/* stripables */
|
||||
@@ -80,6 +87,10 @@ class MixLayout : public Push2Layout
|
||||
Push2::Button* mode_button;
|
||||
VPotMode vpot_mode;
|
||||
void show_vpot_mode ();
|
||||
|
||||
void solo_changed (uint32_t n);
|
||||
void mute_changed (uint32_t n);
|
||||
void solo_mute_changed (uint32_t n);
|
||||
};
|
||||
|
||||
} /* namespace */
|
||||
|
||||
@@ -33,7 +33,6 @@
|
||||
#include "ardour/async_midi_port.h"
|
||||
#include "ardour/audioengine.h"
|
||||
#include "ardour/debug.h"
|
||||
#include "ardour/filesystem_paths.h"
|
||||
#include "ardour/midiport_manager.h"
|
||||
#include "ardour/midi_track.h"
|
||||
#include "ardour/midi_port.h"
|
||||
@@ -45,13 +44,14 @@
|
||||
|
||||
#include "canvas/colors.h"
|
||||
|
||||
#include "push2.h"
|
||||
#include "canvas.h"
|
||||
#include "gui.h"
|
||||
#include "layout.h"
|
||||
#include "scale.h"
|
||||
#include "mix.h"
|
||||
#include "track_mix.h"
|
||||
#include "menu.h"
|
||||
#include "mix.h"
|
||||
#include "push2.h"
|
||||
#include "scale.h"
|
||||
#include "track_mix.h"
|
||||
|
||||
#include "pbd/i18n.h"
|
||||
|
||||
@@ -63,10 +63,6 @@ using namespace ArdourSurface;
|
||||
|
||||
#include "pbd/abstract_ui.cc" // instantiate template
|
||||
|
||||
const int Push2::cols = 960;
|
||||
const int Push2::rows = 160;
|
||||
const int Push2::pixels_per_row = 1024;
|
||||
|
||||
#define ABLETON 0x2982
|
||||
#define PUSH2 0x1967
|
||||
|
||||
@@ -127,8 +123,6 @@ Push2::Push2 (ARDOUR::Session& s)
|
||||
: ControlProtocol (s, string (X_("Ableton Push 2")))
|
||||
, AbstractUI<Push2Request> (name())
|
||||
, handle (0)
|
||||
, device_buffer (0)
|
||||
, frame_buffer (Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, cols, rows))
|
||||
, _modifier_state (None)
|
||||
, splash_start (0)
|
||||
, _current_layout (0)
|
||||
@@ -143,7 +137,6 @@ Push2::Push2 (ARDOUR::Session& s)
|
||||
, percussion (false)
|
||||
, _pressure_mode (AfterTouch)
|
||||
{
|
||||
context = Cairo::Context::create (frame_buffer);
|
||||
|
||||
build_maps ();
|
||||
build_color_map ();
|
||||
@@ -234,15 +227,17 @@ Push2::open ()
|
||||
return -1;
|
||||
}
|
||||
|
||||
device_frame_buffer = new uint16_t[rows*pixels_per_row];
|
||||
|
||||
memset (device_frame_buffer, 0, sizeof (uint16_t) * rows * pixels_per_row);
|
||||
|
||||
frame_header[0] = 0xef;
|
||||
frame_header[1] = 0xcd;
|
||||
frame_header[2] = 0xab;
|
||||
frame_header[3] = 0x89;
|
||||
memset (&frame_header[4], 0, 12);
|
||||
try {
|
||||
_canvas = new Push2Canvas (*this, 160, 960);
|
||||
mix_layout = new MixLayout (*this, *session);
|
||||
scale_layout = new ScaleLayout (*this, *session);
|
||||
track_mix_layout = new TrackMixLayout (*this, *session);
|
||||
} catch (...) {
|
||||
error << _("Cannot construct Canvas for display") << endmsg;
|
||||
libusb_release_interface (handle, 0x00);
|
||||
libusb_close (handle);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* setup ports */
|
||||
|
||||
@@ -284,9 +279,7 @@ Push2::open ()
|
||||
|
||||
connect_to_parser ();
|
||||
|
||||
mix_layout = new MixLayout (*this, *session, context);
|
||||
scale_layout = new ScaleLayout (*this, *session, context);
|
||||
track_mix_layout = new TrackMixLayout (*this, *session, context);
|
||||
_canvas->splash ();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -321,7 +314,6 @@ Push2::close ()
|
||||
_input_port = 0;
|
||||
_output_port = 0;
|
||||
|
||||
vblank_connection.disconnect ();
|
||||
periodic_connection.disconnect ();
|
||||
session_connections.drop_connections ();
|
||||
|
||||
@@ -338,9 +330,6 @@ Push2::close ()
|
||||
handle = 0;
|
||||
}
|
||||
|
||||
delete [] device_frame_buffer;
|
||||
device_frame_buffer = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -460,63 +449,8 @@ Push2::stop ()
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** render host-side frame buffer (a Cairo ImageSurface) to the current
|
||||
* device-side frame buffer. The device frame buffer will be pushed to the
|
||||
* device on the next call to vblank()
|
||||
*/
|
||||
|
||||
int
|
||||
Push2::blit_to_device_frame_buffer ()
|
||||
{
|
||||
/* ensure that all drawing has been done before we fetch pixel data */
|
||||
|
||||
frame_buffer->flush ();
|
||||
|
||||
const int stride = 3840; /* bytes per row for Cairo::FORMAT_ARGB32 */
|
||||
const uint8_t* data = frame_buffer->get_data ();
|
||||
|
||||
/* fill frame buffer (320kB) */
|
||||
|
||||
uint16_t* fb = (uint16_t*) device_frame_buffer;
|
||||
|
||||
for (int row = 0; row < rows; ++row) {
|
||||
|
||||
const uint8_t* dp = data + row * stride;
|
||||
|
||||
for (int col = 0; col < cols; ++col) {
|
||||
|
||||
/* fetch r, g, b (range 0..255). Ignore alpha */
|
||||
|
||||
const int r = (*((const uint32_t*)dp) >> 16) & 0xff;
|
||||
const int g = (*((const uint32_t*)dp) >> 8) & 0xff;
|
||||
const int b = *((const uint32_t*)dp) & 0xff;
|
||||
|
||||
/* convert to 5 bits, 6 bits, 5 bits, respectively */
|
||||
/* generate 16 bit BGB565 value */
|
||||
|
||||
*fb++ = (r >> 3) | ((g & 0xfc) << 3) | ((b & 0xf8) << 8);
|
||||
|
||||
/* the push2 docs state that we should xor the pixel
|
||||
* data. Doing so doesn't work correctly, and not doing
|
||||
* so seems to work fine (colors roughly match intended
|
||||
* values).
|
||||
*/
|
||||
|
||||
dp += 4;
|
||||
}
|
||||
|
||||
/* skip 128 bytes to next line. This is filler, used to avoid line borders occuring in the middle of 512
|
||||
byte USB buffers
|
||||
*/
|
||||
|
||||
fb += 64; /* 128 bytes = 64 int16_t */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool
|
||||
Push2::redraw ()
|
||||
Push2::vblank ()
|
||||
{
|
||||
if (splash_start) {
|
||||
|
||||
@@ -524,50 +458,15 @@ Push2::redraw ()
|
||||
|
||||
if (get_microseconds() - splash_start > 3000000) {
|
||||
splash_start = 0;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
} else {
|
||||
|
||||
_canvas->vblank();
|
||||
|
||||
}
|
||||
|
||||
Glib::Threads::Mutex::Lock lm (layout_lock, Glib::Threads::TRY_LOCK);
|
||||
|
||||
if (!lm.locked()) {
|
||||
/* can't get layout, no re-render needed */
|
||||
return false;
|
||||
}
|
||||
|
||||
bool render_needed = false;
|
||||
|
||||
if (drawn_layout != _current_layout) {
|
||||
render_needed = true;
|
||||
}
|
||||
|
||||
bool dirty = _current_layout->redraw (context, render_needed);
|
||||
drawn_layout = _current_layout;
|
||||
|
||||
return dirty || render_needed;
|
||||
}
|
||||
|
||||
bool
|
||||
Push2::vblank ()
|
||||
{
|
||||
int transferred = 0;
|
||||
const int timeout_msecs = 1000;
|
||||
int err;
|
||||
|
||||
if ((err = libusb_bulk_transfer (handle, 0x01, frame_header, sizeof (frame_header), &transferred, timeout_msecs))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (redraw()) {
|
||||
/* things changed */
|
||||
blit_to_device_frame_buffer ();
|
||||
}
|
||||
|
||||
if ((err = libusb_bulk_transfer (handle, 0x01, (uint8_t*) device_frame_buffer , 2 * rows * pixels_per_row, &transferred, timeout_msecs))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1210,60 +1109,6 @@ Push2::end_shift ()
|
||||
void
|
||||
Push2::splash ()
|
||||
{
|
||||
std::string splash_file;
|
||||
|
||||
Searchpath rc (ARDOUR::ardour_data_search_path());
|
||||
rc.add_subdirectory_to_paths ("resources");
|
||||
|
||||
if (!find_file (rc, PROGRAM_NAME "-splash.png", splash_file)) {
|
||||
cerr << "Cannot find splash screen image file\n";
|
||||
throw failed_constructor();
|
||||
}
|
||||
|
||||
Cairo::RefPtr<Cairo::ImageSurface> img = Cairo::ImageSurface::create_from_png (splash_file);
|
||||
|
||||
double x_ratio = (double) img->get_width() / (cols - 20);
|
||||
double y_ratio = (double) img->get_height() / (rows - 20);
|
||||
double scale = min (x_ratio, y_ratio);
|
||||
|
||||
/* background */
|
||||
|
||||
context->set_source_rgb (0.764, 0.882, 0.882);
|
||||
context->paint ();
|
||||
|
||||
/* image */
|
||||
|
||||
context->save ();
|
||||
context->translate (5, 5);
|
||||
context->scale (scale, scale);
|
||||
context->set_source (img, 0, 0);
|
||||
context->paint ();
|
||||
context->restore ();
|
||||
|
||||
/* text */
|
||||
|
||||
Glib::RefPtr<Pango::Layout> some_text = Pango::Layout::create (context);
|
||||
|
||||
Pango::FontDescription fd ("Sans 38");
|
||||
some_text->set_font_description (fd);
|
||||
some_text->set_text (string_compose ("%1 %2", PROGRAM_NAME, VERSIONSTRING));
|
||||
|
||||
context->move_to (200, 10);
|
||||
context->set_source_rgb (0, 0, 0);
|
||||
some_text->update_from_cairo_context (context);
|
||||
some_text->show_in_cairo_context (context);
|
||||
|
||||
Pango::FontDescription fd2 ("Sans Italic 18");
|
||||
some_text->set_font_description (fd2);
|
||||
some_text->set_text (_("Ableton Push 2 Support"));
|
||||
|
||||
context->move_to (200, 80);
|
||||
context->set_source_rgb (0, 0, 0);
|
||||
some_text->update_from_cairo_context (context);
|
||||
some_text->show_in_cairo_context (context);
|
||||
|
||||
splash_start = get_microseconds ();
|
||||
blit_to_device_frame_buffer ();
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -1730,7 +1575,7 @@ Push2::fill_color_table ()
|
||||
|
||||
}
|
||||
|
||||
uint32_t
|
||||
ArdourCanvas::Color
|
||||
Push2::get_color (ColorName name)
|
||||
{
|
||||
Colors::iterator c = colors.find (name);
|
||||
@@ -1745,14 +1590,15 @@ void
|
||||
Push2::set_current_layout (Push2Layout* layout)
|
||||
{
|
||||
if (_current_layout) {
|
||||
_current_layout->on_hide ();
|
||||
_current_layout->hide ();
|
||||
_canvas->root()->remove (_current_layout);
|
||||
}
|
||||
|
||||
_current_layout = layout;
|
||||
drawn_layout = 0;
|
||||
|
||||
if (_current_layout) {
|
||||
_current_layout->on_show ();
|
||||
_current_layout->show ();
|
||||
_canvas->root()->add (_current_layout);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,8 +27,6 @@
|
||||
|
||||
#include <libusb.h>
|
||||
|
||||
#include <cairomm/refptr.h>
|
||||
|
||||
#define ABSTRACT_UI_EXPORTS
|
||||
#include "pbd/abstract_ui.h"
|
||||
|
||||
@@ -42,11 +40,6 @@
|
||||
#include "midi_byte_array.h"
|
||||
#include "mode.h"
|
||||
|
||||
namespace Cairo {
|
||||
class ImageSurface;
|
||||
class Context;
|
||||
}
|
||||
|
||||
namespace Pango {
|
||||
class Layout;
|
||||
}
|
||||
@@ -74,6 +67,7 @@ public:
|
||||
class P2GUI;
|
||||
class Push2Menu;
|
||||
class Push2Layout;
|
||||
class Push2Canvas;
|
||||
|
||||
class Push2 : public ARDOUR::ControlProtocol
|
||||
, public AbstractUI<Push2Request>
|
||||
@@ -341,6 +335,7 @@ class Push2 : public ARDOUR::ControlProtocol
|
||||
bool in_key() const { return _in_key; }
|
||||
|
||||
Push2Layout* current_layout() const;
|
||||
Push2Canvas* canvas() const { return _canvas; }
|
||||
|
||||
enum ModifierState {
|
||||
None = 0,
|
||||
@@ -357,33 +352,20 @@ class Push2 : public ARDOUR::ControlProtocol
|
||||
uint8_t get_color_index (uint32_t rgb);
|
||||
uint32_t get_color (ColorName);
|
||||
|
||||
static const int cols;
|
||||
static const int rows;
|
||||
|
||||
PressureMode pressure_mode () const { return _pressure_mode; }
|
||||
void set_pressure_mode (PressureMode);
|
||||
PBD::Signal1<void,PressureMode> PressureModeChange;
|
||||
|
||||
|
||||
libusb_device_handle* usb_handle() const { return handle; }
|
||||
|
||||
private:
|
||||
libusb_device_handle *handle;
|
||||
uint8_t frame_header[16];
|
||||
uint16_t* device_frame_buffer;
|
||||
int device_buffer;
|
||||
Cairo::RefPtr<Cairo::ImageSurface> frame_buffer;
|
||||
sigc::connection vblank_connection;
|
||||
sigc::connection periodic_connection;
|
||||
|
||||
ModifierState _modifier_state;
|
||||
|
||||
static const int pixels_per_row;
|
||||
|
||||
void do_request (Push2Request*);
|
||||
int stop ();
|
||||
int open ();
|
||||
int close ();
|
||||
bool redraw ();
|
||||
int blit_to_device_frame_buffer ();
|
||||
bool vblank ();
|
||||
|
||||
void relax () {}
|
||||
|
||||
@@ -431,6 +413,8 @@ class Push2 : public ARDOUR::ControlProtocol
|
||||
void handle_midi_sysex (MIDI::Parser&, MIDI::byte *, size_t count);
|
||||
|
||||
bool midi_input_handler (Glib::IOCondition ioc, MIDI::Port* port);
|
||||
|
||||
sigc::connection periodic_connection;
|
||||
bool periodic ();
|
||||
|
||||
void thread_init ();
|
||||
@@ -517,13 +501,16 @@ class Push2 : public ARDOUR::ControlProtocol
|
||||
boost::shared_ptr<ARDOUR::Stripable> master;
|
||||
boost::shared_ptr<ARDOUR::Stripable> monitor;
|
||||
|
||||
/* Cairo graphics context */
|
||||
|
||||
Cairo::RefPtr<Cairo::Context> context;
|
||||
sigc::connection vblank_connection;
|
||||
bool vblank ();
|
||||
|
||||
void splash ();
|
||||
ARDOUR::microseconds_t splash_start;
|
||||
|
||||
/* the canvas */
|
||||
|
||||
Push2Canvas* _canvas;
|
||||
|
||||
/* Layouts */
|
||||
|
||||
mutable Glib::Threads::Mutex layout_lock;
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <cairomm/region.h>
|
||||
#include <pangomm/layout.h>
|
||||
|
||||
#include "pbd/compose.h"
|
||||
@@ -51,37 +52,26 @@ using namespace std;
|
||||
using namespace PBD;
|
||||
using namespace Glib;
|
||||
using namespace ArdourSurface;
|
||||
using namespace ArdourCanvas;
|
||||
|
||||
ScaleLayout::ScaleLayout (Push2& p, Session& s, Cairo::RefPtr<Cairo::Context> context)
|
||||
ScaleLayout::ScaleLayout (Push2& p, Session& s)
|
||||
: Push2Layout (p, s)
|
||||
{
|
||||
build_scale_menu (context);
|
||||
build_scale_menu ();
|
||||
}
|
||||
|
||||
ScaleLayout::~ScaleLayout ()
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
ScaleLayout::redraw (Cairo::RefPtr<Cairo::Context> context, bool force) const
|
||||
void
|
||||
ScaleLayout::render (Rect const& area, Cairo::RefPtr<Cairo::Context> context) const
|
||||
{
|
||||
bool draw = false;
|
||||
|
||||
if (scale_menu->dirty()) {
|
||||
draw = true;
|
||||
}
|
||||
|
||||
if (!draw) {
|
||||
return false;
|
||||
}
|
||||
|
||||
context->set_source_rgb (0.764, 0.882, 0.882);
|
||||
context->rectangle (0, 0, 960, 160);
|
||||
context->fill ();
|
||||
|
||||
scale_menu->redraw (context, force);
|
||||
|
||||
return true;
|
||||
scale_menu->render (area, context);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -109,11 +99,11 @@ ScaleLayout::strip_vpot_touch (int, bool)
|
||||
}
|
||||
|
||||
void
|
||||
ScaleLayout::build_scale_menu (Cairo::RefPtr<Cairo::Context> context)
|
||||
ScaleLayout::build_scale_menu ()
|
||||
{
|
||||
vector<string> v;
|
||||
|
||||
scale_menu = new Push2Menu (context);
|
||||
scale_menu = new Push2Menu (this);
|
||||
|
||||
v.push_back ("Dorian");
|
||||
v.push_back ("IonianMajor");
|
||||
|
||||
@@ -30,10 +30,10 @@ namespace ArdourSurface {
|
||||
class ScaleLayout : public Push2Layout
|
||||
{
|
||||
public:
|
||||
ScaleLayout (Push2& p, ARDOUR::Session&, Cairo::RefPtr<Cairo::Context>);
|
||||
ScaleLayout (Push2& p, ARDOUR::Session&);
|
||||
~ScaleLayout ();
|
||||
|
||||
bool redraw (Cairo::RefPtr<Cairo::Context>, bool force) const;
|
||||
void render (ArdourCanvas::Rect const &, Cairo::RefPtr<Cairo::Context>) const;
|
||||
|
||||
void button_upper (uint32_t n);
|
||||
void button_lower (uint32_t n);
|
||||
@@ -43,7 +43,7 @@ class ScaleLayout : public Push2Layout
|
||||
|
||||
private:
|
||||
Push2Menu* scale_menu;
|
||||
void build_scale_menu (Cairo::RefPtr<Cairo::Context>);
|
||||
void build_scale_menu ();
|
||||
};
|
||||
|
||||
} /* namespace */
|
||||
|
||||
98
libs/surfaces/push2/splash.cc
Normal file
98
libs/surfaces/push2/splash.cc
Normal file
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
Copyright (C) 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.
|
||||
*/
|
||||
|
||||
#include <pangomm/layout.h>
|
||||
|
||||
#include "pbd/compose.h"
|
||||
#include "pbd/failed_constructor.h"
|
||||
#include "pbd/file_utils.h"
|
||||
#include "pbd/i18n.h"
|
||||
#include "pbd/search_path.h"
|
||||
|
||||
#include "ardour/filesystem_paths.h"
|
||||
|
||||
#include "splash.h"
|
||||
|
||||
using namespace ARDOUR;
|
||||
using namespace PBD;
|
||||
using namespace std;
|
||||
using namespace ArdourSurface;
|
||||
using namespace ArdourCanvas;
|
||||
|
||||
SplashLayout::SplashLayout (Push2& p, Session& s)
|
||||
: Push2Layout (p, s)
|
||||
{
|
||||
std::string splash_file;
|
||||
|
||||
Searchpath rc (ARDOUR::ardour_data_search_path());
|
||||
rc.add_subdirectory_to_paths ("resources");
|
||||
|
||||
if (!find_file (rc, PROGRAM_NAME "-splash.png", splash_file)) {
|
||||
cerr << "Cannot find splash screen image file\n";
|
||||
throw failed_constructor();
|
||||
}
|
||||
|
||||
img = Cairo::ImageSurface::create_from_png (splash_file);
|
||||
}
|
||||
|
||||
void
|
||||
SplashLayout::render (Rect const& area, Cairo::RefPtr<Cairo::Context> context) const
|
||||
{
|
||||
int rows = display_height ();
|
||||
int cols = display_width ();
|
||||
|
||||
double x_ratio = (double) img->get_width() / (cols - 20);
|
||||
double y_ratio = (double) img->get_height() / (rows - 20);
|
||||
double scale = min (x_ratio, y_ratio);
|
||||
|
||||
/* background */
|
||||
|
||||
context->set_source_rgb (0.764, 0.882, 0.882);
|
||||
context->paint ();
|
||||
|
||||
/* image */
|
||||
|
||||
context->save ();
|
||||
context->translate (5, 5);
|
||||
context->scale (scale, scale);
|
||||
context->set_source (img, 0, 0);
|
||||
context->paint ();
|
||||
context->restore ();
|
||||
|
||||
/* text */
|
||||
|
||||
Glib::RefPtr<Pango::Layout> some_text = Pango::Layout::create (context);
|
||||
|
||||
Pango::FontDescription fd ("Sans 38");
|
||||
some_text->set_font_description (fd);
|
||||
some_text->set_text (string_compose ("%1 %2", PROGRAM_NAME, VERSIONSTRING));
|
||||
|
||||
context->move_to (200, 10);
|
||||
context->set_source_rgb (0, 0, 0);
|
||||
some_text->update_from_cairo_context (context);
|
||||
some_text->show_in_cairo_context (context);
|
||||
|
||||
Pango::FontDescription fd2 ("Sans Italic 18");
|
||||
some_text->set_font_description (fd2);
|
||||
some_text->set_text (_("Ableton Push 2 Support"));
|
||||
|
||||
context->move_to (200, 80);
|
||||
context->set_source_rgb (0, 0, 0);
|
||||
some_text->update_from_cairo_context (context);
|
||||
some_text->show_in_cairo_context (context);
|
||||
}
|
||||
47
libs/surfaces/push2/splash.h
Normal file
47
libs/surfaces/push2/splash.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
Copyright (C) 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.
|
||||
*/
|
||||
|
||||
#ifndef __ardour_push2_splash_h__
|
||||
#define __ardour_push2_splash_h__
|
||||
|
||||
#include <cairomm/surface.h>
|
||||
|
||||
#include "layout.h"
|
||||
#include "push2.h"
|
||||
|
||||
namespace ARDOUR {
|
||||
class Stripable;
|
||||
}
|
||||
|
||||
namespace ArdourSurface {
|
||||
|
||||
class SplashLayout : public Push2Layout
|
||||
{
|
||||
public:
|
||||
SplashLayout (Push2& p, ARDOUR::Session&);
|
||||
~SplashLayout ();
|
||||
|
||||
void render (ArdourCanvas::Rect const &, Cairo::RefPtr<Cairo::Context>) const;
|
||||
|
||||
private:
|
||||
Cairo::RefPtr<Cairo::ImageSurface> img;
|
||||
};
|
||||
|
||||
} /* namespace */
|
||||
|
||||
#endif /* __ardour_push2_splash_h__ */
|
||||
@@ -16,6 +16,7 @@
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <cairomm/region.h>
|
||||
#include <pangomm/layout.h>
|
||||
|
||||
#include "pbd/compose.h"
|
||||
@@ -23,6 +24,7 @@
|
||||
#include "pbd/debug.h"
|
||||
#include "pbd/failed_constructor.h"
|
||||
#include "pbd/file_utils.h"
|
||||
#include "pbd/i18n.h"
|
||||
#include "pbd/search_path.h"
|
||||
#include "pbd/enumwriter.h"
|
||||
|
||||
@@ -43,70 +45,77 @@
|
||||
#include "gtkmm2ext/gui_thread.h"
|
||||
#include "gtkmm2ext/rgb_macros.h"
|
||||
|
||||
#include "canvas.h"
|
||||
#include "knob.h"
|
||||
#include "menu.h"
|
||||
#include "push2.h"
|
||||
#include "track_mix.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include "pbd/i18n.h"
|
||||
|
||||
using namespace ARDOUR;
|
||||
using namespace std;
|
||||
using namespace PBD;
|
||||
using namespace Glib;
|
||||
using namespace ArdourSurface;
|
||||
using namespace ArdourCanvas;
|
||||
|
||||
TrackMixLayout::TrackMixLayout (Push2& p, Session& s, Cairo::RefPtr<Cairo::Context> context)
|
||||
TrackMixLayout::TrackMixLayout (Push2& p, Session& s)
|
||||
: Push2Layout (p, s)
|
||||
, _dirty (true)
|
||||
{
|
||||
Pango::FontDescription fd2 ("Sans 10");
|
||||
|
||||
for (int n = 0; n < 8; ++n) {
|
||||
upper_layout[n] = Pango::Layout::create (context);
|
||||
upper_layout[n]->set_font_description (fd2);
|
||||
Text* t = new Text (this);
|
||||
t->set_font_description (fd2);
|
||||
t->set_color (p2.get_color (Push2::ParameterName));
|
||||
t->set_position ( Duple (10 + (n*Push2Canvas::inter_button_spacing()), 2));
|
||||
|
||||
lower_layout[n] = Pango::Layout::create (context);
|
||||
lower_layout[n]->set_font_description (fd2);
|
||||
upper_text.push_back (t);
|
||||
|
||||
t = new Text (this);
|
||||
t->set_font_description (fd2);
|
||||
t->set_color (p2.get_color (Push2::ParameterName));
|
||||
t->set_position (Duple (10 + (n*Push2Canvas::inter_button_spacing()), 140));
|
||||
|
||||
lower_text.push_back (t);
|
||||
|
||||
switch (n) {
|
||||
case 0:
|
||||
upper_layout[n]->set_text (_("TRACK VOLUME"));
|
||||
lower_layout[n]->set_text (_("MUTE"));
|
||||
upper_text[n]->set (_("TRACK VOLUME"));
|
||||
lower_text[n]->set (_("MUTE"));
|
||||
break;
|
||||
case 1:
|
||||
upper_layout[n]->set_text (_("TRACK PAN"));
|
||||
lower_layout[n]->set_text (_("SOLO"));
|
||||
upper_text[n]->set (_("TRACK PAN"));
|
||||
lower_text[n]->set (_("SOLO"));
|
||||
break;
|
||||
case 2:
|
||||
upper_layout[n]->set_text (_("TRACK WIDTH"));
|
||||
lower_layout[n]->set_text (_("REC-ENABLE"));
|
||||
upper_text[n]->set (_("TRACK WIDTH"));
|
||||
lower_text[n]->set (_("REC-ENABLE"));
|
||||
break;
|
||||
case 3:
|
||||
upper_layout[n]->set_text (_("TRACK TRIM"));
|
||||
lower_layout[n]->set_text (_("IN"));
|
||||
upper_text[n]->set (_("TRACK TRIM"));
|
||||
lower_text[n]->set (_("IN"));
|
||||
break;
|
||||
case 4:
|
||||
upper_layout[n]->set_text (_(""));
|
||||
lower_layout[n]->set_text (_("DISK"));
|
||||
upper_text[n]->set (_(""));
|
||||
lower_text[n]->set (_("DISK"));
|
||||
break;
|
||||
case 5:
|
||||
upper_layout[n]->set_text (_(""));
|
||||
lower_layout[n]->set_text (_("SOLO ISO"));
|
||||
upper_text[n]->set (_(""));
|
||||
lower_text[n]->set (_("SOLO ISO"));
|
||||
break;
|
||||
case 6:
|
||||
upper_layout[n]->set_text (_(""));
|
||||
lower_layout[n]->set_text (_("SOLO LOCK"));
|
||||
upper_text[n]->set (_(""));
|
||||
lower_text[n]->set (_("SOLO LOCK"));
|
||||
break;
|
||||
case 7:
|
||||
upper_layout[n]->set_text (_(""));
|
||||
lower_layout[n]->set_text (_(""));
|
||||
upper_text[n]->set (_(""));
|
||||
lower_text[n]->set (_(""));
|
||||
break;
|
||||
}
|
||||
|
||||
knobs[n] = new Push2Knob (p2, context);
|
||||
knobs[n]->set_position (60 + (120*n), 95);
|
||||
knobs[n] = new Push2Knob (p2, this);
|
||||
knobs[n]->set_position (60 + (Push2Canvas::inter_button_spacing()*n), 95);
|
||||
knobs[n]->set_radius (25);
|
||||
}
|
||||
|
||||
@@ -129,62 +138,24 @@ TrackMixLayout::selection_changed ()
|
||||
}
|
||||
}
|
||||
void
|
||||
TrackMixLayout::on_show ()
|
||||
TrackMixLayout::show ()
|
||||
{
|
||||
selection_changed ();
|
||||
}
|
||||
|
||||
bool
|
||||
TrackMixLayout::redraw (Cairo::RefPtr<Cairo::Context> context, bool force) const
|
||||
void
|
||||
TrackMixLayout::render (ArdourCanvas::Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
|
||||
{
|
||||
bool children_dirty = false;
|
||||
|
||||
for (int n = 0; n < 8; ++n) {
|
||||
if (knobs[n]->dirty()) {
|
||||
children_dirty = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!children_dirty) {
|
||||
return false;
|
||||
}
|
||||
|
||||
set_source_rgb (context, p2.get_color (Push2::DarkBackground));
|
||||
context->rectangle (0, 0, p2.cols, p2.rows);
|
||||
context->rectangle (0, 0, display_width(), display_height());
|
||||
context->fill ();
|
||||
|
||||
for (int n = 0; n < 8; ++n) {
|
||||
|
||||
if (!upper_layout[n]->get_text().empty()) {
|
||||
|
||||
/* Draw highlight box */
|
||||
|
||||
uint32_t color = p2.get_color (Push2::ParameterName);
|
||||
set_source_rgb (context, color);
|
||||
|
||||
context->move_to (10 + (n*120), 2);
|
||||
upper_layout[n]->update_from_cairo_context (context);
|
||||
upper_layout[n]->show_in_cairo_context (context);
|
||||
}
|
||||
|
||||
if (!lower_layout[n]->get_text().empty()) {
|
||||
context->move_to (10 + (n*120), 140);
|
||||
lower_layout[n]->update_from_cairo_context (context);
|
||||
lower_layout[n]->show_in_cairo_context (context);
|
||||
}
|
||||
}
|
||||
|
||||
context->move_to (0, 22.5);
|
||||
context->line_to (p2.cols, 22.5);
|
||||
context->line_to (display_width(), 22.5);
|
||||
context->set_line_width (1.0);
|
||||
context->stroke ();
|
||||
|
||||
for (int n = 0; n < 8; ++n) {
|
||||
knobs[n]->redraw (context, force);
|
||||
}
|
||||
|
||||
return true;
|
||||
Container::render_children (area, context);
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -27,6 +27,10 @@ namespace ARDOUR {
|
||||
class Stripable;
|
||||
}
|
||||
|
||||
namespace ArdourCanvas {
|
||||
class Text;
|
||||
}
|
||||
|
||||
namespace ArdourSurface {
|
||||
|
||||
class Push2Knob;
|
||||
@@ -34,13 +38,14 @@ class Push2Knob;
|
||||
class TrackMixLayout : public Push2Layout
|
||||
{
|
||||
public:
|
||||
TrackMixLayout (Push2& p, ARDOUR::Session&, Cairo::RefPtr<Cairo::Context>);
|
||||
TrackMixLayout (Push2& p, ARDOUR::Session&);
|
||||
~TrackMixLayout ();
|
||||
|
||||
void set_stripable (boost::shared_ptr<ARDOUR::Stripable>);
|
||||
|
||||
bool redraw (Cairo::RefPtr<Cairo::Context>, bool force) const;
|
||||
void on_show ();
|
||||
void render (ArdourCanvas::Rect const &, Cairo::RefPtr<Cairo::Context>) const;
|
||||
|
||||
void show ();
|
||||
|
||||
void button_upper (uint32_t n);
|
||||
void button_lower (uint32_t n);
|
||||
@@ -53,8 +58,8 @@ class TrackMixLayout : public Push2Layout
|
||||
PBD::ScopedConnectionList stripable_connections;
|
||||
bool _dirty;
|
||||
|
||||
Glib::RefPtr<Pango::Layout> upper_layout[8];
|
||||
Glib::RefPtr<Pango::Layout> lower_layout[8];
|
||||
std::vector<ArdourCanvas::Text*> upper_text;
|
||||
std::vector<ArdourCanvas::Text*> lower_text;
|
||||
|
||||
Push2Knob* knobs[8];
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ def build(bld):
|
||||
obj.source = '''
|
||||
push2.cc
|
||||
buttons.cc
|
||||
canvas.cc
|
||||
interface.cc
|
||||
midi_byte_array.cc
|
||||
leds.cc
|
||||
@@ -32,6 +33,7 @@ def build(bld):
|
||||
menu.cc
|
||||
mix.cc
|
||||
scale.cc
|
||||
splash.cc
|
||||
track_mix.cc
|
||||
utils.cc
|
||||
'''
|
||||
|
||||
Reference in New Issue
Block a user