first compiling and theoretically correct version of Push2 canvas display.

Not tested with device at this point
This commit is contained in:
Paul Davis
2016-09-16 08:39:28 -05:00
parent 2810e5619a
commit 7c9f3acc60
19 changed files with 1204 additions and 507 deletions

View 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;
}

View 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
View 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 ();
}

View 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__ */

View File

@@ -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();
}

View File

@@ -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) {}

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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;

View File

@@ -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 */

View File

@@ -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);
}
}

View File

@@ -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;

View File

@@ -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");

View File

@@ -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 */

View 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);
}

View 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__ */

View File

@@ -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

View File

@@ -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];

View File

@@ -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
'''